diff --git a/.gitignore b/.gitignore
index 3d6e38e478b05257200c1eaac5c9f7ab7b6b619c..c8ecab12314217e4d24892d24af7183de91ba2cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@
 /src/Makefile.in
 /src/tests/Makefile
 /src/tests/Makefile.in
+/src/zscanner/Makefile
+/src/zscanner/Makefile.in
 /samples/Makefile
 /samples/Makefile.in
 /doc/Makefile
@@ -41,21 +43,21 @@
 /samples/knot.sample.conf
 /src/stamp-*
 /doc/html/
-# Binaries
-/src/tests/unittests
-/src/zscanner-tool
-/src/knotc
-/src/knotd
-/src/knot-zcompile
+/INSTALL
 /m4/libtool.m4
 /m4/ltoptions.m4
 /m4/ltsugar.m4
 /m4/ltversion.m4
 /m4/lt~obsolete.m4
-/INSTALL
+# Binaries
+/src/tests/unittests
+/src/zscanner/zscanner-tool
+/src/knotc
+/src/knotd
 /src/kdig
 /src/khost
 /src/knsupdate
+/src/knsec3hash
 /src/knot/conf/libknotd_la-cf-lex.c
 /src/knot/conf/libknotd_la-cf-parse.c
 /src/knot/conf/libknotd_la-cf-parse.h
@@ -65,8 +67,9 @@
 /src/tests/libknot/raw_data_queries.rc
 /src/tests/libknot/realdata/
 /src/tests/sample_conf.rc
+src/zscanner/descriptor.h
+src/zscanner/descriptor.c
 src/zscanner/test/cases/06-0_INCLUDE.in
 src/zscanner/test/cases/06-3_INCLUDE.in
 src/zscanner/test/cases/06-4_INCLUDE.in
 src/zscanner/test/run_tests.sh
-/knot-*/
diff --git a/CodingStyle b/CodingStyle
deleted file mode 100644
index 121cf459981296c330ca7e7576725f9db3460296..0000000000000000000000000000000000000000
--- a/CodingStyle
+++ /dev/null
@@ -1,77 +0,0 @@
-Coding style
-============
-* Indentation: TAB (8 spaces width)
-* Braces: K&R, 1TBS
-* Max line width: 80 chars
-* Pointer asterisk attached to the name of the variable
-* Own structures/types: _t suffix (f.e. nameserver_t)
-* Header guard format: _KNOTD_HEADER_H_
-* Spaces around binary operators
-* Space between keyword and bracket (f.e. "if (predicate)")
-* No space between variable and typecast (f.e. "return (int)val;")
-
-To sum it up, Linux KNF is used, see [1].
-
-[1] Linux Coding Style:
-    http://kerneltrap.org/files/Jeremy/CodingStyle.txt
-
-AStyle command format
-=====================
-astyle --style=1tbs -t8 -w -p -H -U -j --align-pointer=name
-
-Doxygen
-=======
-* Format: Qt-style
-  * "\brief", not "@brief"
-  * "/*!", not "/**"
-* Order of sections
-  * brief description
-  * long description
-  * notes
-  * warnings
-  * parameters
-  * return values
-  * todos
-* Always use \brief (no autobrief)
-* Indent text (using spaces only) in multiple-line sections
-* In multi-line comments, opening line (/*!) should be empty
-* One empty line between two consecutive sections
-* Struct and union members documented on the same line if the comment fits
-* Use \retval (or more of them) instead of \return
-  if the function returns some distinct values
-  (such as 0 for no error, -1 for something else, etc.)
-
-Example
-=======
-/*!
- * \brief Some structure.
- *
- * Longer description.
- */
- struct some_struct {
-    /*!
-     * \brief This comment does not fit on the same line as the member
-     *        as it is rather large.
-     */
-    int fd;
-    int flags; /*!< Flags. */
- };
-
-/*!
- * \brief Brief description of some function.
- *
- * Longer description.
- *
- * \note This function is deprecated.
- *
- * \warning Do not use this function!
- *
- * \param param1 Some parameter.
- * \param param2 Other parameter. This one has rather large comment,
- *               so its next line is indented for better readability.
- *
- * \retval 0 on success.
- * \retval -1 if some error occured.
- *
- * \todo Remove (deprecated).
- */
diff --git a/Doxyfile b/Doxyfile
index e7ec75dd9a47005855ff333b18bb4fa0e942ed21..40e7644aa23fc0954b8a13ab7087698907965422 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,14 +1,14 @@
-# Doxyfile 1.7.1
+# Doxyfile 1.8.3.1
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a hash (#) is considered a comment and will be ignored.
 # The format is:
 #       TAG = value [value, ...]
 # For lists items can also be appended using:
 #       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# Values that contain spaces should be placed between quotes (" ").
 
 #---------------------------------------------------------------------------
 # Project related configuration options
@@ -22,8 +22,9 @@
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
 
 PROJECT_NAME           = Knot
 
@@ -31,7 +32,20 @@ PROJECT_NAME           = Knot
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.1.0
+PROJECT_NUMBER         = 1.4
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
@@ -57,7 +71,7 @@ CREATE_SUBDIRS         = NO
 # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
 # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
 # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
 # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
@@ -122,10 +136,11 @@ FULL_PATH_NAMES        = YES
 # only done if one of the specified strings matches the left-hand part of
 # the path. The tag can be used to show relative paths in the file list.
 # If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
 
-STRIP_FROM_PATH        =  \
-                         src/
+STRIP_FROM_PATH        = src/
 
 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
 # the path mentioned in the documentation of a class, which tells
@@ -137,7 +152,7 @@ STRIP_FROM_PATH        =  \
 STRIP_FROM_INC_PATH    =
 
 # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
+# (but less readable) file names. This can be useful if your file system
 # doesn't support long names like on DOS, Mac, or CD-ROM.
 
 SHORT_NAMES            = NO
@@ -192,6 +207,13 @@ TAB_SIZE               = 8
 
 ALIASES                =
 
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
 # sources only. Doxygen will then generate output that is more tailored for C.
 # For instance, some of the names that are used will be different. The list
@@ -219,22 +241,39 @@ OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 
 # Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
 
 EXTENSION_MAPPING      =
 
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
 # to include (a tag file for) the STL sources as input, then you should
 # set this tag to YES in order to let doxygen match functions declarations and
 # definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
+# func(std::string) {}). This also makes the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
 
 BUILTIN_STL_SUPPORT    = NO
@@ -250,10 +289,10 @@ CPP_CLI_SUPPORT        = NO
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
 # setting a simple type. If this is not the case, or you want to show the
 # methods anyway, you should set this option to NO.
 
@@ -274,6 +313,22 @@ DISTRIBUTE_GROUP_DOC   = NO
 
 SUBGROUPING            = YES
 
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
 # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
 # is documented as struct, union, or enum with the name of the typedef. So
 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
@@ -290,16 +345,27 @@ TYPEDEF_HIDES_STRUCT   = YES
 # For small to medium size projects (<1000 input files) the default value is
 # probably good enough. For larger projects a too small cache size can cause
 # doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
+# causing a significant performance penalty.
 # If the system has enough physical memory increasing the cache will improve the
 # performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
+# a logarithmic scale so increasing the size by one will roughly double the
 # memory usage. The cache size is given by this formula:
 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
+# corresponding to a cache size of 2^16 = 65536 symbols.
 
 SYMBOL_CACHE_SIZE      = 0
 
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -316,6 +382,11 @@ EXTRACT_ALL            = YES
 
 EXTRACT_PRIVATE        = NO
 
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE        = NO
+
 # If the EXTRACT_STATIC tag is set to YES all static members of a file
 # will be included in the documentation.
 
@@ -338,7 +409,7 @@ EXTRACT_LOCAL_METHODS  = NO
 # extracted and appear in the documentation as a namespace called
 # 'anonymous_namespace{file}', where file will be replaced with the base
 # name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
+# anonymous namespaces are hidden.
 
 EXTRACT_ANON_NSPACES   = NO
 
@@ -449,6 +520,15 @@ SORT_GROUP_NAMES       = NO
 
 SORT_BY_SCOPE_NAME     = NO
 
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
 # The GENERATE_TODOLIST tag can be used to enable (YES) or
 # disable (NO) the todo list. This list is created by putting \todo
 # commands in the documentation.
@@ -474,15 +554,16 @@ GENERATE_BUGLIST       = YES
 GENERATE_DEPRECATEDLIST= YES
 
 # The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
 
 ENABLED_SECTIONS       =
 
 # The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
+# the initial value of a variable or macro consists of for it to appear in
 # the documentation. If the initializer consists of more lines than specified
 # here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
+# The appearance of the initializer of individual variables and macros in the
 # documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
@@ -494,12 +575,6 @@ MAX_INITIALIZER_LINES  = 50
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = YES
-
 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
 # This will remove the Files entry from the Quick Index and from the
 # Folder Tree View (if specified). The default is YES.
@@ -507,7 +582,8 @@ SHOW_DIRECTORIES       = YES
 SHOW_FILES             = YES
 
 # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
 # and from the Folder Tree View (if specified). The default is YES.
 
 SHOW_NAMESPACES        = YES
@@ -524,13 +600,24 @@ FILE_VERSION_FILTER    =
 
 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
 # by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
+# output files in an output format independent way. To create the layout file
 # that represents doxygen's defaults, run doxygen with the -l option.
 # You can optionally specify a file name after the option, if omitted
 # DoxygenLayout.xml will be used as the name of the layout file.
 
 LAYOUT_FILE            =
 
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES         =
+
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
@@ -559,7 +646,7 @@ WARN_IF_UNDOCUMENTED   = YES
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
 # functions that are documented, but have no documentation for their parameters
 # or return value. If set to NO (the default) doxygen will only warn about
 # wrong or incomplete parameter documentation, but not about the absence of
@@ -611,8 +698,9 @@ INPUT_ENCODING         = UTF-8
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
 # and *.h) to filter out the source-files in the directories. If left
 # blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
 
 FILE_PATTERNS          = *.c \
                          *.h
@@ -623,14 +711,16 @@ FILE_PATTERNS          = *.c \
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should
+# The EXCLUDE tag can be used to specify files and/or directories that should be
 # 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.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
 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
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
 
 EXCLUDE_SYMLINKS       = NO
@@ -682,17 +772,20 @@ IMAGE_PATH             =
 # by executing (via popen()) the command <filter> <input-file>, where <filter>
 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
 # input file. Doxygen will then use the output that the filter program writes
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
 # ignored.
 
 INPUT_FILTER           =
 
 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.  Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.  The filters are a list of the form:
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
 # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
 
 FILTER_PATTERNS        =
 
@@ -702,6 +795,21 @@ FILTER_PATTERNS        =
 
 FILTER_SOURCE_FILES    = NO
 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
@@ -720,7 +828,7 @@ INLINE_SOURCES         = NO
 
 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
 # doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = YES
 
@@ -739,7 +847,8 @@ REFERENCES_RELATION    = NO
 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentation.
+# link to the source code.
+# Otherwise they will link to the documentation.
 
 REFERENCES_LINK_SOURCE = YES
 
@@ -803,7 +912,14 @@ HTML_FILE_EXTENSION    = .html
 
 # The HTML_HEADER tag can be used to specify a personal HTML header for
 # each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
 
 HTML_HEADER            =
 
@@ -815,15 +931,34 @@ HTML_FOOTER            =
 
 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
 # style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
 
 HTML_STYLESHEET        =
 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
+# Doxygen will adjust the colors in the style sheet and background images
 # according to this color. Hue is specified as an angle on a colorwheel,
 # see http://en.wikipedia.org/wiki/Hue for more information.
 # For instance the value 0 represents red, 60 is yellow, 120 is green,
@@ -853,20 +988,23 @@ HTML_COLORSTYLE_GAMMA  = 80
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
 # If the GENERATE_DOCSET tag is set to YES, additional index files
 # will be generated that can be used as input for Apple's Xcode 3
 # integrated development environment, introduced with OSX 10.5 (Leopard).
@@ -894,9 +1032,9 @@ DOCSET_FEEDNAME        = "Doxygen generated docs"
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
 
@@ -1002,7 +1140,7 @@ QHP_SECT_FILTER_ATTRS  =
 QHG_LOCATION           =
 
 # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
+#  will be generated, which together with the HTML files, form an Eclipse help
 # plugin. To install this plugin and make it available under the help contents
 # menu in Eclipse, the contents of the directory containing the HTML and XML
 # files needs to be copied into the plugins directory of eclipse. The name of
@@ -1018,17 +1156,14 @@ GENERATE_ECLIPSEHELP   = NO
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
 
 DISABLE_INDEX          = NO
 
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
 # structure should be generated to display hierarchical information.
 # If the tag value is set to YES, a side panel will be generated
@@ -1036,13 +1171,17 @@ ENUM_VALUES_PER_LINE   = 4
 # is generated for HTML Help). For this to work a browser that supports
 # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
 # Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
 
 GENERATE_TREEVIEW      = NO
 
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
 
-USE_INLINE_TREES       = NO
+ENUM_VALUES_PER_LINE   = 4
 
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
 # used to set the initial width (in pixels) of the frame in which the tree
@@ -1071,6 +1210,39 @@ FORMULA_FONTSIZE       = 10
 
 FORMULA_TRANSPARENT    = YES
 
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
 # When the SEARCHENGINE tag is enabled doxygen will generate a search box
 # for the HTML output. The underlying search engine uses javascript
 # and DHTML and should work on any modern browser. Note that when using
@@ -1082,15 +1254,55 @@ FORMULA_TRANSPARENT    = YES
 SEARCHENGINE           = YES
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
 
 SERVER_BASED_SEARCH    = NO
 
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS  =
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -1127,7 +1339,7 @@ MAKEINDEX_CMD_NAME     = makeindex
 COMPACT_LATEX          = NO
 
 # The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
+# by the printer. Possible values are: a4, letter, legal and
 # executive. If left blank a4wide will be used.
 
 PAPER_TYPE             = a4wide
@@ -1144,6 +1356,13 @@ EXTRA_PACKAGES         =
 
 LATEX_HEADER           =
 
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
 # contain links (just like the HTML output) instead of page references
@@ -1177,6 +1396,12 @@ LATEX_HIDE_INDICES     = NO
 
 LATEX_SOURCE_CODE      = NO
 
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
 #---------------------------------------------------------------------------
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
@@ -1208,7 +1433,7 @@ COMPACT_RTF            = NO
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# Load style sheet definitions from file. Syntax is similar to doxygen's
 # config file, i.e. a series of assignments. You only have to provide
 # replacements, missing definitions are set to their default value.
 
@@ -1313,8 +1538,10 @@ GENERATE_PERLMOD       = NO
 PERLMOD_LATEX          = NO
 
 # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.  This is useful
-# if you want to understand what is going on.  On the other hand, if this
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
 # tag is set to NO the size of the Perl module output will be much smaller
 # and Perl will parse it just the same.
 
@@ -1351,7 +1578,7 @@ MACRO_EXPANSION        = NO
 EXPAND_ONLY_PREDEF     = NO
 
 # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
 
 SEARCH_INCLUDES        = YES
 
@@ -1381,15 +1608,15 @@ PREDEFINED             =
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
 # The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
 
 EXPAND_AS_DEFINED      =
 
 # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
 
 SKIP_FUNCTION_MACROS   = YES
 
@@ -1397,20 +1624,18 @@ SKIP_FUNCTION_MACROS   = YES
 # Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#   TAGFILES = file1 file2 ...
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
 # Adding location for the tag files is done as follows:
-#   TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
 
 TAGFILES               =
 
@@ -1443,9 +1668,8 @@ PERL_PATH              = /usr/bin/perl
 # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
 # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
 # or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
 
 CLASS_DIAGRAMS         = YES
 
@@ -1479,14 +1703,12 @@ HAVE_DOT               = NO
 
 DOT_NUM_THREADS        = 0
 
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
 
 DOT_FONTNAME           = FreeSans.ttf
 
@@ -1495,17 +1717,16 @@ DOT_FONTNAME           = FreeSans.ttf
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
 
 DOT_FONTPATH           =
 
 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
 # will generate a graph for each documented class showing the direct and
 # indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
@@ -1527,6 +1748,15 @@ GROUP_GRAPHS           = YES
 
 UML_LOOK               = NO
 
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
 # If set to YES, the inheritance and collaboration graphs will show the
 # relations between templates and their instances.
 
@@ -1563,11 +1793,11 @@ CALL_GRAPH             = NO
 CALLER_GRAPH           = NO
 
 # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
+# will generate a graphical hierarchy of all classes instead of a textual one.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
 # then doxygen will show the dependencies a directory has on other directories
 # in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
@@ -1575,11 +1805,22 @@ GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
 
 DOT_IMAGE_FORMAT       = png
 
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
 # The tag DOT_PATH can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
 
@@ -1591,6 +1832,12 @@ DOT_PATH               =
 
 DOTFILE_DIRS           =
 
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
 # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
 # nodes that will be shown in the graph. If the number of nodes in a graph
 # becomes larger than this value, doxygen will truncate the graph, which is
diff --git a/Doxyfile.devel b/Doxyfile.devel
deleted file mode 100644
index 43cf6814239f73e42da796f8860742753b7c0b87..0000000000000000000000000000000000000000
--- a/Doxyfile.devel
+++ /dev/null
@@ -1,1634 +0,0 @@
-# Doxyfile 1.7.1
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME           = Knot
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER         = 0.1.0
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       = doc-devel
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       = "The $name class" \
-                         "The $name widget" \
-                         "The $name file" \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC    = YES
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES        = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH        =  \
-                         src/
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING      =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = YES
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE      = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL            = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC         = YES
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES       = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = YES
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES   = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 50
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
-LAYOUT_FILE            =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC       = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT                  = src/ \
-                         Doxy.page.h
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS          = *.c \
-                         *.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE              = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# 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                =
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH           =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS       = *
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.  Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.  The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER         = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT            = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER            =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER            =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
-HTML_COLORSTYLE_HUE    = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
-HTML_COLORSTYLE_SAT    = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
-HTML_COLORSTYLE_GAMMA  = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
-HTML_TIMESTAMP         = YES
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
-GENERATE_DOCSET        = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
-
-DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
-DOCSET_PUBLISHER_NAME  = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE               =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING     =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
-GENERATE_QHP           = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE               =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE          = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER     = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME   =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS  =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS  =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION           =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
-GENERATE_ECLIPSEHELP   = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID         = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX          = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW      = NO
-
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES       = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
-EXT_LINKS_IN_WINDOW    = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE       = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
-FORMULA_TRANSPARENT    = YES
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE           = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
-
-SERVER_BASED_SEARCH    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX         = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME         = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE             = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES         =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS         = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX           = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE        = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES     = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE      = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS         = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE    =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE    =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION          = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML           = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT             = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA             =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD                =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING     = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.  This is useful
-# if you want to understand what is going on.  On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH           =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#   TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#   TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH              = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS         = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT               = NO
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
-DOT_NUM_THREADS        = 0
-
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
-
-DOT_FONTNAME           = FreeSans.ttf
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE           = 10
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK               = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT       = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS           =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH    = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS      = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP            = YES
diff --git a/Knot.files b/Knot.files
index 489aeff5005a7c10e0fba6f5e59471fc75fae821..05cf3d3fe4b841332790e89af702c15d8ec49f54 100644
--- a/Knot.files
+++ b/Knot.files
@@ -1,6 +1,4 @@
-CodingStyle
 Doxyfile
-Doxyfile.devel
 KNOWN_ISSUES
 Makefile.am
 README
@@ -54,6 +52,8 @@ src/common/hattrie/murmurhash3.c
 src/common/hattrie/murmurhash3.h
 src/common/heap.c
 src/common/heap.h
+src/common/hex.c
+src/common/hex.h
 src/common/latency.c
 src/common/latency.h
 src/common/libtap/tap.c
@@ -63,6 +63,7 @@ src/common/lists.c
 src/common/lists.h
 src/common/log.c
 src/common/log.h
+src/common/memdup.h
 src/common/mempattern.c
 src/common/mempattern.h
 src/common/mempool.c
@@ -78,6 +79,7 @@ src/common/slab/slab.c
 src/common/slab/slab.h
 src/common/sockaddr.c
 src/common/sockaddr.h
+src/common/strtonum.h
 src/knot/conf/cf-lex.l
 src/knot/conf/cf-parse.y
 src/knot/conf/conf.c
@@ -136,6 +138,28 @@ src/libknot/consts.c
 src/libknot/consts.h
 src/libknot/dname.c
 src/libknot/dname.h
+src/libknot/dnssec/algorithm.c
+src/libknot/dnssec/algorithm.h
+src/libknot/dnssec/key.c
+src/libknot/dnssec/key.h
+src/libknot/dnssec/nsec-bitmap.h
+src/libknot/dnssec/nsec3.c
+src/libknot/dnssec/nsec3.h
+src/libknot/dnssec/policy.h
+src/libknot/dnssec/rrset-sign.c
+src/libknot/dnssec/rrset-sign.h
+src/libknot/dnssec/sig0.c
+src/libknot/dnssec/sig0.h
+src/libknot/dnssec/sign.c
+src/libknot/dnssec/sign.h
+src/libknot/dnssec/zone-events.c
+src/libknot/dnssec/zone-events.h
+src/libknot/dnssec/zone-keys.c
+src/libknot/dnssec/zone-keys.h
+src/libknot/dnssec/zone-nsec.c
+src/libknot/dnssec/zone-nsec.h
+src/libknot/dnssec/zone-sign.c
+src/libknot/dnssec/zone-sign.h
 src/libknot/edns.c
 src/libknot/edns.h
 src/libknot/libknot.h
@@ -143,26 +167,17 @@ src/libknot/nameserver/chaos.c
 src/libknot/nameserver/chaos.h
 src/libknot/nameserver/name-server.c
 src/libknot/nameserver/name-server.h
-src/libknot/nsec3.c
-src/libknot/nsec3.h
 src/libknot/packet/packet.c
 src/libknot/packet/packet.h
 src/libknot/packet/query.c
 src/libknot/packet/query.h
 src/libknot/packet/response.c
 src/libknot/packet/response.h
+src/libknot/rdata.h
 src/libknot/rrset-dump.c
 src/libknot/rrset-dump.h
 src/libknot/rrset.c
 src/libknot/rrset.h
-src/libknot/sign/bnutils.c
-src/libknot/sign/bnutils.h
-src/libknot/sign/dnssec.c
-src/libknot/sign/dnssec.h
-src/libknot/sign/key.c
-src/libknot/sign/key.h
-src/libknot/sign/sig0.c
-src/libknot/sign/sig0.h
 src/libknot/tsig-op.c
 src/libknot/tsig-op.h
 src/libknot/tsig.c
@@ -222,17 +237,19 @@ src/tests/knot/server_tests.c
 src/tests/knot/server_tests.h
 src/tests/libknot/dname_tests.c
 src/tests/libknot/dname_tests.h
+src/tests/libknot/dnssec_keys_tests.c
+src/tests/libknot/dnssec_keys_tests.h
+src/tests/libknot/dnssec_nsec3_tests.c
+src/tests/libknot/dnssec_nsec3_tests.h
+src/tests/libknot/dnssec_sign_tests.c
+src/tests/libknot/dnssec_sign_tests.h
 src/tests/libknot/rrset_tests.c
 src/tests/libknot/rrset_tests.h
-src/tests/libknot/sign_tests.c
-src/tests/libknot/sign_tests.h
 src/tests/libknot/wire_tests.c
 src/tests/libknot/wire_tests.h
 src/tests/libknot/ztree_tests.c
 src/tests/libknot/ztree_tests.h
 src/tests/unittests_main.c
-src/tests/zscanner/zscanner_tests.c
-src/tests/zscanner/zscanner_tests.h
 src/utils/common/exec.c
 src/utils/common/exec.h
 src/utils/common/msg.c
@@ -253,11 +270,15 @@ src/utils/dig/dig_params.h
 src/utils/host/host_main.c
 src/utils/host/host_params.c
 src/utils/host/host_params.h
+src/utils/nsec3hash/nsec3hash_main.c
 src/utils/nsupdate/nsupdate_exec.c
 src/utils/nsupdate/nsupdate_exec.h
 src/utils/nsupdate/nsupdate_main.c
 src/utils/nsupdate/nsupdate_params.c
 src/utils/nsupdate/nsupdate_params.h
+src/zscanner/Makefile.am
+src/zscanner/error.c
+src/zscanner/error.h
 src/zscanner/file_loader.c
 src/zscanner/file_loader.h
 src/zscanner/scanner.c
@@ -271,3 +292,4 @@ src/zscanner/test/processing.h
 src/zscanner/test/tests.c
 src/zscanner/test/tests.h
 src/zscanner/test/zscanner-tool.c
+src/zscanner/zscanner.h
diff --git a/README b/README
index 2d880349d30e6723c2b875c8480d9c9124d98b92..4d24701d0da3b8995466679bcc0eb617a61a65b8 100644
--- a/README
+++ b/README
@@ -16,11 +16,6 @@ Optional packages:
 Dependencies for building documentation:
 * texinfo
 
-Knot DNS requires compiler to support atomic intrinsics.
-GCC version at least 4.1 supports legacy atomic builtins, however 4.7
-or newer is preferred.
-Clang supports atomics since version 2.9.
-
 By default Knot DNS is distributed with a slower zone file parser because of
 smaller source file and quick compile time. In most cases it is sufficient.
 If you plan to process large zone files, we recommend to build Knot DNS
@@ -30,119 +25,49 @@ and it is normal if the C compiler takes much more time (minutes).
 Installation
 ============
 
-Knot DNS may be already included in your operating system distribution and
+Knot DNS may already be included in your operating system distribution and
 therefore can be installed from packages (Linux) or ports (BSD). This is
 always preferred unless you want to test the latest features, contribute to
 Knot development, or you just know what you are doing.
 
 Following sections describe how to build Knot DNS from the source code.
 
-Installation on Debian based distributions
-==========================================
-
-The following steps should work (verified in VirtualBox only)
-for the distribution/architecture/release combinations as listed bellow.
+1) Install prerequisites
 
-----------------------------------------------
-Debian         (AMD64, I386) 6.0.2.1 (squeeze)
-Ubuntu Server  (AMD64, I386) 10.04 LTS
-Ubuntu Desktop (AMD64, I386) 10.04 LTS
-----------------------------------------------
-
-# Make the system up-to-date
+Debian based distributions
+--------------------------
+Update the system:
 $ sudo apt-get update
 $ sudo apt-get upgrade
 
-# Ensure all prerequisites are installed
-$ sudo apt-get install git-core autoconf libtool flex bison libssl-dev liburcu-dev
+Install prerequisites:
+$ sudo apt-get install git-core libtool autoconf flex bison libssl-dev liburcu-dev
 
-# Install optional packages (POSIX 1003.1e capabilities)
-$ sudo apt-get install libcap-ng-dev
+Install optional packages:
+($ sudo apt-get install libcap-ng-dev)
+($ sudo apt-get install ragel)
 
-# If the liburcu-dev package is not present, install it directly
-
-# Get the source code
-$ git clone git://git.nic.cz/knot-dns.git
-$ cd knot-dns
-$ autoreconf -if
-$ ./configure
-$ make
-$ sudo make install
-$ sudo ldconfig
-
-Installation on Fedora
-======================
-
-Notice: Knot DNS is available in official distribution repositories since
-Fedora 18 (Spherical Cow). Search for 'knot' package.
-
-All commands with the '#' prompt should be run as the root user, commands with
-the '$' prompt should be run as a regular non-root user.
+If the liburcu-dev package is not present, install it from the source code
+(http://lttng.org/urcu)
 
+Fedora like distributions
+-------------------------
 Update the system:
-# yum upgrade
+$ yum upgrade
 
 Ensure all base development tools are available:
-# yum install @buildsys-build
+$ yum install @buildsys-build
 
 Ensure all prerequisites are installed:
-# yum install libtool autoconf flex bison openssl-devel userspace-rcu-devel
-
-You can also install optional packages:
-# yum install libcap-ng-devel ragel
-
-Download the latest source code from Git and compile it:
-$ git clone git://git.nic.cz/knot-dns.git
-$ cd knot
-$ autoreconf -if
-$ ./configure
-$ make
-
-Install Knot DNS into system (run only if you really know what these commands do):
-# make install
-# ldconfig
+$ yum install libtool autoconf flex bison openssl-devel userspace-rcu-devel
 
-Installation on BSD
-===================
-
-Not all prerequisites are available as ports on BSD.
-
-- liburcu must be compiled from sources
-  - version 0.6.4 compiles on BSD without any source code modifications
-  - in case of x86_64 build, CFLAGS nad build type has to be set appropriately.
-    $ CFLAGS=-fPIC ./configure --build amd64
-- flex must be newer version from ports that support reentrant parsers
-
-Knot DNS requires more recent version of flex from ports, to prevent name clash
-specify flex destination.
-$ cd <knot sources>
-$ 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.
-Prerequisites:
-- flex and bison from packages
-
-$ mkdir liburcu && cd liburcu
-$ wget "http://lttng.org/files/urcu/userspace-rcu-0.8.0.tar.bz2"
-$ autoconf && ./configure && make && sudo make install
-$ # or follow installation instructions in INSTALL
-
-Knot DNS installation is the same as in previous section (Installation on BSD).
-
-Installation on OS X
-====================
+Install optional packages:
+($ yum install libcap-ng-devel)
+($ yum install ragel)
 
+OS X
+----
 Not all prerequisites are preinstalled for OS X.
-
 - liburcu must be compiled from sources
   - liburcu requires gcc-4.6 from MacPorts, as it depends on __thread keyword
   $ CC=gcc-mp-4.6 ARCH=x86_64 ./configure
@@ -152,32 +77,54 @@ Not all prerequisites are preinstalled for OS X.
 
 Compiling Knot DNS with gcc-mp-4.6 is recommended, but not necessary.
 
+2) Install Knot DNS
+
+Knot DNS requires compiler to support atomic intrinsics.
+GCC version at least 4.1 supports legacy atomic builtins, however 4.7
+or newer is preferred.
+Clang supports atomics since version 2.9.
+
+Get the source code:
+$ git clone git://git.nic.cz/knot-dns.git
+Or extract source package to knot-dns directory
+
+Compile Knot
+$ cd knot-dns
+$ autoreconf -if
+$ ./configure
+$ make
+
+Install Knot DNS into system:
+$ sudo make install
+$ sudo ldconfig
+
 Running
 =======
 
-First, each server needs configuration file.
-Please see samples/knot.sample.conf for reference.
-More examples can be found in samples/knot.full.conf
+1) Each server needs configuration file. Please see samples/knot.sample.conf
+for reference or samples/knot.full.conf for more examples.
 Configuration file has to specify:
-* storage for PID files, journal files etc.
-* network interfaces
-* served zones
-
-$ cp samples/knot.sample.conf myserver.conf
-$ vim myserver.conf # or your favourite text editor
-$ knotd -h # see what it can do
+- storage for PID files, journal files etc.
+- network interfaces
+- served zones
 
-Second, prepare working directory.
+E.g. use the default config file:
+$ cd /etc/knot
+$ mv knot.sample.conf knot.conf
+Modify the config:
+$ vim knot.conf
 
-$ mkdir -p /tmp/knot-minimal/samples; cp samples/example.com.zone /tmp/knot-minimal/samples/
+2) Prepare working directory
+$ mv example.com.zone /var/lib/knot/
 
-Third, let's start the server. This can be done by running the 'knotd' command.
+3) Start the server. This can be done by running the 'knotd' command.
 Alternatively, your distribution should have an init script available, if you've
 installed Knot using a binary package.
 
-Lets start our server in foreground to see if it runs:
-
+Start Knot in foreground to see if it runs:
 $ knotd -c myserver.conf
 
-For more information, refer to the user manual.
+For more information, refer to the user manual or:
+$ knotc -h
+$ knotd -h
 
diff --git a/THANKS b/THANKS
index 770873140634cf97e791244e05e3e337c8e4f357..02eba12d115e07e268c5cb1490e08e08efbb4063 100644
--- a/THANKS
+++ b/THANKS
@@ -1,9 +1,7 @@
-Knot DNS THANKS file
-
-Knot DNS was originally written by CZ.NIC Labs.  It would not be what
+Knot DNS was originally written by CZ.NIC Labs. It would not be what
 it is today without the invaluable help of these people, who have
 reported problems, suggested improvements, or submitted actual code.
-Please help us keep this list complete and free from errors.  Also see
+Please help us keep this list complete and free from errors. Also see
 the AUTHORS file for the list of people with contributions significant
 enough to warrant copyright assignment.
 
@@ -12,26 +10,3 @@ Geert Hendrickx			geert@hendrickx.be
 Michal 'vorner' Vaner		vorner@ucw.cz
 Ondřej Caletka			ondrej@caletka.cz
 Anand Buddhdev			anandb@ripe.net
-
-================
-
-Local Variables:
-mode: text
-coding: utf-8
-End:
-
-
-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/>.
diff --git a/astylerc b/astylerc
deleted file mode 100644
index c37e3fd78ffbd5585531f610843d9e367aae807f..0000000000000000000000000000000000000000
--- a/astylerc
+++ /dev/null
@@ -1,11 +0,0 @@
---style=1tbs
---indent=tab=8
---indent-preprocessor
---pad-oper
---pad-header
---unpad-paren
---add-brackets
---convert-tabs
---align-pointer=name
---mode=c
---lineend=linux
diff --git a/configure.ac b/configure.ac
index 3efe4c36e87f5ce527ae461373841f5f0a620cbc..e301e1397d8f2ca7be16e3a853724a9e57fd1d95 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 #                                               -*- Autoconf -*-
 
 AC_PREREQ([2.60])
-AC_INIT([knot], [1.3.2], [knot-dns@labs.nic.cz])
+AC_INIT([knot], [1.4.0-alpha], [knot-dns@labs.nic.cz])
 AM_INIT_AUTOMAKE([gnits subdir-objects dist-bzip2 dist-xz -Wall -Werror])
 AM_SILENT_RULES([yes])
 AC_CONFIG_SRCDIR([src/knot/main.c])
@@ -92,6 +92,7 @@ AC_ARG_ENABLE([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.]) ;;
+	  dnssec) AC_DEFINE([KNOT_DNSSEC_DEBUG], [1], [DNSSEC debug.]) ;;
         esac
     done
     ], [])
@@ -313,12 +314,12 @@ gl_VISIBILITY()
 CFLAGS="$CFLAGS $CFLAG_VISIBILITY"
 
 AC_CONFIG_FILES([Makefile
-		 samples/Makefile
-		 src/Makefile
-		 src/tests/Makefile
 		 doc/Makefile
 		 man/Makefile
-		 src/zscanner/test/run_tests.sh
+		 src/Makefile
+		 samples/Makefile
+		 src/tests/Makefile
+		 src/zscanner/Makefile
 		 src/zscanner/test/cases/06-3_INCLUDE.in:src/zscanner/test/cases/06-3_INCLUDE.inin
 		 src/zscanner/test/cases/06-4_INCLUDE.in:src/zscanner/test/cases/06-4_INCLUDE.inin
 		 src/zscanner/test/cases/06-0_INCLUDE.in:src/zscanner/test/cases/06-0_INCLUDE.inin
@@ -328,5 +329,9 @@ AC_CONFIG_FILES([Makefile
 		 man/kdig.1
 		 man/knsupdate.1
 		 man/knot.conf.5
+		 man/knsec3hash.1
 		 ])
+
+AC_CONFIG_FILES([src/zscanner/test/run_tests.sh], [chmod a+x src/zscanner/test/run_tests.sh])
+
 AC_OUTPUT
diff --git a/doc/configuration.texi b/doc/configuration.texi
index e6cdf53ca444d7bfb02bf786920b0c55f550f236..ce05d48c8e95bdd9c596b21a6f0cd5ab96ab53d9 100644
--- a/doc/configuration.texi
+++ b/doc/configuration.texi
@@ -13,6 +13,7 @@ In this chapter we provide suggested configurations and explain the meaning of i
 * Enabling zone semantic checks::
 * Creating IXFR differences from zone file changes::
 * Using Response Rate Limiting::
+* Online DNSSEC signing (experimental)::
 @end menu
 
 @node Minimal configuration
@@ -283,3 +284,119 @@ system @{
 	rate-limit-slip 1; # Every response is slipped (default)
 @}
 @end example
+
+@node Online DNSSEC signing (experimental)
+@section Online DNSSEC signing (experimental)
+
+Knot DNS 1.4 is the first release to include online DNSSEC signing feature.
+Online DNSSEC signing is currently highly experimental and there is a lot of
+issues we are working on and limitations we will try to eliminate. By all
+means, anything in the current implementation can change including
+configuration options. We have no intention to maintain backward compatibility.
+
+@subsection Example configuration
+
+The example configuration enables automatic signing for all zones using
+@ref{dnssec-enable} option in the @code{zones} section, but the signing is
+explicitly disabled for zone @code{example.dev} using the same option directly
+in zone configuration. The location of directory with signing keys is set
+globally by option @ref{dnssec-keydir}.
+
+@sp 1
+
+@example
+zones @{
+  dnssec-enable on;
+  dnssec-keydir "/var/lib/knot/keys";
+
+  example.com @{
+    file "example.com.zone";
+  @}
+
+  example.dev @{
+    file "example.dev.zone";
+    dnssec-enable off;
+  @}
+@}
+@end example
+
+@subsection Signing keys
+
+The signing keys can be generated using ISC @code{dnssec-keygen} tool only
+and there are some limitations:
+
+@itemize @bullet
+@item
+Keys for all zones must be placed in one directory.
+
+@item
+Algorithms based on RSA, DSA, and ECDSA are supported, support for GOST
+algorithm is not finished yet.
+
+@item
+Only key activation and inactivation time stamps are utilized and defined
+times are used both for key publication and signing. Other timestamps are
+ignored.
+
+@item
+It is required, that both @code{.private} and @code{.key} files for each key
+are available in the key directory in order to use the keys (even for
+verification only).
+
+@item
+There cannot be more than eight active keys per zone.
+
+@end itemize
+
+@sp 1
+
+Example how to generate NSEC3 capable zone signing key (ZSK) and key signing
+key (KSK) for zone @code{example.com}:
+
+@sp 1
+@example
+$ cd /var/lib/knot/keys
+$ dnssec-keygen -3 example.com
+$ dnssec-keygen -3 -f KSK example.com
+@end example
+
+@subsection Signing policy
+
+Currently the signing policy is not configurable, except for signature lifetime.
+
+@itemize @bullet
+@item Signature lifetime can be set in configuration globally for all zones and for each zone in particular. @xref{signature-lifetime}. If not set, the default value is 30 days.
+@item Signature is refreshed 2 hours before expiration. The signature lifetime must thus be set to more than 2 hours.
+@end itemize
+
+@subsection Zone signing
+
+The signing process consists of the following steps:
+
+@itemize @bullet
+@item
+Fixing @code{NSEC} or @code{NSEC3} records. This is determined by
+@code{NSEC3PARAM} record presence in unsigned zone.
+
+@item
+Updating @code{DNSKEY} records. This also means adding DNSKEY records for any keys that are present in keydir, but missing in zone file.
+
+@item
+Removing expired signatures, invalid signatures, signatures expiring in a short
+time, and signatures with unknown key.
+
+@item
+Creating any missing signatures. @code{DNSKEY} records are signed by both ZSK
+and KSK keys, other records are signed only by ZSK keys.
+
+@item
+SOA record is updated and resigned if any changes were performed.
+
+@end itemize
+
+@sp 1
+
+The zone signing is performed when the zone is loaded into server, on zone
+reload, before any signature is expiring, and after DDNS update. The signing
+can be also forced using @code{signzone} command issued by @code{knotc}, in
+this case all signatures are recreated.
diff --git a/doc/knot.texi b/doc/knot.texi
index 4398432fded0249335da471a418b001dbd6bdf7d..23c3a700b832257ee39c7f2e16f310b17a8ae9cf 100644
--- a/doc/knot.texi
+++ b/doc/knot.texi
@@ -40,6 +40,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 @title Knot DNS Reference Manual
 @subtitle for version @value{VERSION}, @value{UPDATED}
 @author Jan Kadlec (@email{jan.kadlec@@nic.cz})
+@author Daniel Salzman (@email{daniel.salzman@@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})
diff --git a/doc/reference.texi b/doc/reference.texi
index 4b1e46e66ff90f68d5062a7b53b0596e9257566f..4823420dc8344014da8c9ec520d99b73227a9834 100644
--- a/doc/reference.texi
+++ b/doc/reference.texi
@@ -283,6 +283,7 @@ system @{
   rundir "/var/run/knot";
   workers 16;
   user knot.knot;
+  max-udp-payload 4096;
 @}
 @end example
 
@@ -718,6 +719,8 @@ The @code{zones} statement contains definition of zones served by Knot DNS.
     [ @code{notify-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
     [ @code{notify-out} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
     [ @code{update-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
+    [ @code{dnssec-enable} ( @code{on} | @code{off} )@code{;} ]
+    [ @code{dnssec-keydir} @kbd{string}@code{;} ]
     [ @kbd{zone_options} ]
   @code{@}}
 @code{@}}
@@ -731,6 +734,8 @@ The @code{zones} statement contains definition of zones served by Knot DNS.
   [ @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{;} ]
+  [ @code{dnssec-enable} ( @code{on} | @code{off} )@code{;} ]
+  [ @code{signature-lifetime} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
 @end example
 
 @node zones Statement Definition and Grammar
@@ -751,6 +756,9 @@ The @code{zones} statement contains definition of zones served by Knot DNS.
 * notify-retries::
 * zonefile-sync::
 * ixfr-fslimit::
+* dnssec-enable::
+* dnssec-keydir::
+* signature-lifetime::
 @end menu
 
 @node zone_id
@@ -855,6 +863,33 @@ are 1 to INT_MAX and default value is 5.
 
 @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 dnssec-enable
+@subsubsection dnssec-enable
+@vindex dnssec-enable
+
+EXPERIMENTAL: Enable online DNSSEC signing for the zone.
+
+Default value (in @code{zones} section): on if @code{dnssec-keydir} is set
+
+Default value (in @code{zone} config): inherited from @code{zones} section
+
+@node dnssec-keydir
+@subsubsection dnssec-keydir
+@vindex dnssec-keydir
+
+Location of DNSSEC signing keys.
+
+Default value: not set
+
+@node signature-lifetime
+@subsubsection signature-lifetime
+@vindex signature-lifetime
+
+Specifies how long should the automatically generated DNSSEC signatures be valid. Expiration will thus be set as current time (in the moment of signing) + @code{signature-lifetime}.
+Possible values are from 7201 to INT_MAX. The lower limit is because the server will trigger resign when any of the signatures expires in 7200 seconds or less.
+
+Default value: @kbd{30d} (@kbd{2592000})
+
 @node zones Example
 @subsection zones Example
 
@@ -870,6 +905,9 @@ zones @{
   notify-retries 5;
   zonefile-sync 1h;
   ixfr-fslimit 1G;
+  dnssec-enable on;
+  dnssec-keydir "keys";
+  signature-lifetime 60d;
   example.com @{
     file "samples/example.com.zone";
     ixfr-from-differences off; #experimental
@@ -878,6 +916,8 @@ zones @{
     notify-timeout 60;
     notify-retries 5;
     zonefile-sync 1h;
+    dnssec-enable off;
+    signature-lifetime 30d;
     xfr-in server0;
     xfr-out server0, server1;
     notify-in server0;
diff --git a/doc/running.texi b/doc/running.texi
index 2e26f098d43ef3ccb3c6e903208e782cb8e2a3da..e8422191d4c55099b3dbad4046f8c6ef2b28a314 100644
--- a/doc/running.texi
+++ b/doc/running.texi
@@ -72,7 +72,7 @@ files are stored in the folowing directories.
 @itemize @bullet
 @item
 @emph{Zone files} - default directory for storing zone files. This can be overriden
-using absolute zone file location. 
+using absolute zone file location.
 
 @item
 @emph{Journal files} - each zone has a journal file to store differences for IXFR and
diff --git a/man/Makefile.am b/man/Makefile.am
index a6f4010efbaf27d7b36cc01450344141d9a0f565..8b9d5cb7b7d6001e91e3ed61507cd08f11643034 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,2 +1,2 @@
-MANPAGES = knot.conf.5 knotc.8 knotd.8 kdig.1 khost.1 knsupdate.1
+MANPAGES = knot.conf.5 knotc.8 knotd.8 kdig.1 khost.1 knsupdate.1 knsec3hash.1
 dist_man_MANS = $(MANPAGES)
diff --git a/man/knot.conf.5.in b/man/knot.conf.5.in
index 8988746f58ad4c0c5d3276791c837b1cf9d19a3d..78d3c0487f1529dfa24221b5354907cd4344270a 100644
--- a/man/knot.conf.5.in
+++ b/man/knot.conf.5.in
@@ -102,7 +102,8 @@ serves as an example of the configuration for knotc(8) and knotd(8).
   rate-limit-slip 1;
 
   # Maximum EDNS0 UDP payload size
-  # max-udp-payload 4096;
+  # Default value: 4096
+  max-udp-payload 4096;
  }
 
  # Includes can be placed anywhere at any level in the configuration file. The
@@ -255,6 +256,24 @@ serves as an example of the configuration for knotc(8) and knotd(8).
   # f.e. 1k, 100M, 2G
   ixfr-fslimit 1G;
 
+  # Enable DNSSEC online signing (EXPERIMENTAL)
+  # Possible values: on | off;
+  # Default value: on if dnssec-keydir is set; otherwise off
+  dnssec-enable on;
+
+  # Location of DNSSEC signing keys (relative to storage directory).
+  # Default value: not set
+  dnssec-keydir "keys";
+
+  # Validity period for DNSSEC signatures
+  # Possible values: (7200..INT_MAX> (seconds)
+  # Default value: 30d (30 days or 2592000 seconds)
+  # 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
+  # The lower limit is because the server will trigger resign when any of the
+  # signatures expires in 7200 seconds or less.
+  signature-lifetime 30d;
+
   # Zone entry
   #
   # Format: <zone-name> { file "<path-to-zone-file>"; }
@@ -296,6 +315,27 @@ serves as an example of the configuration for knotc(8) and knotd(8).
     # f.e. 1s = 1 second, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
     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;
+
+    # Enable DNSSEC online signing (EXPERIMENTAL)
+    # Possible values: on | off;
+    # Default value: inherited from zones section
+    dnssec-enable off;
+
+    # Validity period for DNSSEC signatures
+    # Possible values: (7200..INT_MAX> (seconds)
+    # Default value: 30d (30 days or 2592000 seconds)
+    # 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
+    # The lower limit is because the server will trigger resign when any of the
+    # signatures expires in 7200 seconds or less.
+    signature-lifetime 30d;
+
     # XFR master server
     xfr-in server0;
 
diff --git a/man/knsec3hash.1.in b/man/knsec3hash.1.in
new file mode 100644
index 0000000000000000000000000000000000000000..e3d7a887adbe02094651f80e08297e245bd056a0
--- /dev/null
+++ b/man/knsec3hash.1.in
@@ -0,0 +1,37 @@
+.TH "knsec3hash" "8" "June 2013" "CZ.NIC Labs" "Knot DNS, version @VERSION@"
+.SH NAME
+.B knsec3hash
+\- Simple utility to compute NSEC3 hash (libknot equivalent of ISC nsec3hash)
+.SH SYNOPSIS
+.B knsec3hash
+{\fIsalt\fR} {\fIalgorithm\fR} {\fIiterations\fR} {\fIdomain-name\fR}
+.SH DESCRIPTION
+This utility generates NSEC3 hash for a given domain name and parameters of
+NSEC3 hash.
+.SH ARGUMENTS
+.TP
+\fIsalt\fR
+Specifies binary salt encoded as a hexadecimal string.
+.TP
+\fIalgorithm\fR
+Specifies hashing algorithm number. Currently the only supported algorithm is
+SHA-1 (number 1).
+.TP
+\fIiterations\fR
+Specifies the number of additional iterations of the hashing algorithm.
+.TP
+\fIdomain-name\fR
+Specifies the domain name to be hashed.
+.SH SEE ALSO
+RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence.
+.SH EXAMPLE
+$ knsec3hash c01dcafe 1 10 knot-dns.cz
+.br
+7PTVGE7QV67EM61ROS9238P5RAKR2DM7 (salt=c01dcafe, hash=1, iterations=10)
+.SH AUTHOR
+Jan Vcelak (\fBhttp://knot-dns.cz\fR)
+.TP
+Please send any bugs or comments to \fBknot-dns@labs.nic.cz\fR
+.SH SEE ALSO
+.BI knotc\fR(8),
+.BI knotd\fR(8).
diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index 4450f6c8e4dba18ded1cdeab3a8910cc4ee3846b..e72bc4ef765ee0efd84399c2b5f84a3139ced9b3 100644
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -96,7 +96,8 @@ system {
   rate-limit-slip 1;
 
   # Maximum EDNS0 UDP payload size
-  # max-udp-payload 4096;
+  # Default value: 4096
+  max-udp-payload 4096;
 }
 
 # Includes can be placed anywhere at any level in the configuration file. The
@@ -249,6 +250,24 @@ zones {
   # f.e. 1k, 100M, 2G
   ixfr-fslimit 1G;
 
+  # Enable DNSSEC online signing (EXPERIMENTAL)
+  # Possible values: on | off;
+  # Default value: on if dnssec-keydir is set; otherwise off
+  # dnssec-enable on;
+
+  # Location of DNSSEC signing keys (relative to storage dir).
+  # Default value: not set
+  # dnssec-keydir "keys";
+
+  # Validity period for DNSSEC signatures
+  # Possible values: (7200..INT_MAX> (seconds)
+  # Default value: 30d (30 days or 2592000 seconds)
+  # 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
+  # The lower limit is because the server will trigger resign when any of the
+  # signatures expires in less than 7200 seconds.
+  # signature-lifetime 30d;
+
   # Zone entry
   #
   # Format: <zone-name> { file "<path-to-zone-file>"; }
@@ -290,6 +309,27 @@ zones {
     # f.e. 1s = 1 second, 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;
+
+    # Enable DNSSEC online signing (EXPERIMENTAL)
+    # Possible values: on | off;
+    # Default value: inherited from zones section
+    # dnssec-enable on;
+
+    # Validity period for DNSSEC signatures
+    # Possible values: (7200..INT_MAX> (seconds)
+    # Default value: 30d (30 days or 2592000 seconds)
+    # 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
+    # The lower limit is because the server will trigger resign when any of the
+    # signatures expires in 7200 seconds or less.
+    # signature-lifetime 30d;
+
     # XFR master server
     xfr-in server0;
 
diff --git a/scripts/rrset-deep-free-remove-arg.cocci b/scripts/rrset-deep-free-remove-arg.cocci
new file mode 100644
index 0000000000000000000000000000000000000000..4a63a511500feff0c40b2b2d9ff1ceacd054ba45
--- /dev/null
+++ b/scripts/rrset-deep-free-remove-arg.cocci
@@ -0,0 +1,15 @@
+//
+// Remove third argument from knot_rrset_deep_free{,no_sig}() calls
+//
+
+@@
+expression E1, E2, E3;
+@@
+(
+ knot_rrset_deep_free
+|
+ knot_rrset_deep_free_no_sig
+)
+ (E1, E2
+- , E3
+ )
diff --git a/scripts/update-project-files.py b/scripts/update-project-files.py
index ded53844b0605d4b2efa6a424322198f8101c9be..f5c4dea4434cd7efbe16d0b4dfed9d6a3e769440 100755
--- a/scripts/update-project-files.py
+++ b/scripts/update-project-files.py
@@ -6,7 +6,7 @@
 
 SOURCES = [
     # documentation
-    "README", "KNOWN_ISSUES", "CodingStyle",
+    "README", "KNOWN_ISSUES", 
     "Doxyfile*", "Doxy.file.h", "doc/*.texi",
 
     # build-system
diff --git a/src/Makefile.am b/src/Makefile.am
index 524dedee2f0526a54c0eff95dd316f0e37680454..b2ff2d3edf7c3cdbecd6bc0cd978bb47160578e8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,10 +1,9 @@
 ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
-SUBDIRS = . tests
+SUBDIRS = zscanner . tests
 
 sbin_PROGRAMS = knotc knotd
-bin_PROGRAMS = kdig khost knsupdate
-noinst_PROGRAMS = zscanner-tool
-noinst_LTLIBRARIES = libknot.la libknotd.la libknots.la libzscanner.la
+bin_PROGRAMS = kdig khost knsupdate knsec3hash
+noinst_LTLIBRARIES = libknot.la libknotd.la libknots.la
 
 # $(YACC) will generate header file
 AM_CPPFLAGS = -I$(top_srcdir)/src/libknot -DSYSCONFDIR='"$(sysconfdir)"' \
@@ -14,12 +13,6 @@ AM_YFLAGS = -d
 libknotd_la_YFLAGS = -pcf_ -d
 libknotd_la_LFLAGS = # TODO: reentrant parser, prefix
 
-EXTRA_DIST =					\
-	zscanner/scanner.rl			\
-	zscanner/scanner_body.rl		\
-	zscanner/test/run_tests.sh		\
-	zscanner/test/cases
-
 BUILT_SOURCES =					\
 	knot/conf/libknotd_la-cf-lex.c		\
 	knot/conf/libknotd_la-cf-parse.c	\
@@ -30,15 +23,6 @@ CLEANFILES =					\
 	knot/conf/libknotd_la-cf-parse.c	\
 	knot/conf/libknotd_la-cf-parse.h
 
-if HAVE_RAGEL
-BUILT_SOURCES += zscanner/scanner.c
-CLEANFILES += zscanner/scanner.c
-
-zscanner/scanner.c: zscanner/scanner.rl zscanner/scanner_body.rl
-	$(RAGEL) $(FSM_TYPE) -s -o $@ $(srcdir)/zscanner/scanner.rl
-
-endif
-
 knotc_SOURCES =					\
 	knot/ctl/knotc_main.c
 
@@ -104,6 +88,9 @@ knsupdate_SOURCES =				\
 	utils/nsupdate/nsupdate_exec.h		\
 	utils/nsupdate/nsupdate_exec.c
 
+knsec3hash_SOURCES =				\
+	utils/nsec3hash/nsec3hash_main.c
+
 libknot_la_SOURCES =				\
 	libknot/common.h			\
 	libknot/consts.h			\
@@ -145,15 +132,14 @@ libknot_la_SOURCES =				\
 	libknot/updates/ddns.h			\
 	libknot/updates/ddns.c			\
 	libknot/dname.c				\
-	libknot/nsec3.c				\
 	libknot/consts.h			\
 	libknot/edns.h				\
 	libknot/edns.c				\
 	libknot/libknot.h			\
 	libknot/dname.h				\
+	libknot/rdata.h				\
 	libknot/rrset.h				\
 	libknot/rrset.c				\
-	libknot/nsec3.h				\
 	libknot/rrset-dump.h			\
 	libknot/rrset-dump.c			\
 	libknot/tsig.h				\
@@ -162,14 +148,28 @@ libknot_la_SOURCES =				\
 	libknot/tsig-op.c			\
 	libknot/binary.h			\
 	libknot/binary.c			\
-	libknot/sign/key.h			\
-	libknot/sign/key.c			\
-	libknot/sign/bnutils.h			\
-	libknot/sign/bnutils.c			\
-	libknot/sign/dnssec.h			\
-	libknot/sign/dnssec.c			\
-	libknot/sign/sig0.h			\
-	libknot/sign/sig0.c
+	libknot/dnssec/algorithm.c		\
+	libknot/dnssec/algorithm.h		\
+	libknot/dnssec/key.c			\
+	libknot/dnssec/key.h			\
+	libknot/dnssec/nsec-bitmap.h		\
+	libknot/dnssec/nsec3.c			\
+	libknot/dnssec/nsec3.h			\
+	libknot/dnssec/policy.h			\
+	libknot/dnssec/rrset-sign.c		\
+	libknot/dnssec/rrset-sign.h		\
+	libknot/dnssec/sig0.c			\
+	libknot/dnssec/sig0.h			\
+	libknot/dnssec/sign.c			\
+	libknot/dnssec/sign.h			\
+	libknot/dnssec/zone-events.c		\
+	libknot/dnssec/zone-events.h		\
+	libknot/dnssec/zone-keys.c		\
+	libknot/dnssec/zone-keys.h		\
+	libknot/dnssec/zone-nsec.c		\
+	libknot/dnssec/zone-nsec.h		\
+	libknot/dnssec/zone-sign.c		\
+	libknot/dnssec/zone-sign.h
 
 libknots_la_SOURCES =				\
 	common/slab/slab.c			\
@@ -179,6 +179,7 @@ libknots_la_SOURCES =				\
 	common/libtap/tap.h			\
 	common/libtap/tap_unit.h		\
 	common/atomic.h				\
+	common/memdup.h				\
 	common/mempattern.h			\
 	common/mempattern.c			\
 	common/descriptor.h			\
@@ -227,7 +228,10 @@ libknots_la_SOURCES =				\
 	common/hattrie/hat-trie.c		\
 	common/hattrie/hat-trie.h		\
 	common/hattrie/murmurhash3.c		\
-	common/hattrie/murmurhash3.h
+	common/hattrie/murmurhash3.h		\
+	common/hex.c				\
+	common/hex.h				\
+	common/strtonum.h
 
 libknotd_la_SOURCES =				\
 	knot/stat/gatherer.c			\
@@ -281,30 +285,14 @@ libknotd_la_SOURCES =				\
 	knot/zone/estimator.c			\
 	knot/server/server.h
 
-zscanner_tool_SOURCES =				\
-	zscanner/test/zscanner-tool.c		\
-	zscanner/test/tests.h			\
-	zscanner/test/tests.c			\
-	zscanner/test/processing.h		\
-	zscanner/test/processing.c
-
-libzscanner_la_SOURCES =			\
-	zscanner/file_loader.h			\
-	zscanner/file_loader.c			\
-	zscanner/scanner.h			\
-	zscanner/scanner.c			\
-	zscanner/scanner_functions.h		\
-	zscanner/scanner_functions.c
-
 libknotd_la_LIBADD = libknot.la libknots.la @LIBOBJS@
-libknots_la_LIBADD = libzscanner.la @LIBOBJS@
-libzscanner_la_LIBADD = @LIBOBJS@
+libknots_la_LIBADD = zscanner/libzscanner.la @LIBOBJS@
 knotd_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
 knotc_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
 kdig_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
 khost_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
-knsupdate_LDADD = libknotd.la libknot.la libknots.la libzscanner.la @LIBOBJS@
-zscanner_tool_LDADD = libknots.la libknot.la libknotd.la libzscanner.la @LIBOBJS@
+knsupdate_LDADD = libknotd.la libknot.la libknots.la zscanner/libzscanner.la @LIBOBJS@
+knsec3hash_LDADD = libknot.la libknots.la @LIBOBJS@
 
 # Create storage and run-time directories
 install-data-hook:
diff --git a/src/common/acl.h b/src/common/acl.h
index 11ef1914948afd1d554f0cde914ff0417dbfa674..616e72f8afe079cafa8c7c737bb5582c85c550d7 100644
--- a/src/common/acl.h
+++ b/src/common/acl.h
@@ -34,11 +34,11 @@
 #include "common/sockaddr.h"
 
 /*! \brief ACL structure. */
-typedef list acl_t;
+typedef list_t acl_t;
 
 /*! \brief Single ACL match. */
 typedef struct acl_match {
-	node n;
+	node_t n;
 	sockaddr_t addr; /*!< \brief Address for comparison. */
 	void *val;       /*!< \brief Associated value (or NULL). */
 } acl_match_t;
diff --git a/src/common/descriptor.c b/src/common/descriptor.c
index 5f2af940a8c09218b355a1f0c0555549b020838e..a61beb4f375e52efa8d2b33982817ecf823d62ff 100644
--- a/src/common/descriptor.c
+++ b/src/common/descriptor.c
@@ -15,26 +15,20 @@
  */
 
 #include <config.h>
-#include "common/descriptor.h"
-#include "libknot/util/utils.h"		// knot_lookup_table_t
-
 #include <stdio.h>			// snprintf
 #include <stdlib.h>			// strtoul
+#include <strings.h>			// strcasecmp
 
-/*!
- * \brief The last RR type number in the descriptors table.
- */
-const int KNOT_RRTYPE_LAST = KNOT_RRTYPE_ANY;
+#include <common/descriptor.h>
 
 /*!
  * \brief Table with DNS classes.
  */
-static knot_lookup_table_t dns_classes[] = {
-	{ KNOT_CLASS_IN,	"IN" },
-	{ KNOT_CLASS_CH,	"CH" },
-	{ KNOT_CLASS_NONE,	"NONE" },
-	{ KNOT_CLASS_ANY,	"ANY" },
-	{ 0, NULL }
+static const char* dns_classes[] = {
+	[KNOT_CLASS_IN]   = "IN",
+	[KNOT_CLASS_CH]   = "CH",
+	[KNOT_CLASS_NONE] = "NONE",
+	[KNOT_CLASS_ANY]  = "ANY"
 };
 
 /*!
@@ -133,7 +127,7 @@ static const rdata_descriptor_t rdata_descriptors[] = {
 	[KNOT_RRTYPE_AXFR]       = { { KNOT_RDATA_WF_REMAINDER,
 	                               KNOT_RDATA_WF_END }, "AXFR" },
 	[KNOT_RRTYPE_ANY]        = { { KNOT_RDATA_WF_REMAINDER,
-	                               KNOT_RDATA_WF_END }, "ANY" },
+	                               KNOT_RDATA_WF_END }, "ANY" }
 };
 
 /*!
@@ -162,7 +156,8 @@ static const rdata_descriptor_t obsolete_rdata_descriptors[] = {
 
 const rdata_descriptor_t *get_rdata_descriptor(const uint16_t type)
 {
-	if (type <= KNOT_RRTYPE_ANY && rdata_descriptors[type].type_name != 0) {
+	if (type <= KNOT_RRTYPE_ANY &&
+	    rdata_descriptors[type].type_name != NULL) {
 		return &rdata_descriptors[type];
 	} else {
 		return &rdata_descriptors[0];
@@ -187,7 +182,7 @@ int knot_rrtype_to_string(const uint16_t rrtype,
 
 	const rdata_descriptor_t *descr = get_rdata_descriptor(rrtype);
 
-	if (descr->type_name != 0) {
+	if (descr->type_name != NULL) {
 		ret = snprintf(out, out_len, "%s", descr->type_name);
 	} else {
 		ret = snprintf(out, out_len, "TYPE%u", rrtype);
@@ -207,8 +202,8 @@ int knot_rrtype_from_string(const char *name, uint16_t *num)
 	unsigned long n;
 
 	// Try to find name in descriptors table.
-	for (i = 0; i <= KNOT_RRTYPE_LAST; i++) {
-		if (rdata_descriptors[i].type_name != 0 &&
+	for (i = 0; i <= KNOT_RRTYPE_ANY; i++) {
+		if (rdata_descriptors[i].type_name != NULL &&
 		    strcasecmp(rdata_descriptors[i].type_name, name) == 0) {
 			*num = i;
 			return 0;
@@ -238,10 +233,8 @@ int knot_rrclass_to_string(const uint16_t rrclass,
 {
 	int ret;
 
-	knot_lookup_table_t *entry = knot_lookup_by_id(dns_classes, rrclass);
-
-	if (entry != NULL) {
-		ret = snprintf(out, out_len, "%s", entry->name);
+	if (rrclass <= KNOT_CLASS_ANY && dns_classes[rrclass] != NULL) {
+		ret = snprintf(out, out_len, "%s", dns_classes[rrclass]);
 	} else {
 		ret = snprintf(out, out_len, "CLASS%u", rrclass);
 	}
@@ -255,15 +248,17 @@ int knot_rrclass_to_string(const uint16_t rrclass,
 
 int knot_rrclass_from_string(const char *name, uint16_t *num)
 {
+	int i;
 	char *end;
 	unsigned long n;
 
-	// Try to find name in lookup table.
-	knot_lookup_table_t *entry = knot_lookup_by_name(dns_classes, name);
-
-	if (entry != NULL) {
-		*num = entry->id;
-		return 0;
+	// Try to find the name in classes table.
+	for (i = 0; i <= KNOT_CLASS_ANY; i++) {
+		if (dns_classes[i] != NULL &&
+		    strcasecmp(dns_classes[i], name) == 0) {
+			*num = i;
+			return 0;
+		}
 	}
 
 	// Class name must begin with CLASS.
@@ -322,3 +317,12 @@ int knot_rrtype_is_metatype(const uint16_t type)
 	       type == KNOT_RRTYPE_AXFR ||
 	       type == KNOT_RRTYPE_ANY;
 }
+
+int knot_rrtype_is_ddns_forbidden(const uint16_t type)
+{
+	return type == KNOT_RRTYPE_RRSIG      ||
+	       type == KNOT_RRTYPE_DNSKEY     ||
+	       type == KNOT_RRTYPE_NSEC3PARAM ||
+	       type == KNOT_RRTYPE_NSEC       ||
+	       type == KNOT_RRTYPE_NSEC3;
+}
diff --git a/src/common/descriptor.h b/src/common/descriptor.h
index f7c2327adbf09d022fa43274a838b3a4effa35c5..b8d3b3d55aaf54863a9b3b53fea43543fd20e832 100644
--- a/src/common/descriptor.h
+++ b/src/common/descriptor.h
@@ -16,6 +16,7 @@
 /*!
  * \file descriptor.h
  *
+ * \author Daniel Salzman <daniel.salzman@nic.cz>
  * \author Jan Kadlec <jan.kadlec@nic.cz>
  *
  * \addtogroup common_lib
@@ -32,6 +33,8 @@
 
 /*!
  * \brief Resource record class codes.
+ *
+ * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml
  */
 enum knot_rr_class {
 	KNOT_CLASS_IN   =   1,
@@ -138,7 +141,7 @@ enum knot_rdata_wireformat {
 	KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
 	/*!< Initial part of NAPTR record before dname. */
 	KNOT_RDATA_WF_NAPTR_HEADER,
-	/*!< Uninteresting final part of a record. */
+	/*!< Final part of a record. */
 	KNOT_RDATA_WF_REMAINDER,
 	/*!< The last descriptor in array. */
 	KNOT_RDATA_WF_END                =   0
@@ -229,7 +232,7 @@ int knot_rrclass_from_string(const char *name, uint16_t *num);
  *
  * \param item Item value.
  *
- * \retval 1 if YES.
+ * \retval > 0 if YES.
  * \retval 0 if NO.
  */
 int descriptor_item_is_dname(const int item);
@@ -239,7 +242,7 @@ int descriptor_item_is_dname(const int item);
  *
  * \param item Item value.
  *
- * \retval 1 if YES.
+ * \retval > 0 if YES.
  * \retval 0 if NO.
  */
 int descriptor_item_is_compr_dname(const int item);
@@ -269,11 +272,21 @@ int descriptor_item_is_remainder(const int item);
  *
  * \param item Item value.
  *
- * \retval 1 if YES.
+ * \retval > 0 if YES.
  * \retval 0 if NO.
  */
 int knot_rrtype_is_metatype(const uint16_t type);
 
+/*!
+ * \brief Checks if given item is one of the types we do not accept via DDNS.
+ *
+ * \param item Item value.
+ *
+ * \retval > 0 if YES.
+ * \retval 0 if NO.
+ */
+int knot_rrtype_is_ddns_forbidden(const uint16_t type);
+
 #endif // _KNOT_DESCRIPTOR_H_
 
 /*! @} */
diff --git a/src/common/errcode.c b/src/common/errcode.c
index 4f60ed91d4771023fce7d6c8ee02ac7b112b2e8c..10b6546e828d0aeb81ab171f01a7d1e6ab29e358 100644
--- a/src/common/errcode.c
+++ b/src/common/errcode.c
@@ -73,6 +73,7 @@ const error_table_t knot_error_msgs[] = {
 	{ KNOT_EDSDIGESTLEN, "DS digest length does not match digest type." },
 	{ KNOT_ENOTSIG, "expected a TSIG or SIG(0)" },
 	{ KNOT_ELIMIT, "Exceeded response rate limit." },
+	{ KNOT_EWRITABLE, "File is not writable." },
 
 	/* Control states. */
 	{ KNOT_CTL_STOP, "Stopping server." },
@@ -85,74 +86,6 @@ const error_table_t knot_error_msgs[] = {
 	{ KNOT_NET_ERECV, "Can't receive data." },
 	{ KNOT_NET_ETIMEOUT, "Network timeout." },
 
-	/* Zone file loader errors. */
-	{ FLOADER_EFSTAT, "Fstat error." },
-	{ FLOADER_EDIRECTORY, "Zone file is a directory." },
-	{ FLOADER_EEMPTY, "Empty zone file." },
-	{ FLOADER_EDEFAULTS, "Zone defaults processing error." },
-	{ FLOADER_EMMAP, "Mmap error." },
-	{ FLOADER_EMUNMAP, "Munmap error." },
-	{ FLOADER_ESCANNER, "Zone processing error." },
-
-	/* Zone scanner errors. */
-	{ ZSCANNER_UNCOVERED_STATE, "General scanner error." },
-	{ ZSCANNER_UNCLOSED_MULTILINE, "Unclosed last multiline block." },
-	{ ZSCANNER_ELEFT_PARENTHESIS, "Too many left parentheses." },
-	{ ZSCANNER_ERIGHT_PARENTHESIS, "Too many right parentheses." },
-	{ ZSCANNER_EUNSUPPORTED_TYPE, "Unsupported record type." },
-	{ ZSCANNER_EBAD_PREVIOUS_OWNER, "Previous owner is invalid." },
-	{ ZSCANNER_EBAD_DNAME_CHAR, "Bad domain name character." },
-	{ ZSCANNER_EBAD_OWNER, "Owner is invalid." },
-	{ ZSCANNER_ELABEL_OVERFLOW, "Maximal domain name label length has exceeded." },
-	{ ZSCANNER_EDNAME_OVERFLOW, "Maximal domain name length has exceeded." },
-	{ ZSCANNER_EBAD_NUMBER, "Bad number." },
-	{ ZSCANNER_ENUMBER64_OVERFLOW, "Number is too big." },
-	{ ZSCANNER_ENUMBER32_OVERFLOW, "Number is bigger than 32 bits." },
-	{ ZSCANNER_ENUMBER16_OVERFLOW, "Number is bigger than 16 bits." },
-	{ ZSCANNER_ENUMBER8_OVERFLOW, "Number is bigger than 8 bits." },
-	{ ZSCANNER_EFLOAT_OVERFLOW, "Float number overflow." },
-	{ ZSCANNER_ERDATA_OVERFLOW, "Maximal record data length has exceeded." },
-	{ ZSCANNER_EITEM_OVERFLOW, "Maximal item length has exceeded." },
-	{ ZSCANNER_EBAD_ADDRESS_CHAR, "Bad address character." },
-	{ ZSCANNER_EBAD_IPV4, "Bad IPv4 address." },
-	{ ZSCANNER_EBAD_IPV6, "Bad IPv6 address." },
-	{ ZSCANNER_EBAD_GATEWAY, "Bad gateway." },
-	{ ZSCANNER_EBAD_GATEWAY_KEY, "Bad gateway key." },
-	{ ZSCANNER_EBAD_APL, "Bad adress prefix list." },
-	{ ZSCANNER_EBAD_RDATA, "Bad record data." },
-	{ ZSCANNER_EBAD_HEX_RDATA, "Bad record data in hex format." },
-	{ ZSCANNER_EBAD_HEX_CHAR, "Bad hexadecimal character." },
-	{ ZSCANNER_EBAD_BASE64_CHAR, "Bad Base64 character." },
-	{ ZSCANNER_EBAD_BASE32HEX_CHAR, "Bad Base32hex character." },
-	{ ZSCANNER_EBAD_REST, "Unexpected data." },
-	{ ZSCANNER_EBAD_TIMESTAMP_CHAR, "Bad timestamp character." },
-	{ ZSCANNER_EBAD_TIMESTAMP_LENGTH, "Bad timestamp length." },
-	{ ZSCANNER_EBAD_TIMESTAMP, "Bad timestamp." },
-	{ ZSCANNER_EBAD_DATE, "Bad date." },
-	{ ZSCANNER_EBAD_TIME, "Bad time." },
-	{ ZSCANNER_EBAD_TIME_UNIT, "Bad time unit." },
-	{ ZSCANNER_EBAD_BITMAP, "Bad bitmap." },
-	{ ZSCANNER_ETEXT_OVERFLOW, "Text is too long." },
-	{ ZSCANNER_EBAD_TEXT_CHAR, "Bad text character." },
-	{ ZSCANNER_EBAD_TEXT, "Bad text string." },
-	{ ZSCANNER_EBAD_DIRECTIVE, "Bad directive." },
-	{ ZSCANNER_EBAD_TTL, "Bad zone TTL." },
-	{ ZSCANNER_EBAD_ORIGIN, "Bad zone origin." },
-	{ ZSCANNER_EBAD_INCLUDE_FILENAME, "Bad filename in include directive." },
-	{ ZSCANNER_EBAD_INCLUDE_ORIGIN, "Bad origin in include directive." },
-	{ ZSCANNER_EUNPROCESSED_INCLUDE, "Include file processing error." },
-	{ ZSCANNER_EUNOPENED_INCLUDE, "Include file opening error." },
-	{ ZSCANNER_EBAD_RDATA_LENGTH, "The rdata length statement is incorrect." },
-	{ ZSCANNER_ECANNOT_TEXT_DATA, "Unable to process text form for this type." },
-	{ ZSCANNER_EBAD_LOC_DATA, "Bad zone location data." },
-	{ ZSCANNER_EUNKNOWN_BLOCK, "Unknown rdata block." },
-	{ ZSCANNER_EBAD_ALGORITHM, "Bad algorithm." },
-	{ ZSCANNER_EBAD_CERT_TYPE, "Bad certificate type." },
-	{ ZSCANNER_EBAD_EUI_LENGTH, "Bad EUI length." },
-	{ ZSCANNER_EBAD_L64_LENGTH, "Bad 64-bit locator." },
-	{ ZSCANNER_EBAD_CHAR_COLON, "Missing colon character." },
-	{ ZSCANNER_EBAD_CHAR_DASH, "Missing dash character." },
-
 	/* Encoding errors. */
 	{ KNOT_BASE64_ESIZE, "Invalid base64 string length." },
 	{ KNOT_BASE64_ECHAR, "Invalid base64 character." },
@@ -164,14 +97,18 @@ const error_table_t knot_error_msgs[] = {
 	{ KNOT_KEY_EPRIVATE_KEY_OPEN, "Cannot open private key file." },
 	{ KNOT_KEY_EPUBLIC_KEY_INVALID, "Public key file is invalid." },
 
-	/* Key signing errors. */
+	/* Key signing/verification errors. */
 	{ KNOT_DNSSEC_ENOTSUP, "Signing algorithm is not supported." },
 	{ KNOT_DNSSEC_EINVALID_KEY, "The signing key is invalid." },
 	{ KNOT_DNSSEC_EASSIGN_KEY, "Cannot assign the key." },
 	{ KNOT_DNSSEC_ECREATE_DIGEST_CONTEXT, "Cannot create digest context." },
 	{ KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE, "Unexpected signature size." },
 	{ KNOT_DNSSEC_EDECODE_RAW_SIGNATURE, "Cannot decode the raw signature." },
+	{ KNOT_DNSSEC_EINVALID_SIGNATURE, "Signature is invalid." },
 	{ KNOT_DNSSEC_ESIGN, "Cannot create the signature." },
 
+	/* NSEC3 errors. */
+	{ KNOT_NSEC3_ECOMPUTE_HASH, "Cannot compute NSEC3 hash." },
+
 	{ KNOT_ERROR, 0 } /* Terminator */
 };
diff --git a/src/common/errcode.h b/src/common/errcode.h
index 7436b069d3f03e2bc133e79df89fabb970ac2e98..038cb16d6965c36b6912db8a989d2cf89401b686 100644
--- a/src/common/errcode.h
+++ b/src/common/errcode.h
@@ -92,6 +92,7 @@ enum knot_error {
 	KNOT_EDSDIGESTLEN,    /*!< DS digest length does not match digest type. */
 	KNOT_ENOTSIG,         /*!< Expected a TSIG or SIG(0). */
 	KNOT_ELIMIT,          /*!< Exceeded response rate limit. */
+	KNOT_EWRITABLE,       /*!< File is not writable. */
 
 	/* Control states. */
 	KNOT_CTL_STOP,        /*!< Stop requested. */
@@ -104,75 +105,6 @@ enum knot_error {
 	KNOT_NET_ERECV,
 	KNOT_NET_ETIMEOUT,
 
-	/* Zone file loader errors. */
-	FLOADER_EFSTAT,
-	FLOADER_EDIRECTORY,
-	FLOADER_EWRITABLE,
-	FLOADER_EEMPTY,
-	FLOADER_EDEFAULTS,
-	FLOADER_EMMAP,
-	FLOADER_EMUNMAP,
-	FLOADER_ESCANNER,
-
-	/* Zone scanner errors. */
-	ZSCANNER_UNCOVERED_STATE,
-	ZSCANNER_UNCLOSED_MULTILINE,
-	ZSCANNER_ELEFT_PARENTHESIS,
-	ZSCANNER_ERIGHT_PARENTHESIS,
-	ZSCANNER_EUNSUPPORTED_TYPE,
-	ZSCANNER_EBAD_PREVIOUS_OWNER,
-	ZSCANNER_EBAD_DNAME_CHAR,
-	ZSCANNER_EBAD_OWNER,
-	ZSCANNER_ELABEL_OVERFLOW,
-	ZSCANNER_EDNAME_OVERFLOW,
-	ZSCANNER_EBAD_NUMBER,
-	ZSCANNER_ENUMBER64_OVERFLOW,
-	ZSCANNER_ENUMBER32_OVERFLOW,
-	ZSCANNER_ENUMBER16_OVERFLOW,
-	ZSCANNER_ENUMBER8_OVERFLOW,
-	ZSCANNER_EFLOAT_OVERFLOW,
-	ZSCANNER_ERDATA_OVERFLOW,
-	ZSCANNER_EITEM_OVERFLOW,
-	ZSCANNER_EBAD_ADDRESS_CHAR,
-	ZSCANNER_EBAD_IPV4,
-	ZSCANNER_EBAD_IPV6,
-	ZSCANNER_EBAD_GATEWAY,
-	ZSCANNER_EBAD_GATEWAY_KEY,
-	ZSCANNER_EBAD_APL,
-	ZSCANNER_EBAD_RDATA,
-	ZSCANNER_EBAD_HEX_RDATA,
-	ZSCANNER_EBAD_HEX_CHAR,
-	ZSCANNER_EBAD_BASE64_CHAR,
-	ZSCANNER_EBAD_BASE32HEX_CHAR,
-	ZSCANNER_EBAD_REST,
-	ZSCANNER_EBAD_TIMESTAMP_CHAR,
-	ZSCANNER_EBAD_TIMESTAMP_LENGTH,
-	ZSCANNER_EBAD_TIMESTAMP,
-	ZSCANNER_EBAD_DATE,
-	ZSCANNER_EBAD_TIME,
-	ZSCANNER_EBAD_TIME_UNIT,
-	ZSCANNER_EBAD_BITMAP,
-	ZSCANNER_ETEXT_OVERFLOW,
-	ZSCANNER_EBAD_TEXT_CHAR,
-	ZSCANNER_EBAD_TEXT,
-	ZSCANNER_EBAD_DIRECTIVE,
-	ZSCANNER_EBAD_TTL,
-	ZSCANNER_EBAD_ORIGIN,
-	ZSCANNER_EBAD_INCLUDE_FILENAME,
-	ZSCANNER_EBAD_INCLUDE_ORIGIN,
-	ZSCANNER_EUNPROCESSED_INCLUDE,
-	ZSCANNER_EUNOPENED_INCLUDE,
-	ZSCANNER_EBAD_RDATA_LENGTH,
-	ZSCANNER_ECANNOT_TEXT_DATA,
-	ZSCANNER_EBAD_LOC_DATA,
-	ZSCANNER_EUNKNOWN_BLOCK,
-	ZSCANNER_EBAD_ALGORITHM,
-	ZSCANNER_EBAD_CERT_TYPE,
-	ZSCANNER_EBAD_EUI_LENGTH,
-	ZSCANNER_EBAD_L64_LENGTH,
-	ZSCANNER_EBAD_CHAR_COLON,
-	ZSCANNER_EBAD_CHAR_DASH,
-
 	/* Encoding errors. */
 	KNOT_BASE64_ESIZE,
 	KNOT_BASE64_ECHAR,
@@ -191,7 +123,11 @@ enum knot_error {
 	KNOT_DNSSEC_ECREATE_DIGEST_CONTEXT,
 	KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE,
 	KNOT_DNSSEC_EDECODE_RAW_SIGNATURE,
-	KNOT_DNSSEC_ESIGN
+	KNOT_DNSSEC_EINVALID_SIGNATURE,
+	KNOT_DNSSEC_ESIGN,
+
+	/* NSEC3 errors. */
+	KNOT_NSEC3_ECOMPUTE_HASH
 };
 
 /*! \brief Table linking error messages to error codes. */
diff --git a/src/common/evqueue.h b/src/common/evqueue.h
index 794b2d53d994c6744be32ca4e8bf383eaa6ab282..f37afe3963c490d70462a9e6b742ff3758941f83 100644
--- a/src/common/evqueue.h
+++ b/src/common/evqueue.h
@@ -56,7 +56,7 @@ typedef int (*event_cb_t)(struct event_t *);
  * \brief Event structure.
  */
 typedef struct event_t {
-	node n;            /*!< Node for event queue. */
+	node_t n;          /*!< Node for event queue. */
 	int type;          /*!< Event type. */
 	struct timeval tv; /*!< Event scheduled time. */
 	void *data;        /*!< Usable data ptr. */
diff --git a/src/common/hex.c b/src/common/hex.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb4ac869e9ffe4225b8a8f0355235af28692e32f
--- /dev/null
+++ b/src/common/hex.c
@@ -0,0 +1,78 @@
+/*  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 <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common/errcode.h"
+
+/*!
+ * \brief Convert HEX char to byte.
+ * \note Expects valid lowercase letters.
+ */
+static uint8_t hex_to_num(int c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else
+		return c - 'a' + 10;
+}
+
+/*!
+ * \brief Convert string encoded in hex to bytes.
+ */
+int hex_decode(const char *input, uint8_t **output, size_t *output_size)
+{
+	if (!input || !output || !output_size) {
+		return KNOT_EINVAL;
+	}
+
+	// input validation (length and content)
+
+	size_t input_size = strlen(input);
+	if (input_size % 2 != 0) {
+		return KNOT_EINVAL;
+	}
+
+	for (size_t i = 0; i < input_size; i++) {
+		if (!isxdigit((unsigned char)input[i])) {
+			return KNOT_EINVAL;
+		}
+	}
+
+	// output allocation
+
+	size_t result_size = input_size / 2;
+	uint8_t *result = malloc(result_size * sizeof(uint8_t));
+	if (!result) {
+		return KNOT_ENOMEM;
+	}
+
+	// conversion
+
+	for (size_t i = 0; i < result_size; i++) {
+		int high_nib = tolower((unsigned char)input[2 * i]);
+		int low_nib  = tolower((unsigned char)input[2 * i + 1]);
+
+		result[i] = hex_to_num(high_nib) << 4 | hex_to_num(low_nib);
+	}
+
+	*output = result;
+	*output_size = result_size;
+
+	return KNOT_EOK;
+}
diff --git a/src/libknot/sign/bnutils.h b/src/common/hex.h
similarity index 60%
rename from src/libknot/sign/bnutils.h
rename to src/common/hex.h
index 6eedb698435594f9017d3d193431e406a207db82..54262ed4bddf535ba858def8055c589ae4e1dc1f 100644
--- a/src/libknot/sign/bnutils.h
+++ b/src/common/hex.h
@@ -13,30 +13,34 @@
     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 bnutils.h
+ * \file hex.h
  *
- * \author Jan Vcelak <jan.vcelak@nic.cz>
+ * \brief Coversion between HEX strings and bytes.
  *
- * \brief Conversion between Base64 and OpenSSL BIGNUM formats.
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
  *
- * \addtogroup dnssec
+ * \addtogroup common_lib
  * @{
  */
 
-#ifndef _KNOT_SIGN_BNUTILS_H_
-#define _KNOT_SIGN_BNUTILS_H_
+#ifndef _KNOT_COMMON_HEX_H_
+#define _KNOT_COMMON_HEX_H_
 
-#include <openssl/bn.h>
+#include <stdint.h>
+#include <stdlib.h>
 
 /*!
- * \brief Convert Base64 encoded number into OpenSSL BIGNUM format.
+ * \brief Convert string encoded in hex to bytes.
  *
- * \param input  Base64 encoded input number
- * \return Input number represented in OpenSSL BIGNUM format.
+ * \param input        Hex encoded input string.
+ * \param output       Decoded bytes.
+ * \param output_size  Size of the output.
+ *
+ * \return Error code, KNOT_EOK if successful.
  */
-BIGNUM *knot_b64_to_bignum(const char *input);
-
-#endif // _KNOT_SIGN_BNUTILS_H_
+int hex_decode(const char *input, uint8_t **output, size_t *output_size);
 
+#endif // _KNOT_COMMON_HEX_H_
 /*! @} */
diff --git a/src/common/lists.c b/src/common/lists.c
index e629e9dcc24e5431a0ddd8a0a0fb82f4a7918fcf..363c0ed148e86cbaa546f7c055ec59fa11af3486 100644
--- a/src/common/lists.c
+++ b/src/common/lists.c
@@ -39,11 +39,11 @@
  * add_tail() takes a node @n and appends it at the end of the list @l.
  */
 LIST_INLINE void
-add_tail(list *l, node *n)
+add_tail(list_t *l, node_t *n)
 {
-  node *z = l->tail;
+  node_t *z = l->tail;
 
-  n->next = (node *) &l->null;
+  n->next = (node_t *) &l->null;
   n->prev = z;
   z->next = n;
   l->tail = n;
@@ -57,12 +57,12 @@ add_tail(list *l, node *n)
  * add_head() takes a node @n and prepends it at the start of the list @l.
  */
 LIST_INLINE void
-add_head(list *l, node *n)
+add_head(list_t *l, node_t *n)
 {
-  node *z = l->head;
+  node_t *z = l->head;
 
   n->next = z;
-  n->prev = (node *) &l->head;
+  n->prev = (node_t *) &l->head;
   z->prev = n;
   l->head = n;
 }
@@ -76,9 +76,9 @@ add_head(list *l, node *n)
  * node @after.
  */
 LIST_INLINE void
-insert_node(node *n, node *after)
+insert_node(node_t *n, node_t *after)
 {
-  node *z = after->next;
+  node_t *z = after->next;
 
   n->next = z;
   n->prev = after;
@@ -93,10 +93,10 @@ insert_node(node *n, node *after)
  * Removes a node @n from the list it's linked in.
  */
 LIST_INLINE void
-rem_node(node *n)
+rem_node(node_t *n)
 {
-  node *z = n->prev;
-  node *x = n->next;
+  node_t *z = n->prev;
+  node_t *x = n->next;
 
   z->next = x;
   x->prev = z;
@@ -112,11 +112,11 @@ rem_node(node *n)
  * fields, so that it represents an empty list.
  */
 LIST_INLINE void
-init_list(list *l)
+init_list(list_t *l)
 {
-  l->head = (node *) &l->null;
+  l->head = (node_t *) &l->null;
   l->null = NULL;
-  l->tail = (node *) &l->head;
+  l->tail = (node_t *) &l->head;
 }
 
 /**
@@ -128,15 +128,15 @@ init_list(list *l)
  * the list @to in constant time.
  */
 LIST_INLINE void
-add_tail_list(list *to, list *l)
+add_tail_list(list_t *to, list_t *l)
 {
-  node *p = to->tail;
-  node *q = l->head;
+  node_t *p = to->tail;
+  node_t *q = l->head;
 
   p->next = q;
   q->prev = p;
   q = l->tail;
-  q->next = (node *) &to->null;
+  q->next = (node_t *) &to->null;
   to->tail = q;
 }
 
@@ -150,11 +150,11 @@ add_tail_list(list *to, list *l)
  *
  * This function only works with a homogenous item size.
  */
-void list_dup(list *dst, list *src, size_t itemsz)
+void list_dup(list_t *dst, list_t *src, size_t itemsz)
 {
-	node *n = 0;
+	node_t *n = 0;
 	WALK_LIST(n, *src) {
-		node *i = malloc(itemsz);
+		node_t *i = malloc(itemsz);
 		memcpy(i, n, itemsz);
 		add_tail(dst, i);
 	}
@@ -166,11 +166,11 @@ void list_dup(list *dst, list *src, size_t itemsz)
  *
  * This function counts nodes in list @l and returns this number.
  */
-size_t list_size(const list *l)
+size_t list_size(const list_t *l)
 {
 	size_t count = 0;
 
-	node *n = 0;
+	node_t *n = 0;
 	WALK_LIST(n, *l) {
 		count++;
 	}
diff --git a/src/common/lists.h b/src/common/lists.h
index 392f75dd73943548eaa4fa6dc1d369db07701d17..373fb538b0f930a86b8f67f1a72ab4695519f02d 100644
--- a/src/common/lists.h
+++ b/src/common/lists.h
@@ -41,13 +41,13 @@
 
 typedef struct node {
   struct node *next, *prev;
-} node;
+} node_t;
 
 typedef struct list {			/* In fact two overlayed nodes */
   struct node *head, *null, *tail;
-} list;
+} list_t;
 
-#define NODE (node *)
+#define NODE (node_t *)
 #define HEAD(list) ((void *)((list).head))
 #define TAIL(list) ((void *)((list).tail))
 #define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; \
@@ -67,27 +67,27 @@ typedef struct list {			/* In fact two overlayed nodes */
 /*! \brief Free every node in the list. */
 #define WALK_LIST_FREE(list) \
 	do { \
-	node *n=0,*nxt=0;  \
+	node_t *n=0,*nxt=0;  \
 	WALK_LIST_DELSAFE(n,nxt,list) { \
 	    free(n); \
 	} \
 	init_list(&list); \
 	} while(0)
 
-void add_tail(list *, node *);
-void add_head(list *, node *);
-void rem_node(node *);
-void add_tail_list(list *, list *);
-void init_list(list *);
-void insert_node(node *, node *);
-void list_dup(list *dst, list *src, size_t itemsz);
-size_t list_size(const list *);
+void add_tail(list_t *, node_t *);
+void add_head(list_t *, node_t *);
+void rem_node(node_t *);
+void add_tail_list(list_t *, list_t *);
+void init_list(list_t *);
+void insert_node(node_t *, node_t *);
+void list_dup(list_t *dst, list_t *src, size_t itemsz);
+size_t list_size(const list_t *);
 
 /*!
  * \brief List item for string lists.
  */
 typedef struct strnode_t {
-	node n;
+	node_t n;
 	char *str;
 } strnode_t;
 
diff --git a/src/common/memdup.h b/src/common/memdup.h
new file mode 100644
index 0000000000000000000000000000000000000000..063c155e9ef90b01991686913ec2e9cc0403510c
--- /dev/null
+++ b/src/common/memdup.h
@@ -0,0 +1,34 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KNOT_COMMON_MEMDUP
+#define KNOT_COMMON_MEMDUP
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint8_t *knot_memdup(const uint8_t *data, size_t data_size)
+{
+	uint8_t *result = (uint8_t *)malloc(data_size);
+	if (!result) {
+		return NULL;
+	}
+
+	return memcpy(result, data, data_size);
+}
+
+#endif // KNOT_COMMON_MEMDUP
diff --git a/src/common/mempattern.h b/src/common/mempattern.h
index 7bef8513616633dfcbcff69a692b9d9b64d7223a..c7062cafadaaa7c2439fe586702ad1a6f071596b 100644
--- a/src/common/mempattern.h
+++ b/src/common/mempattern.h
@@ -34,6 +34,9 @@ typedef void* (*mm_alloc_t)(void* ctx, size_t len);
 typedef void (*mm_free_t)(void *p);
 typedef void (*mm_flush_t)(void *p);
 
+/* Reusable functions. */
+static inline void mm_nofree(void *p) {}
+
 /* Memory allocation context. */
 typedef struct mm_ctx {
 	void *ctx; /* \note Must be first */
diff --git a/src/common/sockaddr.c b/src/common/sockaddr.c
index f87b45844493b60e63dd396ae01cd13dca79e734..b82edc8c8d98d7d533f8a11f62512d83a47ea9fc 100644
--- a/src/common/sockaddr.c
+++ b/src/common/sockaddr.c
@@ -198,8 +198,8 @@ void sockaddr_prep(sockaddr_t *addr)
 char *sockaddr_hostname(void)
 {
 	/* Fetch hostname. */
-	char host[KNOT_MAX_DNAME_LENGTH];
-	if (gethostname(host, KNOT_MAX_DNAME_LENGTH) != 0) {
+	char host[KNOT_DNAME_MAXLEN];
+	if (gethostname(host, KNOT_DNAME_MAXLEN) != 0) {
 		return NULL;
 	}
 
diff --git a/src/common/strtonum.h b/src/common/strtonum.h
new file mode 100644
index 0000000000000000000000000000000000000000..926ce888bd415e4f020f17e7930439ae8a0b03b5
--- /dev/null
+++ b/src/common/strtonum.h
@@ -0,0 +1,128 @@
+/*  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 strtonum.h
+ *
+ * \brief Universal interface for safe conversion of strings to numbers.
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOT_COMMON_STRTONUM_
+#define _KNOT_COMMON_STRTONUM_
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "common/errcode.h"
+
+typedef long long int knot_strtoll_result_t;
+typedef unsigned long long int knot_strtoull_result_t;
+
+/*!
+ * \brief Convert string to signed integer.
+ *
+ * \param[in]  src   Input string.
+ * \param[out] dest  Output integral value.
+ *
+ * \return Error code.
+ * \retval KNOT_EOK     The conversion was successful.
+ * \retval KNOT_ERANGE  The value is outside target type range.
+ * \retval KNOT_EMALF   The input value is not terminated.
+ */
+static int knot_strtoll(const char *src, knot_strtoll_result_t *dest)
+{
+	char *end;
+	knot_strtoll_result_t result = strtoll(src, &end, 10);
+	if (errno == ERANGE)
+		return KNOT_ERANGE;
+
+	if (src == end || *end != '\0')
+		return KNOT_EMALF;
+
+	*dest = result;
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Convert string to unsigned integer.
+ *
+ * \see knot_strtoll
+ */
+static int knot_strtoull(const char *src, knot_strtoull_result_t *dest)
+{
+	char *end;
+	knot_strtoull_result_t result = strtoull(src, &end, 10);
+	if (errno == ERANGE)
+		return KNOT_ERANGE;
+
+	if (src == end || *end != '\0')
+		return KNOT_EMALF;
+
+	*dest = result;
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Helper macro defining body of individual conversion functions.
+ *
+ * \param type      Target data type.
+ * \param function  Underlying conversion function.
+ * \param min       Minimal value valid for given data type.
+ * \param max       Maximal value valid for given data type.
+ * \param src       Pointer to source string.
+ * \param dest      Pointer to destination type.
+ *
+ * \return Error code.
+ * \retval KNOT_EOK     The conversion was successful.
+ * \retval KNOT_ERANGE  The value is outside target type range.
+ * \retval KNOT_EMALF   The input value is not terminated.
+ */
+#define KNOT_STR2NUM(type, function, min, max, src, dest) \
+{                                                         \
+	function##_result_t value;                        \
+	errno = 0;                                        \
+	int result = function((src), &value);             \
+	if (result != KNOT_EOK)                           \
+		return result;                            \
+	                                                  \
+	if (value < (min) || value > (max))               \
+		return KNOT_ERANGE;                       \
+	                                                  \
+	*(dest) = (type)value;                            \
+	return KNOT_EOK;                                  \
+}
+
+inline static int knot_str2int(const char *src, int *dest)
+{
+	KNOT_STR2NUM(int, knot_strtoll, INT_MIN, INT_MAX, src, dest)
+}
+
+inline static int knot_str2uint8t(const char *src, uint8_t *dest)
+{
+	KNOT_STR2NUM(uint8_t, knot_strtoull, 0, UINT8_MAX, src, dest)
+}
+
+inline static int knot_str2uint16t(const char *src, uint16_t *dest)
+{
+	KNOT_STR2NUM(uint16_t, knot_strtoull, 0, UINT16_MAX, src, dest)
+}
+
+#endif // _KNOT_COMMON_STRTONUM_
+/*! @} */
diff --git a/src/knot.service b/src/knot.service
deleted file mode 100644
index da800cea178a320a924ce843acd17cbc701bf70d..0000000000000000000000000000000000000000
--- a/src/knot.service
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=Knot DNS daemon
-After=syslog.target network.target auditd.service
-
-[Service]
-EnvironmentFile=/etc/sysconfig/knot
-ExecReload=/usr/sbin/knotc reload
-ExecStart=/usr/sbin/knotd $KNOTARGS
-Restart=on-abort
-
-[Install]
-WantedBy=multi-user.target
diff --git a/src/knot.spec b/src/knot.spec
deleted file mode 100644
index 65191c4f1ac494e4c6dedc384627f86141345ba6..0000000000000000000000000000000000000000
--- a/src/knot.spec
+++ /dev/null
@@ -1,90 +0,0 @@
-Summary: KNOT DNS daemon
-Name: knot
-Version: 0.9.1
-Release: 1
-License: GPL
-Group: Networking/Daemons
-Source: http://public.nic.cz/files/knot-dns/knot-%{version}.tar.gz
-Source1: %{name}.sysconfig
-Source2: %{name}.service
-Patch: %{name}.diff
-Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-build
-Url: http://www.knot-dns.cz
-BuildRequires: flex userspace-rcu-devel openssl-devel bison
-
-%description
-KNOT DNS is a high-performance authoritative DNS server implementation.
-
-%prep
-%setup -n %{name}-%{version}
-%patch -p1
-
-
-%build
-./configure --prefix=%{_prefix} --sysconfdir=%{_sysconfdir}/%{name} --localstatedir=%{_var}/lib --libexecdir=%{_libexecdir}/%{name}
-make -C samples %{name}.sample.conf
-make
-
-
-%install
-rm -rf %{buildroot}/*
-make install prefix=%{buildroot}/%{_prefix} sysconfdir=%{buildroot}/%{_sysconfdir}/%{name} localstatedir=%{buildroot}/%{_var}/lib mandir=%{buildroot}/%{_mandir} libexecdir=%{buildroot}/%{_libexecdir}/%{name}
-
-install -d %{buildroot}/%{_sysconfdir}/sysconfig
-install $RPM_SOURCE_DIR/%{name}.sysconfig %{buildroot}/%{_sysconfdir}/sysconfig/%{name}
-install -d %{buildroot}/%{_var}/lib/%{name}
-install -d %{buildroot}/lib/systemd/system/
-install $RPM_SOURCE_DIR/%{name}.service %{buildroot}/lib/systemd/system/%{name}.service
-
-%post
-# run after an installation
-if [ $1 -eq 1 ] ; then
-    # Initial installation
-    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
-fi
-
-%preun
-# run before a package is removed
-if [ $1 -eq 0 ]; then
-    /bin/systemctl --no-reload disable %{name}.service >/dev/null 2>&1 || :
-    /bin/systemctl stop %{name}.service > /dev/null 2>&1 || :
-fi
-
-%postun
-# run after a package is removed
-/bin/systemctl daemon-reload >/dev/null 2>&1 || :
-if [ $1 -ge 1 ]; then
-    /bin/systemctl try-restart %{name}.service >/dev/null 2>&1 || :
-fi
-
-%triggerun -- knot < 0.9
-# Save the current service runlevel info
-# User must manually run systemd-sysv-convert --apply %{name}
-# to migrate them to systemd targets
-/usr/bin/systemd-sysv-convert --save %{name}
-
-/sbin/chkconfig --del %{name} >/dev/null 2>&1 || :
-/bin/systemctl try-restart %{name}.service >/dev/null 2>&1 || :
-/bin/systemctl daemon-reload >/dev/null 2>&1 || :
-
-
-
-%files
-%defattr(-,root,root,-)
-%config %attr(644,root,root) %{_sysconfdir}/%{name}/*
-%{_sbindir}/*
-%{_libexecdir}/%{name}/*
-%dir %{_var}/lib/%{name}/
-%doc %{_mandir}/man8/*
-%attr(0644,root,root) /lib/systemd/system/%{name}.service
-%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
-
-
-
-%changelog
-* Mon Jan 16 2012 - feela@network.cz
-- Support for systemd.
-- Specfile cleanup
-
-* Thu Nov 3 2011 - feela@network.cz
-- Initial version
diff --git a/src/knot.sysconfig b/src/knot.sysconfig
deleted file mode 100644
index 857f9f20eed8a97465b90659ae9827251ef2adb4..0000000000000000000000000000000000000000
--- a/src/knot.sysconfig
+++ /dev/null
@@ -1,3 +0,0 @@
-# Settings for the KNOT DNS daemon.
-# KNOTARGS= :  any extra command-line startup arguments for knot dns
-KNOTARGS=
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index db2c8dae1836e93dfc87a307e93a234e6e09722a..46a435ad04a441c85d72a53358b62838a0c3e787 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -125,6 +125,9 @@ rate-limit      { lval.t = yytext; return RATE_LIMIT; }
 rate-limit-size { lval.t = yytext; return RATE_LIMIT_SIZE; }
 rate-limit-slip { lval.t = yytext; return RATE_LIMIT_SLIP; }
 transfers       { lval.t = yytext; return TRANSFERS; }
+dnssec-enable   { lval.t = yytext; return DNSSEC_ENABLE; }
+dnssec-keydir   { lval.t = yytext; return DNSSEC_KEYDIR; }
+signature-lifetime { lval.t = yytext; return SIGNATURE_LIFETIME; }
 
 interfaces      { lval.t = yytext; return INTERFACES; }
 address         { lval.t = yytext; return ADDRESS; }
@@ -306,6 +309,7 @@ hmac-sha512     { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
 	}
 
 	yypop_buffer_state(yyscanner);
+
 	if (!YY_CURRENT_BUFFER) {
 		return END;
 	}
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index e2539f170fe5e0f46b9083693bc1410dea4e3748..74e51df6d759e30163f096fd793a6cdf7bb0419d 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -42,7 +42,7 @@ static conf_iface_t *this_iface = 0;
 static conf_iface_t *this_remote = 0;
 static conf_zone_t *this_zone = 0;
 static conf_group_t *this_group = 0;
-static list *this_list = 0;
+static list_t *this_list = 0;
 static conf_log_t *this_log = 0;
 static conf_log_map_t *this_logmap = 0;
 //#define YYERROR_VERBOSE 1
@@ -100,7 +100,7 @@ static void conf_start_remote(void *scanner, char *remote)
 
 static void conf_remote_set_via(void *scanner, char *item) {
    /* Find existing node in interfaces. */
-   node* r = 0; conf_iface_t* found = 0;
+   node_t* r = 0; conf_iface_t* found = 0;
    WALK_LIST (r, new_config->ifaces) {
       if (strcmp(((conf_iface_t*)r)->name, item) == 0) {
          found = (conf_iface_t*)r;
@@ -176,7 +176,7 @@ static void conf_add_member_into_group(void *scanner, char *name)
 	// add the remote into the group while silently ignoring duplicates
 
 	conf_group_remote_t *remote;
-	node *n;
+	node_t *n;
 	WALK_LIST (n, this_group->remotes) {
 		remote = (conf_group_remote_t *)n;
 		if (strcmp(remote->name, name) == 0) {
@@ -264,10 +264,11 @@ static void conf_acl_item(void *scanner, char *item)
 static int conf_key_exists(void *scanner, char *item)
 {
     /* Find existing node in keys. */
-    knot_dname_t *sample = knot_dname_new_from_str(item, strlen(item), 0);
+    knot_dname_t *sample = knot_dname_from_str(item, strlen(item));
+    knot_dname_to_lower(sample);
     conf_key_t* r = 0;
     WALK_LIST (r, new_config->keys) {
-        if (knot_dname_compare(r->k.name, sample) == 0) {
+        if (knot_dname_cmp(r->k.name, sample) == 0) {
            cf_error(scanner, "key '%s' is already defined", item);
 	   knot_dname_free(&sample);
            return 1;
@@ -284,11 +285,12 @@ static int conf_key_add(void *scanner, knot_tsig_key_t **key, char *item)
     *key = 0;
 
     /* Find in keys */
-    knot_dname_t *sample = knot_dname_new_from_str(item, strlen(item), 0);
+    knot_dname_t *sample = knot_dname_from_str(item, strlen(item));
+    knot_dname_to_lower(sample);
 
     conf_key_t* r = 0;
     WALK_LIST (r, new_config->keys) {
-        if (knot_dname_compare(r->k.name, sample) == 0) {
+        if (knot_dname_cmp(r->k.name, sample) == 0) {
            *key = &r->k;
            knot_dname_free(&sample);
            return 0;
@@ -314,6 +316,7 @@ static void conf_zone_start(void *scanner, char *name) {
    this_zone->dbsync_timeout = -1; // Default policy applies
    this_zone->disable_any = -1; // Default policy applies
    this_zone->build_diffs = -1; // Default policy applies
+   this_zone->sig_lifetime = -1; // Default policy applies
 
    // Append mising dot to ensure FQDN
    size_t nlen = strlen(name);
@@ -329,6 +332,12 @@ static void conf_zone_start(void *scanner, char *name) {
       this_zone->name = name; /* Already FQDN */
    }
 
+   // DNSSEC configuration
+   this_zone->dnssec_enable = true;
+   if (new_config->dnssec_global) {
+      this_zone->dnssec_enable = new_config->dnssec_enable;
+   }
+
    /* Initialize ACL lists. */
    init_list(&this_zone->acl.xfr_in);
    init_list(&this_zone->acl.xfr_out);
@@ -339,7 +348,7 @@ static void conf_zone_start(void *scanner, char *name) {
    /* Check domain name. */
    knot_dname_t *dn = NULL;
    if (this_zone->name != NULL) {
-      dn = knot_dname_new_from_str(this_zone->name, nlen, 0);
+      dn = knot_dname_from_str(this_zone->name, nlen);
    }
    if (dn == NULL) {
      free(this_zone->name);
@@ -348,7 +357,7 @@ static void conf_zone_start(void *scanner, char *name) {
      cf_error(scanner, "invalid zone origin");
    } else {
      /* Check for duplicates. */
-     if (hattrie_tryget(new_config->names, (const char*)dn->name, dn->size) != NULL) {
+     if (hattrie_tryget(new_config->names, (const char *)dn, knot_dname_size(dn)) != NULL) {
            cf_error(scanner, "zone '%s' is already present, refusing to "
 			     "duplicate", this_zone->name);
            knot_dname_free(&dn);
@@ -362,7 +371,7 @@ static void conf_zone_start(void *scanner, char *name) {
 
      /* Directly discard dname, won't be needed. */
      add_tail(&new_config->zones, &this_zone->n);
-     *hattrie_get(new_config->names, (const char*)dn->name, dn->size) = (void *)1;
+     *hattrie_get(new_config->names, (const char *)dn, knot_dname_size(dn)) = (void *)1;
      ++new_config->zones_count;
      knot_dname_free(&dn);
    }
@@ -467,6 +476,9 @@ static void ident_auto(int tok, conf_t *conf, bool val)
 %token <tok> RATE_LIMIT_SIZE
 %token <tok> RATE_LIMIT_SLIP
 %token <tok> TRANSFERS
+%token <tok> DNSSEC_ENABLE
+%token <tok> DNSSEC_KEYDIR
+%token <tok> SIGNATURE_LIFETIME
 
 %token <tok> INTERFACES ADDRESS PORT
 %token <tok> IPA
@@ -656,8 +668,8 @@ keys:
      }
 
      if (fqdn != NULL && !conf_key_exists(scanner, fqdn)) {
-         knot_dname_t *dname = knot_dname_new_from_str(fqdn, fqdnl, 0);
-         if (!dname) {
+         knot_dname_t *dname = knot_dname_from_str(fqdn, fqdnl);
+	 if (!dname) {
              cf_error(scanner, "key name '%s' not in valid domain name format",
                       fqdn);
          } else {
@@ -845,7 +857,7 @@ zone_acl_list:
 zone_acl:
  | zone_acl TEXT ';' {
       /* Find existing node in remotes. */
-      node* r = 0; conf_iface_t* found = 0;
+      node_t* r = 0; conf_iface_t* found = 0;
       WALK_LIST (r, new_config->remotes) {
 	 if (strcmp(((conf_iface_t*)r)->name, $2.t) == 0) {
 	    found = (conf_iface_t*)r;
@@ -924,6 +936,21 @@ zone:
 	   this_zone->notify_timeout = $3.i;
        }
    }
+ | zone DNSSEC_ENABLE BOOL ';' { this_zone->dnssec_enable = $3.i; }
+ | zone SIGNATURE_LIFETIME NUM ';' {
+	if ($3.i <= 7200) {
+	   cf_error(scanner, "signature lifetime must be more than 7200 seconds");
+	} else {
+	   this_zone->sig_lifetime = $3.i;
+	}
+ }
+ | zone SIGNATURE_LIFETIME INTERVAL ';' {
+	 if ($3.i <= 7200) {
+	    cf_error(scanner, "signature lifetime must be more than 7200 seconds");
+	 } else {
+	    this_zone->sig_lifetime = $3.i;
+	 }
+ }
  ;
 
 zones:
@@ -956,6 +983,23 @@ zones:
        }
  }
  | zones DBSYNC_TIMEOUT INTERVAL ';' { new_config->dbsync_timeout = $3.i; }
+ | zones DNSSEC_ENABLE BOOL ';' { new_config->dnssec_enable = $3.i;
+                                  new_config->dnssec_global = true; }
+ | zones DNSSEC_KEYDIR TEXT ';' { new_config->dnssec_keydir = $3.t; }
+ | zones SIGNATURE_LIFETIME NUM ';' {
+	if ($3.i <= 7200) {
+	   cf_error(scanner, "signature lifetime must be more than 7200 seconds");
+	} else {
+	   new_config->sig_lifetime = $3.i;
+	}
+ }
+ | zones SIGNATURE_LIFETIME INTERVAL ';' {
+	 if ($3.i <= 7200) {
+	    cf_error(scanner, "signature lifetime must be more than 7200 seconds");
+	 } else {
+	    new_config->sig_lifetime = $3.i;
+	 }
+ }
  ;
 
 log_prios_start: {
@@ -982,7 +1026,7 @@ log_src:
 log_dest: LOG_DEST {
   /* Find already existing rule. */
   this_log = 0;
-  node *n = 0;
+  node_t *n = 0;
   WALK_LIST(n, new_config->logs) {
     conf_log_t* log = (conf_log_t*)n;
     if (log->type == $1.i) {
@@ -1005,7 +1049,7 @@ log_dest: LOG_DEST {
 log_file: FILENAME TEXT {
   /* Find already existing rule. */
   this_log = 0;
-  node *n = 0;
+  node_t *n = 0;
   WALK_LIST(n, new_config->logs) {
     conf_log_t* log = (conf_log_t*)n;
     if (log->type == LOGT_FILE) {
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index d52c9e50a7547b7d75d12f8f625cf63458479159..6983bede1f10cf90eced26ff794fa41162973776 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -60,7 +60,7 @@ extern void cf_lex_destroy(void *scanner);
 extern void switch_input(const char *str, void *scanner);
 extern char *cf_current_filename(void *scanner);
 
-conf_t *new_config = 0; /*!< \brief Currently parsed config. */
+conf_t *new_config = NULL; /*!< \brief Currently parsed config. */
 static volatile int _parser_res = 0; /*!< \brief Parser result. */
 static pthread_mutex_t _parser_lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -130,7 +130,7 @@ static void conf_parse_end(conf_t *conf)
  */
 static void conf_update_hooks(conf_t *conf)
 {
-	node *n = 0;
+	node_t *n = NULL;
 	conf->_touched = CONF_ALL;
 	WALK_LIST (n, conf->hooks) {
 		conf_hook_t *hook = (conf_hook_t*)n;
@@ -261,7 +261,7 @@ static int conf_process(conf_t *conf)
 
 	// Postprocess zones
 	int ret = KNOT_EOK;
-	node *n = 0;
+	node_t *n = NULL;
 	WALK_LIST (n, conf->zones) {
 		conf_zone_t *zone = (conf_zone_t*)n;
 
@@ -296,10 +296,18 @@ static int conf_process(conf_t *conf)
 		}
 
 		// Default policy for IXFR FSLIMIT
+		/*! \todo In cf-parse.y:313 is this value set to -1, shouldn't
+		 *        it be checked also for negative values?
+		 */
 		if (zone->ixfr_fslimit == 0) {
 			zone->ixfr_fslimit = conf->ixfr_fslimit;
 		}
 
+		// Default policy for DNSSEC signature lifetime
+		if (zone->sig_lifetime <= 0) {
+			zone->sig_lifetime = conf->sig_lifetime;
+		}
+
 		// Default zone file
 		if (zone->file == NULL) {
 			zone->file = strcdup(zone->name, "zone");
@@ -309,6 +317,20 @@ static int conf_process(conf_t *conf)
 			}
 		}
 
+		// Default policy for DNSSEC
+		if (conf->dnssec_keydir) {
+			conf->dnssec_keydir = conf_abs_path(conf->storage,
+			                                    conf->dnssec_keydir);
+		} else {
+			zone->dnssec_enable = false;
+		}
+
+		// Turn zone diff on with DNSSEC enabled
+		if (zone->dnssec_enable && !zone->build_diffs) {
+			// Silent force enable
+			zone->build_diffs = true;
+		}
+
 		// Relative zone filenames should be relative to storage
 		zone->file = conf_abs_path(conf->storage, zone->file);
 		if (zone->file == NULL) {
@@ -322,6 +344,7 @@ static int conf_process(conf_t *conf)
 		size_t size = stor_len + zname_len + 9; // /diff.db,\0
 		char *dest = malloc(size);
 		if (dest == NULL) {
+			ERR_ALLOC_FAILED;
 			zone->ixfr_db = NULL; /* Not enough memory. */
 			ret = KNOT_ENOMEM; /* Error report. */
 			continue;
@@ -365,7 +388,7 @@ static int conf_process(conf_t *conf)
  * Singletion configuration API.
  */
 
-conf_t *s_config = 0; /*! \brief Singleton config instance. */
+conf_t *s_config = NULL; /*! \brief Singleton config instance. */
 
 /*! \brief Singleton config constructor (automatically called on load). */
 void __attribute__ ((constructor)) conf_init()
@@ -393,7 +416,7 @@ void __attribute__ ((constructor)) conf_init()
 	/* Syslog */
 	conf_log_t *log = malloc(sizeof(conf_log_t));
 	log->type = LOGT_SYSLOG;
-	log->file = 0;
+	log->file = NULL;
 	init_list(&log->map);
 
 	conf_log_map_t *map = malloc(sizeof(conf_log_map_t));
@@ -406,7 +429,7 @@ void __attribute__ ((constructor)) conf_init()
 	/* Stderr */
 	log = malloc(sizeof(conf_log_t));
 	log->type = LOGT_STDERR;
-	log->file = 0;
+	log->file = NULL;
 	init_list(&log->map);
 
 	map = malloc(sizeof(conf_log_map_t));
@@ -425,7 +448,7 @@ void __attribute__ ((destructor)) conf_deinit()
 {
 	if (s_config) {
 		conf_free(s_config);
-		s_config = 0;
+		s_config = NULL;
 	}
 }
 
@@ -446,7 +469,7 @@ static int conf_fparser(conf_t *conf)
 	// Hook new configuration
 	new_config = conf;
 	FILE *f = fopen(conf->filename, "r");
-	if (f == 0) {
+	if (f == NULL) {
 		pthread_mutex_unlock(&_parser_lock);
 		return KNOT_ENOENT;
 	}
@@ -532,6 +555,7 @@ conf_t *conf_new(const char* path)
 	c->notify_timeout = CONFIG_NOTIFY_TIMEOUT;
 	c->dbsync_timeout = CONFIG_DBSYNC_TIMEOUT;
 	c->max_udp_payload = EDNS_MAX_UDP_PAYLOAD;
+	c->sig_lifetime = KNOT_DNSSEC_DEFAULT_LIFETIME;
 	c->ixfr_fslimit = -1;
 	c->uid = -1;
 	c->gid = -1;
@@ -540,6 +564,9 @@ conf_t *conf_new(const char* path)
 	c->build_diffs = 0; /* Disable by default. */
 	c->logs_count = -1;
 
+	/* DNSSEC. */
+	c->dnssec_enable = true;
+
 	/* ACLs. */
 	c->ctl.acl = acl_new();
 	if (!c->ctl.acl) {
@@ -615,7 +642,7 @@ void conf_truncate(conf_t *conf, int unload_hooks)
 		return;
 	}
 
-	node *n = 0, *nxt = 0;
+	node_t *n = NULL, *nxt = NULL;
 
 	// Unload hooks
 	if (unload_hooks) {
@@ -666,33 +693,39 @@ void conf_truncate(conf_t *conf, int unload_hooks)
 	conf->zones_count = 0;
 	init_list(&conf->zones);
 
+	conf->dnssec_global = false;
+	conf->dnssec_enable = true;
 	if (conf->filename) {
 		free(conf->filename);
-		conf->filename = 0;
+		conf->filename = NULL;
 	}
 	if (conf->identity) {
 		free(conf->identity);
-		conf->identity = 0;
+		conf->identity = NULL;
 	}
 	if (conf->version) {
 		free(conf->version);
-		conf->version = 0;
+		conf->version = NULL;
 	}
 	if (conf->storage) {
 		free(conf->storage);
-		conf->storage = 0;
+		conf->storage = NULL;
 	}
 	if (conf->rundir) {
 		free(conf->rundir);
-		conf->rundir = 0;
+		conf->rundir = NULL;
 	}
 	if (conf->pidfile) {
 		free(conf->pidfile);
-		conf->pidfile = 0;
+		conf->pidfile = NULL;
 	}
 	if (conf->nsid) {
 		free(conf->nsid);
-		conf->nsid = 0;
+		conf->nsid = NULL;
+	}
+	if (conf->dnssec_keydir) {
+		free(conf->dnssec_keydir);
+		conf->dnssec_keydir = NULL;
 	}
 
 	/* Free remote control list. */
@@ -728,7 +761,7 @@ void conf_free(conf_t *conf)
 char* conf_find_default()
 {
 	/* Try sequentially each default path. */
-	char *path = 0;
+	char *path = NULL;
 	for (int i = 0; i < DEFAULT_CONF_COUNT; ++i) {
 		path = strcpath(strdup(DEFAULT_CONFIG[i]));
 
@@ -744,7 +777,7 @@ char* conf_find_default()
 		/* Keep the last item. */
 		if (i < DEFAULT_CONF_COUNT - 1) {
 			free(path);
-			path = 0;
+			path = NULL;
 		}
 	}
 
@@ -791,7 +824,7 @@ int conf_open(const char* path)
 
 	/* Copy hooks. */
 	if (oldconf) {
-		node *n = 0, *nxt = 0;
+		node_t *n = NULL, *nxt = NULL;
 		WALK_LIST_DELSAFE (n, nxt, oldconf->hooks) {
 			conf_hook_t *hook = (conf_hook_t*)n;
 			conf_add_hook(nconf, hook->sections,
@@ -933,7 +966,7 @@ void conf_free_log(conf_log_t *log)
 	free(log->file);
 
 	/* Free loglevel mapping. */
-	node *n = 0, *nxt = 0;
+	node_t *n = NULL, *nxt = NULL;
 	WALK_LIST_DELSAFE(n, nxt, log->map) {
 		free((conf_log_map_t*)n);
 	}
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index 0bb44e673966a8468960d18c09cb9221c84d02a3..79517f8cd7df1af3658031045f2ae30b4649cc1d 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -30,12 +30,14 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <stdbool.h>
 
 #include <urcu.h>
 
 #include "libknot/dname.h"
 #include "libknot/tsig.h"
-#include "libknot/sign/key.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/policy.h"
 #include "common/lists.h"
 #include "common/log.h"
 #include "common/acl.h"
@@ -62,7 +64,7 @@
  * listening and outgoing function.
  */
 typedef struct conf_iface_t {
-	node n;
+	node_t n;
 	char *name;           /*!< Internal name for the interface. */
 	char *address;        /*!< IP (IPv4/v6) address for this interface */
 	unsigned prefix;      /*!< IP subnet prefix. */
@@ -78,7 +80,7 @@ typedef struct conf_iface_t {
  * Used for zone ACL lists to prevent node duplication.
  */
 typedef struct conf_remote_t {
-	node n;               /*!< List node. */
+	node_t n;             /*!< List node. */
 	conf_iface_t *remote; /*!< Pointer to interface descriptor. */
 } conf_remote_t;
 
@@ -88,7 +90,7 @@ typedef struct conf_remote_t {
  * Holds the name of a remote in the list.
  */
 typedef struct conf_group_remote_t {
-	node n;
+	node_t n;
 	char *name;
 } conf_group_remote_t;
 
@@ -96,9 +98,9 @@ typedef struct conf_group_remote_t {
  * \brief Group of remotes.
  */
 typedef struct conf_group_t {
-	node n;		/*!< List node. */
+	node_t n;	/*!< List node. */
 	char *name;	/*!< Unique name of the group. */
-	list remotes;	/*!< List of remote names. */
+	list_t remotes;	/*!< List of remote names. */
 } conf_group_t;
 
 /*!
@@ -111,24 +113,26 @@ typedef struct conf_group_t {
  * zone transfers.  Same logic applies for the NOTIFY.
  */
 typedef struct conf_zone_t {
-	node n;
-	char *name;               /*!< Zone name. */
-	uint16_t cls;             /*!< Zone class (IN or CH). */
-	char *file;               /*!< Path to a zone file. */
-	char *ixfr_db;            /*!< Path to a IXFR database file. */
-	size_t ixfr_fslimit;      /*!< File size limit for IXFR journal. */
-	int dbsync_timeout;       /*!< Interval between syncing to zonefile.*/
-	int enable_checks;        /*!< Semantic checks for parser.*/
-	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. */
+	node_t n;
+	char *name;                /*!< Zone name. */
+	uint16_t cls;              /*!< Zone class (IN or CH). */
+	char *file;                /*!< Path to a zone file. */
+	char *ixfr_db;             /*!< Path to a IXFR database file. */
+	bool dnssec_enable;        /*!< DNSSEC: Online signing enabled. */
+	size_t ixfr_fslimit;       /*!< File size limit for IXFR journal. */
+	int sig_lifetime;          /*!< Validity period of DNSSEC signatures. */
+	int dbsync_timeout;        /*!< Interval between syncing to zonefile.*/
+	int enable_checks;         /*!< Semantic checks for parser.*/
+	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.*/
-		list notify_in;   /*!< Remotes accepted for notify-in.*/
-		list notify_out;  /*!< Remotes accepted for notify-out.*/
-		list update_in;   /*!< Remotes accepted for DDNS.*/
+		list_t xfr_in;     /*!< Remotes accepted for for xfr-in.*/
+		list_t xfr_out;    /*!< Remotes accepted for xfr-out.*/
+		list_t notify_in;  /*!< Remotes accepted for notify-in.*/
+		list_t notify_out; /*!< Remotes accepted for notify-out.*/
+		list_t update_in;  /*!< Remotes accepted for DDNS.*/
 	} acl;
 } conf_zone_t;
 
@@ -136,7 +140,7 @@ typedef struct conf_zone_t {
  * \brief Mapping of loglevels to message sources.
  */
 typedef struct conf_log_map_t {
-	node n;
+	node_t n;
 	int source; /*!< Log message source mask. */
 	int prios;  /*!< Log priorities mask. */
 } conf_log_map_t;
@@ -145,10 +149,10 @@ typedef struct conf_log_map_t {
  * \brief Log facility descriptor.
  */
 typedef struct conf_log_t {
-	node n;
+	node_t n;
 	logtype_t type;  /*!< Type of the log (SYSLOG/STDERR/FILE). */
 	char *file;      /*!< Filename in case of LOG_FILE, else NULL. */
-	list map;        /*!< Log levels mapping. */
+	list_t map;      /*!< Log levels mapping. */
 } conf_log_t;
 
 /*!
@@ -166,7 +170,7 @@ typedef enum conf_section_t {
  * \brief TSIG key list item.
  */
 typedef struct conf_key_t {
-	node n;
+	node_t n;
 	knot_tsig_key_t k;
 } conf_key_t;
 
@@ -175,7 +179,7 @@ typedef struct conf_key_t {
  */
 typedef struct conf_control_t {
 	conf_iface_t *iface; /*!< Remote control interface. */
-	list allow;          /*!< List of allowed remotes. */
+	list_t allow;        /*!< List of allowed remotes. */
 	acl_t* acl;          /*!< ACL. */
 	bool have;           /*!< Set if configured. */
 } conf_control_t;
@@ -212,45 +216,49 @@ typedef struct conf_t {
 	/*
 	 * Log
 	 */
-	list logs;        /*!< List of logging facilites. */
+	list_t logs;      /*!< List of logging facilites. */
 	int logs_count;   /*!< Count of logging facilities. */
 
 	/*
 	 * Interfaces
 	 */
-	list ifaces;      /*!< List of interfaces. */
+	list_t ifaces;    /*!< List of interfaces. */
 	int ifaces_count; /*!< Count of interfaces. */
 
 	/*
 	 * TSIG keys
 	 */
-	list keys;     /*!< List of TSIG keys. */
+	list_t keys;   /*!< List of TSIG keys. */
 	int key_count; /*!< Count of TSIG keys. */
 
 	/*
 	 * Remotes
 	 */
-	list remotes;     /*!< List of remotes. */
-	int remotes_count;/*!< Count of remotes. */
+	list_t remotes;    /*!< List of remotes. */
+	int remotes_count; /*!< Count of remotes. */
 
 	/*
 	 * Groups of remotes.
 	 */
-	list groups;      /*!< List of groups of remotes. */
+	list_t groups; /*!< List of groups of remotes. */
 
 	/*
 	 * Zones
 	 */
-	list zones;       /*!< List of zones. */
-	int zones_count;  /*!< Count of zones. */
-	int zone_checks;  /*!< Semantic checks for parser.*/
-	int disable_any;  /*!< Disable ANY type queries for AA.*/
-	int notify_retries; /*!< NOTIFY query retries. */
-	int notify_timeout; /*!< Timeout for NOTIFY response in seconds. */
-	int dbsync_timeout; /*!< Default interval between syncing to zonefile.*/
+	list_t zones;        /*!< List of zones. */
+	int zones_count;     /*!< Count of zones. */
+	int zone_checks;     /*!< Semantic checks for parser.*/
+	int disable_any;     /*!< Disable ANY type queries for AA.*/
+	int notify_retries;  /*!< NOTIFY query retries. */
+	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. */
-	hattrie_t *names; /*!< Zone tree for duplicate checking. */
+	hattrie_t *names;    /*!< Zone tree for duplicate checking. */
+	bool dnssec_global;  /*!< DNSSEC: Configured for all zones. */
+	bool dnssec_enable;  /*!< DNSSEC: Online signing enabled. */
+	char *dnssec_keydir; /*!< DNSSEC: Path to key directory. */
+	int sig_lifetime;    /*!< DNSSEC: Signature lifetime. */
 
 	/*
 	 * Remote control interface.
@@ -260,7 +268,7 @@ typedef struct conf_t {
 	/*
 	 * Implementation specifics
 	 */
-	list hooks;      /*!< List of config hooks. */
+	list_t hooks;    /*!< List of config hooks. */
 	int hooks_count; /*!< Count of config hooks. */
 	int _touched;    /*!< Bitmask of sections touched by last update. */
 } conf_t;
@@ -269,7 +277,7 @@ typedef struct conf_t {
  * \brief Config hook prototype.
  */
 typedef struct conf_hook_t {
-	node n;
+	node_t n;
 	int sections; /*!< Bitmask of watched sections. */
 	int (*update)(const conf_t*, void*); /*!< Function executed on config load. */
 	void *data;
diff --git a/src/knot/conf/logconf.c b/src/knot/conf/logconf.c
index 4f5975525cd40847f84bb7477d3e42f93eedf7b9..2971f536e18af5251f56c758b6f8364fee41ecb8 100644
--- a/src/knot/conf/logconf.c
+++ b/src/knot/conf/logconf.c
@@ -42,7 +42,7 @@ int log_conf_hook(const struct conf_t *conf, void *data)
 	}
 
 	// Find maximum log facility id
-	node *n = NULL; size_t files = 0;
+	node_t *n = NULL; size_t files = 0;
 	WALK_LIST(n, conf->logs) {
 		conf_log_t* log = (conf_log_t*)n;
 		if (log->type == LOGT_FILE) {
@@ -73,7 +73,7 @@ int log_conf_hook(const struct conf_t *conf, void *data)
 		}
 
 		// Setup sources mapping
-		node *m = 0;
+		node_t *m = 0;
 		WALK_LIST(m, log->map) {
 
 			// Assign mapped level
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 23d49ee35f8ac52adcd76f2dd64e1113cd12d386..6b5bb79422edf7868676526b4c3db61c557dc2d2 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -79,6 +79,7 @@ static int cmd_zonestatus(int argc, char *argv[], unsigned flags);
 static int cmd_checkconf(int argc, char *argv[], unsigned flags);
 static int cmd_checkzone(int argc, char *argv[], unsigned flags);
 static int cmd_memstats(int argc, char *argv[], unsigned flags);
+static int cmd_signzone(int argc, char *argv[], unsigned flags);
 
 /*! \brief Table of remote commands. */
 knot_cmd_t knot_cmd_tbl[] = {
@@ -91,6 +92,7 @@ knot_cmd_t knot_cmd_tbl[] = {
 	{&cmd_checkconf,  1, "checkconf",  "",       "\tCheck current server configuration."},
 	{&cmd_checkzone,  1, "checkzone",  "[zone]", "Check zone (all if not specified)."},
 	{&cmd_memstats,  1, "memstats",  "[zone]", "Estimate memory use for zone (all if not specified)."},
+	{&cmd_signzone,   0, "signzone",   "[zone]", "Sign all zones with available DNSSEC keys."},
 	{NULL, 0, NULL, NULL, NULL}
 };
 
@@ -137,7 +139,7 @@ static int cmd_remote_print_reply(const knot_rrset_t *rr)
 static int cmd_remote_reply(int c)
 {
 	uint8_t *rwire = malloc(SOCKET_MTU_SZ);
-	knot_packet_t *reply = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+	knot_packet_t *reply = knot_packet_new();
 	if (!rwire || !reply) {
 		free(rwire);
 		knot_packet_free(&reply);
@@ -220,7 +222,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
 	}
 
 	if (knot_packet_to_wire(qr, &buf, &buflen) != KNOT_EOK) {
-		knot_rrset_deep_free(&rr, 1, 1);
+		knot_rrset_deep_free(&rr, 1);
 		knot_packet_free(&qr);
 		return 1;
 	}
@@ -256,7 +258,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[])
 
 	/* Cleanup. */
 	if (rc == 0) printf("\n");
-	knot_rrset_deep_free(&rr, 1, 1);
+	knot_rrset_deep_free(&rr, 1);
 
 	/* Close connection. */
 	socket_close(s);
@@ -283,7 +285,7 @@ static int tsig_parse_str(knot_tsig_key_t *key, const char *str)
 	if (s) {
 		*s++ = '\0';               /* Last part separator */
 		knot_lookup_table_t *alg = NULL;
-		alg = knot_lookup_by_name(knot_tsig_alg_domain_names, h);
+		alg = knot_lookup_by_name(knot_tsig_alg_dnames_str, h);
 		if (alg) {
 			algorithm = alg->id;
 		} else {
@@ -612,6 +614,11 @@ static int cmd_zonestatus(int argc, char *argv[], unsigned flags)
 	return cmd_remote("zonestatus", KNOT_RRTYPE_TXT, 0, NULL);
 }
 
+static int cmd_signzone(int argc, char *argv[], unsigned flags)
+{
+	return cmd_remote("signzone", KNOT_RRTYPE_NS, argc, argv);
+}
+
 static int cmd_checkconf(int argc, char *argv[], unsigned flags)
 {
 	UNUSED(argc);
@@ -636,7 +643,7 @@ static int cmd_checkzone(int argc, char *argv[], unsigned flags)
 
 	/* Zone checking */
 	int rc = 0;
-	node *n = 0;
+	node_t *n = 0;
 
 	/* Generate databases for all zones */
 	WALK_LIST(n, conf()->zones) {
@@ -696,7 +703,7 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags)
 
 	/* Zone checking */
 	int rc = 0;
-	node *n = 0;
+	node_t *n = 0;
 	size_t total_size = 0;
 
 	/* Generate databases for all zones */
@@ -730,21 +737,10 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags)
 
 		/* Init memory estimation context. */
 		zone_estim_t est = {.node_table = hattrie_create_n(TRIE_BUCKET_SIZE, &mem_ctx),
-		                    .dname_table = hattrie_create_n(TRIE_BUCKET_SIZE, &mem_ctx),
 		                    .dname_size = 0, .rrset_size = 0,
 		                    .node_size = 0, .ahtable_size = 0,
 		                    .rdata_size = 0, .record_count = 0 };
 		if (est.node_table == NULL) {
-			if (est.dname_table) {
-				hattrie_free(est.dname_table);
-			}
-			log_server_error("Not enough memory.\n");
-			continue;
-		}
-		if (est.dname_table == NULL) {
-			if (est.node_table) {
-				hattrie_free(est.node_table);
-			}
 			log_server_error("Not enough memory.\n");
 			continue;
 		}
@@ -759,9 +755,7 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags)
 			rc = 1;
 			log_zone_error("Could not load zone.\n");
 			hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL);
-			hattrie_apply_rev(est.dname_table, estimator_free_trie_node, NULL);
 			hattrie_free(est.node_table);
-			hattrie_free(est.dname_table);
 			break;
 		}
 
@@ -771,9 +765,7 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags)
 			rc = 1;
 			log_zone_error("Failed to parse zone.\n");
 			hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL);
-			hattrie_apply_rev(est.dname_table, estimator_free_trie_node, NULL);
 			hattrie_free(est.node_table);
-			hattrie_free(est.dname_table);
 			file_loader_free(loader);
 			break;
 		}
@@ -784,9 +776,7 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags)
 
 		/* Cleanup */
 		hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL);
-		hattrie_apply_rev(est.dname_table, estimator_free_trie_node, NULL);
 		hattrie_free(est.node_table);
-		hattrie_free(est.dname_table);
 
 		size_t zone_size = (size_t)(((double)(est.rdata_size +
 		                   est.node_size +
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index 9530b3adf5db6c77513950b4bd0a218b118495f6..00fb0a498cba4401b8dcfb38de6ddef4ca9a0f75 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -31,6 +31,9 @@
 #include "libknot/packet/response.h"
 #include "libknot/nameserver/name-server.h"
 #include "libknot/tsig-op.h"
+#include "libknot/rdata.h"
+#include "libknot/dnssec/zone-sign.h"
+#include "libknot/dnssec/zone-nsec.h"
 
 #define KNOT_CTL_REALM "knot."
 #define KNOT_CTL_REALM_EXT ("." KNOT_CTL_REALM)
@@ -66,6 +69,7 @@ static int remote_c_refresh(server_t *s, remote_cmdargs_t* a);
 static int remote_c_status(server_t *s, remote_cmdargs_t* a);
 static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a);
 static int remote_c_flush(server_t *s, remote_cmdargs_t* a);
+static int remote_c_signzone(server_t *s, remote_cmdargs_t* a);
 
 /*! \brief Table of remote commands. */
 struct remote_cmd_t remote_cmd_tbl[] = {
@@ -75,6 +79,7 @@ struct remote_cmd_t remote_cmd_tbl[] = {
 	{ "status",    &remote_c_status },
 	{ "zonestatus",&remote_c_zonestatus },
 	{ "flush",     &remote_c_flush },
+	{ "signzone",  &remote_c_signzone },
 	{ NULL,        NULL }
 };
 
@@ -102,7 +107,7 @@ static int remote_rdata_apply(server_t *s, remote_cmdargs_t* a, remote_zonef_t *
 		for (uint16_t i = 0; i < knot_rrset_rdata_rr_count(rr); i++) {
 			/* Refresh zones. */
 			const knot_dname_t *dn =
-				knot_rrset_rdata_ns_name(rr, i);
+				knot_rdata_ns_name(rr, i);
 			rcu_read_lock();
 			zone = knot_zonedb_find_zone(ns->zone_db, dn);
 			if (cb(s, zone) != KNOT_EOK) {
@@ -163,6 +168,22 @@ static int remote_zone_flush(server_t *s, const knot_zone_t *z)
 	return KNOT_EOK;
 }
 
+/*! \brief Sign zone callback. */
+static int remote_zone_sign(server_t *server, const knot_zone_t *zone)
+{
+	if (!server || !zone) {
+		return KNOT_EINVAL;
+	}
+
+	char *zone_name = knot_dname_to_str(zone->name);
+	log_server_info("Requested zone resign for '%s'.\n", zone_name);
+	free(zone_name);
+
+	zones_schedule_dnssec((knot_zone_t *)zone, 0, true);
+
+	return KNOT_EOK;
+}
+
 /*!
  * \brief Remote command 'stop' handler.
  *
@@ -203,6 +224,21 @@ static int remote_c_status(server_t *s, remote_cmdargs_t* a)
 	return KNOT_EOK;
 }
 
+static char *dnssec_info(const zonedata_t *zd, char *buf, size_t buf_size)
+{
+	assert(zd && zd->dnssec_timer);
+	assert(buf);
+
+	time_t diff_time = zd->dnssec_timer->tv.tv_sec;
+	struct tm *t = localtime(&diff_time);
+
+	int written = snprintf(buf, buf_size, "%s", asctime(t));
+	UNUSED(written);
+	assert(written >= 0);
+
+	return buf;
+}
+
 /*!
  * \brief Remote command 'zonestatus' handler.
  *
@@ -230,7 +266,7 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
 			soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
 			                          KNOT_RRTYPE_SOA);
 			assert(soa_rrs != NULL);
-			serial = knot_rrset_rdata_soa_serial(soa_rrs);
+			serial = knot_rdata_soa_serial(soa_rrs);
 		}
 
 		/* Evalute zone type. */
@@ -255,6 +291,7 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
 				break;
 			}
 			/*! Workaround until proper zone fetching API and locking
+
 			 *  is implemented (ref #31)
 			 */
 			if (dif.tv_sec < 0) {
@@ -272,14 +309,17 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
 		}
 
 		/* Workaround, some platforms ignore 'size' with snprintf() */
-		char buf[256];
+		char buf[512] = { '\0' };
+		char dnssec_buf[128] = { '\0' };
 		int n = snprintf(buf, sizeof(buf),
-		                 "%s\ttype=%s | serial=%u | %s %s\n",
+		                 "%s\ttype=%s | serial=%u | %s %s | %s %s\n",
 		                 zd->conf->name,
 		                 zd->xfr_in.has_master ? "slave" : "master",
 		                 serial,
 		                 state ? state : "",
-		                 when ? when : "");
+		                 when ? when : "",
+		                 zd->conf->dnssec_enable ? "automatic DNSSEC, resigning at:" : "",
+		                 zd->conf->dnssec_enable ? dnssec_info(zd, dnssec_buf, sizeof(dnssec_buf)) : "");
 		free(when);
 		if (n < 0 || (size_t)n > rb) {
 			*dst = '\0';
@@ -349,6 +389,23 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a)
 	return remote_rdata_apply(s, a, &remote_zone_flush);
 }
 
+/*!
+ * \brief Remote command 'signzone' handler.
+ *
+ */
+static int remote_c_signzone(server_t *server, remote_cmdargs_t* arguments)
+{
+	dbg_server("remote: %s\n", __func__);
+
+	if (arguments->argc == 0) {
+		log_server_error("signzone for all zone was requested\n");
+		// TODO
+		return KNOT_ENOTSUP;
+	}
+
+	return remote_rdata_apply(server, arguments, remote_zone_sign);
+}
+
 /*!
  * \brief Prepare and send error response.
  * \param c Client fd.
@@ -459,7 +516,7 @@ static int remote_send_chunk(int c, knot_packet_t *pkt, const char* d,
                              uint16_t dlen, uint8_t* rwire, size_t rlen)
 {
 	int ret = KNOT_ERROR;
-	knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+	knot_packet_t *resp = knot_packet_new_mm(&pkt->mm);
 	if (!resp) {
 		return ret;
 	}
@@ -470,7 +527,7 @@ static int remote_send_chunk(int c, knot_packet_t *pkt, const char* d,
 		knot_packet_free(&resp);
 		return ret;
 	}
-	ret = knot_response_init_from_query(resp, pkt, 1);
+	ret = knot_response_init_from_query(resp, pkt);
 	if (ret != KNOT_EOK)  {
 		knot_packet_free(&resp);
 		return ret;
@@ -498,13 +555,13 @@ static int remote_send_chunk(int c, knot_packet_t *pkt, const char* d,
 	size_t rrlen = rlen;
 	ret = knot_rrset_to_wire(rr, rwire + len, &rrlen, rlen, &rr_count, NULL);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&rr, 1, 1);
+		knot_rrset_deep_free(&rr, 1);
 		return ret;
 	}
 	knot_wire_set_nscount(rwire, rr_count);
 	len += rrlen;
 	rlen -= rrlen;
-	knot_rrset_deep_free(&rr, 1, 1);
+	knot_rrset_deep_free(&rr, 1);
 
 	if (len > 0) {
 		return tcp_send(c, rwire, len);
@@ -529,9 +586,9 @@ int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_
 		return KNOT_EMALF;
 	}
 
-	knot_dname_t *realm = knot_dname_new_from_str(KNOT_CTL_REALM,
-	                                              KNOT_CTL_REALM_LEN, NULL);
-	if (!knot_dname_is_subdomain(qname, realm) != 0) {
+	knot_dname_t *realm = knot_dname_from_str(KNOT_CTL_REALM,
+	                                          KNOT_CTL_REALM_LEN);
+	if (!knot_dname_is_sub(qname, realm) != 0) {
 		dbg_server("remote: qname != *%s\n", KNOT_CTL_REALM_EXT);
 		knot_dname_free(&realm);
 		return KNOT_EMALF;
@@ -541,8 +598,8 @@ int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_
 	/* Command:
 	 * QNAME: leftmost label of QNAME
 	 */
-	size_t cmd_len = knot_dname_label_size(qname, 0);
-	char *cmd = strndup((char*)qname->name + 1, cmd_len);
+	size_t cmd_len = *qname;
+	char *cmd = strndup((char*)qname + 1, cmd_len);
 
 	/* Data:
 	 * NS: TSIG
@@ -593,7 +650,7 @@ int remote_answer(int fd, server_t *s, knot_packet_t *pkt, uint8_t* rwire, size_
 int remote_process(server_t *s, conf_iface_t *ctl_if, int r,
                    uint8_t* buf, size_t buflen)
 {
-	knot_packet_t *pkt =  knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *pkt =  knot_packet_new();
 	if (!pkt) {
 		dbg_server("remote: not enough space to allocate query\n");
 		return KNOT_ENOMEM;
@@ -678,7 +735,7 @@ knot_packet_t* remote_query(const char *query, const knot_tsig_key_t *key)
 		return NULL;
 	}
 
-	knot_packet_t *qr = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *qr = knot_packet_new();
 	if (!qr) {
 		return NULL;
 	}
@@ -693,25 +750,23 @@ knot_packet_t* remote_query(const char *query, const knot_tsig_key_t *key)
 	}
 
 	/* Question section. */
-	knot_question_t q;
 	char *qname = strcdup(query, KNOT_CTL_REALM_EXT);
-	q.qname = knot_dname_new_from_str(qname, strlen(qname), 0);
-	if (!q.qname) {
+	knot_dname_t *dname = knot_dname_from_str(qname, strlen(qname));
+	if (!dname) {
 		knot_packet_free(&qr);
 		free(qname);
 		return NULL;
 	}
-	q.qtype = KNOT_RRTYPE_ANY;
-	q.qclass = KNOT_CLASS_CH;
 
 	/* Cannot return != KNOT_EOK, but still. */
-	if (knot_query_set_question(qr, &q) != KNOT_EOK) {
+	if (knot_query_set_question(qr, dname, KNOT_CLASS_CH, KNOT_RRTYPE_ANY) != KNOT_EOK) {
 		knot_packet_free(&qr);
+		knot_dname_free(&dname);
 		free(qname);
 		return NULL;
 	}
 
-	knot_dname_release(q.qname);
+	knot_dname_free(&dname);
 	free(qname);
 
 	return qr;
@@ -728,7 +783,7 @@ int remote_query_append(knot_packet_t *qry, knot_rrset_t *data)
 	size_t bsize = 0;
 	int ret = knot_rrset_to_wire(data, sp, &bsize, qry->max_size, &rrs, 0);
 	if (ret == KNOT_EOK) {
-		qry->header.nscount += rrs;
+		knot_wire_add_nscount(qry->wireformat, rrs);
 	}
 
 	/* Finalize packet size. */
@@ -764,14 +819,16 @@ knot_rrset_t* remote_build_rr(const char *k, uint16_t t)
 	}
 
 	/* Assert K is FQDN. */
-	knot_dname_t *key = knot_dname_new_from_nonfqdn_str(k, strlen(k), 0);
-	if (!key) {
+	knot_dname_t *key = knot_dname_from_str(k, strlen(k));
+	if (key == NULL) {
 		return NULL;
 	}
 
 	/* Create RRSet. */
 	knot_rrset_t *rr = knot_rrset_new(key, t, KNOT_CLASS_CH, 0);
-	knot_dname_release(key);
+	if (rr == NULL)
+		knot_dname_free(&key);
+
 	return rr;
 }
 
@@ -812,20 +869,17 @@ int remote_create_ns(knot_rrset_t *rr, const char *d)
 	}
 
 	/* Create dname. */
-	knot_dname_t *dn = knot_dname_new_from_nonfqdn_str(d, strlen(d), NULL);
+	knot_dname_t *dn = knot_dname_from_str(d, strlen(d));
 	if (!dn) {
 		return KNOT_ERROR;
 	}
 
 	/* Build RDATA. */
-	uint8_t *rdata = knot_rrset_create_rdata(rr, sizeof(knot_dname_t *));
-	if (!rdata) {
-		knot_dname_free(&dn);
-		return KNOT_ERROR;
-	}
-	memcpy(rdata, &dn, sizeof(knot_dname_t *));
+	int dn_size = knot_dname_size(dn);
+	int result = knot_rrset_add_rdata(rr, dn, dn_size);
+	knot_dname_free(&dn);
 
-	return KNOT_EOK;
+	return result;
 }
 
 int remote_print_txt(const knot_rrset_t *rr, uint16_t i)
diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h
index bfc9f1c5bd5a6aed1641f0625a3f6174060b748f..fd7c37b5c225a63e5c9241d6f630311ba942fe53 100644
--- a/src/knot/ctl/remote.h
+++ b/src/knot/ctl/remote.h
@@ -30,7 +30,7 @@
 #include "knot/conf/conf.h"
 #include "libknot/packet/packet.h"
 #include "libknot/rrset.h"
-#include "libknot/sign/key.h"
+#include "libknot/dnssec/key.h"
 #include "knot/server/server.h"
 
 /*! \brief Default remote control tool port. */
diff --git a/src/knot/main.c b/src/knot/main.c
index 66eb450463f3429c3c89ef3c0c127319e7ffa26a..145b44de9cdbf5f464d0bb1d7ee316584262a502 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -383,9 +383,7 @@ int main(int argc, char **argv)
 				log_server_info("Starting integrity check of "
 				                "zone: %s\n", zone);
 				knot_dname_t *zdn =
-					knot_dname_new_from_str(zone,
-					                        strlen(zone),
-					                        NULL);
+					knot_dname_from_str(zone, strlen(zone));
 				knot_zone_t *z =
 					knot_zonedb_find_zone(server->nameserver->zone_db,
 					                      zdn);
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index e75bf2717a17df5895ecbc68ed17b3e77152863d..d93fee8554d1df43708cc28e36510d62c8712590 100644
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -34,6 +34,7 @@
 #include "common/evsched.h"
 #include "knot/other/debug.h"
 #include "knot/server/server.h"
+#include "libknot/rdata.h"
 
 
 /* Messages. */
@@ -47,7 +48,7 @@
 static int notify_request(const knot_rrset_t *rrset,
                           uint8_t *buffer, size_t *size)
 {
-	knot_packet_t *pkt = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *pkt = knot_packet_new();
 	CHECK_ALLOC_LOG(pkt, KNOT_ENOMEM);
 
 	/*! \todo Get rid of the numeric constant. */
@@ -63,14 +64,7 @@ static int notify_request(const knot_rrset_t *rrset,
 		return KNOT_ERROR;
 	}
 
-	knot_question_t question;
-
-	// this is ugly!!
-	question.qname = rrset->owner;
-	question.qtype = rrset->type;
-	question.qclass = rrset->rclass;
-
-	rc = knot_query_set_question(pkt, &question);
+	rc = knot_query_set_question(pkt, rrset->owner, rrset->rclass, rrset->type);
 	if (rc != KNOT_EOK) {
 		knot_packet_free(&pkt);
 		return KNOT_ERROR;
@@ -78,7 +72,6 @@ static int notify_request(const knot_rrset_t *rrset,
 
 	/* Set random query ID. */
 	knot_packet_set_random_id(pkt);
-	knot_wire_set_id(pkt->wireformat, pkt->header.id);
 
 	/*! \todo add the SOA RR to the Answer section as a hint */
 	/*! \todo this should not use response API!! */
@@ -123,14 +116,13 @@ static int notify_request(const knot_rrset_t *rrset,
 int notify_create_response(knot_packet_t *request, uint8_t *buffer,
                            size_t *size)
 {
-	knot_packet_t *response =
-		knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *response = knot_packet_new_mm(&request->mm);
 	CHECK_ALLOC_LOG(response, KNOT_ENOMEM);
 
 	/* Set maximum packet size. */
 	int rc = knot_packet_set_max_size(response, *size);
 	if (rc == KNOT_EOK) {
-		rc = knot_response_init_from_query(response, request, 1);
+		rc = knot_response_init_from_query(response, request);
 	}
 
 	/* Aggregated result check. */
@@ -275,7 +267,7 @@ int notify_process_request(knot_nameserver_t *ns,
 		const knot_rrset_t *soa_rr = NULL;
 		soa_rr = knot_packet_answer_rrset(notify, 0);
 		if (soa_rr && knot_rrset_type(soa_rr) == KNOT_RRTYPE_SOA) {
-			serial = knot_rrset_rdata_soa_serial(soa_rr);
+			serial = knot_rdata_soa_serial(soa_rr);
 		}
 	}
 	rcu_read_unlock();
diff --git a/src/knot/server/rrl.c b/src/knot/server/rrl.c
index f064d8fd87738c3b312905c877e9fab2940663d7..3b204eb6e91a94d738e39e1904b143f1abc088bf 100644
--- a/src/knot/server/rrl.c
+++ b/src/knot/server/rrl.c
@@ -99,8 +99,8 @@ static uint8_t rrl_clsid(rrl_req_t *p)
 	}
 
 	/* Check query type for spec. classes. */
-	if (p->qst) {
-		switch(p->qst->qtype) {
+	if (p->query) {
+		switch(knot_packet_qtype(p->query)) {
 		case KNOT_RRTYPE_ANY:      /* ANY spec. class */
 			return CLS_ANY;
 			break;
@@ -128,14 +128,15 @@ static uint8_t rrl_clsid(rrl_req_t *p)
 }
 
 static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls,
-                       rrl_req_t *p, const knot_zone_t *z)
+                       rrl_req_t *req, const knot_zone_t *zone)
 {
-	const knot_dname_t *dn = NULL;
-	const uint8_t *n = (const uint8_t*)"\x00"; /* Fallback zone (for errors etc.) */
-	int nb = 1;
-	if (z) { /* Found associated zone. */
-		dn = knot_zone_name(z);
-	}
+	/* Fallback zone (for errors etc.) */
+	const knot_dname_t *dn = (const knot_dname_t*)"\x00";
+
+	/* Found associated zone. */
+	if (zone != NULL)
+		dn = knot_zone_name(zone);
+
 	switch (cls) {
 	case CLS_ERROR:    /* Could be a non-existent zone or garbage. */
 	case CLS_NXDOMAIN: /* Queries to non-existent names in zone. */
@@ -143,25 +144,14 @@ static int rrl_clsname(char *dst, size_t maxlen, uint8_t cls,
 		dbg_rrl_verb("%s: using zone/fallback name\n", __func__);
 		break;
 	default:
-		if (p->qst) dn = p->qst->qname;
+		/* Use QNAME */
+		if (req->query)
+			dn = knot_packet_qname(req->query);
 		break;
 	}
 
-	if (dn) { /* Check used dname. */
-		assert(dn); /* Should be always set. */
-		n = knot_dname_name(dn);
-		nb = (int)knot_dname_size(dn);
-	}
-
 	/* Write to wire */
-	if ((size_t)nb > maxlen) return KNOT_ESPACE;
-	if (memcpy(dst, n, nb) == NULL) {
-		dbg_rrl("%s: failed to serialize name=%p len=%u\n",
-		        __func__, n, nb);
-		return KNOT_ERROR;
-	}
-
-	return nb;
+	return knot_dname_to_wire((uint8_t *)dst, dn, maxlen);
 }
 
 static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a,
@@ -191,7 +181,8 @@ static int rrl_classify(char *dst, size_t maxlen, const sockaddr_t *a,
 	uint16_t *nlen = (uint16_t*)(dst + blklen);
 	blklen += sizeof(uint16_t);
 	int len = rrl_clsname(dst + blklen, maxlen - blklen, cls, p, z);
-	if (len < 0) return len;
+	if (len < 0)
+		return len;
 	*nlen = len;
 	blklen += len;
 
diff --git a/src/knot/server/rrl.h b/src/knot/server/rrl.h
index 59ac9a6bfd622246763916f107a118e92750f24f..549a7b6bd392c37e8ad45aabfb6f3ed43cd7a3a9 100644
--- a/src/knot/server/rrl.h
+++ b/src/knot/server/rrl.h
@@ -78,7 +78,7 @@ typedef struct rrl_req {
 	const uint8_t *w;
 	uint16_t len;
 	unsigned flags;
-	const knot_question_t *qst;
+	knot_packet_t *query;
 } rrl_req_t;
 
 /*!
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 6522652a9ebba29a6c793bf0dfb2eb231db3a8a4..b892e701872d736a82c325146c9e76b27dedd678 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -249,7 +249,7 @@ static int server_bind_sockets(server_t *s)
 	}
 
 	/* Update bound interfaces. */
-	node *n = 0;
+	node_t *n = 0;
 	WALK_LIST(n, conf()->ifaces) {
 
 		/* Find already matching interface. */
@@ -267,7 +267,7 @@ static int server_bind_sockets(server_t *s)
 
 		/* Found already bound interface. */
 		if (found_match) {
-			rem_node((node *)m);
+			rem_node((node_t *)m);
 		} else {
 			log_server_info("Binding to interface %s port %d.\n",
 			                cfg_if->address, cfg_if->port);
@@ -282,7 +282,7 @@ static int server_bind_sockets(server_t *s)
 
 		/* Move to new list. */
 		if (m) {
-			add_tail(&newlist->l, (node *)m);
+			add_tail(&newlist->l, (node_t *)m);
 			++bound;
 		}
 	}
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index a766b0b14a4362ef339ca8df4577b628e1d138d3..879424993cf0d4ff0115aa45fa68c91145597157 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -99,8 +99,8 @@ enum {
 
 typedef struct ifacelist {
 	ref_t ref;
-	list l;
-	list u;
+	list_t l;
+	list_t u;
 } ifacelist_t;
 
 /*!
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 10aab3c9739c8cd2ffa2f11314e978bfe2deb64e..c91ad64009640159e6676ab92704beb0874b4c06 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -48,6 +48,13 @@ typedef struct tcp_worker_t {
 	int pipe[2];      /*!< Master-worker signalization pipes. */
 } tcp_worker_t;
 
+/*! \brief Buffers .*/
+enum {
+	QBUF   = 0, /* Query buffer ID. */
+	QRBUF  = 1, /* Response buffer ID. */
+	NBUFS  = 2  /* Buffer count. */
+};
+
 /*
  * Forward decls.
  */
@@ -131,7 +138,7 @@ static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data)
  *       and ensure that in case of good packet the response
  *       is proper.
  */
-static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen)
+static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *buf[], size_t qbuf_maxlen)
 {
 	if (fd < 0 || !w || !w->ioh) {
 		dbg_net("tcp: tcp_handle(%p, %d) - invalid parameters\n", w, fd);
@@ -148,7 +155,7 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 	sockaddr_prep(&addr);
 
 	/* Receive data. */
-	int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr);
+	int n = tcp_recv(fd, buf[QBUF], qbuf_maxlen, &addr);
 	if (n <= 0) {
 		dbg_net("tcp: client on fd=%d disconnected\n", fd);
 		if (n == KNOT_EAGAIN) {
@@ -167,27 +174,27 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 	/* Parse query. */
 	size_t resp_len = qbuf_maxlen; // 64K
 	knot_packet_type_t qtype = KNOT_QUERY_NORMAL;
-	knot_packet_t *packet = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *packet = knot_packet_new();
 	if (packet == NULL) {
-		int ret = knot_ns_error_response_from_query_wire(ns, qbuf, n,
+		int ret = knot_ns_error_response_from_query_wire(ns, buf[QBUF], n,
 		                                            KNOT_RCODE_SERVFAIL,
-		                                            qbuf, &resp_len);
+		                                            buf[QRBUF], &resp_len);
 
 		if (ret == KNOT_EOK) {
-			tcp_reply(fd, qbuf, resp_len);
+			tcp_reply(fd, buf[QRBUF], resp_len);
 		}
 
 		return KNOT_EOK;
 	}
 
-	int parse_res = knot_ns_parse_packet(qbuf, n, packet, &qtype);
+	int parse_res = knot_ns_parse_packet(buf[QBUF], n, packet, &qtype);
 	if (knot_unlikely(parse_res != KNOT_EOK)) {
 		if (parse_res > 0) { /* Returned RCODE */
 			int ret = knot_ns_error_response_from_query(ns, packet,
-			                            parse_res, qbuf, &resp_len);
+			                          parse_res, buf[QRBUF], &resp_len);
 
 			if (ret == KNOT_EOK) {
-				tcp_reply(fd, qbuf, resp_len);
+				tcp_reply(fd, buf[QRBUF], resp_len);
 			}
 		}
 		knot_packet_free(&packet);
@@ -204,7 +211,7 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 	case KNOT_QUERY_NORMAL:
 		//res = knot_ns_answer_normal(ns, packet, qbuf, &resp_len);
 		if (zones_normal_query_answer(ns, packet, &addr,
-		                              qbuf, &resp_len,
+		                              buf[QRBUF], &resp_len,
 		                              NS_TRANSPORT_TCP) == KNOT_EOK) {
 			res = KNOT_EOK;
 		}
@@ -222,12 +229,12 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 		if (xfr == NULL) {
 			knot_ns_error_response_from_query(ns, packet,
 			                                  KNOT_RCODE_SERVFAIL,
-			                                  qbuf, &resp_len);
+			                                  buf[QRBUF], &resp_len);
 			res = KNOT_EOK;
 			break;
 		}
 		xfr->session = fd;
-		xfr->wire = qbuf;
+		xfr->wire = buf[QRBUF];
 		xfr->wire_size = qbuf_maxlen;
 		xfr->query = packet;
 		xfr_task_setaddr(xfr, &addr, NULL);
@@ -236,17 +243,13 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 		return res;
 
 	case KNOT_QUERY_UPDATE:
-//		knot_ns_error_response_from_query(ns, packet,
-//		                                  KNOT_RCODE_NOTIMPL,
-//		                                  qbuf, &resp_len);
-		res = zones_process_update(ns, packet, &addr, qbuf, &resp_len,
+		res = zones_process_update(ns, packet, &addr, buf[QRBUF], &resp_len,
 		                           fd, NS_TRANSPORT_TCP);
-//		res = KNOT_EOK;
 		break;
 
 	case KNOT_QUERY_NOTIFY:
 		res = notify_process_request(ns, packet, &addr,
-					     qbuf, &resp_len);
+					     buf[QRBUF], &resp_len);
 		break;
 
 	/* Unhandled opcodes. */
@@ -256,7 +259,7 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 	case KNOT_RESPONSE_IXFR:   /*!< Processed in XFR handler. */
 		knot_ns_error_response_from_query(ns, packet,
 		                                  KNOT_RCODE_REFUSED,
-		                                  qbuf, &resp_len);
+		                                  buf[QRBUF], &resp_len);
 		res = KNOT_EOK;
 		break;
 
@@ -264,21 +267,21 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
 	default:
 		knot_ns_error_response_from_query(ns, packet,
 		                                  KNOT_RCODE_FORMERR,
-		                                  qbuf, &resp_len);
+		                                  buf[QRBUF], &resp_len);
 		res = KNOT_EOK;
 		break;
 	}
 
-	knot_packet_free(&packet);
-
 	/* Send answer. */
 	if (res == KNOT_EOK) {
-		tcp_reply(fd, qbuf, resp_len);
+		tcp_reply(fd, buf[QRBUF], resp_len);
 	} else {
 		dbg_net("tcp: failed to respond to query type=%d on fd=%d - %s\n",
 		        qtype, fd, knot_strerror(res));;
 	}
 
+	knot_packet_free(&packet);
+
 	return res;
 }
 
@@ -572,10 +575,16 @@ int tcp_loop_worker(dthread_t *thread)
 	}
 #endif /* HAVE_CAP_NG_H */
 
-	uint8_t *qbuf = malloc(SOCKET_MTU_SZ);
+	uint8_t *buf[NBUFS];
+	for (unsigned i = 0; i < NBUFS; ++i) {
+		buf[i] = malloc(SOCKET_MTU_SZ);
+	}
+
 	tcp_worker_t *w = thread->data;
-	if (w == NULL || qbuf == NULL) {
-		free(qbuf);
+	if (w == NULL || buf[QBUF] == NULL || buf[QRBUF] == NULL) {
+		for (unsigned i = 0; i < NBUFS; ++i) {
+			free(buf[i]);
+		}
 		return KNOT_EINVAL;
 	}
 
@@ -625,7 +634,7 @@ int tcp_loop_worker(dthread_t *thread)
 			if (fd == w->pipe[0]) {
 				tcp_loop_assign(fd, set);
 			} else {
-				int ret = tcp_handle(w, fd, qbuf, SOCKET_MTU_SZ);
+				int ret = tcp_handle(w, fd, buf, SOCKET_MTU_SZ);
 				if (ret == KNOT_EOK) {
 					/* Update socket activity timer. */
 					fdset_set_watchdog(set, i, max_idle);
@@ -653,7 +662,9 @@ int tcp_loop_worker(dthread_t *thread)
 	}
 
 	/* Stop whole unit. */
-	free(qbuf);
+	for (unsigned i = 0; i < NBUFS; ++i) {
+		free(buf[i]);
+	}
 	dbg_net("tcp: worker %p finished\n", w);
 	return KNOT_EOK;
 }
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index f84c8c2c23bf245c8d76ab7ccc7988c38ed9535f..de52a79000a7aea59d313eb8d3864cc150e81422 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -152,7 +152,7 @@ int udp_handle(struct answer_ctx *ans, int fd,
 	return KNOT_EOK;
 #endif
 
-	knot_packet_t *packet = knot_packet_new_mm(KNOT_PACKET_PREALLOC_QUERY, ans->mm);
+	knot_packet_t *packet = knot_packet_new_mm(ans->mm);
 	if (packet == NULL) {
 		dbg_net("udp: failed to create packet\n");
 		int ret = knot_ns_error_response_from_query_wire(ns, qbuf, qbuflen,
@@ -220,7 +220,7 @@ int udp_handle(struct answer_ctx *ans, int fd,
 		rrl_req_t rrl_rq;
 		memset(&rrl_rq, 0, sizeof(rrl_req_t));
 		rrl_rq.w = qbuf; /* Wire */
-		rrl_rq.qst = &packet->question;
+		rrl_rq.query = packet;
 
 		rcu_read_lock();
 		rrl_rq.flags = packet->flags;
@@ -535,7 +535,7 @@ int udp_reader(iohandler_t *h, dthread_t *thread)
 	mm_ctx_t mm;
 	mm.ctx = pool;
 	mm.alloc = (mm_alloc_t)mp_alloc;
-	mm.free = NULL;
+	mm.free = mm_nofree;
 
 	/* Create UDP answering context. */
 	struct answer_ctx ans_ctx;
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 3fe436cb3e49e42f09f6fef83ca8c1d5edc20b40..57befccba273aa1e7ded2bb43846841d3ae6b979 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -293,7 +293,7 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq)
 		}
 	} else if (rq->type == XFR_TYPE_IIN) {
 		knot_changesets_t *chs = (knot_changesets_t *)rq->data;
-		knot_free_changesets(&chs);
+		knot_changesets_free(&chs);
 		rq->data = NULL;
 		assert(rq->new_contents == NULL);
 	} else if (rq->type == XFR_TYPE_FORWARD) {
@@ -577,6 +577,7 @@ static int xfr_async_finish(fdset_t *set, unsigned id)
 static int xfr_task_finalize(xfrworker_t *w, knot_ns_xfr_t *rq)
 {
 	int ret = KNOT_EINVAL;
+	rcu_read_lock();
 	knot_nameserver_t *ns = w->master->ns;
 
 	if (rq->type == XFR_TYPE_AIN) {
@@ -614,6 +615,8 @@ static int xfr_task_finalize(xfrworker_t *w, knot_ns_xfr_t *rq)
 		rq->new_contents = NULL; /* Do not free. */
 	}
 
+	rcu_read_unlock();
+
 	return ret;
 }
 
@@ -621,7 +624,7 @@ static int xfr_task_finalize(xfrworker_t *w, knot_ns_xfr_t *rq)
 static int xfr_task_resp(xfrworker_t *w, knot_ns_xfr_t *rq)
 {
 	knot_nameserver_t *ns = w->master->ns;
-	knot_packet_t *re = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+	knot_packet_t *re = knot_packet_new();
 	if (re == NULL) {
 		return KNOT_ENOMEM;
 	}
@@ -937,7 +940,7 @@ static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode, char **tag)
 	/* Evaluate configured key for claimed key name.*/
 	key = xfr->tsig_key; /* Expects already set key (check_zone) */
 	xfr->tsig_key = 0;
-	if (key && kname && knot_dname_compare(key->name, kname) == 0) {
+	if (key && kname && knot_dname_cmp(key->name, kname) == 0) {
 		dbg_xfr("xfr: found claimed TSIG key for comparison\n");
 	} else {
 
@@ -1357,7 +1360,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 
 	/* Cleanup. */
 	knot_packet_free(&rq->response);  /* Free response. */
-	knot_free_changesets((knot_changesets_t **)(&rq->data));
+	knot_changesets_free((knot_changesets_t **)(&rq->data));
 	free(rq->zname);
 
 	/* Free request. */
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
index 91de3e99380986c0303a4181d916c43f935627a9..17b3dac32366c2f6d087fa2bb855ff750c1a2a03 100644
--- a/src/knot/server/xfr-handler.h
+++ b/src/knot/server/xfr-handler.h
@@ -55,7 +55,7 @@ typedef struct xfrworker_t
  */
 typedef struct xfrhandler_t
 {
-	list queue;
+	list_t queue;
 	unsigned pending; /*!< \brief Pending transfers. */
 	pthread_mutex_t pending_mx;
 	pthread_mutex_t mx; /*!< \brief Tasks synchronisation. */
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 0fb9001f15941e3265acbcdf581c698e56a54aa0..abb3ac953626a408926ed6059fda967e505286db 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -42,6 +42,8 @@
 #include "libknot/packet/response.h"
 #include "libknot/zone/zone-diff.h"
 #include "libknot/updates/ddns.h"
+#include "libknot/rdata.h"
+#include "libknot/dnssec/zone-events.h"
 
 static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100;
 static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
@@ -88,6 +90,14 @@ static int zonedata_destroy(knot_zone_t *zone)
 		zd->ixfr_dbsync = 0;
 	}
 
+	/* Cancel DNSSEC timer. */
+	if (zd->dnssec_timer) {
+		evsched_t *sch = zd->dnssec_timer->parent;
+		evsched_cancel(sch, zd->dnssec_timer);
+		evsched_event_free(sch, zd->dnssec_timer);
+		zd->dnssec_timer = NULL;
+	}
+
 	acl_delete(&zd->xfr_in.acl);
 	acl_delete(&zd->xfr_out);
 	acl_delete(&zd->notify_in);
@@ -168,7 +178,7 @@ static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone)
 		soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
 					  KNOT_RRTYPE_SOA);
 		assert(soa_rrs != NULL);
-		int64_t serial = knot_rrset_rdata_soa_serial(soa_rrs);
+		int64_t serial = knot_rdata_soa_serial(soa_rrs);
 		zd->zonefile_serial = (uint32_t)serial;
 		if (serial < 0) {
 			return KNOT_EINVAL;
@@ -238,7 +248,7 @@ static uint32_t zones_soa_timer(knot_zone_t *zone,
  */
 static uint32_t zones_soa_refresh(knot_zone_t *zone)
 {
-	return zones_soa_timer(zone, knot_rrset_rdata_soa_refresh);
+	return zones_soa_timer(zone, knot_rdata_soa_refresh);
 }
 
 /*!
@@ -249,7 +259,7 @@ static uint32_t zones_soa_refresh(knot_zone_t *zone)
  */
 static uint32_t zones_soa_retry(knot_zone_t *zone)
 {
-	return zones_soa_timer(zone, knot_rrset_rdata_soa_retry);
+	return zones_soa_timer(zone, knot_rdata_soa_retry);
 }
 
 /*!
@@ -260,7 +270,7 @@ static uint32_t zones_soa_retry(knot_zone_t *zone)
  */
 static uint32_t zones_soa_expire(knot_zone_t *zone)
 {
-	return zones_soa_timer(zone, knot_rrset_rdata_soa_expire);
+	return zones_soa_timer(zone, knot_rdata_soa_expire);
 }
 
 /*!
@@ -285,7 +295,7 @@ static int zones_expire_ev(event_t *e)
 
 	knot_zone_retain(zone); /* Keep a reference. */
 	rcu_read_unlock();
-	
+
 	/* Mark the zone as expired. This will remove the zone contents. */
 	knot_zone_contents_t *contents = knot_zonedb_expire_zone(
 			zd->server->nameserver->zone_db, zone->name);
@@ -293,7 +303,7 @@ static int zones_expire_ev(event_t *e)
 	/* Early finish this event to prevent lockup during cancellation. */
 	dbg_zones("zones: zone expired, removing from database\n");
 	evsched_event_finished(e->parent);
-	
+
 	/* Publish expired zone, must be after evsched_event_finished.
 	 * This is because some other thread may hold rcu_read_lock and
 	 * wait for event cancellation. */
@@ -430,6 +440,48 @@ static int zones_ixfrdb_sync_apply(journal_t *j, journal_node_t *n)
 	return KNOT_EOK;
 }
 
+static bool zones_changesets_empty(const knot_changesets_t *chs)
+{
+	if (chs == NULL) {
+		return true;
+	}
+
+	if (EMPTY_LIST(chs->sets)) {
+		return true;
+	}
+
+	return knot_changeset_is_empty(HEAD(chs->sets));
+}
+
+static int zones_store_changesets_begin_and_store(knot_zone_t *zone,
+                                                  knot_changesets_t *chgsets,
+                                                  journal_t **transaction)
+{
+	assert(zone != NULL);
+	assert(chgsets != NULL);
+
+	if (zones_changesets_empty(chgsets)) {
+		return KNOT_EINVAL;
+	}
+
+	*transaction = zones_store_changesets_begin(zone);
+	if (*transaction == NULL) {
+		dbg_zones("Could not start journal operation.\n");
+		return KNOT_ERROR;
+	}
+
+	int ret = zones_store_changesets(zone, chgsets, *transaction);
+	if (ret != KNOT_EOK) {
+		zones_store_changesets_rollback(*transaction);
+		*transaction = NULL;
+		dbg_zones("Could not store in the journal. Reason: %s.\n",
+		          knot_strerror(ret));
+		return ret;
+	}
+
+	return KNOT_EOK;
+}
+
 /*!
  * \brief Sync chagnes in zone to zonefile.
  */
@@ -489,7 +541,7 @@ static int zones_zonefile_sync_ev(event_t *e)
  * \retval KNOT_EINVAL on invalid parameters.
  * \retval KNOT_ENOMEM on failed memory allocation.
  */
-static int zones_set_acl(acl_t **acl, list* acl_list)
+static int zones_set_acl(acl_t **acl, list_t* acl_list)
 {
 	if (!acl || !acl_list) {
 		return KNOT_EINVAL;
@@ -579,8 +631,8 @@ static int zones_load_zone(knot_zone_t **dst, const char *zone_name,
 	/* 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, strlen(zone_name), 0);
-	if (knot_dname_compare(dname, dname_req) != 0) {
+	dname_req = knot_dname_from_str(zone_name, strlen(zone_name));
+	if (knot_dname_cmp(dname, dname_req) != 0) {
 		log_server_error("Origin of the zone db file is "
 				 "different than '%s'\n",
 				 zone_name);
@@ -669,7 +721,6 @@ int zones_changesets_from_binary(knot_changesets_t *chgsets)
 	/*! \todo #1291 Why doesn't this just increment stream ptr? */
 
 	assert(chgsets != NULL);
-	assert(chgsets->allocated >= chgsets->count);
 	/*
 	 * Parses changesets from the binary format stored in chgsets->data
 	 * into the changeset_t structures.
@@ -677,10 +728,9 @@ int zones_changesets_from_binary(knot_changesets_t *chgsets)
 	knot_rrset_t *rrset = 0;
 	int ret = 0;
 
-	for (int i = 0; i < chgsets->count; ++i) {
-
+	knot_changeset_t* chs = NULL;
+	WALK_LIST(chs, chgsets->sets) {
 		/* Read changeset flags. */
-		knot_changeset_t* chs = chgsets->sets + i;
 		size_t remaining = chs->size;
 		memcpy(&chs->flags, chs->data, sizeof(uint32_t));
 		remaining -= sizeof(uint32_t);
@@ -701,10 +751,8 @@ int zones_changesets_from_binary(knot_changesets_t *chgsets)
 		dbg_xfr_verb("xfr: reading RRSets to REMOVE, first RR is %hu\n",
 		             knot_rrset_type(rrset));
 		assert(knot_rrset_type(rrset) == KNOT_RRTYPE_SOA);
-		assert(chs->serial_from ==
-		       knot_rrset_rdata_soa_serial(rrset));
-		knot_changeset_store_soa(&chs->soa_from, &chs->serial_from,
-					 rrset);
+		assert(chs->serial_from == knot_rdata_soa_serial(rrset));
+		knot_changeset_add_soa(chs, rrset, KNOT_CHANGESET_REMOVE);
 
 		/* Read remaining RRSets */
 		int in_remove_section = 1;
@@ -726,35 +774,28 @@ int zones_changesets_from_binary(knot_changesets_t *chgsets)
 
 				/* Move to ADD section if in REMOVE. */
 				if (in_remove_section) {
-					knot_changeset_store_soa(
-						&chgsets->sets[i].soa_to,
-						&chgsets->sets[i].serial_to,
-						rrset);
+					knot_changeset_add_soa(chs, rrset,
+					                       KNOT_CHANGESET_ADD);
 					dbg_xfr_verb("xfr: reading RRSets"
 					             " to ADD\n");
 					in_remove_section = 0;
 				} else {
 					/* Final SOA. */
 					dbg_xfr_verb("xfr: extra SOA\n");
-					knot_rrset_deep_free(&rrset, 1, 1);
+					knot_rrset_deep_free(&rrset, 1);
 					break;
 				}
 			} else {
 				/* Remove RRSets. */
 				if (in_remove_section) {
 					ret = knot_changeset_add_rrset(
-						&chgsets->sets[i].remove,
-						&chgsets->sets[i].remove_count,
-						&chgsets->sets[i]
-						    .remove_allocated,
-						rrset);
+						chs, rrset,
+						KNOT_CHANGESET_REMOVE);
 				} else {
 				/* Add RRSets. */
 					ret = knot_changeset_add_rrset(
-						&chgsets->sets[i].add,
-						&chgsets->sets[i].add_count,
-						&chgsets->sets[i].add_allocated,
-						rrset);
+						chs, rrset,
+						KNOT_CHANGESET_ADD);
 				}
 
 				/* Check result. */
@@ -830,15 +871,9 @@ static int zones_load_changesets(const knot_zone_t *zone,
 			break;
 		}
 
-		/*! \todo Increment and decrement to reserve +1,
-		 *        but not incremented counter.*/
-		/* Check changesets size if needed. */
-		++dst->count;
-		ret = knot_changesets_check_size(dst);
-		--dst->count;
-		if (ret != KNOT_EOK) {
-			//--dst->count;
-			dbg_xfr("xfr: failed to check changesets size: %s\n",
+		knot_changeset_t *chs = knot_changesets_create_changeset(dst);
+		if (chs == NULL) {
+			dbg_xfr("xfr: failed to create changeset: %s\n",
 			        knot_strerror(ret));
 			journal_release(j);
 			return KNOT_ERROR;
@@ -853,7 +888,6 @@ static int zones_load_changesets(const knot_zone_t *zone,
 		/* Initialize changeset. */
 		dbg_xfr_detail("xfr: reading entry #%zu id=%llu\n",
 		               dst->count, (unsigned long long)n->id);
-		knot_changeset_t *chs = dst->sets + dst->count;
 		chs->serial_from = ixfrdb_key_from(n->id);
 		chs->serial_to = ixfrdb_key_to(n->id);
 		chs->data = malloc(n->len);
@@ -872,11 +906,10 @@ static int zones_load_changesets(const knot_zone_t *zone,
 		}
 
 		/* Update changeset binary size. */
-		chs->size = chs->allocated = n->len;
+		chs->size = n->len;
 
 		/* Next node. */
 		found_to = chs->serial_to;
-		++dst->count;
 		++n;
 
 		/*! \todo Check consistency. */
@@ -904,8 +937,6 @@ static int zones_load_changesets(const knot_zone_t *zone,
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
 /*!
  * \brief Apply changesets to zone from journal.
  *
@@ -937,7 +968,7 @@ static int zones_journal_apply(knot_zone_t *zone)
 	soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
 	                            KNOT_RRTYPE_SOA);
 	assert(soa_rrs != NULL);
-	int64_t serial_ret = knot_rrset_rdata_soa_serial(soa_rrs);
+	int64_t serial_ret = knot_rdata_soa_serial(soa_rrs);
 	if (serial_ret < 0) {
 		rcu_read_unlock();
 		return KNOT_EINVAL;
@@ -947,12 +978,17 @@ static int zones_journal_apply(knot_zone_t *zone)
 	/* Load all pending changesets. */
 	dbg_zones_verb("zones: loading all changesets of '%s' from SERIAL %u\n",
 	               zd->conf->name, serial);
-	knot_changesets_t* chsets = malloc(sizeof(knot_changesets_t));
-	memset(chsets, 0, sizeof(knot_changesets_t));
+	knot_changesets_t* chsets =
+		knot_changesets_create(KNOT_CHANGESET_TYPE_IXFR);
+	if (chsets == NULL) {
+		rcu_read_unlock();
+		return KNOT_ERROR;
+	}
+
 	/*! \todo Check what should be the upper bound. */
 	int ret = zones_load_changesets(zone, chsets, serial, serial - 1);
 	if (ret == KNOT_EOK || ret == KNOT_ERANGE) {
-		if (chsets->count > 0) {
+		if (!EMPTY_LIST(chsets->sets)) {
 			/* Apply changesets. */
 			log_server_info("Applying '%zu' changesets from journal "
 			                "to zone '%s'.\n",
@@ -966,23 +1002,20 @@ static int zones_journal_apply(knot_zone_t *zone)
 				                 zd->conf->name,
 				                 knot_strerror(apply_ret));
 				ret = KNOT_ERROR;
-
-				// Cleanup old and new contents
-				xfrin_rollback_update(zone->contents,
-				                      &contents,
-				                      &chsets->changes);
 			} else {
 				/* Switch zone immediately. */
 				log_server_info("Zone '%s' serial %u -> %u.\n",
 				                zd->conf->name,
 				                serial, knot_zone_serial(contents));
+				dbg_zones("Old zone contents: %p, new: %p\n",
+				          zone->contents, contents);
 				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);
+							chsets->changes);
 				} else {
 					log_server_error("Failed to apply "
 					  "changesets to '%s' - Switch failed: "
@@ -993,7 +1026,7 @@ static int zones_journal_apply(knot_zone_t *zone)
 					// Cleanup old and new contents
 					xfrin_rollback_update(zone->contents,
 					                      &contents,
-					                      &chsets->changes);
+					                      chsets->changes);
 				}
 			}
 		}
@@ -1004,7 +1037,294 @@ static int zones_journal_apply(knot_zone_t *zone)
 
 	/* Free changesets and return. */
 	rcu_read_unlock();
-	knot_free_changesets(&chsets);
+	knot_changesets_free(&chsets);
+	return ret;
+}
+
+static void zones_free_merged_changesets(knot_changesets_t *diff_chs,
+                                         knot_changesets_t *sec_chs)
+{
+	/*!
+	 * Merged changesets freeing can be quite complicated, since there
+	 * are several cases to handle. (NULL and empty changesets)
+	 */
+	if (diff_chs == NULL &&
+	    sec_chs == NULL) {
+	} else if (diff_chs == NULL &&
+	           sec_chs != NULL) {
+		knot_changesets_free(&sec_chs);
+	} else if (sec_chs == NULL &&
+	           diff_chs != NULL) {
+		knot_changesets_free(&diff_chs);
+	} else {
+		/*!
+		 * Merged changesets, deep free 'diff_chs',
+		 * shallow free 'sec_chs', unless one of them is empty.
+		 */
+		if (zones_changesets_empty(sec_chs)
+		    || zones_changesets_empty(diff_chs)) {
+			knot_changesets_free(&sec_chs);
+			knot_changesets_free(&diff_chs);
+		} else {
+			/*!
+			 * Ending SOA from the merged changeset was used in
+			 * zone (same as in DNSSEC changeset). It thus must not
+			 * be freed.
+			 */
+			knot_changesets_get_last(diff_chs)->soa_to = NULL;
+			knot_changesets_free(&diff_chs);
+
+			/*!
+			 * The "SOA from" from the second changeset is not used,
+			 * thus must be freed
+			 */
+			knot_rrset_deep_free_no_sig(
+			  &(knot_changesets_get_last(sec_chs)->soa_from), 1);
+
+			// Reset sec_chs' chngeset list, else we'd double free.
+			init_list(&sec_chs->sets);
+			knot_changesets_free(&sec_chs);
+		}
+	}
+}
+
+static int zones_merge_and_store_changesets(knot_zone_t *zone,
+                                            knot_changesets_t *diff_chs,
+                                            knot_changesets_t *sec_chs,
+                                            journal_t **transaction)
+{
+	if (zones_changesets_empty(diff_chs) &&
+	    zones_changesets_empty(sec_chs)) {
+		return KNOT_EOK;
+	}
+	if (!zones_changesets_empty(diff_chs) &&
+	    zones_changesets_empty(sec_chs)) {
+		return zones_store_changesets_begin_and_store(zone, diff_chs,
+		                                              transaction);
+	}
+	if (zones_changesets_empty(diff_chs) &&
+	    !zones_changesets_empty(sec_chs)) {
+		return zones_store_changesets_begin_and_store(zone, sec_chs,
+		                                              transaction);
+	}
+
+	knot_changeset_t *diff_ch = knot_changesets_get_last(diff_chs);
+	knot_changeset_t *sec_ch =  knot_changesets_get_last(sec_chs);
+
+	/*!
+	 * Beginning SOA of second changeset should be equal to ending SOA
+	 * of the first changeset.
+	 */
+	assert(diff_ch->serial_to == sec_ch->serial_from);
+
+	int ret = knot_changeset_merge(diff_ch, sec_ch);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	/*!
+	 * Now the ending serial of first changeset (the merged one) should be
+	 * equal to the ending serial of second changeset. Also the SOAs should
+	 * be the same.
+	 */
+	assert(diff_ch->serial_to == sec_ch->serial_to);
+	assert(diff_ch->soa_to == sec_ch->soa_to);
+
+	// Store *ALL* changes to disk.
+	ret = zones_store_changesets_begin_and_store(zone, diff_chs,
+	                                             transaction);
+	if (ret != KNOT_EOK) {
+		log_zone_error("Could not store changesets to journal (%s)!",
+		               knot_strerror(ret));
+		return ret;
+	}
+
+	return KNOT_EOK;
+}
+
+static int64_t expiration_to_relative(uint32_t exp) {
+	time_t t = time(NULL);
+	/*!
+	 * This assert could happen only if signing itself took more
+	 * than the 'refresh' time and some (or one) signatures would be kept.
+	 */
+	assert(t < exp);
+	// We need the time in miliseconds
+	return (exp - t) * 1000;
+}
+
+/*! \brief Creates diff and DNSSEC changesets and stores them to journal. */
+static int zones_do_diff_and_sign(const conf_zone_t *z,
+                                  knot_zone_t *zone,
+                                  const knot_nameserver_t *ns,
+                                  const knot_dname_t *origin,
+                                  bool zone_changed)
+{
+	/* Calculate differences. */
+	rcu_read_lock();
+	knot_zone_t *z_old = knot_zonedb_find_zone(ns->zone_db,
+	                                           origin);
+	/* 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);
+		dbg_zones("Going to calculate diff. "
+		          "Old contents: %p, new: %p\n",
+		          zc_old, zc);
+
+	knot_changesets_t *diff_chs = NULL;
+	if (z->build_diffs && zc && zc_old && zone_changed) {
+		diff_chs = knot_changesets_create(KNOT_CHANGESET_TYPE_IXFR);
+		if (diff_chs == NULL) {
+			rcu_read_unlock();
+			return KNOT_ENOMEM;
+		}
+		knot_changeset_t *diff_ch =
+			knot_changesets_create_changeset(diff_chs);
+		if (diff_ch == NULL) {
+			knot_changesets_free(&diff_chs);
+			rcu_read_unlock();
+			return KNOT_ENOMEM;
+		}
+		dbg_zones("Generating diff.\n");
+		int ret = zones_create_changeset(z_old,
+		                                 zone, diff_ch);
+		if (ret == KNOT_ENODIFF) {
+			log_zone_warning("Zone file for "
+			                 "'%s' changed, "
+			                 "but serial didn't - "
+			                 "won't create changesets.\n",
+			                 z->name);
+		} else if (ret != KNOT_EOK) {
+			log_zone_warning("Failed to calculate "
+			                 "differences from the "
+			                 "zone file update: "
+			                 "%s\n", knot_strerror(ret));
+		}
+		/* Even if there's nothing to create the diff from
+		 * we can still sign the zone - inconsistencies may happen. */
+		// TODO consider returning straight away when serial did not change
+		if (ret != KNOT_EOK && ret != KNOT_ENODIFF) {
+			knot_changesets_free(&diff_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+	}
+
+	/* Run DNSSEC signing if enabled (no zone change needed) */
+	knot_changesets_t *sec_chs = NULL;
+	knot_changeset_t *sec_ch = NULL;
+	knot_zone_contents_t *new_contents = NULL;
+	if (z->dnssec_enable) {
+		sec_chs = knot_changesets_create(KNOT_CHANGESET_TYPE_IXFR);
+		if (sec_chs == NULL) {
+			knot_changesets_free(&diff_chs);
+			rcu_read_unlock();
+			return KNOT_ENOMEM;
+		}
+		/* Extra changeset is needed. */
+		sec_ch = knot_changesets_create_changeset(sec_chs);
+		if (sec_ch == NULL) {
+			knot_changesets_free(&diff_chs);
+			knot_changesets_free(&sec_chs);
+			rcu_read_unlock();
+			return KNOT_ENOMEM;
+		}
+
+		/*!
+		 * Increment serial even if diff did that. This way it's always
+		 * possible to flush the changes to zonefile.
+		 */
+		knot_update_serial_t soa_up =  KNOT_SOA_SERIAL_INC;
+
+		uint32_t expires_at = 0;
+		int ret = knot_dnssec_zone_sign(zone, sec_ch, soa_up,
+		                                &expires_at);
+		if (ret != KNOT_EOK) {
+			knot_changesets_free(&diff_chs);
+			knot_changesets_free(&sec_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+
+		// Schedule next zone signing
+		ret = zones_schedule_dnssec(zone,
+		                            expiration_to_relative(expires_at),
+		                            false);
+		if (ret != KNOT_EOK) {
+			knot_changesets_free(&diff_chs);
+			knot_changesets_free(&sec_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+	}
+
+	/* Merge changesets created by diff and sign. */
+	journal_t *transaction = NULL;
+	int ret = zones_merge_and_store_changesets(zone, diff_chs,
+	                                           sec_chs,
+	                                           &transaction);
+	if (ret != KNOT_EOK) {
+		knot_changesets_free(&diff_chs);
+		knot_changesets_free(&sec_chs);
+		rcu_read_unlock();
+		return ret;
+	}
+
+	bool new_signatures = !knot_changeset_is_empty(sec_ch);
+	/* Apply DNSSEC changeset. */
+	if (new_signatures) {
+		ret = xfrin_apply_changesets(zone, sec_chs,
+		                             &new_contents);
+		if (ret != KNOT_EOK) {
+			zones_store_changesets_rollback(transaction);
+			zones_free_merged_changesets(diff_chs, sec_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+		assert(new_contents);
+	}
+
+	/* Commit transaction. */
+	if (transaction) {
+		ret = zones_store_changesets_commit(transaction);
+		if (ret != KNOT_EOK) {
+			log_zone_error("Failed to commit stored "
+			               "changesets: %s."
+			               "\n", knot_strerror(ret));
+			zones_free_merged_changesets(diff_chs, sec_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+	}
+
+	/* Switch zone contents. */
+	if (new_contents) {
+		rcu_read_unlock();
+		ret = xfrin_switch_zone(zone, new_contents,
+		                        XFR_TYPE_DNSSEC);
+		rcu_read_lock();
+		if (ret != KNOT_EOK) {
+			// Cleanup old and new contents
+			xfrin_rollback_update(zone->contents,
+			                      &new_contents,
+			                      sec_chs->changes);
+			zones_free_merged_changesets(diff_chs, sec_chs);
+			rcu_read_unlock();
+			return ret;
+		}
+	}
+
+	if (new_signatures) {
+		xfrin_cleanup_successful_update(sec_chs->changes);
+		char *zname = knot_dname_to_str(zone->name);
+		log_zone_info("Zone %s was successfully signed.\n",
+		              zname);
+		free(zname);
+	}
+
+	rcu_read_unlock();
+
+	zones_free_merged_changesets(diff_chs, sec_chs);
 	return ret;
 }
 
@@ -1033,8 +1353,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 
 	/* Convert the zone name into a domain name. */
 	/* Local allocation, will be discarded. */
-	knot_dname_t *dname = knot_dname_new_from_str(z->name, strlen(z->name),
-	                                              NULL);
+	knot_dname_t *dname = knot_dname_from_str(z->name, strlen(z->name));
 	if (dname == NULL) {
 		log_server_error("Error creating domain name from zone"
 		                 " name\n");
@@ -1066,7 +1385,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 			dbg_zones_verb("zones: loading stub zone '%s' "
 			               "for bootstrap.\n",
 			               z->name);
-			knot_dname_t *owner = knot_dname_deep_copy(dname);
+			knot_dname_t *owner = knot_dname_copy(dname);
 			zone = knot_zone_new_empty(owner);
 			if (zone != NULL) {
 				ret = KNOT_EOK;
@@ -1088,7 +1407,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 					KNOT_RRTYPE_SOA);
 				int64_t sn = 0;
 				if (apex && soa) {
-					sn = knot_rrset_rdata_soa_serial(soa);
+					sn = knot_rdata_soa_serial(soa);
 					if (sn < 0) sn = 0;
 				}
 				log_server_info("Loaded zone '%s' serial %u\n",
@@ -1128,6 +1447,16 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 			zd->conf = z;
 		}
 
+		/* DNSSEC. */
+		if (z->dnssec_enable && (!EMPTY_LIST(z->acl.notify_in) ||
+		                         !EMPTY_LIST(z->acl.xfr_in))) {
+			log_server_warning("DNSSEC signing enabled for zone "
+			                   "'%s', disabling incoming XFR.\n",
+			                   z->name);
+			WALK_LIST_FREE(z->acl.notify_in);
+			WALK_LIST_FREE(z->acl.xfr_in);
+		}
+
 		/* Update ACLs. */
 		dbg_zones("Updating zone ACLs.\n");
 		zones_set_acl(&zd->xfr_in.acl, &z->acl.xfr_in);
@@ -1214,33 +1543,10 @@ 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 == KNOT_ENODIFF) {
-				log_zone_warning("Zone file for '%s' changed, "
-				                 "but serial didn't - "
-				                 "won't create changesets.\n",
-				                 z->name);
-			} else if (bd != KNOT_EOK) {
-				log_zone_warning("Failed to calculate differences"
-				                 " from the zone file update: "
-				                 "%s\n", knot_strerror(bd));
-			}
-		}
-		rcu_read_unlock();
+		/* Create and apply changesets (zone-diff and DNSSEC). */
+		ret = zones_do_diff_and_sign(z, zone, ns, dname, zone_changed);
 	}
 
-	/* CLEANUP */
-//	knot_zone_contents_dump(knot_zone_get_contents(zone), 1);
-
-	/* Directly discard zone. */
 	knot_dname_free(&dname);
 	return ret;
 }
@@ -1331,7 +1637,7 @@ static int zonewalker(dthread_t *thread)
  * \return Number of inserted zones.
  */
 static int zones_insert_zones(knot_nameserver_t *ns,
-                              const list *zone_conf,
+                              const list_t *zone_conf,
                               knot_zonedb_t *db_new)
 {
 	int ret = 0;
@@ -1519,7 +1825,7 @@ static int zones_update_forward(int fd, knot_ns_transport_t ttype,
 	rq->packet_nr = (int)knot_packet_id(query);
 
 	/* Duplicate query to keep it in memory during forwarding. */
-	rq->query = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	rq->query = knot_packet_new();
 	if (!rq->query) {
 		xfr_task_free(rq);
 		rcu_read_unlock();
@@ -1533,7 +1839,7 @@ static int zones_update_forward(int fd, knot_ns_transport_t ttype,
 		rcu_read_unlock();
 		return KNOT_ENOMEM;
 	}
-	rq->query->free_wireformat = 1;
+	rq->query->flags |= KNOT_PF_FREE_WIRE;
 	memcpy(rq->query->wireformat, query->wireformat, knot_packet_size(query));
 
 	/* Retain pointer to zone and issue. */
@@ -1546,42 +1852,8 @@ static int zones_update_forward(int fd, knot_ns_transport_t ttype,
 	return KNOT_EOK;
 }
 
-
-
 /*----------------------------------------------------------------------------*/
 
-static int zones_store_changesets_to_disk(knot_zone_t *zone,
-                                          knot_changesets_t *chgsets)
-{
-	journal_t *journal = zones_store_changesets_begin(zone);
-	if (journal == NULL) {
-		dbg_zones("zones: create_changesets: "
-		          "Could not start journal operation.\n");
-		return KNOT_ERROR;
-	}
-
-	int ret = zones_store_changesets(zone, chgsets, journal);
-	if (ret != KNOT_EOK) {
-		zones_store_changesets_rollback(journal);
-		dbg_zones("zones: create_changesets: "
-		          "Could not store in the journal. Reason: %s.\n",
-		          knot_strerror(ret));
-
-		return ret;
-	}
-
-	ret = zones_store_changesets_commit(journal);
-	if (ret != KNOT_EOK) {
-		dbg_zones("zones: create_changesets: "
-		          "Could not commit to journal. Reason: %s.\n",
-		          knot_strerror(ret));
-
-		return ret;
-	}
-
-	return KNOT_EOK;
-}
-
 /*! \brief Process UPDATE query.
  *
  * Functions expects that the query is already authenticated
@@ -1616,41 +1888,36 @@ static int zones_process_update_auth(knot_zone_t *zone,
 	free(keytag);
 	log_zone_info("%s Started.\n", msg);
 
-
 	/* Reserve place for the TSIG */
 	if (tsig_key != NULL) {
 		size_t tsig_max_size = tsig_wire_maxsize(tsig_key);
 		knot_packet_set_tsig_size(resp, tsig_max_size);
 	}
 
-	/* We must prepare a changesets_t structure even if
-	 * there is only one changeset - because of the API. */
-	knot_changesets_t *chgsets = NULL;
-	ret = knot_changeset_allocate(&chgsets, KNOT_CHANGESET_TYPE_DDNS);
-	if (ret != KNOT_EOK) {
+	/*!
+	 * We must prepare a changesets_t structure even though there will
+	 * be only one changeset - because of the API.
+	 */
+	knot_changesets_t *chgsets =
+		knot_changesets_create(KNOT_CHANGESET_TYPE_DDNS);
+	if (chgsets == NULL) {
 		*rcode = KNOT_RCODE_SERVFAIL;
-		log_zone_error("%s %s\n", msg, knot_strerror(ret));
+		log_zone_error("%s Cannot create changesets structure.\n", msg);
 		free(msg);
 		return ret;
 	}
 
-	assert(chgsets->allocated >= 1);
-
-	/*
-	 * NEW DDNS PROCESSING -------------------------------------------------
-	 */
-	/* 1) Process the UPDATE packet, apply to zone, create changesets. */
-
+	// Process the UPDATE packet, apply to zone, create changesets.
 	dbg_zones_verb("Processing UPDATE packet.\n");
-	chgsets->count = 1; /* DU is represented by a single chset. */
+	if (knot_changesets_create_changeset(chgsets) == NULL) {
+		return KNOT_ENOMEM;
+	}
 	*rcode = KNOT_RCODE_SERVFAIL; /* SERVFAIL unless it applies correctly. */
 
 	knot_zone_contents_t *new_contents = NULL;
-	ret = knot_ns_process_update2(knot_packet_query(resp),
-	                              knot_zone_get_contents(zone),
-	                              &new_contents,
-	                              chgsets, rcode);
-
+	ret = knot_ns_process_update(knot_packet_query(resp),
+	                             knot_zone_get_contents(zone),
+	                             &new_contents, chgsets, rcode);
 	if (ret != KNOT_EOK) {
 		if (ret < 0) {
 			log_zone_error("%s %s\n", msg, knot_strerror(ret));
@@ -1665,47 +1932,137 @@ static int zones_process_update_auth(knot_zone_t *zone,
 			}
 		}
 
-		knot_free_changesets(&chgsets);
+		knot_changesets_free(&chgsets);
 		free(msg);
 		return (ret < 0) ? ret : KNOT_EOK;
 	}
 
-	/* 2) Store changesets, (TODO: but do not commit???). */
-	ret = zones_store_changesets_to_disk(zone, chgsets);
+	knot_changesets_t *sec_chs =
+		knot_changesets_create(KNOT_CHANGESET_TYPE_DNSSEC);
+	knot_changeset_t *sec_ch = knot_changesets_create_changeset(sec_chs);
+	if (sec_chs == NULL || sec_ch == NULL) {
+		xfrin_rollback_update(zone->contents, &new_contents,
+		                      chgsets->changes);
+		knot_changesets_free(&chgsets);
+		free(msg);
+		return KNOT_ENOMEM;
+	}
+
+	dbg_zones_verb("%s: Signing the UPDATE\n", msg);
+	// Sign the created changeset
+	conf_zone_t *zone_config = ((zonedata_t *)knot_zone_data(zone))->conf;
+	assert(zone_config);
+	if (zone_config->dnssec_enable) {
+		ret = knot_dnssec_sign_changeset(new_contents,
+		                                 knot_changesets_get_last(chgsets),
+		                                 sec_ch, KNOT_SOA_SERIAL_KEEP);
+		if (ret != KNOT_EOK) {
+			log_zone_error("%s: Failed to sign incoming update (%s)\n",
+			               msg, knot_strerror(ret));
+			xfrin_rollback_update(zone->contents, &new_contents,
+			                      chgsets->changes);
+			knot_changesets_free(&chgsets);
+			free(msg);
+			return ret;
+		}
+	}
+
+	dbg_zones_detail("%s: UPDATE signed (%zu changes)\n", msg,
+	                 knot_changeset_size(sec_ch));
+
+	// Merge changesets
+	journal_t *transaction = NULL;
+	ret = zones_merge_and_store_changesets(zone, chgsets, sec_chs,
+	                                       &transaction);
 	if (ret != KNOT_EOK) {
-		log_zone_error("%s %s\n", msg, knot_strerror(ret));
+		log_zone_error("%s: Failed to store changesets (%s)\n",
+		               msg, knot_strerror(ret));
 		xfrin_rollback_update(zone->contents, &new_contents,
-		                      &chgsets->changes);
-		knot_free_changesets(&chgsets);
+		                      chgsets->changes);
+		zones_free_merged_changesets(chgsets, sec_chs);
 		free(msg);
 		return ret;
 	}
 
-	/* 3) Switch zone contents. */
+	bool new_signatures = !knot_changeset_is_empty(sec_ch);
+	knot_zone_contents_t *dnssec_contents = NULL;
+	// Apply DNSSEC changeset
+	if (new_signatures) {
+		knot_zone_t *fake_zone = knot_zone_new_empty(zone->name);
+		if (fake_zone == NULL) {
+			log_zone_error("%s: Failed to apply changesets (%s)\n",
+			               msg, knot_strerror(KNOT_ENOMEM));
+			xfrin_rollback_update(zone->contents, &new_contents,
+			                      chgsets->changes);
+			zones_free_merged_changesets(chgsets, sec_chs);
+			free(msg);
+			return KNOT_ENOMEM;
+		}
+		// Apply changeset to zone created by DDNS processing
+		fake_zone->contents = new_contents;
+		// Set zone generation to old, else applying fails
+		knot_zone_contents_set_gen_old(new_contents);
+		ret = xfrin_apply_changesets(fake_zone, sec_chs,
+		                             &dnssec_contents);
+		free(fake_zone);
+		if (ret != KNOT_EOK) {
+			log_zone_error("%s: Failed to sign incoming update %s\n",
+			               msg, knot_strerror(ret));
+			zones_store_changesets_rollback(transaction);
+			zones_free_merged_changesets(chgsets, sec_chs);
+			return ret;
+		}
+		assert(dnssec_contents);
+	}
+
+	dbg_zload_verb("%s: DNSSEC changes applied\n", msg);
+
+	// Commit transaction.
+	if (transaction) {
+		ret = zones_store_changesets_commit(transaction);
+		if (ret != KNOT_EOK) {
+			log_zone_error("%s: Failed to commit stored "
+			               "changesets: %s."
+			               "\n", msg, knot_strerror(ret));
+			xfrin_rollback_update(zone->contents, &new_contents,
+			                      chgsets->changes);
+			zones_free_merged_changesets(chgsets, sec_chs);
+			free(msg);
+			return ret;
+		}
+	}
+
+	// Switch zone contents.
 	knot_zone_retain(zone); /* Retain pointer for safe RCU unlock. */
 	rcu_read_unlock();      /* Unlock for switch. */
-	ret = xfrin_switch_zone(zone, new_contents, XFR_TYPE_UPDATE);
+	ret = xfrin_switch_zone(zone,
+	                        dnssec_contents ? dnssec_contents : new_contents,
+	                        XFR_TYPE_UPDATE);
 	rcu_read_lock();        /* Relock */
 	knot_zone_release(zone);/* Release held pointer. */
-
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s Failed to replace current zone - %s\n",
 		               msg, knot_strerror(ret));
 		// Cleanup old and new contents
 		xfrin_rollback_update(zone->contents, &new_contents,
-		                      &chgsets->changes);
+		                      chgsets->changes);
 
 		/* Free changesets, but not the data. */
-		knot_free_changesets(&chgsets);
+		zones_free_merged_changesets(chgsets, sec_chs);
 		return KNOT_ERROR;
 	}
 
-	/* 4) Cleanup. */
-
-	xfrin_cleanup_successful_update(&chgsets->changes);
+	// Cleanup.
+	xfrin_cleanup_successful_update(chgsets->changes);
+	if (sec_chs) {
+		xfrin_cleanup_successful_update(sec_chs->changes);
+	}
+	if (new_contents && new_signatures) {
+		xfrin_zone_contents_free(&new_contents);
+	}
 
-	/* Free changesets, but not the data. */
-	knot_free_changesets(&chgsets);
+	// Free changesets, but not the data.
+	zones_free_merged_changesets(chgsets, sec_chs);
 	assert(ret == KNOT_EOK);
 	*rcode = KNOT_RCODE_NOERROR; /* Mark as successful. */
 	log_zone_info("%s Finished.\n", msg);
@@ -1713,53 +2070,7 @@ static int zones_process_update_auth(knot_zone_t *zone,
 	free(msg);
 	msg = NULL;
 
-	/*
-	 * \NEW DDNS PROCESSING ------------------------------------------------
-	 */
-
-
-//	/* 1) Process the incoming packet, prepare
-//	 *    prerequisities and changeset.
-//	 */
-//	dbg_zones_verb("Processing UPDATE packet.\n");
-//	chgsets->count = 1; /* DU is represented by a single chset. */
-//	ret = knot_ns_process_update(knot_packet_query(resp),
-//				     knot_zone_contents(zone),
-//				     &chgsets->sets[0], rcode);
-
-//	if (ret != KNOT_EOK) {
-//		log_zone_error("%s %s\n", msg, knot_strerror(ret));
-//		knot_free_changesets(&chgsets);
-//		free(msg);
-//		return ret;
-//	}
-
-//	/* 2) Save changeset to journal.
-//	 *    Apply changeset to zone.
-//	 *    Commit changeset to journal.
-//	 *    Switch the zone.
-//	 */
-//	knot_zone_contents_t *contents_new = NULL;
-//	knot_zone_retain(zone); /* Retain pointer for safe RCU unlock. */
-//	rcu_read_unlock();      /* Unlock for switch. */
-//	dbg_zones_verb("Storing and applying changesets.\n");
-//	ret = zones_store_and_apply_chgsets(chgsets, zone, &contents_new, msg,
-//					    XFR_TYPE_UPDATE);
-//	rcu_read_lock();        /* Relock */
-//	knot_zone_release(zone);/* Release held pointer. */
-//	free(msg);
-//	msg = NULL;
-
-//	/* Changesets should be freed by now. */
-//	if (ret != KNOT_EOK) {
-//		dbg_zones_verb("Storing and applying changesets failed: %s.\n",
-//			       knot_strerror(ret));
-//		*rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
-//		                             : KNOT_RCODE_SERVFAIL;
-//		return ret;
-//	}
-
-	/* 3) Prepare DDNS response. */
+	// Prepare DDNS response.
 	assert(*rcode == KNOT_RCODE_NOERROR);
 	dbg_zones_verb("Preparing NOERROR UPDATE response RCODE=%u "
 		       "pkt=%p resp_wire=%p\n", *rcode, resp, resp_wire);
@@ -1772,14 +2083,14 @@ static int zones_process_update_auth(knot_zone_t *zone,
 		*rcode = KNOT_RCODE_SERVFAIL;
 		return ret;
 	} else {
-		/* This is strange, but the knot_packet_to_wire() can't write
-		 * to already existing buffer. */
+		/*!
+		 * This is strange, but the knot_packet_to_wire() can't write
+		 * to already existing buffer. TODO: fix API
+		 */
 		memcpy(resp_wire, tmp_wire, *rsize);
 	}
 
 	dbg_zones("DDNS reply rsize = %zu\n", *rsize);
-
-
 	return ret;
 }
 
@@ -1902,7 +2213,7 @@ int zones_zonefile_sync(knot_zone_t *zone, journal_t *journal)
 	                            KNOT_RRTYPE_SOA);
 	assert(soa_rrs != NULL);
 
-	int64_t serial_ret = knot_rrset_rdata_soa_serial(soa_rrs);
+	int64_t serial_ret = knot_rdata_soa_serial(soa_rrs);
 	if (serial_ret < 0) {
 		rcu_read_unlock();
 		pthread_mutex_unlock(&zd->lock);
@@ -2045,7 +2356,6 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
 	int ret = knot_ns_prep_normal_response(nameserver, query, &resp, &zone,
 	                                       (transport == NS_TRANSPORT_TCP)
 	                                       ? *rsize : 0);
-	query->zone = zone;
 
 	switch (ret) {
 	case KNOT_EOK:
@@ -2141,7 +2451,10 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
 					      zone, resp, resp_wire, &answer_size,
 					      transport == NS_TRANSPORT_UDP);
 				}
-				query->flags = resp->flags; /* Copy markers. */
+
+				/* Copy wildcard markers. */
+				if (resp->flags & KNOT_PF_WILDCARD)
+					query->flags |= KNOT_PF_WILDCARD;
 			}
 
 			dbg_zones_detail("rsize = %zu\n", *rsize);
@@ -2578,7 +2891,7 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname)
 	char *new_fname = NULL;
 	int fd = zones_open_free_filename(fname, &new_fname);
 	if (fd < 0) {
-		return FLOADER_EWRITABLE;
+		return KNOT_EWRITABLE;
 	}
 
 	FILE *f = fdopen(fd, "w");
@@ -2629,20 +2942,22 @@ int zones_save_zone(const knot_ns_xfr_t *xfr)
 
 	dbg_xfr("xfr: %s Saving new zone file.\n", xfr->msg);
 
+	rcu_read_lock();
+
 	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;
 
 	/* 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);
+	knot_dname_t *cur_name = knot_dname_from_str(zd->conf->name,
+	                                                 strlen(zd->conf->name));
 	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);
+	int r = knot_dname_cmp(cur_name, new_name);
 	knot_dname_free(&cur_name);
 	if (r != 0) {
+		rcu_read_unlock();
 		return KNOT_EINVAL;
 	}
 
@@ -2650,6 +2965,7 @@ int zones_save_zone(const knot_ns_xfr_t *xfr)
 
 	/* dump the zone into text zone file */
 	int ret = zones_dump_zone_text(new_zone, zonefile);
+	rcu_read_unlock();
 	return ret;
 }
 
@@ -2735,15 +3051,16 @@ int zones_changeset_binary_size(const knot_changeset_t *chgset, size_t *size)
 	size_t soa_to_size = rrset_binary_size(chgset->soa_to);
 
 	size_t remove_size = 0;
-	for (int i = 0; i < chgset->remove_count; ++i)
-	{
-		remove_size += rrset_binary_size(chgset->remove[i]);
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, chgset->remove) {
+		knot_rrset_t *rrset = rr_node->rr;
+		remove_size += rrset_binary_size(rrset);
 	}
 
 	size_t add_size = 0;
-	for (int i = 0; i < chgset->add_count; ++i)
-	{
-		add_size += rrset_binary_size(chgset->add[i]);
+	WALK_LIST(rr_node, chgset->add) {
+		knot_rrset_t *rrset = rr_node->rr;
+		add_size += rrset_binary_size(rrset);
 	}
 
 	/*! \todo How is the changeset serialized? Any other parts? */
@@ -2788,8 +3105,10 @@ static int zones_serialize_and_store_chgset(const knot_changeset_t *chs,
 	}
 
 	/* Serialize RRSets from the 'remove' section. */
-	for (int i = 0; i < chs->remove_count; ++i) {
-		ret = zones_rrset_write_to_mem(chs->remove[i], &entry, &max_size);
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, chs->remove) {
+		knot_rrset_t *rrset = rr_node->rr;
+		ret = zones_rrset_write_to_mem(rrset, &entry, &max_size);
 		if (ret != KNOT_EOK) {
 			dbg_zones("%s:%d ret = %s\n", __func__, __LINE__, knot_strerror(ret));
 			return KNOT_ERROR;  /*! \todo Other code? */
@@ -2804,8 +3123,9 @@ static int zones_serialize_and_store_chgset(const knot_changeset_t *chs,
 	}
 
 	/* Serialize RRSets from the 'add' section. */
-	for (int i = 0; i < chs->add_count; ++i) {
-		ret = zones_rrset_write_to_mem(chs->add[i], &entry, &max_size);
+	WALK_LIST(rr_node, chs->add) {
+		knot_rrset_t *rrset = rr_node->rr;
+		ret = zones_rrset_write_to_mem(rrset, &entry, &max_size);
 		if (ret != KNOT_EOK) {
 			dbg_zones("%s:%d ret = %s\n", __func__, __LINE__, knot_strerror(ret));
 			return KNOT_ERROR;  /*! \todo Other code? */
@@ -2870,13 +3190,12 @@ journal_t *zones_store_changesets_begin(knot_zone_t *zone)
 	}
 
 	/* Fetch zone-specific data. */
-	//knot_zone_t *zone = xfr->zone;
 	zonedata_t *zd = (zonedata_t *)zone->data;
 	if (!zd->ixfr_db) {
 		return NULL;
 	}
 
-	/* Begin transaction, will be release on commit/rollback. */
+	/* Begin transaction, will be released on commit/rollback. */
 	journal_t *j = journal_retain(zd->ixfr_db);
 	if (journal_trans_begin(j) != KNOT_EOK) {
 		journal_release(j);
@@ -2929,10 +3248,9 @@ int zones_store_changesets(knot_zone_t *zone, knot_changesets_t *src, journal_t
 	}
 
 	/* Begin writing to journal. */
-	for (unsigned i = 0; i < src->count; ++i) {
+	knot_changeset_t *chs = NULL;
+	WALK_LIST(chs, src->sets) {
 		/* Make key from serials. */
-		knot_changeset_t* chs = src->sets + i;
-
 		ret = zones_store_changeset(chs, j, zone, zd);
 		if (ret != KNOT_EOK)
 			break;
@@ -2960,8 +3278,7 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
 		return KNOT_EINVAL;
 	}
 
-	knot_changesets_t *chgsets = (knot_changesets_t *)
-	                               calloc(1, sizeof(knot_changesets_t));
+	knot_changesets_t *chgsets = knot_changesets_create(KNOT_CHANGESET_TYPE_IXFR);
 	CHECK_ALLOC_LOG(chgsets, KNOT_ENOMEM);
 
 	int ret = ns_serial_compare(serial_to, serial_from);
@@ -2979,7 +3296,7 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
 	if (ret != KNOT_EOK) {
 		dbg_xfr("xfr: failed to load changesets: %s\n",
 		        knot_strerror(ret));
-		knot_free_changesets(&chgsets);
+		knot_changesets_free(&chgsets);
 		return ret;
 	}
 
@@ -2989,74 +3306,40 @@ 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)
+int zones_create_changeset(const knot_zone_t *old_zone,
+                           const knot_zone_t *new_zone,
+                           knot_changeset_t *changeset)
 {
 	if (old_zone == NULL || old_zone->contents == NULL
-	    || new_zone == NULL || new_zone->contents == NULL) {
+	    || new_zone == NULL || new_zone->contents == NULL
+	    || changeset == NULL) {
 		dbg_zones("zones: create_changesets: "
 		          "NULL arguments.\n");
 		return KNOT_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);
+	int ret = knot_zone_contents_create_diff(old_zone->contents,
+	                                         new_zone->contents,
+	                                         changeset);
 	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 KNOT_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 KNOT_ENODIFF;
 		} else {
 			dbg_zones("zones: create_changesets: "
 			          "Could not create changesets. Reason: %s\n",
 			          knot_strerror(ret));
-			knot_free_changesets(&changesets);
 			return KNOT_ERROR;
 		}
 	}
 
-	xfr.data = changesets;
-	journal_t *journal = zones_store_changesets_begin(xfr.zone);
-	if (journal == NULL) {
-		dbg_zones("zones: create_changesets: "
-		          "Could not start journal operation.\n");
-		return KNOT_ERROR;
-	}
-
-	ret = zones_store_changesets(xfr.zone, (knot_changesets_t *)xfr.data, journal);
-	if (ret != KNOT_EOK) {
-		zones_store_changesets_rollback(journal);
-		dbg_zones("zones: create_changesets: "
-		          "Could not store in the journal. Reason: %s.\n",
-		          knot_strerror(ret));
-
-		return ret;
-	}
-
-	ret = zones_store_changesets_commit(journal);
-	if (ret != KNOT_EOK) {
-		dbg_zones("zones: create_changesets: "
-		          "Could not commit to journal. Reason: %s.\n",
-		          knot_strerror(ret));
-
-		return ret;
-	}
-
-	knot_free_changesets(&changesets);
-
 	return KNOT_EOK;
 }
 
@@ -3071,20 +3354,15 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	int apply_ret = KNOT_EOK;
 	int switch_ret = KNOT_EOK;
 
-	/* Serialize and store changesets. */
 	dbg_xfr("xfr: IXFR/IN serializing and saving changesets\n");
-	journal_t *transaction = zones_store_changesets_begin(zone);
-	if (transaction != NULL) {
-		ret = zones_store_changesets(zone, chs, transaction);
-	} else {
-		ret = KNOT_ERROR;
-	}
+	journal_t *transaction = NULL;
+	ret = zones_store_changesets_begin_and_store(zone, chs, &transaction);
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s Failed to serialize and store "
-		               "changesets.\n", msgpref);
+		               "changesets: %s.\n", msgpref,
+		               knot_strerror(ret));
 		/* Free changesets, but not the data. */
-		zones_store_changesets_rollback(transaction);
-		knot_free_changesets(&chs);
+		knot_changesets_free(&chs);
 		return ret;
 	}
 
@@ -3096,37 +3374,42 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 
 		/* Free changesets, but not the data. */
 		zones_store_changesets_rollback(transaction);
-		knot_free_changesets(&chs);
+		knot_changesets_free(&chs);
 		return apply_ret;  // propagate the error above
 	}
 
 	/* Commit transaction. */
 	ret = zones_store_changesets_commit(transaction);
 	if (ret != KNOT_EOK) {
-		/*! \todo THIS WILL LEAK!! xfrin_rollback_update() needed. */
+		xfrin_rollback_update(zone->contents, new_contents,
+		                      chs->changes);
 		log_zone_error("%s Failed to commit stored changesets.\n", msgpref);
-		knot_free_changesets(&chs);
+		knot_changesets_free(&chs);
 		return ret;
 	}
 
 	/* Switch zone contents. */
+	// Unlock RCU for the switching procedure (would result in deadlock)
+	/*! \todo Maybe the unlocking should go inside the switching function.*/
+	rcu_read_unlock();
 	switch_ret = xfrin_switch_zone(zone, *new_contents, type);
+	rcu_read_lock();
 
 	if (switch_ret != KNOT_EOK) {
 		log_zone_error("%s Failed to replace current zone.\n", msgpref);
 		// Cleanup old and new contents
 		xfrin_rollback_update(zone->contents, new_contents,
-		                      &chs->changes);
+		                      chs->changes);
 
 		/* Free changesets, but not the data. */
-		knot_free_changesets(&chs);
+		knot_changesets_free(&chs);
 		return KNOT_ERROR;
 	}
 
-	xfrin_cleanup_successful_update(&chs->changes);
+	xfrin_cleanup_successful_update(chs->changes);
 
 	/* Free changesets, but not the data. */
-	knot_free_changesets(&chs);
+	knot_changesets_free(&chs);
 	assert(ret == KNOT_EOK);
 	return KNOT_EOK;
 }
@@ -3211,7 +3494,7 @@ int zones_schedule_refresh(knot_zone_t *zone, int64_t time)
 		zd->xfr_in.timer = evsched_schedule_cb(sch, zones_refresh_ev,
 		                                       zone, time);
 		dbg_zones("zone: REFRESH '%s' set to %u\n",
-		          zd->conf->name, time);
+		          zd->conf->name, (unsigned)time);
 		zd->xfr_in.state = XFR_SCHED;
 	}
 	rcu_read_unlock();
@@ -3220,6 +3503,141 @@ int zones_schedule_refresh(knot_zone_t *zone, int64_t time)
 	return KNOT_EOK;
 }
 
+static int zones_dnssec_ev(event_t *event, bool force)
+{
+	assert(conf()->dnssec_enable);
+	// We will be working with zone, don't want it to change in the meantime
+	rcu_read_lock();
+	knot_zone_t *zone = (knot_zone_t *)event->data;
+
+	zonedata_t *zd = (zonedata_t *)zone->data;
+	pthread_mutex_lock(&zd->lock);
+
+	knot_changesets_t *chs =
+		knot_changesets_create(KNOT_CHANGESET_TYPE_DNSSEC);
+	if (chs == NULL) {
+		evsched_event_free(event->parent, event);
+		zd->dnssec_timer = NULL;
+		pthread_mutex_unlock(&zd->lock);
+		rcu_read_unlock();
+		return KNOT_ENOMEM;
+	}
+	knot_changeset_t *ch = knot_changesets_create_changeset(chs);
+	if (ch == NULL) {
+		knot_changesets_free(&chs);
+		evsched_event_free(event->parent, event);
+		zd->dnssec_timer = NULL;
+		pthread_mutex_unlock(&zd->lock);
+		rcu_read_unlock();
+		return KNOT_ENOMEM;
+	}
+
+	int ret = 0;
+	uint32_t expires_at = 0;
+	if (force) {
+		ret = knot_dnssec_zone_sign_force(zone, ch, &expires_at);
+	} else {
+		ret = knot_dnssec_zone_sign(zone, ch, KNOT_SOA_SERIAL_INC,
+		                            &expires_at);
+	}
+	if (ret != KNOT_EOK) {
+		knot_changesets_free(&chs);
+		evsched_event_free(event->parent, event);
+		zd->dnssec_timer = NULL;
+		pthread_mutex_unlock(&zd->lock);
+		rcu_read_unlock();
+		return ret;
+	}
+
+	if (!zones_changesets_empty(chs)) {
+		knot_zone_contents_t *new_c = NULL;
+		ret = zones_store_and_apply_chgsets(chs, zone, &new_c, "DNSSEC",
+		                                    XFR_TYPE_UPDATE);
+		if (ret != KNOT_EOK) {
+			char *zname = knot_dname_to_str(zone->name);
+			log_server_error("Could not sign zone %s (%s).\n",
+			                 zname, knot_strerror(ret));
+			evsched_event_free(event->parent, event);
+			zd->dnssec_timer = NULL;
+			pthread_mutex_unlock(&zd->lock);
+			free(zname);
+			rcu_read_unlock();
+			return ret;
+		}
+	} else {
+		knot_changesets_free(&chs);
+	}
+
+	// cleanup
+	evsched_event_free(event->parent, event);
+	zd->dnssec_timer = NULL;
+	pthread_mutex_unlock(&zd->lock);
+
+	char *zname = knot_dname_to_str(zone->name);
+	log_zone_info("Zone %s forced signed successfully.\n", zname);
+	free(zname);
+
+	// Schedule next signing
+	ret = zones_schedule_dnssec(zone, expiration_to_relative(expires_at),
+	                            force);
+
+	rcu_read_unlock();
+
+	return ret;
+}
+
+static int zones_dnssec_regular_ev(event_t *event)
+{
+	assert(event);
+	if (event->data == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return zones_dnssec_ev(event, false);
+}
+
+static int zones_dnssec_forced_ev(event_t *event)
+{
+	assert(event);
+	if (event->data == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return zones_dnssec_ev(event, true);
+}
+
+int zones_schedule_dnssec(knot_zone_t *zone, int64_t time, bool force)
+{
+	if (!zone || !zone->data) {
+		return KNOT_EINVAL;
+	}
+
+	zonedata_t *zd = (zonedata_t *)zone->data;
+	evsched_t *scheduler = zd->server->sched;
+
+	// Cancel previous event, if any
+	if (zd->dnssec_timer) {
+		evsched_cancel(scheduler, zd->dnssec_timer);
+		evsched_event_free(scheduler, zd->dnssec_timer);
+		zd->dnssec_timer = NULL;
+	}
+
+//	TODO: throw an error if the new signing event is more in the future than the old one
+
+	if (force) {
+		zd->dnssec_timer = evsched_schedule_cb(scheduler,
+		                                       zones_dnssec_forced_ev,
+		                                       zone, time);
+	} else {
+		zd->dnssec_timer = evsched_schedule_cb(scheduler,
+		                                       zones_dnssec_regular_ev,
+		                                       zone, time);
+	}
+
+	return KNOT_EOK;
+}
+
+
 int zones_process_update_response(knot_ns_xfr_t *data, uint8_t *rwire, size_t *rsize)
 {
 	/* Processing of a forwarded response:
@@ -3281,7 +3699,7 @@ 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_cmp(key->name, kname) == 0
 	    && key->algorithm == alg) {
 		dbg_zones_verb("Found claimed TSIG key for comparison\n");
 	} else {
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index d6f0942aeaf480157ad1700e66465e7b5632f723..6583c688cc534380fac373808a14e2b0e73b53e2 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -84,6 +84,8 @@ typedef struct zonedata_t
 		unsigned state;
 	} xfr_in;
 
+	struct event_t *dnssec_timer;  /*!< Timer for DNSSEC events. */
+
 	/*! \brief Zone IXFR history. */
 	journal_t *ixfr_db;
 	struct event_t *ixfr_dbsync;   /*!< Syncing IXFR db to zonefile. */
@@ -209,7 +211,7 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data);
 /*!
  * \brief Store changesets in journal.
  *
- * Changesets will be stored on a permanent storage.
+ * Changesets will be stored to a permanent storage.
  * Journal may be compacted, resulting in flattening changeset history.
  *
  * \param zone Zone associated with the changeset.
@@ -293,8 +295,9 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
  * \retval KNOT_ENODIFF when new zone's serial are equal.
  * \retval KNOT_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);
+int zones_create_changeset(const knot_zone_t *old_zone,
+                           const knot_zone_t *new_zone,
+                           knot_changeset_t *changeset);
 
 int zones_store_and_apply_chgsets(knot_changesets_t *chs,
                                   knot_zone_t *zone,
@@ -324,6 +327,16 @@ int zones_schedule_refresh(knot_zone_t *zone, int64_t time);
  */
 int zones_schedule_notify(knot_zone_t *zone);
 
+/*!
+ * \brief Schedule DNSSEC event.
+ * \param zone Related zone.
+ * \param time When to schedule
+ * \param force Force sign or not
+ *
+ * \return Error code, KNOT_OK if successful.
+ */
+int zones_schedule_dnssec(knot_zone_t *zone, int64_t time, bool force);
+
 /*!
  * \brief Processes forwarded UPDATE response packet.
  * \todo #1291 move to appropriate section (DDNS).
diff --git a/src/knot/zone/estimator.c b/src/knot/zone/estimator.c
index a939851117e6c2d7cfe3ba790cd24233be8a5339..0977babc2cf229166406c5834e846e1fd9af4e72 100644
--- a/src/knot/zone/estimator.c
+++ b/src/knot/zone/estimator.c
@@ -41,19 +41,19 @@ enum estim_consts {
 };
 
 typedef struct type_list_item {
-	node n;
+	node_t n;
 	uint16_t type;
 } type_list_item_t;
 
 typedef struct dummy_node {
 	// For now only contains list of RR types
-	list node_list;
+	list_t node_list;
 } dummy_node_t;
 
 // return: 0 not present, 1 - present
-static int find_in_list(list *node_list, uint16_t type)
+static int find_in_list(list_t *node_list, uint16_t type)
 {
-	node *n = NULL;
+	node_t *n = NULL;
 	WALK_LIST(n, *node_list) {
 		type_list_item_t *l_entr = (type_list_item_t *)n;
 		assert(l_entr);
@@ -65,7 +65,7 @@ static int find_in_list(list *node_list, uint16_t type)
 	type_list_item_t *new_entry = xmalloc(sizeof(type_list_item_t));
 	new_entry->type = type;
 
-	add_head(node_list, (node *)new_entry);
+	add_head(node_list, (node_t *)new_entry);
 	return 0;
 }
 
@@ -77,29 +77,30 @@ static int dummy_node_add_type(dummy_node_t *n, uint16_t t)
 
 static size_t dname_memsize(const knot_dname_t *d)
 {
-	size_t d_size = d->size;
-	size_t l_size = d->label_count;
-	if (d->size < MALLOC_MIN) {
+
+	size_t d_size = knot_dname_size(d);
+	if (d_size < MALLOC_MIN) {
 		d_size = MALLOC_MIN;
 	}
-	if (d->label_count < MALLOC_MIN) {
-		l_size = MALLOC_MIN;
-	}
 
-	return (sizeof(knot_dname_t) + d_size + l_size)
-	       * DNAME_MULT + DNAME_ADD;
+	return d_size * DNAME_MULT + DNAME_ADD;
 }
 
 // return: 0 - unique, 1 - duplicate
 static int insert_dname_into_table(hattrie_t *table, knot_dname_t *d,
                                    dummy_node_t **n)
 {
-	value_t *val = hattrie_tryget(table, (char *)d->name, d->size);
+	int d_size = knot_dname_size(d);
+	if (d_size < 0) {
+		return KNOT_EINVAL;
+	}
+
+	value_t *val = hattrie_tryget(table, (char *)d, d_size);
 	if (val == NULL) {
 		// Create new dummy node to use for this dname
 		*n = xmalloc(sizeof(dummy_node_t));
 		init_list(&(*n)->node_list);
-		*hattrie_get(table, (char *)d->name, d->size) = *n;
+		*hattrie_get(table, (char *)d, d_size) = *n;
 		return 0;
 	} else {
 		// Return previously found dummy node
@@ -108,58 +109,15 @@ static int insert_dname_into_table(hattrie_t *table, knot_dname_t *d,
 	}
 }
 
-// return: RDATA memsize, minus size of dnames inside
-static size_t rdata_memsize(zone_estim_t *est, const scanner_t *scanner)
-{
-	const rdata_descriptor_t *desc = get_rdata_descriptor(scanner->r_type);
-	size_t size = 0;
-	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
-		// DNAME - pointer in memory
-		int item = desc->block_types[i];
-		if (descriptor_item_is_dname(item)) {
-			size += sizeof(knot_dname_t *);
-			knot_dname_t *dname =
-				knot_dname_new_from_wire(scanner->r_data +
-			                                 scanner->r_data_blocks[i],
-			                                 scanner->r_data_blocks[i + 1] -
-			                                 scanner->r_data_blocks[i],
-			                                 NULL);
-			if (dname == NULL) {
-				return KNOT_ERROR;
-			}
-
-			knot_dname_to_lower(dname);
-			dummy_node_t *n = NULL;
-			if (insert_dname_into_table(est->dname_table,
-			                            dname, &n) == 0) {
-				// First time we see this dname, add size
-				est->dname_size += dname_memsize(dname);
-			}
-			knot_dname_free(&dname);
-		} else if (descriptor_item_is_fixed(item)) {
-		// Fixed length
-			size += item;
-		} else {
-		// Variable length
-			size += scanner->r_data_blocks[i + 1] -
-			        scanner->r_data_blocks[i];
-		}
-	}
-
-	return size * RDATA_MULT + RDATA_ADD;
-}
-
 static void rrset_memsize(zone_estim_t *est, const scanner_t *scanner)
 {
 	// Handle RRSet's owner
-	knot_dname_t *owner = knot_dname_new_from_wire(scanner->r_owner,
-	                         scanner->r_owner_length,
-	                         NULL);
+	knot_dname_t *owner = knot_dname_copy(scanner->r_owner);
 	if (owner == NULL) {
 		return;
 	}
 
-	dummy_node_t *n;
+	dummy_node_t *n = NULL;
 	if (insert_dname_into_table(est->node_table, owner, &n) == 0) {
 		// First time we see this name == new node
 		est->node_size += sizeof(knot_node_t) * NODE_MULT + NODE_ADD;
@@ -171,13 +129,12 @@ static void rrset_memsize(zone_estim_t *est, const scanner_t *scanner)
 	assert(n);
 
 	// We will always add RDATA
-	size_t rdlen = rdata_memsize(est, scanner);
+	size_t rdlen = scanner->r_data_length;
 	if (rdlen < MALLOC_MIN) {
 		rdlen = MALLOC_MIN;
 	}
-	// DNAME's size not included (handled inside rdata_memsize())
-	est->rdata_size += rdlen;
 
+	est->rdata_size += rdlen;
 	est->record_count++;
 
 	/*
diff --git a/src/knot/zone/estimator.h b/src/knot/zone/estimator.h
index 5029b45b399691ae33edb313624b38b8c96b9e2a..e99979bbb64bb08929f57e4164d77a9b1c2e034f 100644
--- a/src/knot/zone/estimator.h
+++ b/src/knot/zone/estimator.h
@@ -38,7 +38,6 @@ static const double ESTIMATE_MAGIC = 1.2;
  */
 typedef struct zone_estim {
 	hattrie_t *node_table; /*!< Same trie is in actual zone. */
-	hattrie_t *dname_table; /*!< RDATA section DNAMEs. */
 	size_t rdata_size; /*!< Estimated RDATA size. */
 	size_t dname_size; /*!< Estimated DNAME size. */
 	size_t node_size; /*!< Estimated node size. */
@@ -87,3 +86,5 @@ void estimator_rrset_memsize_wrap(const scanner_t *scanner);
 void estimator_free_trie_node(value_t *val, void *data);
 
 #endif /* _KNOT_ESTIMATOR_H_ */
+
+/*! @} */
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index 8a043d05cfb36e479219db06e19d1fc04678624e..09c19c3d0591a8f8a0c5dfd2e6ccc326b45dbf78 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -25,11 +25,12 @@
 #include "knot/knot.h"
 #include "knot/other/debug.h"
 #include "libknot/libknot.h"
-#include "libknot/sign/key.h"
+#include "libknot/dnssec/key.h"
 #include "common/base32hex.h"
 #include "common/crc.h"
 #include "common/descriptor.h"
 #include "common/mempattern.h"
+#include "libknot/rdata.h"
 
 #include "semantic-check.h"
 
@@ -48,6 +49,8 @@ static char *error_messages[(-ZC_ERR_UNKNOWN) + 1] = {
 	"RRSIG: Labels rdata field is wrong!",
 	[-ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER] =
 	"RRSIG: Signer name is different than in DNSKEY!",
+	[-ZC_ERR_RRSIG_NO_DNSKEY] =
+	"RRSIG: Missing DNSKEY for RRSIG!",
 	[-ZC_ERR_RRSIG_RDATA_SIGNED_WRONG] =
 	"RRSIG: Key error!",
 	[-ZC_ERR_RRSIG_NO_RRSIG] =
@@ -60,8 +63,6 @@ static char *error_messages[(-ZC_ERR_UNKNOWN) + 1] = {
 	"RRSIG: Class is wrong!",
 	[-ZC_ERR_RRSIG_TTL] =
 	"RRSIG: TTL is wrong!",
-	[-ZC_ERR_RRSIG_NOT_ALL] =
-	"RRSIG: Not all RRs are signed!",
 
 	[-ZC_ERR_NO_NSEC] =
 	"NSEC: Missing NSEC record",
@@ -292,9 +293,10 @@ static int check_rrsig_rdata(err_handler_t *handler,
                              const knot_rrset_t *dnskey_rrset)
 {
 	/* Prepare additional info string. */
-	char info_str[50];
-	int ret = snprintf(info_str, sizeof(info_str), "Record type: %d.",
-	                   knot_rrset_type(rrset));
+	char info_str[50] = { '\0' };
+	char type_str[10] = { '\0' };
+	knot_rrtype_to_string(knot_rrset_type(rrset), type_str, sizeof(type_str));
+	int ret = snprintf(info_str, sizeof(info_str), "Record type: %s", type_str);
 	if (ret < 0 || ret >= sizeof(info_str)) {
 		return KNOT_ENOMEM;
 	}
@@ -305,7 +307,7 @@ static int check_rrsig_rdata(err_handler_t *handler,
 		return KNOT_EOK;
 	}
 
-	if (knot_rrset_rdata_rrsig_type_covered(rrsig) !=
+	if (knot_rdata_rrsig_type_covered(rrsig, 0) !=
 	    knot_rrset_type(rrset)) {
 		/* zoneparser would not let this happen
 		 * but to be on the safe side
@@ -316,10 +318,9 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	}
 
 	/* label number at the 2nd index should be same as owner's */
-	uint8_t labels_rdata = knot_rrset_rdata_rrsig_labels(rrsig, rr_pos);
+	uint8_t labels_rdata = knot_rdata_rrsig_labels(rrsig, rr_pos);
 
-	int tmp = knot_dname_label_count(knot_rrset_owner(rrset)) -
-		  labels_rdata;
+	int tmp = knot_dname_labels(knot_rrset_owner(rrset), NULL) - labels_rdata;
 
 	if (tmp != 0) {
 		/* if name has wildcard, label must not be included */
@@ -338,7 +339,7 @@ static int check_rrsig_rdata(err_handler_t *handler,
 
 	/* check original TTL */
 	uint32_t original_ttl =
-		knot_rrset_rdata_rrsig_original_ttl(rrsig, rr_pos);
+		knot_rdata_rrsig_original_ttl(rrsig, rr_pos);
 
 	if (original_ttl != knot_rrset_ttl(rrset)) {
 		err_handler_handle_error(handler, node, ZC_ERR_RRSIG_RDATA_TTL,
@@ -346,19 +347,26 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	}
 
 	/* Check for expired signature. */
-	if (knot_rrset_rdata_rrsig_sig_expiration(rrsig, rr_pos) < time(NULL)) {
+	if (knot_rdata_rrsig_sig_expiration(rrsig, rr_pos) < time(NULL)) {
 		err_handler_handle_error(handler, node,
 		                         ZC_ERR_RRSIG_RDATA_EXPIRATION,
 		                         info_str);
 	}
 
+	/* Check if DNSKEY exists. */
+	if (!dnskey_rrset) {
+		err_handler_handle_error(handler, node,
+		                         ZC_ERR_RRSIG_NO_DNSKEY, info_str);
+	}
+
 	/* signer's name is same as in the zone apex */
 	const knot_dname_t *signer_name =
-		knot_rrset_rdata_rrsig_signer_name(rrsig, rr_pos);
+		knot_rdata_rrsig_signer_name(rrsig, rr_pos);
 
 	/* dnskey is in the apex node */
-	if (knot_dname_compare(signer_name,
-				 knot_rrset_owner(dnskey_rrset)) != 0) {
+	if (dnskey_rrset &&
+	    knot_dname_cmp(signer_name, knot_rrset_owner(dnskey_rrset)) != 0
+	) {
 		err_handler_handle_error(handler, node,
 		                         ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
 		                         info_str);
@@ -369,12 +377,12 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	 * before */
 	
 	int match = 0;
-	uint8_t rrsig_alg = knot_rrset_rdata_rrsig_algorithm(rrsig, rr_pos);
-	uint16_t key_tag_rrsig = knot_rrset_rdata_rrsig_key_tag(rrsig, rr_pos);
+	uint8_t rrsig_alg = knot_rdata_rrsig_algorithm(rrsig, rr_pos);
+	uint16_t key_tag_rrsig = knot_rdata_rrsig_key_tag(rrsig, rr_pos);
 	for (uint16_t i = 0; i < knot_rrset_rdata_rr_count(dnskey_rrset) &&
 	     !match; ++i) {
 		uint8_t dnskey_alg =
-			knot_rrset_rdata_dnskey_alg(dnskey_rrset, i);
+			knot_rdata_dnskey_alg(dnskey_rrset, i);
 		if (rrsig_alg != dnskey_alg) {
 			continue;
 		}
@@ -398,8 +406,8 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	}
 	
 	if (!match) {
-		err_handler_handle_error(handler, node, ZC_ERR_RRSIG_NO_RRSIG,
-		                         info_str);
+		err_handler_handle_error(handler, node,
+		                         ZC_ERR_RRSIG_NO_DNSKEY, info_str);
 	}
 
 	return KNOT_EOK;
@@ -421,8 +429,7 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
                                 const knot_rrset_t *rrset,
                                 const knot_rrset_t *dnskey_rrset)
 {
-	if (handler == NULL || node == NULL || rrset == NULL ||
-	    dnskey_rrset == NULL) {
+	if (handler == NULL || node == NULL || rrset == NULL) {
 		return KNOT_EINVAL;
 	}
 	
@@ -434,8 +441,6 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
 		return KNOT_ENOMEM;
 	}
 	
-	assert(dnskey_rrset && rrset);
-
 	const knot_rrset_t *rrsigs = knot_rrset_rrsigs(rrset);
 
 	if (rrsigs == NULL) {
@@ -455,7 +460,7 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
 
 	/* Different owner, class, ttl */
 
-	if (knot_dname_compare(knot_rrset_owner(rrset),
+	if (knot_dname_cmp(knot_rrset_owner(rrset),
 				 knot_rrset_owner(rrsigs)) != 0) {
 		err_handler_handle_error(handler, node,
 		                         ZC_ERR_RRSIG_OWNER,
@@ -487,14 +492,6 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
 			             knot_strerror(ret));
 		}
 	}
-	
-	int all_signed =
-		knot_rrset_rdata_rr_count(rrset) == knot_rrset_rdata_rr_count(rrsigs);
-	if (!all_signed) {
-		err_handler_handle_error(handler, node,
-		                         ZC_ERR_RRSIG_NOT_ALL,
-		                         info_str);
-	}
 
 	return KNOT_EOK;
 }
@@ -535,9 +532,9 @@ static int rdata_nsec_to_type_array(const knot_rrset_t *rrset, size_t pos,
 	uint8_t *data = NULL;
 	uint16_t rr_bitmap_size = 0;
 	if (rrset->type == KNOT_RRTYPE_NSEC) {
-		knot_rrset_rdata_nsec_bitmap(rrset, pos, &data, &rr_bitmap_size);
+		knot_rdata_nsec_bitmap(rrset, pos, &data, &rr_bitmap_size);
 	} else {
-		knot_rrset_rdata_nsec3_bitmap(rrset, pos, &data, &rr_bitmap_size);
+		knot_rdata_nsec3_bitmap(rrset, pos, &data, &rr_bitmap_size);
 	}
 	if (data == NULL) {
 		return KNOT_EMALF;
@@ -645,7 +642,7 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 
 			/* check for Opt-Out flag */
 			uint8_t flags =
-				knot_rrset_rdata_nsec3_flags(previous_rrset, 0);
+				knot_rdata_nsec3_flags(previous_rrset, 0);
 			uint8_t opt_out_mask = 1;
 
 			if (!(flags & opt_out_mask)) {
@@ -667,7 +664,7 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 		knot_node_rrset(knot_zone_contents_apex(zone),
 	                        KNOT_RRTYPE_SOA);
 	assert(soa_rrset);
-	uint32_t minimum_ttl = knot_rrset_rdata_soa_minimum(soa_rrset);
+	uint32_t minimum_ttl = knot_rdata_soa_minimum(soa_rrset);
 
 	if (knot_rrset_ttl(nsec3_rrset) != minimum_ttl) {
 			err_handler_handle_error(handler, node,
@@ -678,7 +675,7 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 	uint8_t *next_dname_str = NULL;
 	uint8_t next_dname_size = 0;
 	uint8_t *next_dname_decoded = NULL;
-	knot_rrset_rdata_nsec3_next_hashed(nsec3_rrset, 0, &next_dname_str,
+	knot_rdata_nsec3_next_hashed(nsec3_rrset, 0, &next_dname_str,
 	                                   &next_dname_size);
 	size_t real_size =
 		base32hex_encode_alloc(next_dname_str,
@@ -695,8 +692,8 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 	
 	/* Local allocation, will be discarded. */
 	knot_dname_t *next_dname =
-		knot_dname_new_from_str((char *)next_dname_decoded,
-					   real_size, NULL);
+		knot_dname_from_str((char *)next_dname_decoded,
+					   real_size);
 	if (next_dname == NULL) {
 		free(next_dname_decoded);
 		log_zone_warning("Could not create new dname!\n");
@@ -705,11 +702,10 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone,
 	
 	free(next_dname_decoded);
 	knot_dname_to_lower(next_dname);
-	
-	if (knot_dname_cat(next_dname,
-		     knot_node_owner(knot_zone_contents_apex(zone))) == NULL) {
+	next_dname = knot_dname_cat(next_dname,
+	                            knot_node_owner(knot_zone_contents_apex(zone)));
+	if (next_dname == NULL) {
 		log_zone_warning("Could not concatenate dnames!\n");
-		knot_dname_free(&next_dname);
 		return KNOT_ERROR;
 
 	}
@@ -880,7 +876,7 @@ static int sem_check_node_optional(knot_zone_contents_t *zone,
 	                node) {
 		const knot_rrset_t *ns_rrset =
 				knot_node_rrset(node, KNOT_RRTYPE_NS);
-		if (ns_rrset == NULL) {
+		if (ns_rrset == NULL || ns_rrset->rdata_count == 0) {
 			err_handler_handle_error(handler, node,
 			                         ZC_ERR_MISSING_NS_DEL_POINT,
 			                         NULL);
@@ -890,7 +886,7 @@ static int sem_check_node_optional(knot_zone_contents_t *zone,
 
 		/* TODO How about multiple RRs? */
 		knot_dname_t *ns_dname =
-			knot_dname_deep_copy(knot_rrset_rdata_ns_name(ns_rrset,
+			knot_dname_copy(knot_rdata_ns_name(ns_rrset,
 		                             0));
 		if (ns_dname == NULL) {
 			return KNOT_ENOMEM;
@@ -899,26 +895,15 @@ static int sem_check_node_optional(knot_zone_contents_t *zone,
 		const knot_node_t *glue_node =
 				knot_zone_contents_find_node(zone, ns_dname);
 		
-		if (knot_dname_is_subdomain(ns_dname,
+		if (knot_dname_is_sub(ns_dname,
 			      knot_node_owner(knot_zone_contents_apex(zone)))) {
 			if (glue_node == NULL) {
-				/* Try wildcard. */
-				knot_dname_t *wildcard =
-					knot_dname_new_from_str("*", 1, NULL);
-				if (wildcard == NULL) {
-					knot_dname_free(&ns_dname);
-					return KNOT_ENOMEM;
-				}
-				
-				knot_dname_left_chop_no_copy(ns_dname);
-		
-				if (knot_dname_cat(wildcard,
-				                   ns_dname) == NULL) {
-					knot_dname_free(&ns_dname);
-					knot_dname_free(&wildcard);
-					return KNOT_ENOMEM;
-				}
-				
+				/* Try wildcard ([1]* + suffix). */
+				knot_dname_t wildcard[KNOT_DNAME_MAXLEN];
+				memcpy(wildcard, "\x1""*", 2);
+				knot_dname_to_wire(wildcard + 2,
+				                   knot_wire_next_label(ns_dname, NULL),
+				                   sizeof(wildcard));
 				const knot_node_t *wildcard_node = 
 					knot_zone_contents_find_node(zone,
 				                                     wildcard);
@@ -937,7 +922,6 @@ static int sem_check_node_optional(knot_zone_contents_t *zone,
 								 NULL);
 					}
 				}	
-				knot_dname_free(&wildcard);
 			} else {
 				if ((knot_node_rrset(glue_node,
 					       KNOT_RRTYPE_A) == NULL) &&
@@ -1095,7 +1079,7 @@ static int semantic_checks_dnssec(knot_zone_contents_t *zone,
 
 			if (nsec_rrset != NULL) {
 				const knot_dname_t *next_domain =
-					knot_rrset_rdata_nsec_next(nsec_rrset, 0);
+					knot_rdata_nsec_next(nsec_rrset, 0);
 				assert(next_domain);
 
 				if (knot_zone_contents_find_node(zone,
@@ -1106,7 +1090,7 @@ static int semantic_checks_dnssec(knot_zone_contents_t *zone,
 						ZC_ERR_NSEC_RDATA_CHAIN, NULL);
 				}
 
-				if (knot_dname_compare(next_domain,
+				if (knot_dname_cmp(next_domain,
 				    knot_node_owner(knot_zone_contents_apex(zone)))
 					== 0) {
 					/* saving the last node */
@@ -1237,7 +1221,7 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 		uint8_t *next_dname_str = NULL;
 		uint8_t next_dname_size = 0;
 		uint8_t *next_dname_decoded = NULL;
-		knot_rrset_rdata_nsec3_next_hashed(nsec3_rrset, 0, &next_dname_str,
+		knot_rdata_nsec3_next_hashed(nsec3_rrset, 0, &next_dname_str,
 		                                   &next_dname_size);
 		size_t real_size =
 			base32hex_encode_alloc(next_dname_str,
@@ -1250,8 +1234,8 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 
 		/* Local allocation, will be discarded. */
 		knot_dname_t *next_dname =
-			knot_dname_new_from_str((char *)next_dname_decoded,
-						real_size, NULL);
+			knot_dname_from_str((char *)next_dname_decoded,
+						real_size);
 		if (next_dname == NULL) {
 			dbg_semcheck("Could not allocate dname!\n");
 			free(next_dname_decoded);
@@ -1260,14 +1244,13 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 
 		free(next_dname_decoded);
 		knot_dname_to_lower(next_dname);
+		next_dname = knot_dname_cat(next_dname,
+		                            knot_node_owner(knot_zone_contents_apex(zone)));
 
-		if (knot_dname_cat(next_dname,
-			     knot_node_owner(knot_zone_contents_apex(zone))) ==
-		                NULL) {
+		if (next_dname == NULL) {
 			dbg_semcheck("Could not concatenate dnames!\n");
 			err_handler_handle_error(handler, last_nsec3_node,
 						 ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
-			knot_dname_free(&next_dname);
 			return;
 		}
 
@@ -1280,8 +1263,7 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 						 ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 		} else {
 			/* Compare with the actual first NSEC3 node. */
-			if (knot_dname_compare_non_canon(first_nsec3_node->owner,
-			                                 next_dname) != 0) {
+			if (!knot_dname_is_equal(first_nsec3_node->owner, next_dname)) {
 				err_handler_handle_error(handler, last_nsec3_node,
 							 ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 			}
@@ -1307,14 +1289,14 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 			}
 
 			const knot_dname_t *next_dname =
-				knot_rrset_rdata_nsec_next(nsec_rrset, 0);
+				knot_rdata_nsec_next(nsec_rrset, 0);
 			assert(next_dname);
 
 			const knot_dname_t *apex_dname =
 				knot_node_owner(knot_zone_contents_apex(zone));
 			assert(apex_dname);
 
-			if (knot_dname_compare(next_dname, apex_dname) !=0) {
+			if (knot_dname_cmp(next_dname, apex_dname) !=0) {
 				err_handler_handle_error(handler, last_node,
 					 ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC, NULL);
 			}
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
index 53c2589f84efc9eb92be2aae06967f02196c72d2..e988a9fb4eb637feb71eb0df53c7490ddead9837 100644
--- a/src/knot/zone/semantic-check.h
+++ b/src/knot/zone/semantic-check.h
@@ -47,13 +47,13 @@ enum zonechecks_errors {
 	ZC_ERR_RRSIG_RDATA_EXPIRATION,
 	ZC_ERR_RRSIG_RDATA_LABELS,
 	ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
+	ZC_ERR_RRSIG_NO_DNSKEY,
 	ZC_ERR_RRSIG_RDATA_SIGNED_WRONG,
 	ZC_ERR_RRSIG_NO_RRSIG,
 	ZC_ERR_RRSIG_SIGNED,
 	ZC_ERR_RRSIG_OWNER,
 	ZC_ERR_RRSIG_CLASS,
 	ZC_ERR_RRSIG_TTL,
-	ZC_ERR_RRSIG_NOT_ALL,
 
 	ZC_ERR_RRSIG_GENERAL_ERROR,
 
diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c
index 8f45798c0613ec79420a59922d85fd0067acad71..360eb6ca1613ae4a1141723e553c011270fef1c3 100644
--- a/src/knot/zone/zone-load.c
+++ b/src/knot/zone/zone-load.c
@@ -31,9 +31,11 @@
 #include "libknot/common.h"
 #include "knot/zone/semantic-check.h"
 #include "libknot/zone/zone-contents.h"
+#include "libknot/dnssec/zone-nsec.h"
 #include "knot/other/debug.h"
 #include "knot/zone/zone-load.h"
 #include "zscanner/file_loader.h"
+#include "libknot/rdata.h"
 
 /* ZONE LOADING FROM FILE USING RAGEL PARSER */
 
@@ -90,20 +92,20 @@ static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
 {
 	assert(node);
 
-	assert(knot_dname_compare(rrsig->owner, node->owner) == 0);
+	assert(knot_dname_cmp(rrsig->owner, node->owner) == 0);
 
 	knot_rrset_t *tmp_rrset =
 		knot_node_get_rrset(node,
-	                            knot_rrset_rdata_rrsig_type_covered(rrsig));
+	                            knot_rdata_rrsig_type_covered(rrsig, 0));
 
 	int ret;
 
 	if (tmp_rrset == NULL) {
 		dbg_zp("zp: find_rr_for_sig_in_node: Node does not contain "
 		       "RRSet of type %d.\n",
-		       knot_rrset_rdata_rrsig_type_covered(rrsig));
-		tmp_rrset = knot_rrset_new(rrsig->owner,
-		                           knot_rrset_rdata_rrsig_type_covered(rrsig),
+		       knot_rdata_rrsig_type_covered(rrsig, 0));
+		tmp_rrset = knot_rrset_new(knot_dname_copy(rrsig->owner),
+		                           knot_rdata_rrsig_type_covered(rrsig, 0),
 		                           rrsig->rclass,
 		                           rrsig->ttl);
 		if (tmp_rrset == NULL) {
@@ -127,10 +129,10 @@ static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
 	if (tmp_rrset->ttl != rrsig->ttl) {
 		char *name = knot_dname_to_str(tmp_rrset->owner);
 		assert(name);
-		log_zone_warning("RRSIG owned by: %s (covering type %d) cannot be added to "
-		                 "its RRSet, because their TTLs differ. "
-		                 "Changing TTL=%d to value=%d.\n",
-		                 name, knot_rrset_rdata_rrsig_type_covered(rrsig),
+		log_zone_warning("RRSIG owned by: %s (covering type %d) cannot "
+		                 "be added to its RRSet, because their TTLs "
+		                 "differ. Changing TTL=%d to value=%d.\n",
+		                 name, knot_rdata_rrsig_type_covered(rrsig, 0),
 		                 rrsig->ttl, tmp_rrset->ttl);
 		free(name);
 	}
@@ -142,7 +144,7 @@ static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
 		return KNOT_EINVAL;
 	} else if (ret > 0) {
 		/* Merged, free data + owner, but not DNAMEs inside RDATA. */
-		knot_rrset_deep_free(&rrsig, 1, 0);
+		knot_rrset_deep_free(&rrsig, 1);
 	}
 	assert(tmp_rrset->rrsigs != NULL);
 
@@ -157,8 +159,7 @@ static knot_node_t *create_node(knot_zone_contents_t *zone,
 {
 	dbg_zp_verb("zp: create_node: Creating node using RRSet: %p.\n",
 	            current_rrset);
-	knot_node_t *node =
-		knot_node_new(current_rrset->owner, NULL, 0);
+	knot_node_t *node = knot_node_new(current_rrset->owner, NULL, 0);
 	int ret = node_add_func(zone, node, 1, 0);
 	if (ret != KNOT_EOK) {
 		log_zone_warning("Node could not be added (%s).\n",
@@ -166,8 +167,6 @@ static knot_node_t *create_node(knot_zone_contents_t *zone,
 		return NULL;
 	}
 
-	assert(current_rrset->owner == node->owner);
-
 	return node;
 }
 
@@ -195,38 +194,14 @@ void process_error(const scanner_t *s)
 		log_zone_error("Fatal error in zone file %s:%"PRIu64": %s "
 		               "Stopping zone loading.\n",
 		               s->file_name, s->line_counter,
-		               knot_strerror(s->error_code));
+		               zscanner_strerror(s->error_code));
 	} else {
 		log_zone_error("Error in zone file %s:%"PRIu64": %s\n",
 		               s->file_name, s->line_counter,
-		               knot_strerror(s->error_code));
+		               zscanner_strerror(s->error_code));
 	}
 }
 
-// TODO this could be a part of the cycle below, but we'd need a buffer.
-static size_t calculate_item_size(const knot_rrset_t *rrset,
-                               const scanner_t *scanner)
-{
-	const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type);
-	assert(desc);
-	size_t size = 0;
-	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
-		int item = desc->block_types[i];
-		if (descriptor_item_is_dname(item)) {
-			size += sizeof(knot_dname_t *);
-		} else if (descriptor_item_is_fixed(item)) {
-			assert(item == scanner->r_data_blocks[i + 1] -
-			       scanner->r_data_blocks[i]);
-			size += item;
-		} else {
-			size += scanner->r_data_blocks[i + 1] -
-			        scanner->r_data_blocks[i];
-		}
-	}
-
-	return size;
-}
-
 static int add_rdata_to_rr(knot_rrset_t *rrset, const scanner_t *scanner)
 {
 	if (rrset == NULL) {
@@ -234,65 +209,16 @@ static int add_rdata_to_rr(knot_rrset_t *rrset, const scanner_t *scanner)
 		return KNOT_EINVAL;
 	}
 
-	parser_context_t *parser = scanner->data;
-
-	const rdata_descriptor_t *desc =
-		get_rdata_descriptor(knot_rrset_type(rrset));
-	assert(desc);
-
 	dbg_zp_detail("zp: add_rdata_to_rr: Adding type %d, RRSet has %d RRs.\n",
 	              rrset->type, rrset->rdata_count);
 
-	size_t rdlen = calculate_item_size(rrset, scanner);
-	size_t offset = 0;
-	uint8_t *rdata = knot_rrset_create_rdata(rrset, rdlen);
+	uint8_t *rdata = knot_rrset_create_rdata(rrset, scanner->r_data_length);
 	if (rdata == NULL) {
 		dbg_zp("zp: create_rdata: Could not create RR.\n");
 		return KNOT_ENOMEM;
 	}
 
-	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
-		int item = desc->block_types[i];
-		if (descriptor_item_is_dname(item)) {
-			knot_dname_t *dname =
-				knot_dname_new_from_wire(scanner->r_data +
-			                                 scanner->r_data_blocks[i],
-			                                 scanner->r_data_blocks[i + 1] - scanner->r_data_blocks[i],
-			                                 NULL);
-			if (dname == NULL) {
-				return KNOT_ERROR;
-			}
-			knot_dname_to_lower(dname);
-dbg_zp_exec_detail(
-			char *name = knot_dname_to_str(dname);
-			dbg_zp_detail("zp: arr_rdata_to_rr: "
-			              "Offset=%zu:Adding dname=%s (%p)\n",
-			              offset, name, dname);
-			free(name);
-);
-			/* Handle DNAME duplications. */
-			knot_zone_contents_insert_dname_into_table(&dname,
-							parser->lookup_tree);
-			memcpy(rdata + offset, &dname, sizeof(knot_dname_t *));
-			offset += sizeof(knot_dname_t *);
-		} else if (descriptor_item_is_fixed(item)) {
-			//copy the whole thing
-			// TODO check the size
-			assert(item == scanner->r_data_blocks[i + 1] -
-			       scanner->r_data_blocks[i]);
-			memcpy(rdata + offset,
-			       scanner->r_data + scanner->r_data_blocks[i],
-			       item);
-			offset += item;
-		} else {
-			memcpy(rdata + offset,
-			       scanner->r_data + scanner->r_data_blocks[i],
-			       scanner->r_data_blocks[i + 1] -
-			       scanner->r_data_blocks[i]);
-			offset += scanner->r_data_blocks[i + 1] -
-			          scanner->r_data_blocks[i];
-		}
-	}
+	memcpy(rdata, scanner->r_data, scanner->r_data_length);
 
 	return KNOT_EOK;
 }
@@ -306,40 +232,26 @@ static void process_rr(const scanner_t *scanner)
 	if (parser->ret != KNOT_EOK) {
 		return;
 	}
+
 	knot_zone_contents_t *contents = parser->current_zone;
-	knot_dname_t *current_owner = NULL;
+
+	/*!
+	 * \todo Node/RRSet compression at this level? To avoid duplicate
+	 *       names.
+	 */
+
 	knot_rrset_t *current_rrset = NULL;
-	if (parser->last_node &&
-	    (scanner->r_owner_length == parser->last_node->owner->size) &&
-	    (strncmp((char *)parser->last_node->owner->name,
-	            (char *)scanner->r_owner, scanner->r_owner_length) == 0)) {
-		// no need to create new dname;
-		current_owner = parser->last_node->owner;
-		knot_dname_retain(current_owner);
-	} else {
-		current_owner =
-			knot_dname_new_from_wire(scanner->r_owner,
-			                         scanner->r_owner_length,
-			                         NULL);
-		if (current_owner == NULL) {
-			parser->ret = KNOT_ERROR;
-			return;
-		}
-		knot_dname_to_lower(current_owner);
-		/*!< \todo
-		 * If name is already in the table, we might not need to create
-		 * dname object, just compare wires.
-		 */
-		knot_zone_contents_insert_dname_into_table(&current_owner,
-		                                           parser->lookup_tree);
-	}
+	knot_dname_t *current_owner = knot_dname_copy(scanner->r_owner);
 
-	/*!< \todo Do not create RRSet each time - merging needs to be sorted though. */
+	knot_dname_to_lower(current_owner);
+
+	/*!< \todo Do not create RRSet each time - merging needs to be
+	 *         sorted though.
+	 */
 	current_rrset = knot_rrset_new(current_owner,
 	               scanner->r_type,
 	               scanner->r_class,
 	               scanner->r_ttl);
-	knot_dname_release(current_owner);
 
 	assert(current_owner);
 	assert(current_rrset);
@@ -369,7 +281,7 @@ static void process_rr(const scanner_t *scanner)
 	uint16_t type_covered = 0;
 	if (current_rrset->type == KNOT_RRTYPE_RRSIG) {
 		type_covered =
-			knot_rrset_rdata_rrsig_type_covered(current_rrset);
+			knot_rdata_rrsig_type_covered(current_rrset, 0);
 	}
 
 	if (current_rrset->type != KNOT_RRTYPE_NSEC3 &&
@@ -396,7 +308,7 @@ static void process_rr(const scanner_t *scanner)
 			} else {
 				log_zone_warning("encountered identical "
 				                 "extra SOA record\n");
-				knot_rrset_deep_free(&current_rrset, 1, 1);
+				knot_rrset_deep_free(&current_rrset, 1);
 				parser->ret = KNOT_EOK;
 				return;
 			}
@@ -404,7 +316,7 @@ static void process_rr(const scanner_t *scanner)
 	}
 
 	if (current_rrset->type == KNOT_RRTYPE_SOA) {
-		if (knot_dname_compare(current_rrset->owner,
+		if (knot_dname_cmp(current_rrset->owner,
 					parser->origin_from_config) != 0) {
 			log_zone_error("SOA record has a different "
 				"owner than the one specified "
@@ -420,8 +332,8 @@ static void process_rr(const scanner_t *scanner)
 		knot_rrset_t *tmp_rrsig = current_rrset;
 
 		if (parser->last_node &&
-		    knot_dname_compare_non_canon(parser->last_node->owner,
-		                                 current_rrset->owner) != 0) {
+		    !knot_dname_is_equal(parser->last_node->owner,
+		                         current_rrset->owner)) {
 			/* RRSIG is first in the node, so we have to create it
 			 * before we return
 			 */
@@ -484,9 +396,7 @@ static void process_rr(const scanner_t *scanner)
 	knot_node_t *node = NULL;
 	/* \note this could probably be much simpler */
 	if (parser->last_node && current_rrset->type != KNOT_RRTYPE_SOA &&
-	    knot_dname_compare_non_canon(parser->last_node->owner,
-				 current_rrset->owner) ==
-	    0) {
+	    knot_dname_is_equal(parser->last_node->owner, current_rrset->owner)) {
 		node = parser->last_node;
 	} else {
 		if (parser->last_node && parser->node_rrsigs) {
@@ -547,7 +457,7 @@ static void process_rr(const scanner_t *scanner)
 		parser->ret = ret;
 		return;
 	} else if (ret > 0) {
-		knot_rrset_deep_free(&current_rrset, 1, 0);
+		knot_rrset_deep_free(&current_rrset, 1);
 	}
 	assert(parser->current_zone && node);
 	/* Do mandatory semantic checks. */
@@ -602,16 +512,11 @@ int knot_zload_open(zloader_t **dst, const char *source, const char *origin,
 		return KNOT_ENOMEM;
 	}
 
-	context->origin_from_config =
-		knot_dname_new_from_str(origin, strlen(origin), NULL);
+	/* As it's a first node, no need for compression yet. */
+	context->origin_from_config = knot_dname_from_str(origin, strlen(origin));
 	assert(context->origin_from_config);
 	knot_dname_to_lower(context->origin_from_config);
-	/* Add first DNAME to lookup tree. */
-	knot_zone_contents_insert_dname_into_table(&context->origin_from_config,
-	                                           context->lookup_tree);
-	context->last_node = knot_node_new(context->origin_from_config,
-	                                   NULL, 0);
-	knot_dname_release(context->origin_from_config);
+	context->last_node = knot_node_new(context->origin_from_config, NULL, 0);
 	knot_zone_t *zone = knot_zone_new(context->last_node);
 	context->current_zone = knot_zone_get_contents(zone);
 	context->node_rrsigs = NULL;
@@ -663,7 +568,12 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
 
 	parser_context_t *c = loader->context;
 	assert(c);
-	file_loader_process(loader->file_loader);
+	int ret = file_loader_process(loader->file_loader);
+	if (ret != ZSCANNER_OK) {
+		log_zone_error("Zone could not be loaded (%s).\n",
+		               zscanner_strerror(ret));
+	}
+
 	if (c->last_node && c->node_rrsigs) {
 		process_rrsigs_in_node(c, c->current_zone, c->last_node);
 	}
@@ -688,7 +598,8 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
 	}
 
 	if (knot_zone_contents_apex(c->current_zone) == NULL ||
-	    knot_node_rrset(knot_zone_contents_apex(c->current_zone), KNOT_RRTYPE_SOA) == NULL) {
+	    knot_node_rrset(knot_zone_contents_apex(c->current_zone),
+	                    KNOT_RRTYPE_SOA) == NULL) {
 		log_zone_error("No SOA record in the zone file.\n");
 		rrset_list_delete(&c->node_rrsigs);
 		knot_zone_t *zone_to_free = c->current_zone->zone;
@@ -699,8 +610,16 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
 	knot_node_t *first_nsec3_node = NULL;
 	knot_node_t *last_nsec3_node = NULL;
 	rrset_list_delete(&c->node_rrsigs);
-	knot_zone_contents_adjust(c->current_zone, &first_nsec3_node,
-	                          &last_nsec3_node, 0);
+	int kret = knot_zone_contents_adjust(c->current_zone, &first_nsec3_node,
+	                                     &last_nsec3_node, 0);
+	if (kret != KNOT_EOK)  {
+		log_zone_error("Failed to finalize zone contents: %s\n",
+		               knot_strerror(kret));
+		rrset_list_delete(&c->node_rrsigs);
+		knot_zone_t *zone_to_free = c->current_zone->zone;
+		knot_zone_deep_free(&zone_to_free);
+		return NULL;
+	}
 
 	if (loader->semantic_checks) {
 		int check_level = 1;
@@ -738,6 +657,8 @@ void knot_zload_close(zloader_t *loader)
 
 	file_loader_free(loader->file_loader);
 
+	knot_dname_free(&loader->context->origin_from_config);
+
 	free(loader->source);
 	free(loader->origin);
 	free(loader->context);
diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h
index 71284a27a16b061ba5eb612f7bd206f3ebb30fee..fbc70329fef73fd1ad8f70b2643cd3a76d816a59 100644
--- a/src/knot/zone/zone-load.h
+++ b/src/knot/zone/zone-load.h
@@ -31,7 +31,7 @@
 
 #include "libknot/zone/zone.h"
 #include "knot/zone/semantic-check.h"
-#include "zscanner/file_loader.h"
+#include "zscanner/zscanner.h"
 
 /* TODO this structure is highly redundant, remove. Maybe use oh-so-great BIRD lists. */
 /*!
diff --git a/src/libknot/binary.c b/src/libknot/binary.c
index aba35607efbebecbab9c2a2f91b2245e3628c5d6..7fc467f394eb3bfd133a4afb254f91dc3353484d 100644
--- a/src/libknot/binary.c
+++ b/src/libknot/binary.c
@@ -18,33 +18,53 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "binary.h"
 #include "common/base64.h"
 #include "common/errcode.h"
+#include "common/memdup.h"
+#include "libknot/binary.h"
 
-int knot_binary_from_base64(const char *base64, knot_binary_t *binary)
+int knot_binary_from_base64(const char *base64, knot_binary_t *to)
 {
-	if (!binary)
+	if (!base64 || !to) {
 		return KNOT_EINVAL;
+	}
 
 	uint8_t *data;
 	int32_t size;
 
 	size = base64_decode_alloc((uint8_t *)base64, strlen(base64), &data);
 	if (size < 0) {
-		return size;
+		return (int)size;
+	}
+
+	to->data = data;
+	to->size = size;
+
+	return KNOT_EOK;
+}
+
+int knot_binary_from_string(const uint8_t *data, size_t size, knot_binary_t *to)
+{
+	if (!data || !to) {
+		return KNOT_EINVAL;
 	}
 
-	binary->data = data;
-	binary->size = size;
+	uint8_t *copy = knot_memdup(data, size);
+	if (!copy) {
+		return KNOT_ENOMEM;
+	}
+
+	to->data = copy;
+	to->size = size;
 
 	return KNOT_EOK;
 }
 
 int knot_binary_free(knot_binary_t *binary)
 {
-	if (!binary)
+	if (!binary) {
 		return KNOT_EINVAL;
+	}
 
 	if (binary->data) {
 		free(binary->data);
@@ -54,3 +74,25 @@ int knot_binary_free(knot_binary_t *binary)
 
 	return KNOT_EOK;
 }
+
+int knot_binary_dup(const knot_binary_t *from, knot_binary_t *to)
+{
+	if (!from || !to) {
+		return KNOT_EINVAL;
+	}
+
+	if (from->size == 0) {
+		to->size = 0;
+		to->data = NULL;
+		return KNOT_EOK;
+	}
+
+	to->data = malloc(from->size * sizeof(uint8_t));
+	if (!to->data) {
+		return KNOT_ENOMEM;
+	}
+
+	to->size = from->size;
+	memcpy(to->data, from->data, from->size * sizeof(uint8_t));
+	return KNOT_EOK;
+}
diff --git a/src/libknot/binary.h b/src/libknot/binary.h
index 34d4c1930fdfa05b4380bc6513179816e6a2a89f..8b2198b0f311a505861d3cc7b36f3e424040b5e8 100644
--- a/src/libknot/binary.h
+++ b/src/libknot/binary.h
@@ -43,10 +43,20 @@ typedef struct knot_binary knot_binary_t;
  * \brief Initialize knot_binary_t structure from Base64 encoded string.
  *
  * \param base64  Base64 encoded input data.
- * \param binary  Pointer to structure to write the result into.
+ * \param to      Pointer to structure to write the result into.
  * \return Error code, KNOT_EOK in case of success.
  */
-int knot_binary_from_base64(const char *base64, knot_binary_t *binary);
+int knot_binary_from_base64(const char *base64, knot_binary_t *to);
+
+/*!
+ * \brief Initialize knot_binary_t structure from binary string.
+ *
+ * \param data  Pointer to binary data string.
+ * \param size  Size of the binary data.
+ * \param to    Pointer to structure to write the result into.
+ * \return Error code, KNOT_EOK in case of success.
+ */
+int knot_binary_from_string(const uint8_t *data, size_t size, knot_binary_t *to);
 
 /*!
  * \brief Free content of knot_binary_t structure.
@@ -56,6 +66,17 @@ int knot_binary_from_base64(const char *base64, knot_binary_t *binary);
  */
 int knot_binary_free(knot_binary_t *binary);
 
+/*!
+ * \brief Create deep copy of knot_binary_t structure.
+ *
+ * (Does nothing if source structure is empty.)
+ *
+ * \param from   Source structure.
+ * \param to     Target structure.
+ * \return Error code, KNOT_EOK if case of success.
+ */
+int knot_binary_dup(const knot_binary_t *from, knot_binary_t *to);
+
 #endif // _KNOT_BINARY_H
 
 /*! @} */
diff --git a/src/libknot/consts.c b/src/libknot/consts.c
index 5471d76af8f0a8f042d4ef5454f7a4b35e1372d4..f022ea7ec3e34349095de38fb3507a162d5789dc 100644
--- a/src/libknot/consts.c
+++ b/src/libknot/consts.c
@@ -58,7 +58,7 @@ knot_lookup_table_t knot_tsig_alg_names[] = {
 	{ KNOT_TSIG_ALG_NULL, NULL }
 };
 
-knot_lookup_table_t knot_tsig_alg_domain_names[] = {
+knot_lookup_table_t knot_tsig_alg_dnames_str[] = {
 	{ KNOT_TSIG_ALG_GSS_TSIG,    "gss-tsig." },
 	{ KNOT_TSIG_ALG_HMAC_MD5,    "hmac-md5.sig-alg.reg.int." },
 	{ KNOT_TSIG_ALG_HMAC_SHA1,   "hmac-sha1." },
@@ -69,6 +69,17 @@ knot_lookup_table_t knot_tsig_alg_domain_names[] = {
 	{ KNOT_TSIG_ALG_NULL, NULL }
 };
 
+knot_lookup_table_t knot_tsig_alg_dnames[] = {
+        { KNOT_TSIG_ALG_GSS_TSIG,    "\x08" "gss-tsig" },
+        { KNOT_TSIG_ALG_HMAC_MD5,    "\x08" "hmac-md5" "\x07" "sig-alg" "\x03" "reg" "\x03" "int" },
+	{ KNOT_TSIG_ALG_HMAC_SHA1,   "\x09" "hmac-sha1" },
+	{ KNOT_TSIG_ALG_HMAC_SHA224, "\x0B" "hmac-sha224" },
+	{ KNOT_TSIG_ALG_HMAC_SHA256, "\x0B" "hmac-sha256" },
+	{ KNOT_TSIG_ALG_HMAC_SHA384, "\x0B" "hmac-sha384" },
+	{ KNOT_TSIG_ALG_HMAC_SHA512, "\x0B" "hmac-sha512" },
+	{ KNOT_TSIG_ALG_NULL, NULL }
+};
+
 size_t knot_tsig_digest_length(const uint8_t algorithm)
 {
 	switch (algorithm) {
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
index e2b3a828d096d1e2494c1e310301f002ac63de05..a0a843ec7a25dc06d544a6b2537dec69b1c79eaa 100644
--- a/src/libknot/consts.h
+++ b/src/libknot/consts.h
@@ -34,10 +34,13 @@
 /*!
  * \brief Basic limits for domain names (RFC 1035).
  */
-typedef enum {
-	KNOT_MAX_DNAME_LENGTH = 255, /*!< 1-byte maximum. */
-	KNOT_MAX_DNAME_LABELS = 127  /*!< 1-char labels. */
-} knot_const_t;
+#define KNOT_DNAME_MAXLEN 255     /*!< 1-byte maximum. */
+#define KNOT_DNAME_MAXLABELS 127  /*!< 1-char labels. */
+
+/*!
+ * \brief Often used sizes.
+ */
+#define KNOT_RR_HEADER_SIZE 10
 
 /*!
  * \brief DNS operation codes (OPCODEs).
@@ -127,28 +130,6 @@ typedef enum {
 	KNOT_TSIG_ALG_DIG_LENGTH_SHA512   = 64
 } knot_tsig_algorithm_digest_length_t;
 
-/*!
- * \brief DNSSEC algorithm numbers.
- *
- * http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml
- */
-typedef enum {
-	KNOT_DNSSEC_ALG_RSAMD5             =  1,
-	KNOT_DNSSEC_ALG_DH                 =  2,
-	KNOT_DNSSEC_ALG_DSA                =  3,
-
-	KNOT_DNSSEC_ALG_RSASHA1            =  5,
-	KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1     =  6,
-	KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1 =  7,
-	KNOT_DNSSEC_ALG_RSASHA256          =  8,
-
-	KNOT_DNSSEC_ALG_RSASHA512          = 10,
-
-	KNOT_DNSSEC_ALG_ECC_GOST           = 12,
-	KNOT_DNSSEC_ALG_ECDSAP256SHA256    = 13,
-	KNOT_DNSSEC_ALG_ECDSAP384SHA384    = 14
-} knot_dnssec_algorithm_t;
-
 /*!
  * \brief DS digest lengths.
  */
@@ -190,7 +171,12 @@ extern knot_lookup_table_t knot_tsig_alg_names[];
 /*!
  * \brief TSIG key algorithm names in a domain form.
  */
-extern knot_lookup_table_t knot_tsig_alg_domain_names[];
+extern knot_lookup_table_t knot_tsig_alg_dnames_str[];
+
+/*!
+ * \brief TSIG key algorithm domain names.
+ */
+extern knot_lookup_table_t knot_tsig_alg_dnames[];
 
 /*!
  * \brief Returns length of TSIG digest for given algorithm.
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index 754efe485ecef85665a53883c19b804f324ef6ba..86f92968dedf045341d3099754e284802795b9e8 100644
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -19,7 +19,7 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <string.h>
-#include <ctype.h>	// tolower()
+#include <ctype.h>
 #include <inttypes.h>
 
 #include "common.h"
@@ -31,625 +31,229 @@
 #include "util/utils.h"
 #include "util/wire.h"
 
-/*! \todo dnames allocated from TLS cache will be discarded after thread
- *        termination. This shouldn't happpen.
- */
-#if 0
-/*
- * Memory cache.
- */
-#include "common/slab/slab.h"
-#include <stdio.h>
-#include <pthread.h>
-
-/*! \brief TLS unique key for each thread cache. */
-static pthread_key_t dname_ckey;
-static pthread_once_t dname_once = PTHREAD_ONCE_INIT;
-
-/*! \brief Destroy thread dname cache (automatically called). */
-static void knot_dname_cache_free(void *ptr)
-{
-	slab_cache_t* cache = (slab_cache_t*)ptr;
-	if (cache) {
-		slab_cache_destroy(cache);
-		free(cache);
-	}
-}
-
-/*! \brief Cleanup for main() TLS. */
-static void knot_dname_cache_main_free()
-{
-	knot_dname_cache_free(pthread_getspecific(dname_ckey));
-}
-
-static void knot_dname_cache_init()
-{
-	(void) pthread_key_create(&dname_ckey, knot_dname_cache_free);
-	atexit(knot_dname_cache_main_free); // Main thread cleanup
-}
-#endif
+/*----------------------------------------------------------------------------*/
 
-/*!
- * \brief Allocate item from thread cache.
- * \retval Allocated dname instance on success.
- * \retval NULL on error.
- */
-static knot_dname_t* knot_dname_alloc()
+static int knot_label_is_equal(const uint8_t *lb1, const uint8_t *lb2)
 {
-	return malloc(sizeof(knot_dname_t));
-
-	/*! \todo dnames allocated from TLS cache will be discarded after thread
-	 *        termination. This shouldn't happpen.
-	 */
-#if 0
-	/* Initialize dname cache TLS key. */
-	(void)pthread_once(&dname_once, knot_dname_cache_init);
-
-	/* Create cache if not exists. */
-	slab_cache_t* cache = pthread_getspecific(dname_ckey);
-	if (knot_unlikely(!cache)) {
-		cache = malloc(sizeof(slab_cache_t));
-		if (!cache) {
-			return 0;
-		}
-
-		/* Initialize cache. */
-		slab_cache_init(cache, sizeof(knot_dname_t));
-		(void)pthread_setspecific(dname_ckey, cache);
-	}
-
-	return slab_cache_alloc(cache);
-#endif
+	return (*lb1 == *lb2) && memcmp(lb1 + 1, lb2 + 1, *lb1) == 0;
 }
 
 /*----------------------------------------------------------------------------*/
-/* Non-API functions                                                          */
+/* API functions                                                              */
 /*----------------------------------------------------------------------------*/
 
-static int knot_dname_set(knot_dname_t *dname, uint8_t *wire,
-                            short wire_size, const uint8_t *labels,
-                            short label_count)
-{
-	dname->name = wire;
-	dname->size = wire_size;
-	dname->label_count = label_count;
-
-	assert(label_count >= 0);
-
-	dname->labels = (uint8_t *)malloc(dname->label_count * sizeof(uint8_t));
-	CHECK_ALLOC_LOG(dname->labels, -1);
-	memcpy(dname->labels, labels, dname->label_count);
-
-	return 0;
-}
-
-/*!
- * \brief Converts domain name from string representation to wire format.
- *
- * This function also allocates the space for the wire format.
- *
- * \param name Domain name in string representation (presentation format).
- * \param size Size of the given domain name in characters (not counting the
- *             terminating 0 character.
- * \param dname Domain name where to store the wire format.
- *
- * \return Size of the wire format of the domain name in octets. If 0, no
- *         space has been allocated.
- *
- * \todo handle \X and \DDD (RFC 1035 5.1) or it can be handled by the parser?
- */
-static int knot_dname_str_to_wire(const char *name, uint size,
-                                    knot_dname_t *dname)
+int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp,
+                          const uint8_t *pkt)
 {
-	if (size > KNOT_MAX_DNAME_LENGTH) {
-		return -1;
-	}
+	if (name == NULL || name == endp)
+		return KNOT_EMALF;
 
-	uint wire_size;
-	int root = (*name == '.' && size == 1);
-	// root => different size
-	if (root) {
-		wire_size = 1;
-	} else {
-		wire_size = size + 1;
-	}
+	int wire_len = 0;
+	int name_len = 1; /* Keep \x00 terminal label in advance. */
+	bool is_compressed = false;
 
-	uint8_t *wire;
-	uint8_t labels[KNOT_MAX_DNAME_LABELS];
-	short label_count = 0;
+	while (*name != '\0') {
 
-	// signed / unsigned issues??
-	wire = (uint8_t *)malloc(wire_size * sizeof(uint8_t));
-	if (wire == NULL) {
-		return -1;
-	}
-
-	dbg_dname_verb("Allocated space for wire format of dname: %p\n", wire);
-
-	if (root) {
-		*wire = '\0';
-		label_count = 0;
-		return knot_dname_set(dname, wire, wire_size, labels,
-		                        label_count);
-	}
+		/* Check bounds (must have at least 2 octets remaining). */
+		if (name + 2 > endp)
+			return KNOT_ESPACE;
 
-	const uint8_t *ch = (const uint8_t *)name;
-	uint8_t *label_start = wire;
-	uint8_t *w = wire + 1;
-	uint8_t label_length = 0;
-
-	while (ch - (const uint8_t *)name < size) {
-		assert(w - wire - 1 == ch - (const uint8_t *)name);
-
-		if (*ch == '.') {
-			/* Zero-length label inside a dname - invalid. */
-			if (label_length == 0) {
-				free(wire);
-				return -1;
+		if (knot_wire_is_pointer(name)) {
+			/* Check that the pointer points backwards
+			 * otherwise it could result in infinite loop
+			 */
+			if (pkt == NULL)
+				return KNOT_EINVAL;
+			uint16_t ptr = knot_wire_get_pointer(name);
+			if (ptr >= (name - pkt))
+				return KNOT_EMALF;
+
+			name = pkt + ptr; /* Hop to compressed label */
+			if (!is_compressed) { /* Measure compressed size only */
+				wire_len += sizeof(uint16_t);
+				is_compressed = true;
 			}
-			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_detail("Position %zd (%p): character: %c\n",
-			                 w - wire, w, *ch);
-			*w = *ch;
-			++label_length;
-		}
-
-		++w;
-		++ch;
-		assert(ch >= (const uint8_t *)name);
-	}
-
-	--ch;
-	if (*ch == '.') { // put 0 for root label if the name ended with .
-		--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_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;
-	}
-
-	return knot_dname_set(dname, wire, wire_size, labels, label_count);
-}
-
-/*----------------------------------------------------------------------------*/
-
-static inline int knot_dname_tolower(uint8_t c, int cs)
-{
-	return (cs) ? c : knot_tolower(c);
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int knot_dname_compare_labels(const uint8_t *label1,
-                                       const uint8_t *label2, int cs)
-{
-	const uint8_t *pos1 = label1;
-	const uint8_t *pos2 = label2;
-
-	int label_length = (*pos1 < *pos2) ? *pos1 : *pos2;
-	int i = 0;
-
-	while (i < label_length
-	       && knot_dname_tolower(*(++pos1), cs)
-	          == knot_dname_tolower(*(++pos2), cs)) {
-		++i;
-	}
-
-	if (i < label_length) {  // difference in some octet
-		return (knot_dname_tolower(*pos1, cs)
-		        - knot_dname_tolower(*pos2, cs));
-	}
-
-	return (label1[0] - label2[0]);
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
-{
-	const uint8_t *name = dname->name;
-	const uint8_t *pos = name;
-	const uint size = dname->size;
-
-	uint8_t labels[KNOT_MAX_DNAME_LABELS];
-	short label_count = 0;
-
-	while (pos - name < size && *pos != '\0' && label_count < KNOT_MAX_DNAME_LABELS ) {
-		if (*pos > 63) { /* Check label lengths. */
-			dbg_dname("Wrong wire format of domain name!\n");
-			dbg_dname("Label %d exceeds 63 bytes.\n", label_count);
-			return -1;
+			/* Check label length (maximum 63 bytes allowed). */
+			if (*name > 63)
+				return KNOT_EMALF;
+			/* Check if there's enough space. */
+			int lblen = *name + 1;
+			if (name_len + lblen > KNOT_DNAME_MAXLEN)
+				return KNOT_EMALF;
+			/* Update wire size only for noncompressed part. */
+			name_len += lblen;
+			if (!is_compressed)
+				wire_len += lblen;
+			/* Hop to next label. */
+			name += lblen;
 		}
-		labels[label_count++] = pos - name;
-		pos += *pos + 1;
-	}
-
-	// TODO: how to check if the domain name has right format?
-	if (label_count == KNOT_MAX_DNAME_LABELS) {
-		dbg_dname("Wrong wire format of domain name!\n");
-		dbg_dname("Too many labels: %s\n", name);
-		return -1;
-	}
-
-	if (pos - name > size || *pos != '\0' ) {
-		dbg_dname("Wrong wire format of domain name!\n");
-		dbg_dname("Position: %"PRIuPTR", character: %d, expected size: %d\n",
-		          pos - name, *pos, size);
-		return -1;
-	}
-
-	if (alloc) {
-		dname->labels
-			= (uint8_t *)malloc(label_count * sizeof(uint8_t));
-		CHECK_ALLOC_LOG(dname->labels, KNOT_ENOMEM);
-	}
-
-	memcpy(dname->labels, labels, label_count);
-	dname->label_count = label_count;
-
-	return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2,
-                            int cs)
-{
-dbg_dname_exec_verb(
-	char *name1 = knot_dname_to_str(d1);
-	char *name2 = knot_dname_to_str(d2);
-
-	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]);
-	}
-	for (int i = 0; i < strlen(name2); ++i) {
-		name2[i] = knot_tolower(name2[i]);
-	}
-
-	dbg_dname_detail("After to lower: %s and %s\n", name1, name2);
-
-	free(name1);
-	free(name2);
-);
-
-	if (!cs && d1 == d2) {
-		return 0;
-	}
-
-	int l1 = d1->label_count;
-	int l2 = d2->label_count;
-	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_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]],
-		                   cs);
-		if (res != 0) {
-			return res;
-		} // otherwise the labels are identical, continue with previous
-	}
 
-	// if all labels matched, the shorter name is first
-	if (l1 == 0 && l2 > 0) {
-		return -1;
+		/* Check bounds (must have at least 1 octet). */
+		if (name + 1 > endp)
+			return KNOT_ESPACE;
 	}
 
-	if (l1 > 0 && l2 == 0) {
-		return 1;
-	}
+	if (!is_compressed) /* Terminal label. */
+		wire_len += 1;
 
-	return 0;
+	return wire_len;
 }
 
-/*----------------------------------------------------------------------------*/
-/* API functions                                                              */
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_new()
+knot_dname_t *knot_dname_parse(const uint8_t *pkt, size_t *pos, size_t maxpos)
 {
-	knot_dname_t *dname = knot_dname_alloc();
-
-	dname->name = NULL;
-	dname->labels = NULL;
-	dname->node = NULL;
-	dname->count = 1;
-	dname->size = 0;
-	dname->label_count = 0;
-
-	return dname;
-}
-
-/*----------------------------------------------------------------------------*/
-
-knot_dname_t *knot_dname_new_from_str(const char *name, uint size,
-                                          struct knot_node *node)
-{
-	if (name == NULL || size == 0) {
+	if (pkt == NULL || pos == NULL)
 		return NULL;
-	}
-
-//	knot_dname_t *dname = knot_dname_alloc();
-	knot_dname_t *dname = knot_dname_new();
 
-	if (dname == NULL) {
-		ERR_ALLOC_FAILED;
+	const uint8_t *name = pkt + *pos;
+	const uint8_t *endp = pkt + maxpos;
+	int parsed = knot_dname_wire_check(name, endp, pkt);
+	if (parsed < 0) {
 		return NULL;
 	}
 
-	/*! \todo The function should return error codes. */
-	int ret = knot_dname_str_to_wire(name, size, dname);
-	if (ret != 0) {
-		dbg_dname("Failed to create domain name from string.\n");
-		knot_dname_free(&dname);
+	/* Calculate decompressed length. */
+	int decompressed_len = knot_dname_realsize(name, pkt);
+	if (decompressed_len < 1) {
 		return NULL;
 	}
 
-dbg_dname_exec_verb(
-	dbg_dname_verb("Created dname with size: %d\n", dname->size);
-	dbg_dname_verb("Label offsets: ");
-	for (int i = 0; i < dname->label_count; ++i) {
-		dbg_dname_verb("%d, ", dname->labels[i]);
-	}
-	dbg_dname_verb("\n");
-);
-
-	if (dname->size <= 0) {
-		dbg_dname("Could not parse domain name "
-		          "from string: '%.*s'\n", size, name);
+	/* Allocate space for the name. */
+	knot_dname_t *res = malloc(decompressed_len);
+	if (res) {
+		/* Unpack name (expand compression pointers). */
+		if (knot_dname_unpack(res, name, decompressed_len, pkt) > 0) {
+			*pos += parsed;
+		} else {
+			free(res);
+			res = NULL;
+		}
 	}
-	assert(dname->name != NULL);
 
-	dname->node = node;
-	return dname;
+	return res;
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_new_from_nonfqdn_str(const char *name, uint size,
-                                                  struct knot_node *node)
+knot_dname_t *knot_dname_copy(const knot_dname_t *name)
 {
-	knot_dname_t *dname = NULL;
-
-	if (name[size - 1] != '.') {
-		char *fqdn = strcdup(name, ".");
-		dname = knot_dname_new_from_str(fqdn, size + 1, node);
-		free(fqdn);
-	} else {
-		dname = knot_dname_new_from_str(name, size, node);
-	}
+	if (name == NULL)
+		return NULL;
 
-	return dname;
+	return knot_dname_copy_part(name, knot_dname_size(name));
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
-                                           struct knot_node *node)
+knot_dname_t *knot_dname_copy_part(const knot_dname_t *name, unsigned len)
 {
-	if (name == NULL) { /* && size != 0) { !OS: Nerozumjaju */
-		dbg_dname("No name given!\n");
+	if (name == NULL || len == 0)
 		return NULL;
-	}
-
-	knot_dname_t *dname = knot_dname_new();
 
-	if (dname == NULL) {
-		ERR_ALLOC_FAILED;
+	knot_dname_t *dst = malloc(len);
+	if (knot_dname_to_wire(dst, name, len) < 1) {
+		free(dst);
 		return NULL;
 	}
 
-	dname->name = (uint8_t *)malloc(size * sizeof(uint8_t));
-	if (dname->name == NULL) {
-		ERR_ALLOC_FAILED;
-		knot_dname_free(&dname);
-		return NULL;
-	}
-
-	memcpy(dname->name, name, size);
-	dname->size = size;
-
-	if (knot_dname_find_labels(dname, 1) != 0) {
-		dbg_dname("Could not find labels in dname (new from wire).\n");
-		knot_dname_free(&dname);
-		return NULL;
-	}
-
-	dname->node = node;
-	return dname;
+	return dst;
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
-                                         size_t *pos, size_t size,
-                                         knot_node_t *node,
-                                         knot_dname_t *dname)
+int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen)
 {
-	uint8_t name[KNOT_MAX_DNAME_LENGTH];
-	uint8_t labels[KNOT_MAX_DNAME_LABELS];
-
-	short l = 0;
-	size_t i = 0, p = *pos;
-	int pointer_used = 0;
-
-	while (p < size && wire[p] != 0) {
-		/* Check maximum number of labels (may overflow). */
-		if (l == KNOT_MAX_DNAME_LABELS) {
-			return NULL;
-		}
-		labels[l] = i;
-		dbg_dname_detail("Next label (%d.) position: %zu\n", l, i);
-
-		if (knot_wire_is_pointer(wire + p)) {
-			// pointer.
-			size_t ptr = knot_wire_get_pointer(wire + p);
-
-			/* Check that the pointer points backwards
-			 * otherwise it could result in infinite loop
-			 */
-			if (ptr >= p) {
-				return NULL;
-			}
-
-			p = ptr;
-
-			if (!pointer_used) {
-				*pos += 2;
-				pointer_used = 1;
-			}
-			if (p >= size) {
-				return NULL;
-			}
-		} else {
-			// label; first byte is label length
-			uint8_t length = *(wire + p);
-			/* Check label length (maximum 63 bytes allowed). */
-			if (length > 63) {
-				return NULL;
-			}
-			/* Check if there's enough space. */
-			if (i + length + 2 > KNOT_MAX_DNAME_LENGTH) {
-				return NULL;
-			}
-			//printf("Label %d (max %d), length: %u.\n", l, KNOT_MAX_DNAME_LABELS, length);
-			memcpy(name + i, wire + p, length + 1);
-			p += length + 1;
-			i += length + 1;
-			if (!pointer_used) {
-				*pos += length + 1;
-			}
-			++l;
-		}
-	}
-	if (p >= size) {
-		return NULL;
-	}
-
-	name[i] = 0;
-	if (!pointer_used) {
-		*pos += 1;
-	}
-
-	/* Allocate if NULL. */
-	if (dname == NULL) {
-		dname = knot_dname_new();
-		if (dname) {
-			dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
-			dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
-		}
-	}
-
-	if (dname == NULL) {
-		ERR_ALLOC_FAILED;
-		return NULL;
-	}
+	if (dst == NULL || src == NULL)
+		return KNOT_EINVAL;
 
-	if (dname->name == NULL || dname->labels == NULL) {
-		ERR_ALLOC_FAILED;
-		knot_dname_free(&dname);
-		return NULL;
+	/* Write out non or partially compressed name. */
+	int len = 0;
+	while (*src != '\0' && !knot_wire_is_pointer(src)) {
+		uint8_t lblen = *src + 1;
+		if (len + lblen > maxlen)
+			return KNOT_ESPACE;
+		memcpy(dst + len, src, lblen);
+		len += lblen;
+		src += lblen;
+	}
+
+	/* Terminated either FQDN \x00, or as a pointer. */
+	if (*src == '\0') {
+		if (len + 1> maxlen)
+			return KNOT_ESPACE;
+		*(dst + len) = '\0';
+		len += 1; /* \x00 */
+	} else {
+		if (len + 2 > maxlen)
+			return KNOT_ESPACE;
+		memcpy(dst + len, src, sizeof(uint16_t));
+		len += 2; /* ptr */
 	}
-
-	memcpy(dname->name, name, i + 1);
-	memcpy(dname->labels, labels, l + 1);
-	dname->size = i + 1;
-	dname->label_count = l;
-	dname->node = node;
-
-	return dname;
+	return len;
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname)
+int knot_dname_unpack(uint8_t* dst, const knot_dname_t *src,
+                      size_t maxlen, const uint8_t *pkt)
 {
-	//return knot_dname_new_from_wire(dname->name, dname->size, dname->node);
-	/* 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);
+	if (dst == NULL || src == NULL)
+		return KNOT_EINVAL;
 
-	copy->labels = (uint8_t *)(malloc(dname->label_count));
+	/* Seek first real label occurence. */
+	src = knot_wire_seek_label(src, pkt);
 
-	if (copy->labels == NULL) {
-		knot_dname_free(&copy);
-		return NULL;
-	}
-
-	copy->name = (uint8_t *)(malloc(dname->size));
-	if (copy->name == NULL) {
-		knot_dname_free(&copy);
-		return NULL;
+	/* Unpack rest of the labels. */
+	int len = 0;
+	while (*src != '\0') {
+		uint8_t lblen = *src + 1;
+		if (len + lblen > maxlen)
+			return KNOT_ESPACE;
+		memcpy(dst + len, src, lblen);
+		len += lblen;
+		src = knot_wire_next_label(src, pkt);
 	}
 
-	memcpy(copy->labels, dname->labels, dname->label_count);
-	copy->label_count = dname->label_count;
-
-	memcpy(copy->name, dname->name, dname->size);
-	copy->size = dname->size;
-
-	copy->node = dname->node;
+	/* Terminal label */
+	if (len + 1 > maxlen)
+		return KNOT_EINVAL;
 
-	return copy;
+	*(dst + len) = '\0';
+	return len + 1;
 }
 
 /*----------------------------------------------------------------------------*/
 
-char *knot_dname_to_str(const knot_dname_t *dname)
+char *knot_dname_to_str(const knot_dname_t *name)
 {
-	if (!dname || dname->size == 0) {
-		return 0;
-	}
+	if (name == NULL)
+		return NULL;
 
+	/*! \todo Supply packet. */
+	/*! \todo Write to static buffer? */
 	// Allocate space for dname string + 1 char termination.
-	size_t alloc_size = dname->size + 1;
-	char *name = malloc(alloc_size);
-	if (name == NULL) {
+	int dname_size = knot_dname_size(name);
+	size_t alloc_size = dname_size + 1;
+	char *res = malloc(alloc_size);
+	if (res == NULL) {
 		return NULL;
 	}
 
 	uint8_t label_len = 0;
 	size_t  str_len = 0;
 
-	for (uint i = 0; i < dname->size; i++) {
-		uint8_t c = dname->name[i];
+	for (uint i = 0; i < dname_size; i++) {
+		uint8_t c = name[i];
 
 		// Read next label size.
 		if (label_len == 0) {
 			label_len = c;
 
 			// Write label separation.
-			if (str_len > 0 || dname->size == 1) {
-				name[str_len++] = '.';
+			if (str_len > 0 || dname_size == 1) {
+				res[str_len++] = '.';
 			}
 
 			continue;
@@ -657,35 +261,35 @@ char *knot_dname_to_str(const knot_dname_t *dname)
 
 		if (isalnum(c) != 0 || c == '-' || c == '_' || c == '*' ||
 		    c == '/') {
-			name[str_len++] = c;
+			res[str_len++] = c;
 		} else if (ispunct(c) != 0) {
 			// Increase output size for \x format.
 			alloc_size += 1;
-			char *extended = realloc(name, alloc_size);
+			char *extended = realloc(res, alloc_size);
 			if (extended == NULL) {
-				free(name);
+				free(res);
 				return NULL;
 			}
-			name = extended;
+			res = extended;
 
 			// Write encoded character.
-			name[str_len++] = '\\';
-			name[str_len++] = c;
+			res[str_len++] = '\\';
+			res[str_len++] = c;
 		} else {
 			// Increase output size for \DDD format.
 			alloc_size += 3;
-			char *extended = realloc(name, alloc_size);
+			char *extended = realloc(res, alloc_size);
 			if (extended == NULL) {
-				free(name);
+				free(res);
 				return NULL;
 			}
-			name = extended;
+			res = extended;
 
 			// Write encoded character.
-			int ret = snprintf(name + str_len, alloc_size - str_len,
+			int ret = snprintf(res + str_len, alloc_size - str_len,
 			                   "\\%03u", c);
 			if (ret <= 0 || ret >= alloc_size - str_len) {
-				free(name);
+				free(res);
 				return NULL;
 			}
 
@@ -696,482 +300,401 @@ char *knot_dname_to_str(const knot_dname_t *dname)
 	}
 
 	// String_termination.
-	name[str_len] = 0;
+	res[str_len] = 0;
 
-	return name;
+	return res;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_dname_to_lower(knot_dname_t *dname)
+knot_dname_t *knot_dname_from_str(const char *name, unsigned len)
 {
-	if (dname == NULL) {
-		return KNOT_EINVAL;
+	if (name == NULL || len == 0 || len > KNOT_DNAME_MAXLEN) {
+		return NULL;
 	}
 
-	for (int i = 0; i < dname->size; ++i) {
-		dname->name[i] = knot_tolower(dname->name[i]);
+	/* Estimate wire size for special cases. */
+	unsigned wire_size = len + 1;
+	if (name[0] == '.' && len == 1) {
+		wire_size = 1; /* Root label. */
+		len = 0;      /* Do not parse input. */
+	} else if (name[len - 1] != '.') {
+		++wire_size; /* No FQDN, reserve last root label. */
 	}
-	return KNOT_EOK;
-}
 
-/*----------------------------------------------------------------------------*/
+	/* Create wire. */
+	uint8_t *wire = malloc(wire_size * sizeof(uint8_t));
+	if (wire == NULL)
+		return NULL;
+	*wire = '\0';
 
-int knot_dname_to_lower_copy(const knot_dname_t *dname, char *name,
-                             size_t size)
-{
-	if (dname == NULL || name == NULL || size < dname->size) {
-		return KNOT_EINVAL;
+	/* Parse labels. */
+	const uint8_t *ch = (const uint8_t *)name;
+	const uint8_t *np = ch + len;
+	uint8_t *label = wire;
+	uint8_t *w = wire + 1; /* Reserve 1 for label len */
+	while (ch != np) {
+		if (*ch == '.') {
+			/* Zero-length label inside a dname - invalid. */
+			if (*label == 0) {
+				free(wire);
+				return NULL;
+			}
+			label = w;
+			*label = '\0';
+		} else {
+			*w = *ch;
+			*label += 1;
+		}
+		++w;
+		++ch;
 	}
 
-	for (int i = 0; i < dname->size; ++i) {
-		name[i] = knot_tolower(dname->name[i]);
+	/* Check for non-FQDN name. */
+	if (*label > 0) {
+		*w = '\0';
 	}
-	return KNOT_EOK;
+
+	return wire;
 }
 
 /*----------------------------------------------------------------------------*/
 
-const uint8_t *knot_dname_name(const knot_dname_t *dname)
+int knot_dname_to_lower(knot_dname_t *name)
 {
-	return dname->name;
-}
+	if (name == NULL)
+		return KNOT_EINVAL;
 
-/*----------------------------------------------------------------------------*/
+	/*! \todo Faster with \xdfdf mask. */
+	while (*name != '\0') {
+		for (uint8_t i = 0; i < *name; ++i)
+			name[1 + i] = knot_tolower(name[1 + i]);
+		name = (uint8_t *)knot_wire_next_label(name, NULL);
+		assert(name); /* Must not be used on compressed names. */
+	}
 
-uint knot_dname_size(const knot_dname_t *dname)
-{
-	return dname->size;
+	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
-uint8_t knot_dname_size_part(const knot_dname_t *dname, int labels)
+int knot_dname_size(const knot_dname_t *name)
 {
-	assert(labels < dname->label_count);
-	assert(dname->labels != NULL);
-	return (dname->labels[labels]);
+	if (name == NULL)
+		return KNOT_EINVAL;
+
+	/* Count name size without terminal label. */
+	int len = 0;
+	while (*name != '\0' && !knot_wire_is_pointer(name)) {
+		uint8_t lblen = *name + 1;
+		len += lblen;
+		name += lblen;
+	}
+
+	/* Compression pointer is 2 octets. */
+	if (knot_wire_is_pointer(name))
+		return len + 2;
+
+	return len + 1;
 }
 
 /*----------------------------------------------------------------------------*/
 
-const struct knot_node *knot_dname_node(const knot_dname_t *dname)
-
+int knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt)
 {
-	return knot_dname_get_node(dname);
+	return knot_dname_prefixlen(name, KNOT_DNAME_MAXLABELS, pkt);
 }
 
 /*----------------------------------------------------------------------------*/
 
-struct knot_node *knot_dname_get_node(const knot_dname_t *dname)
+bool knot_dname_is_sub(const knot_dname_t *sub, const knot_dname_t *domain)
 {
-	if (dname == NULL) {
-		return NULL;
-	}
+	if (sub == domain)
+		return false;
 
-	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;
-		}
-	}
+	/* Count labels. */
+	assert(sub != NULL && domain != NULL);
+	int sub_l = knot_dname_labels(sub, NULL);
+	int domain_l = knot_dname_labels(domain, NULL);
 
-	return node;
-}
+	if (sub_l < 0 || domain_l < 0)
+		return false;
 
-/*----------------------------------------------------------------------------*/
+	assert(sub >= 0 && sub_l <= KNOT_DNAME_MAXLABELS);
+	assert(domain_l >= 0 && domain_l <= KNOT_DNAME_MAXLABELS);
 
-void knot_dname_set_node(knot_dname_t *dname, knot_node_t *node)
-{
-	dname->node = node;
-}
+	/* Subdomain must have more labels as parent. */
+	if (sub_l <= domain_l)
+		return false;
 
-/*----------------------------------------------------------------------------*/
+	/* Align end-to-end to common suffix. */
+	int common = knot_dname_align(&sub, sub_l, &domain, domain_l, NULL);
 
-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;
+	/* Compare common suffix. */
+	while(common > 0) {
+		/* Compare label. */
+		if (!knot_label_is_equal(sub, domain))
+			return false;
+		/* Next label. */
+		sub = knot_wire_next_label(sub, NULL);
+		domain = knot_wire_next_label(domain, NULL);
+		--common;
 	}
+	return true;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_dname_is_fqdn(const knot_dname_t *dname)
+bool knot_dname_is_wildcard(const knot_dname_t *name)
 {
-	return (dname->name[dname->size - 1] == '\0');
+	assert(name != NULL);
+	return name[0] == 1 && name[1] == '*';
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname)
+int knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2)
 {
-	if (dname == NULL ||
-		/* Root domain. */
-		((knot_dname_label_count(dname) == 0) &&
-		 (knot_dname_is_fqdn(dname)))) {
-		return NULL;
-	}
-
-	knot_dname_t *parent = knot_dname_new();
-	if (parent == NULL) {
-		return NULL;
-	}
-
-	// last label, the result should be root domain
-	if (dname->label_count == 1) {
-		dbg_dname_verb("Chopping last label.\n");
-		parent->label_count = 0;
-
-		parent->name = (uint8_t *)malloc(1);
-		if (parent->name == NULL) {
-			ERR_ALLOC_FAILED;
-			knot_dname_free(&parent);
-			return NULL;
-		}
-
-		*parent->name = 0;
-
-		parent->size = 1;
-
-		return parent;
-	}
+	if (d1 == NULL || d2 == NULL)
+		return KNOT_EINVAL;
 
-	parent->size = dname->size - dname->name[0] - 1;
-	parent->name = (uint8_t *)malloc(parent->size);
-	if (parent->name == NULL) {
-		ERR_ALLOC_FAILED;
-		knot_dname_free(&parent);
-		return NULL;
-	}
+	/* Count labels. */
+	int l1 = knot_dname_labels(d1, NULL);
+	int l2 = knot_dname_labels(d2, NULL);
 
-	parent->labels = (uint8_t *)malloc(dname->label_count - 1);
-	if (parent->labels == NULL) {
-		ERR_ALLOC_FAILED;
-		free(parent->name);
-		knot_dname_free(&parent);
-		return NULL;
-	}
+	if (l1 < 0 || l2 < 0)
+		return KNOT_EINVAL;
 
-	memcpy(parent->name, &dname->name[dname->name[0] + 1], parent->size);
+	assert(l1 >= 0 && l1 <= KNOT_DNAME_MAXLABELS);
+	assert(l2 >= 0 && l2 <= KNOT_DNAME_MAXLABELS);
 
+	/* Align end-to-end to common suffix. */
+	int common = knot_dname_align(&d1, l1, &d2, l2, NULL);
 
-	short first_label_length = dname->labels[1];
+	/* Count longest chain leading to root label. */
+	int matched = 0;
+	while (common > 0) {
+		if (knot_label_is_equal(d1, d2))
+			++matched;
+		else
+			matched = 0; /* Broken chain. */
 
-	for (int i = 0; i < dname->label_count - 1; ++i) {
-		parent->labels[i] = dname->labels[i + 1] - first_label_length;
+		/* Next label. */
+		d1 = knot_wire_next_label(d1, NULL);
+		d2 = knot_wire_next_label(d2, NULL);
+		--common;
 	}
-	parent->label_count = dname->label_count - 1;
 
-	return parent;
+	return matched;
 }
 
 /*----------------------------------------------------------------------------*/
 
-void knot_dname_left_chop_no_copy(knot_dname_t *dname)
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels,
+                                        const knot_dname_t *suffix)
 {
-	// copy the name
-	if (dname->label_count > 1) {
-		short first_label_length = dname->labels[1];
-
-		memmove(dname->name, &dname->name[dname->labels[1]],
-			dname->size - first_label_length);
-		// adjust labels
-		for (int i = 0; i < dname->label_count - 1; ++i) {
-			dname->labels[i] = dname->labels[i + 1]
-			                   - first_label_length;
-		}
-		dname->label_count = dname->label_count - 1;
-		dname->size -= first_label_length;
-	} else {
-		dname->name[0] = '\0';
-		dname->size = 1;
-		dname->label_count = 0;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
+	if (name == NULL)
+		return NULL;
 
-int knot_dname_is_subdomain(const knot_dname_t *sub,
-                              const knot_dname_t *domain)
-{
-dbg_dname_exec_verb(
-	char *name1 = knot_dname_to_str(sub);
-	char *name2 = knot_dname_to_str(domain);
+	/* Calculate prefix and suffix lengths. */
+	int dname_lbs = knot_dname_labels(name, NULL);
+	assert(dname_lbs >= labels);
+	unsigned prefix_lbs = dname_lbs - labels;
 
-	dbg_dname_verb("Checking if %s is subdomain of %s\n", name1, name2);
-	free(name1);
-	free(name2);
-);
+	/* Trim 1 octet from prefix, as it is measured as FQDN. */
+	int prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL) - 1;
+	int suffix_len = knot_dname_size(suffix);
+	if (prefix_len < 0 || suffix_len < 0)
+		return NULL;
 
-	if (sub == domain) {
-		return 0;
-	}
+	/* Create target name. */
+	int new_len = prefix_len + suffix_len;
+	knot_dname_t *out = malloc(new_len);
+	if (out == NULL)
+		return NULL;
 
-	// if one of the names is fqdn and the other is not
-	if ((sub->name[sub->size - 1] == '\0'
-	      && domain->name[domain->size - 1] != '\0')
-	    || (sub->name[sub->size - 1] != '\0'
-		&& domain->name[domain->size - 1] == '\0')) {
-		return 0;
+	/* Copy prefix. */
+	uint8_t *dst = out;
+	while (prefix_lbs > 0) {
+		memcpy(dst, name, *name + 1);
+		dst += *name + 1;
+		name = knot_wire_next_label(name, NULL);
+		--prefix_lbs;
 	}
 
-	int l1 = sub->label_count;
-	int l2 = domain->label_count;
-
-	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
+	/* Copy suffix. */
+	while (*suffix != '\0') {
+		memcpy(dst, suffix, *suffix + 1);
+		dst += *suffix + 1;
+		suffix = knot_wire_next_label(suffix, NULL);
 	}
+	*dst = '\0';
+	return out;
+}
 
-	// compare labels from last to first
-	while (l1 > 0 && l2 > 0) {
-		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)
-		    != 0) {
-			return 0;  // sub is not a subdomain of domain
-		} // otherwise the labels are identical, continue with previous
-	}
+/*----------------------------------------------------------------------------*/
 
-	// if all labels matched, it should be subdomain (more labels)
-	assert(l1 > l2);
+void knot_dname_free(knot_dname_t **name)
+{
+	if (name == NULL || *name == NULL)
+		return;
 
-	return 1;
+	free(*name);
+	*name = NULL;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_dname_is_wildcard(const knot_dname_t *dname)
+int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2)
 {
-	return (dname->size >= 2
-		&& dname->name[0] == 1
-		&& dname->name[1] == '*');
+	return knot_dname_cmp_wire(d1, d2, NULL);
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_dname_matched_labels(const knot_dname_t *dname1,
-                                const knot_dname_t *dname2)
+int knot_dname_cmp_wire(const knot_dname_t *d1, const knot_dname_t *d2,
+                        const uint8_t *pkt)
 {
-	int l1 = dname1->label_count;
-	int l2 = dname2->label_count;
+	/* This would be hard to catch since -1 is a good result, assert instead. */
+	assert(d1 != NULL || d2 != NULL);
 
-	// compare labels from last to first
-	int matched = 0;
-	while (l1 > 0 && l2 > 0) {
-		int res = knot_dname_compare_labels(
-		               &dname1->name[dname1->labels[--l1]],
-		               &dname2->name[dname2->labels[--l2]], 0);
-		if (res == 0) {
-			++matched;
-		} else  {
-			break;
-		}
+	/* Convert to lookup format. */
+	uint8_t d1_lf[KNOT_DNAME_MAXLEN], d2_lf[KNOT_DNAME_MAXLEN];
+	if (knot_dname_lf(d1_lf, d1, pkt) < 0 || knot_dname_lf(d2_lf, d2, pkt) < 0) {
+		assert(0); /* This must not happend as the d1, d2 are checked. */
+		return KNOT_EINVAL;
 	}
 
-	return matched;
-}
-
-/*----------------------------------------------------------------------------*/
+	/* Compare common part. */
+	uint8_t common = d1_lf[0];
+	if (common > d2_lf[0])
+		common = d2_lf[0];
+	int ret = memcmp(d1_lf+1, d2_lf+1, common);
+	if (ret != 0)
+		return ret;
 
-int knot_dname_label_count(const knot_dname_t *dname)
-{
-	return dname->label_count;
+	/* If they match, compare lengths. */
+	if (d1_lf[0] < d2_lf[0])
+		return -1;
+	if (d1_lf[0] > d2_lf[0])
+		return 1;
+	return 0;
 }
 
 /*----------------------------------------------------------------------------*/
 
-uint8_t knot_dname_label_size(const knot_dname_t *dname, int i)
+bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2)
 {
-	assert(i >= 0);
-	assert(dname->size == 1 || i + 1 == dname->label_count
-	       || dname->labels[i + 1] - dname->labels[i] - 1
-	          == dname->name[dname->labels[i]]);
-	return dname->name[dname->labels[i]];
+	/*! \todo Could be implemented more efficiently, check profile first. */
+	return (knot_dname_cmp(d1, d2) == 0);
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname, int size,
-                                        const knot_dname_t *suffix)
+knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
 {
-dbg_dname_exec_verb(
-	char *name = knot_dname_to_str(dname);
-	dbg_dname_verb("Replacing suffix of name %s, size %d with ", name,
-	               size);
-	free(name);
-	name = knot_dname_to_str(suffix);
-	dbg_dname_verb("%s (size %d)\n", name, suffix->size);
-	free(name);
-);
-	knot_dname_t *res = knot_dname_new();
-	CHECK_ALLOC(res, NULL);
-
-	res->size = dname->size - size + suffix->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);
+	if (d1 == NULL || d2 == NULL)
 		return NULL;
-	}
-
-	dbg_dname_hex((char *)res->name, res->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_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);
+	/* This is problem equal to replacing last \x00 from d1 with d2. */
+	knot_dname_t *ret = knot_dname_replace_suffix(d1, 0, d2);
 
-	knot_dname_find_labels(res, 1);
+	/* Like if we are reallocating d1. */
+	if (ret != NULL)
+		knot_dname_free(&d1);
 
-	return res;
+	return ret;
 }
 
 /*----------------------------------------------------------------------------*/
 
-void knot_dname_free(knot_dname_t **dname)
+int knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt)
 {
-	if (dname == NULL || *dname == NULL) {
-		return;
-	}
-
-	free((*dname)->name);
-
-	free((*dname)->labels);
+	if (name == NULL)
+		return KNOT_EINVAL;
 
+	/* Zero labels means 1 octet \x00 */
+	if (nlabels == 0)
+		return 1;
 
-//	slab_free(*dname);
-	free(*dname);
-	*dname = NULL;
-}
+	/* Seek first real label occurence. */
+	name = knot_wire_seek_label(name, pkt);
 
-/*----------------------------------------------------------------------------*/
+	int len = 1; /* Terminal label */
+	while (*name != '\0') {
+		len += *name + 1;
+		name = knot_wire_next_label(name, pkt);
+		if (--nlabels == 0) /* Count N first labels only. */
+			break;
+	}
 
-int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2)
-{
-	return knot_dname_cmp(d1, d2, 0);
+	return len;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_dname_compare_cs(const knot_dname_t *d1, const knot_dname_t *d2)
+int knot_dname_labels(const uint8_t *name, const uint8_t *pkt)
 {
-	return knot_dname_cmp(d1, d2, 1);
-}
+	if (name == NULL)
+		return KNOT_EINVAL;
 
-int knot_dname_compare_non_canon(const knot_dname_t *d1, const knot_dname_t *d2)
-{
-	int ret = memcmp(d1->name, d2->name,
-	                 d1->size > d2->size ? d2->size : d1->size);
-	if (d1->size != d2->size && ret == 0) {
-		return d1->size < d2->size ? -1 : 1;
-	} else {
-		return ret;
+	uint8_t count = 0;
+	while (*name != '\0') {
+		++count;
+		name = knot_wire_next_label((uint8_t *)name, (uint8_t *)pkt);
+		if (!name)
+			return KNOT_EMALF;
 	}
+	return count;
 }
 
 /*----------------------------------------------------------------------------*/
 
-knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
+int knot_dname_align(const uint8_t **d1, uint8_t d1_labels,
+                     const uint8_t **d2, uint8_t d2_labels,
+                     uint8_t *wire)
 {
-	if (d2->size == 0) {
-		return d1;
-	}
-
-	if (knot_dname_is_fqdn(d1)) {
-		return NULL;
-	}
-
-	// allocate new space
-	uint8_t *new_dname = (uint8_t *)malloc(d1->size + d2->size);
-	CHECK_ALLOC_LOG(new_dname, NULL);
-
-	uint8_t *new_labels = (uint8_t *)malloc(d1->label_count
-	                                        + d2->label_count);
-	if (new_labels == NULL) {
-		ERR_ALLOC_FAILED;
-		free(new_dname);
-		return NULL;
-	}
-
-	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);
+	if (d1 == NULL || d2 == NULL)
+		return KNOT_EINVAL;
 
-	dbg_dname_detail("2: copying %d bytes from adress %p to %p\n",
-	                 d2->size, d2->name, new_dname + d1->size);
+	for (unsigned j = d1_labels; j < d2_labels; ++j)
+		*d2 = knot_wire_next_label(*d2, wire);
 
-	memcpy(new_dname + d1->size, d2->name, d2->size);
+	for (unsigned j = d2_labels; j < d1_labels; ++j)
+		*d1 = knot_wire_next_label(*d1, wire);
 
-	// update labels
-	memcpy(new_labels, d1->labels, d1->label_count);
-	for (int i = 0; i < d2->label_count; ++i) {
-		new_labels[d1->label_count + i] = d2->labels[i] + d1->size;
-	}
+	return (d1_labels < d2_labels) ? d1_labels : d2_labels;
+}
 
-	uint8_t *old_labels = d1->labels;
-	d1->labels = new_labels;
-	free(old_labels);
-	d1->label_count += d2->label_count;
+/*----------------------------------------------------------------------------*/
 
-	uint8_t *old_name = d1->name;
-	d1->name = new_dname;
-	free(old_name);
+int knot_dname_lf(uint8_t *dst, const knot_dname_t *src, const uint8_t *pkt)
+{
+	if (dst == NULL || src == NULL)
+		return KNOT_EINVAL;
 
-	d1->size += d2->size;
+	uint8_t *len = dst++;
+	*len = '\0';
+	*dst = '\0';
+	const uint8_t* l = src;
+	/*! \todo This could be made as offsets to pkt? */
+	const uint8_t* lstack[KNOT_DNAME_MAXLEN];
+	const uint8_t **sp = lstack;
+	while(*l != 0) { /* build label stack */
+		*sp++ = l;
+		l = knot_wire_next_label(l, pkt);
+	}
+	while(sp != lstack) {          /* consume stack */
+		l = *--sp; /* fetch rightmost label */
+		memcpy(dst, l+1, *l);  /* write label */
+		dst += *l;
+		*dst++ = '\0';         /* label separator */
+		*len += *l + 1;
+	}
+
+	/* root label special case */
+	if (*len == 0)
+		*len = 1; /* \x00 */
 
-	return d1;
+	return KNOT_EOK;
 }
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index a872a4d87a61c9a27db052ce9a026ffe9e5832b9..b14da8d3a411b3405052c1ce8b6626f8290eae8f 100644
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -30,224 +30,151 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdbool.h>
 
-struct knot_node;
+#include "libknot/consts.h"
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Structure for representing a domain name.
- *
- * Stores the domain name in wire format.
- *
- * \todo Consider restricting to FQDN only (see knot_dname_new_from_str()).
- */
-struct knot_dname {
-	uint8_t *name;		/*!< Wire format of the domain name. */
-	uint8_t *labels;	/*!< Array of labels positions in name. */
-	struct knot_node *node;	/*!< Zone node the domain name belongs to. */
-	uint32_t count;		/*!< Reference counter. */
-	uint8_t size;		/*!< Length of the domain name. */
-	uint8_t label_count;	/*!< Number of labels. */
-};
-
-typedef struct knot_dname knot_dname_t;
-
-/*----------------------------------------------------------------------------*/
+#define KNOT_DNAME_MAX_LENGTH 255
+typedef uint8_t knot_dname_t;
 
 /*!
- * \brief Creates empty dname structure (no name, no owner node).
+ * \brief Check dname on the wire for constraints.
  *
- * \note Newly created dname is referenced, caller is responsible for releasing
- *       it after use.
+ * If the name passes such checks, it is safe to be used in rest of the functions.
  *
- * \return Newly allocated and initialized dname structure.
+ * \param name Name on the wire.
+ * \param endp Name boundary.
+ * \param pkt Wire.
  *
- * \todo Possibly useless.
+ * \retval KNOT_EOK
+ * \retval KNOT_EMALF
+ * \retval KNOT_ESPACE
  */
-knot_dname_t *knot_dname_new();
+int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp,
+                          const uint8_t *pkt);
 
 /*!
- * \brief Creates a dname structure from domain name given in presentation
- *        format.
- *
- * The resulting domain name is stored in wire format, but it may not end with
- * root label (0).
- *
- * \note Newly created dname is referenced, caller is responsible for releasing
- *       it after use.
+ * \brief Parse dname from wire.
  *
- * \param name Domain name in presentation format (labels separated by dots).
- * \param size Size of the domain name (count of characters with all dots).
- * \param node Zone node the domain name belongs to. Set to NULL if not
- *             applicable.
+ * \param pkt Message in wire format.
+ * \param pos Position of the domain name on wire.
+ * \param maxpos Domain name length.
  *
- * \return Newly allocated and initialized dname structure representing the
- *         given domain name.
+ * \return parsed domain name or NULL.
  */
-knot_dname_t *knot_dname_new_from_str(const char *name, unsigned int size,
-                                          struct knot_node *node);
+knot_dname_t *knot_dname_parse(const uint8_t *pkt, size_t *pos, size_t maxpos);
 
 /*!
- * \brief Creates a dname structure from domain name possibly given in
- *        non-presentation format.
- *
- * Works the same as knot_dname_new_from_str but makes sure, that the name
- * is terminated with a dot.
+ * \brief Duplicates the given domain name.
  *
- * \see knot_dname_new_from_str
+ * \param name Domain name to be copied.
  *
+ * \return New domain name which is an exact copy of \a dname.
  */
-knot_dname_t *knot_dname_new_from_nonfqdn_str(const char *name,
-                                              unsigned int size,
-                                              struct knot_node *node);
+knot_dname_t *knot_dname_copy(const knot_dname_t *name);
 
 /*!
- * \brief Creates a dname structure from domain name given in wire format.
+ * \brief Duplicates part of the given domain name.
  *
- * \note The name is copied into the structure.
- * \note If the given name is not a FQDN, the result will be neither.
- * \note Newly created dname is referenced, caller is responsible for releasing
- *       it after use.
+ * \param name Domain name to be copied.
+ * \param len Part length.
  *
- * \param name Domain name in wire format.
- * \param size Size of the domain name in octets.
- * \param node Zone node the domain name belongs to. Set to NULL if not
- *             applicable.
- *
- * \return Newly allocated and initialized dname structure representing the
- *         given domain name.
- *
- * \todo This function does not check if the given data is in correct wire
- *       format at all. It thus creates a invalid domain name, which if passed
- *       e.g. to knot_dname_to_str() may result in crash. Decide whether it
- *       is OK to retain this and check the data in other functions before
- *       calling this one, or if it should verify the given data.
- *
- * \warning Actually, right now this function does not accept non-FQDN dnames.
- *          For some reason there is a check for this.
+ * \return New domain name which is an partial copy of \a dname.
  */
-knot_dname_t *knot_dname_new_from_wire(const uint8_t *name,
-                                           unsigned int size,
-                                           struct knot_node *node);
+knot_dname_t *knot_dname_copy_part(const knot_dname_t *name, unsigned len);
 
 /*!
- * \brief Parse dname from wire.
+ * \brief Copy name to wire as is, no compression pointer expansion will be done.
  *
- * \param wire Message in wire format.
- * \param pos Position of the domain name on wire.
- * \param size Domain name length.
- * \param node Zone node the domain name belongs to. Set to NULL if not
- *             applicable.
- * \param dname Destination dname (will allocate new when NULL).
+ * \param dst Destination wire.
+ * \param src Source name.
+ * \param maxlen Maximum wire length.
  *
- * \return parsed domain name or NULL.
+ * \return number of bytes written
+ *
+ * \todo For most use cases this could be replaced by counting the length
+ *       and simple memcpy.
  */
-knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
-                                         size_t *pos, size_t size,
-                                         struct knot_node *node,
-                                         knot_dname_t *dname);
+int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen);
 
 /*!
- * \brief Duplicates the given domain name.
+ * \brief Write unpacked name (i.e. compression pointers expanded)
  *
- * \note Copied dname referense count is reset to 1, caller is responsible
- *       for releasing it after use.
+ * \note The function is very similar to the knot_dname_to_wire(), except
+ *       it expands compression pointers. E.g. you want to use knot_dname_unpack()
+ *       if you copy a dname from incoming packet to some persistent storage.
+ *       And you want to use knot_dname_to_wire() if you know the name is not
+ *       compressed or you want to copy it 1:1.
  *
- * \param dname Domain name to be copied.
+ * \param dst Destination wire.
+ * \param src Source name.
+ * \param maxlen Maximum destination wire size.
+ * \param pkt Name packet wire (for compression pointers).
  *
- * \return New domain name which is an exact copy of \a dname.
+ * \return number of bytes written
  */
-knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname);
+int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src,
+                      size_t maxlen, const uint8_t *pkt);
 
 /*!
  * \brief Converts the given domain name to string representation.
  *
  * \note Allocates new memory, remember to free it.
  *
- * \param dname Domain name to be converted.
+ * \todo The function doesn't process escaped characters like \DDD or \X.
+ *
+ * \param name Domain name to be converted.
  *
  * \return 0-terminated string representing the given domain name in
  *         presentation format.
  */
-char *knot_dname_to_str(const knot_dname_t *dname);
-
-int knot_dname_to_lower(knot_dname_t *dname);
-
-int knot_dname_to_lower_copy(const knot_dname_t *dname, char *name,
-                             size_t size);
+char *knot_dname_to_str(const knot_dname_t *name);
 
 /*!
- * \brief Returns the domain name in wire format.
- *
- * \param dname Domain name.
+ * \brief Creates a dname structure from domain name given in presentation
+ *        format.
  *
- * \return Wire format of the domain name.
- */
-const uint8_t *knot_dname_name(const knot_dname_t *dname);
-
-/*!
- * \brief Returns size of the given domain name.
+ * The resulting FQDN is stored in the wire format.
  *
- * \param dname Domain name to get the size of.
+ * \param name Domain name in presentation format (labels separated by dots).
+ * \param len Size of the domain name (count of characters with all dots).
  *
- * \return Size of the domain name in wire format in octets.
+ * \return new name or NULL
  */
-unsigned int knot_dname_size(const knot_dname_t *dname);
+knot_dname_t *knot_dname_from_str(const char *name, unsigned len);
 
 /*!
- * \brief Returns size of a part of domain name.
+ * \brief Convert name to lowercase.
  *
- * \param dname Domain name.
- * \param labels Count of labels to get the size of (counted from left).
+ * \note Name must not be compressed.
  *
- * \return Size of first \a labels labels of \a dname, counted from left.
- */
-uint8_t knot_dname_size_part(const knot_dname_t *dname, int labels);
-
-/*!
- * \brief Returns the zone node the domain name belongs to.
- *
- * \param dname Domain name to get the zone node of.
+ * \param name Domain name to be converted.
  *
- * \return Zone node the domain name belongs to or NULL if none.
+ * \return KNOT_EOK
+ * \retval KNOT_EINVAL
  */
-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_update_node(knot_dname_t *dname);
-
-void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
+int knot_dname_to_lower(knot_dname_t *name);
 
 /*!
- * \brief Checks if the given domain name is a fully-qualified domain name.
+ * \brief Returns size of the given domain name.
  *
- * \param dname Domain name to check.
+ * \param name Domain name to get the size of.
  *
- * \retval <> 0 if \a dname is a FQDN.
- * \retval 0 otherwise.
+ * \retval sze of the domain name.
+ * \retval KNOT_EINVAL
  */
-int knot_dname_is_fqdn(const knot_dname_t *dname);
+int knot_dname_size(const knot_dname_t *name);
 
 /*!
- * \brief Creates new domain name by removing leftmost label from \a dname.
- *
- * \note Newly created dname reference count is set to 1, caller is responsible
- *        for releasing it after use.
- *
- * \param dname Domain name to remove the first label from.
+ * \brief Returns wire size of the given domain name (expaned compression ptrs).
  *
- * \return New domain name with the same labels as \a dname, except for the
- *         leftmost label, which is removed.
- */
-knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname);
-
-/*!
- * \brief Removes leftmost label from \a dname.
+ * \param name Domain name to get the size of.
+ * \param pkt Related packet (or NULL if unpacked)
  *
- * \param dname Domain name to remove the first label from.
+ * \retval size of the domain name
+ * \retval KNOT_EINVAL
  */
-void knot_dname_left_chop_no_copy(knot_dname_t *dname);
+int knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt);
 
 /*!
  * \brief Checks if one domain name is a subdomain of other.
@@ -255,69 +182,46 @@ void knot_dname_left_chop_no_copy(knot_dname_t *dname);
  * \param sub Domain name to be the possible subdomain.
  * \param domain Domain name to be the possible parent domain.
  *
- * \retval <> 0 if \a sub is a subdomain of \a domain.
- * \retval 0 otherwise.
+ * \retval true \a sub is a subdomain of \a domain.
+ * \retval false otherwise.
  */
-int knot_dname_is_subdomain(const knot_dname_t *sub,
-                              const knot_dname_t *domain);
+bool knot_dname_is_sub(const knot_dname_t *sub, const knot_dname_t *domain);
 
 /*!
  * \brief Checks if the domain name is a wildcard.
  *
- * \param dname Domain name to check.
+ * \param name Domain name to check.
  *
- * \retval <> 0 if \a dname is a wildcard domain name.
- * \retval 0 otherwise.
+ * \retval true if \a dname is a wildcard domain name.
+ * \retval false otherwise.
  */
-int knot_dname_is_wildcard(const knot_dname_t *dname);
+bool knot_dname_is_wildcard(const knot_dname_t *name);
 
 /*!
  * \brief Returns the number of labels common for the two domain names (counted
  *        from the rightmost label.
  *
- * \param dname1 First domain name.
- * \param dname2 Second domain name.
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
  *
  * \return Number of labels common for the two domain names.
  */
-int knot_dname_matched_labels(const knot_dname_t *dname1,
-                                const knot_dname_t *dname2);
-
-/*!
- * \brief Returns the number of labels in the domain name.
- *
- * \param dname Domain name to get the label count of.
- *
- * \return Number of labels in \a dname.
- *
- * \todo Find out if this counts the root label also.
- */
-int knot_dname_label_count(const knot_dname_t *dname);
-
-/*!
- * \brief Returns the size of the requested label in the domain name.
- *
- * \param dname Domain name to get the label size from.
- * \param i Index of the label (0 is the leftmost label).
- *
- * \return Size of \a i-th label in \a dname (counted from left).
- */
-uint8_t knot_dname_label_size(const knot_dname_t *dname, int i);
+int knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2);
 
 /*!
  * \brief Replaces the suffix of given size in one domain name with other domain
  *        name.
  *
- * \param dname Domain name where to replace the suffix.
- * \param size Size of the suffix to be replaced.
+ * \param name Domain name where to replace the suffix.
+ * \param labels Size of the suffix to be replaced.
  * \param suffix New suffix to be used as a replacement.
  *
  * \return New domain name created by replacing suffix of \a dname of size
  *         \a size with \a suffix.
  */
-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 *name,
+                                        unsigned labels,
+                                        const knot_dname_t *suffix);
 
 /*!
  * \brief Destroys the given domain name.
@@ -328,12 +232,12 @@ knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname,
  *
  * Sets the given pointer to NULL.
  *
- * \param dname Domain name to be destroyed.
+ * \param name Domain name to be destroyed.
  */
-void knot_dname_free(knot_dname_t **dname);
+void knot_dname_free(knot_dname_t **name);
 
 /*!
- * \brief Compares two domain names (case insensitive).
+ * \brief Compares two domain names (case sensitive).
  *
  * \param d1 First domain name.
  * \param d2 Second domain name.
@@ -342,7 +246,27 @@ void knot_dname_free(knot_dname_t **dname);
  * \retval > 0 if \a d1 goes after \a d2 in canonical order.
  * \retval 0 if the domain names are identical.
  */
-int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2);
+int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Compare domain name by labels.
+ *
+ * \todo No case insensitivity, flags...
+ *
+ * \warning Since it would be hard to catch errors, because negative value
+ *          is also a good result, there are assertions that expect neither
+ *          d1 or d2 to be NULL.
+ *
+ * \param d1 Domain name.
+ * \param d2 Domain name.
+ * \param pkt Packet wire related to names (or NULL).
+ *
+ * \retval 0 if they are identical
+ * \retval 1 if d1 > d2
+ * \retval -1 if d1 < d2
+ */
+int knot_dname_cmp_wire(const knot_dname_t *d1, const knot_dname_t *d2,
+                        const uint8_t *pkt);
 
 /*!
  * \brief Compares two domain names (case sensitive).
@@ -350,52 +274,76 @@ int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2);
  * \param d1 First domain name.
  * \param d2 Second domain name.
  *
- * \retval < 0 if \a d1 goes before \a d2 in canonical order.
- * \retval > 0 if \a d1 goes after \a d2 in canonical order.
- * \retval 0 if the domain names are identical.
+ * \retval true if the domain names are identical
+ * \retval false if the domain names are NOT identical
  */
-int knot_dname_compare_cs(const knot_dname_t *d1, const knot_dname_t *d2);
-int knot_dname_compare_non_canon(const knot_dname_t *d1,
-                                 const knot_dname_t *d2);
+bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2);
 
 /*!
  * \brief Concatenates two domain names.
  *
- * \note Member \a node is ignored, i.e. preserved.
- *
  * \param d1 First domain name (will be modified).
  * \param d2 Second domain name (will not be modified).
  *
- * \return The concatenated domain name (i.e. modified \a d1) or NULL if
- *         the operation is not valid (e.g. \a d1 is a FQDN).
+ * \return The concatenated domain name or NULL
  */
 knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2);
 
 /*!
- * \brief Increment reference counter for dname.
+ * \brief Cound length of the N first labels.
+ *
+ * \param name Domain name.
+ * \param nlabels N first labels.
+ * \param pkt Related packet (or NULL if not compressed).
+ *
+ * \retval length of the prefix
+ */
+int knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt);
+
+/*!
+ * \brief Return number of labels in the domain name.
  *
- * Function makes shallow copy (reference).
+ * Terminal nullbyte is not counted.
  *
- * \param dname Referenced dname.
+ * \param name Domain name.
+ * \param pkt Related packet (or NULL if not compressed).
  */
-static inline void knot_dname_retain(knot_dname_t *dname) {
-	if (dname) {
-		__sync_add_and_fetch(&dname->count, 1);
-	}
-}
+int knot_dname_labels(const uint8_t *name, const uint8_t *pkt);
 
 /*!
- * \brief Decrement reference counter for dname.
+ * \brief Align name end-to-end and return number of common suffix labels.
+ *
+ * \param d1 Domain name.
+ * \param d1_labels Number of labels in d1.
+ * \param d2 Domain name.
+ * \param d2_labels Number of labels in d2.
+ * \param wire Packet wire related to names (or NULL).
+ */
+int knot_dname_align(const uint8_t **d1, uint8_t d1_labels,
+                     const uint8_t **d2, uint8_t d2_labels,
+                     uint8_t *wire);
+
+/*!
+ * \brief Convert domain name from wire to lookup format.
+ *
+ * Formats names from rightmost label to the leftmost, separated by the lowest
+ * possible character (\x00). Sorting such formatted names also gives
+ * correct canonical order (for NSEC/NSEC3).
+ *
+ * Example:
+ * Name: lake.example.com. Wire: \x04lake\x07example\x03com\x00
+ * Lookup format com\x00example\x00lake\x00
+ *
+ * Maximum length of such a domain name is KNOT_DNAME_MAXLEN characters.
+ *
+ * \param dst Memory to store converted name into.
+ * \param src Source domain name.
+ * \param pkt Source name packet (NULL if not any).
  *
- * \param dname Referenced dname.
+ * \retval KNOT_EOK if successful
+ * \retval KNOT_EINVAL on invalid parameters
  */
-static inline void knot_dname_release(knot_dname_t *dname) {
-	if (dname) {
-		if (__sync_sub_and_fetch(&dname->count, 1) == 0) {
-			knot_dname_free(&dname);
-		}
-	}
-}
+int knot_dname_lf(uint8_t *dst, const knot_dname_t *src, const uint8_t *pkt);
 
 #endif /* _KNOT_DNAME_H_ */
 
diff --git a/src/libknot/dnssec/algorithm.c b/src/libknot/dnssec/algorithm.c
new file mode 100644
index 0000000000000000000000000000000000000000..4a8de2d5073a10b40abdd7865fca3f62f7cb7e99
--- /dev/null
+++ b/src/libknot/dnssec/algorithm.c
@@ -0,0 +1,50 @@
+/*  Copyright (C) 2013 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 <stdbool.h>
+#include <stdint.h>
+#include "libknot/dnssec/algorithm.h"
+
+/*!
+ * \brief Check if algorithm is supported for zone signing.
+ */
+bool knot_dnssec_algorithm_is_zonesign(uint8_t algorithm, bool nsec3_enabled)
+{
+	switch (algorithm) {
+	// NSEC only
+	case KNOT_DNSSEC_ALG_DSA:
+	case KNOT_DNSSEC_ALG_RSASHA1:
+		return !nsec3_enabled;
+
+	// NSEC3 only
+	case KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1:
+	case KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1:
+		return true; // allow even with NSEC
+
+	// both NSEC and NSEC3
+	case KNOT_DNSSEC_ALG_RSASHA256:
+	case KNOT_DNSSEC_ALG_RSASHA512:
+	case KNOT_DNSSEC_ALG_ECC_GOST:
+	case KNOT_DNSSEC_ALG_ECDSAP256SHA256:
+	case KNOT_DNSSEC_ALG_ECDSAP384SHA384:
+		return true;
+
+	// unsupported or unknown
+	default:
+		return false;
+	}
+}
diff --git a/src/libknot/dnssec/algorithm.h b/src/libknot/dnssec/algorithm.h
new file mode 100644
index 0000000000000000000000000000000000000000..c08836e6fbae74f84ba0e2fbf6b07e1b049ce441
--- /dev/null
+++ b/src/libknot/dnssec/algorithm.h
@@ -0,0 +1,74 @@
+/*  Copyright (C) 2013 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 algorithm.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \brief DNSSEC key algorithm utilities.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_ALGORITHM_H_
+#define _KNOT_DNSSEC_ALGORITHM_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*!
+ * \brief DNSSEC algorithm numbers.
+ *
+ * http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml
+ */
+typedef enum {
+	KNOT_DNSSEC_ALG_RSAMD5             =  1,
+	KNOT_DNSSEC_ALG_DH                 =  2,
+	KNOT_DNSSEC_ALG_DSA                =  3,
+
+	KNOT_DNSSEC_ALG_RSASHA1            =  5,
+	KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1     =  6,
+	KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1 =  7,
+	KNOT_DNSSEC_ALG_RSASHA256          =  8,
+
+	KNOT_DNSSEC_ALG_RSASHA512          = 10,
+
+	KNOT_DNSSEC_ALG_ECC_GOST           = 12,
+	KNOT_DNSSEC_ALG_ECDSAP256SHA256    = 13,
+	KNOT_DNSSEC_ALG_ECDSAP384SHA384    = 14
+} knot_dnssec_algorithm_t;
+
+/*!
+ * \brief NSEC3 hash algorithm numbers.
+ */
+typedef enum {
+	KNOT_NSEC3_ALGORITHM_SHA1 = 1
+} knot_nsec3_hash_algorithm_t;
+
+/*!
+ * \brief Check if algorithm is supported for zone signing.
+ *
+ * \param algorithm      Algorithm identification.
+ * \param nsec3_enabled  NSEC3 enabled for signed zone.
+ *
+ * \return Given algorithm is allowed for zone signing.
+ */
+bool knot_dnssec_algorithm_is_zonesign(uint8_t algorithm, bool nsec3_enabled);
+
+#endif // _KNOT_DNSSEC_ALGORITHM_H_
+
+/*! @} */
diff --git a/src/libknot/sign/key.c b/src/libknot/dnssec/key.c
similarity index 59%
rename from src/libknot/sign/key.c
rename to src/libknot/dnssec/key.c
index d721e642d7fa3c34ba264147cd32ac6047b239fb..fc673d5c646bcd96eb50541d5f3d4d307543029b 100644
--- a/src/libknot/sign/key.c
+++ b/src/libknot/dnssec/key.c
@@ -17,22 +17,24 @@
 #include <config.h>
 #include <assert.h>
 #include <ctype.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#include <time.h>
 
 #include "binary.h"
 #include "common.h"
 #include "common/getline.h"
 #include "dname.h"
-#include "sign/key.h"
-#include "sign/sig0.h"
+#include "key.h"
+#include "sig0.h"
 #include "tsig.h"
-#include "zscanner/scanner.h"
+#include "zscanner/zscanner.h"
 
 /*!
  * \brief Calculates keytag for RSA/MD5 algorithm.
@@ -53,6 +55,10 @@ static uint16_t keytag_rsa_md5(const uint8_t *rdata, uint16_t rdata_len)
  */
 uint16_t knot_keytag(const uint8_t *rdata, uint16_t rdata_len)
 {
+	if (!rdata || rdata_len < 4) {
+		return 0;
+	}
+
 	uint32_t ac = 0; /* assumed to be 32 bits or larger */
 
 	if (rdata[3] == 1) {
@@ -87,9 +93,9 @@ static char *strndup_with_suffix(const char *base, int length, char *suffix)
 	return result;
 }
 
-static void key_scan_noop(const scanner_t *s)
+static void key_scan_set_done(const scanner_t *s)
 {
-	UNUSED(s);
+	*((bool *)s->data) = true;
 }
 
 /*!
@@ -97,57 +103,76 @@ static void key_scan_noop(const scanner_t *s)
  */
 static int get_key_info_from_public_key(const char *filename,
                                         knot_dname_t **name,
-                                        uint16_t *keytag)
+                                        knot_binary_t *rdata)
 {
-	if (!filename || !name || !keytag)
+	if (!filename || !name || !rdata) {
 		return KNOT_EINVAL;
+	}
 
 	FILE *keyfile = fopen(filename, "r");
-	if (!keyfile)
+	if (!keyfile) {
 		return KNOT_KEY_EPUBLIC_KEY_OPEN;
+	}
 
-	scanner_t *scanner = scanner_create(filename);
+	scanner_t *scanner = scanner_create(filename, ".", KNOT_CLASS_IN, 0,
+	                                    NULL, NULL, NULL);
 	if (!scanner) {
 		fclose(keyfile);
 		return KNOT_ENOMEM;
 	}
 
-	scanner->process_record = key_scan_noop;
-	scanner->process_error = key_scan_noop;
+	bool scan_done = false;
+	bool last_block = false;
+
+	scanner->process_record = key_scan_set_done;
+	scanner->process_error = key_scan_set_done;
 	scanner->default_ttl = 0;
 	scanner->default_class = KNOT_CLASS_IN;
 	scanner->zone_origin[0] = '\0';
 	scanner->zone_origin_length = 1;
+	scanner->data = (void *)&scan_done;
 
 	char *buffer = NULL;
 	size_t buffer_size;
-	ssize_t read = knot_getline(&buffer, &buffer_size, keyfile);
+	ssize_t read;
+	int result = 0;
 
+	while (!scan_done && !last_block && result == 0) {
+		read = knot_getline(&buffer, &buffer_size, keyfile);
+		if (read <= 0) {
+			last_block = true;
+			read = 0;
+		}
+		result = scanner_process(buffer, buffer + read, last_block,
+		                         scanner);
+	}
+
+	free(buffer);
 	fclose(keyfile);
 
-	if (read == -1) {
+	if (scanner->r_type != KNOT_RRTYPE_DNSKEY) {
 		scanner_free(scanner);
 		return KNOT_KEY_EPUBLIC_KEY_INVALID;
 	}
 
-	if (scanner_process(buffer, buffer + read, true, scanner) != 0) {
-		free(buffer);
+	knot_dname_t *owner = knot_dname_copy(scanner->r_owner);
+	if (!owner) {
 		scanner_free(scanner);
-		return KNOT_KEY_EPUBLIC_KEY_INVALID;
+		return KNOT_ENOMEM;
 	}
+	knot_dname_to_lower(owner);
 
-	free(buffer);
-
-	knot_dname_t *owner = knot_dname_new_from_wire(scanner->r_owner,
-	                                               scanner->r_owner_length,
-	                                               NULL);
-	if (!owner) {
+	knot_binary_t rdata_bin = { 0 };
+	result = knot_binary_from_string(scanner->r_data, scanner->r_data_length,
+	                                 &rdata_bin);
+	if (result != KNOT_EOK) {
 		scanner_free(scanner);
-		return KNOT_ENOMEM;
+		knot_dname_free(&owner);
+		return result;
 	}
 
 	*name = owner;
-	*keytag = knot_keytag(scanner->r_data, scanner->r_data_length);
+	*rdata = rdata_bin;
 
 	scanner_free(scanner);
 
@@ -196,16 +221,14 @@ static int get_key_filenames(const char *input, char **pubname, char **privname)
 }
 
 /*!
- * \brief Handle storing of string type key parameter.
+ * \brief Handle storing of base64 encoded data key parameter.
  */
-static int key_param_string(const void *save_to, char *value)
+static int key_param_base64(const void *save_to, char *value)
 {
-	char **parameter = (char **)save_to;
+	knot_binary_t *parameter = (knot_binary_t *)save_to;
+	knot_binary_free(parameter);
 
-	free(*parameter);
-	*parameter = strdup(value);
-
-	return *parameter ? KNOT_EOK : KNOT_ENOMEM;
+	return knot_binary_from_base64(value, parameter);
 }
 
 /*!
@@ -226,6 +249,21 @@ static int key_param_int(const void *save_to, char *value)
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Handle storing of key lifetime parameter.
+ */
+static int key_param_time(const void *save_to, char *value)
+{
+	time_t *parameter = (time_t *)save_to;
+
+	struct tm parsed = { 0 };
+	if (!strptime(value, "%Y%m%d%H%M%S", &parsed))
+		return KNOT_EINVAL;
+
+	*parameter = timegm(&parsed);
+	return KNOT_EOK;
+}
+
 /*!
  * \brief Describes private key parameter used in key_parameters.
  */
@@ -239,27 +277,26 @@ struct key_parameter {
 
 /*!
  * \brief Table of know attributes in private key file.
- *
- * \todo Save some space, save base64 encoded strings as binary data.
  */
 static const struct key_parameter key_parameters[] = {
 	{ "Algorithm",       key_offset(algorithm),        key_param_int },
-	{ "Key",             key_offset(secret),           key_param_string },
-	{ "Modulus",         key_offset(modulus),          key_param_string },
-	{ "PublicExponent",  key_offset(public_exponent),  key_param_string },
-	{ "PrivateExponent", key_offset(private_exponent), key_param_string },
-	{ "Prime1",          key_offset(prime_one),        key_param_string },
-	{ "Prime2",          key_offset(prime_two),        key_param_string },
-	{ "Exponent1",       key_offset(exponent_one),     key_param_string },
-	{ "Exponent2",       key_offset(exponent_two),     key_param_string },
-	{ "Coefficient",     key_offset(coefficient),      key_param_string },
-	{ "Prime(p)",        key_offset(prime),            key_param_string },
-	{ "Subprime(q)",     key_offset(subprime),         key_param_string },
-	{ "Generator(g)",    key_offset(generator),        key_param_string },
-	{ "Base(g)",         key_offset(base),             key_param_string },
-	{ "Private_value(x)",key_offset(private_value),    key_param_string },
-	{ "Public_value(y)", key_offset(public_value),     key_param_string },
-	{ "PrivateKey",      key_offset(private_key),      key_param_string },
+	{ "Key",             key_offset(secret),           key_param_base64 },
+	{ "Modulus",         key_offset(modulus),          key_param_base64 },
+	{ "PublicExponent",  key_offset(public_exponent),  key_param_base64 },
+	{ "PrivateExponent", key_offset(private_exponent), key_param_base64 },
+	{ "Prime1",          key_offset(prime_one),        key_param_base64 },
+	{ "Prime2",          key_offset(prime_two),        key_param_base64 },
+	{ "Exponent1",       key_offset(exponent_one),     key_param_base64 },
+	{ "Exponent2",       key_offset(exponent_two),     key_param_base64 },
+	{ "Coefficient",     key_offset(coefficient),      key_param_base64 },
+	{ "Prime(p)",        key_offset(prime),            key_param_base64 },
+	{ "Subprime(q)",     key_offset(subprime),         key_param_base64 },
+	{ "Base(g)",         key_offset(base),             key_param_base64 },
+	{ "Private_value(x)",key_offset(private_value),    key_param_base64 },
+	{ "Public_value(y)", key_offset(public_value),     key_param_base64 },
+	{ "PrivateKey",      key_offset(private_key),      key_param_base64 },
+	{ "Activate",        key_offset(time_activate),    key_param_time },
+	{ "Inactive",        key_offset(time_inactive),    key_param_time },
 	{ NULL }
 };
 
@@ -310,8 +347,9 @@ static int parse_keyfile_line(knot_key_params_t *key_params,
  */
 int knot_load_key_params(const char *filename, knot_key_params_t *key_params)
 {
-	assert(filename);
-	assert(key_params);
+	if (!filename || !key_params) {
+		return KNOT_EINVAL;
+	}
 
 	int result;
 	char *public_key = NULL;
@@ -322,9 +360,9 @@ int knot_load_key_params(const char *filename, knot_key_params_t *key_params)
 		return result;
 	}
 
-	knot_dname_t *name;
-	uint16_t keytag;
-	result = get_key_info_from_public_key(public_key, &name, &keytag);
+	knot_dname_t *name = NULL;
+	knot_binary_t rdata = { 0 };
+	result = get_key_info_from_public_key(public_key, &name, &rdata);
 	if (result != KNOT_EOK) {
 		free(public_key);
 		free(private_key);
@@ -335,12 +373,14 @@ int knot_load_key_params(const char *filename, knot_key_params_t *key_params)
 	if (!fp) {
 		free(public_key);
 		free(private_key);
-		knot_dname_release(name);
+		knot_dname_free(&name);
 		return KNOT_KEY_EPRIVATE_KEY_OPEN;
 	}
 
 	key_params->name = name;
-	key_params->keytag = keytag;
+	key_params->rdata = rdata;
+	key_params->keytag = knot_keytag(rdata.data, rdata.size);
+	key_params->flags = knot_wire_read_u16(rdata.data);
 
 	char *buffer = NULL;
 	size_t buffer_size = 0;
@@ -363,21 +403,6 @@ int knot_load_key_params(const char *filename, knot_key_params_t *key_params)
 	return result;
 }
 
-static int copy_string_if_set(const char *src, char **dst)
-{
-	if (src != NULL) {
-		*dst = strdup(src);
-
-		if (*dst == NULL) {
-			return -1;
-		}
-	} else {
-		*dst = NULL;
-	}
-
-	return 0;
-}
-
 int knot_copy_key_params(const knot_key_params_t *src, knot_key_params_t *dst)
 {
 	if (src == NULL || dst == NULL) {
@@ -387,7 +412,7 @@ int knot_copy_key_params(const knot_key_params_t *src, knot_key_params_t *dst)
 	int ret = 0;
 
 	if (src->name != NULL) {
-		dst->name = knot_dname_deep_copy(src->name);
+		dst->name = knot_dname_copy(src->name);
 		if (dst->name == NULL) {
 			ret += -1;
 		}
@@ -396,25 +421,24 @@ int knot_copy_key_params(const knot_key_params_t *src, knot_key_params_t *dst)
 	dst->algorithm = src->algorithm;
 	dst->keytag = src->keytag;
 
-	ret += copy_string_if_set(src->secret, &dst->secret);
+	ret += knot_binary_dup(&src->secret, &dst->secret);
 
-	ret += copy_string_if_set(src->modulus, &dst->modulus);
-	ret += copy_string_if_set(src->public_exponent, &dst->public_exponent);
-	ret += copy_string_if_set(src->private_exponent, &dst->private_exponent);
-	ret += copy_string_if_set(src->prime_one, &dst->prime_one);
-	ret += copy_string_if_set(src->prime_two, &dst->prime_two);
-	ret += copy_string_if_set(src->exponent_one, &dst->exponent_one);
-	ret += copy_string_if_set(src->exponent_two, &dst->exponent_two);
-	ret += copy_string_if_set(src->coefficient, &dst->coefficient);
+	ret += knot_binary_dup(&src->modulus, &dst->modulus);
+	ret += knot_binary_dup(&src->public_exponent, &dst->public_exponent);
+	ret += knot_binary_dup(&src->private_exponent, &dst->private_exponent);
+	ret += knot_binary_dup(&src->prime_one, &dst->prime_one);
+	ret += knot_binary_dup(&src->prime_two, &dst->prime_two);
+	ret += knot_binary_dup(&src->exponent_one, &dst->exponent_one);
+	ret += knot_binary_dup(&src->exponent_two, &dst->exponent_two);
+	ret += knot_binary_dup(&src->coefficient, &dst->coefficient);
 
-	ret += copy_string_if_set(src->prime, &dst->prime);
-	ret += copy_string_if_set(src->generator, &dst->generator);
-	ret += copy_string_if_set(src->subprime, &dst->subprime);
-	ret += copy_string_if_set(src->base, &dst->base);
-	ret += copy_string_if_set(src->private_value, &dst->private_value);
-	ret += copy_string_if_set(src->public_value, &dst->public_value);
+	ret += knot_binary_dup(&src->prime, &dst->prime);
+	ret += knot_binary_dup(&src->subprime, &dst->subprime);
+	ret += knot_binary_dup(&src->base, &dst->base);
+	ret += knot_binary_dup(&src->private_value, &dst->private_value);
+	ret += knot_binary_dup(&src->public_value, &dst->public_value);
 
-	ret += copy_string_if_set(src->private_key, &dst->private_key);
+	ret += knot_binary_dup(&src->private_key, &dst->private_key);
 
 	if (ret < 0) {
 		knot_free_key_params(dst);
@@ -429,30 +453,31 @@ int knot_copy_key_params(const knot_key_params_t *src, knot_key_params_t *dst)
  */
 int knot_free_key_params(knot_key_params_t *key_params)
 {
-	assert(key_params);
+	if (!key_params) {
+		return KNOT_EINVAL;
+	}
 
-	if (key_params->name)
-		knot_dname_release(key_params->name);
+	knot_dname_free(&key_params->name);
+	knot_binary_free(&key_params->rdata);
 
-	free(key_params->secret);
+	knot_binary_free(&key_params->secret);
 
-	free(key_params->modulus);
-	free(key_params->public_exponent);
-	free(key_params->private_exponent);
-	free(key_params->prime_one);
-	free(key_params->prime_two);
-	free(key_params->exponent_one);
-	free(key_params->exponent_two);
-	free(key_params->coefficient);
+	knot_binary_free(&key_params->modulus);
+	knot_binary_free(&key_params->public_exponent);
+	knot_binary_free(&key_params->private_exponent);
+	knot_binary_free(&key_params->prime_one);
+	knot_binary_free(&key_params->prime_two);
+	knot_binary_free(&key_params->exponent_one);
+	knot_binary_free(&key_params->exponent_two);
+	knot_binary_free(&key_params->coefficient);
 
-	free(key_params->prime);
-	free(key_params->generator);
-	free(key_params->subprime);
-	free(key_params->base);
-	free(key_params->private_value);
-	free(key_params->public_value);
+	knot_binary_free(&key_params->prime);
+	knot_binary_free(&key_params->subprime);
+	knot_binary_free(&key_params->base);
+	knot_binary_free(&key_params->private_value);
+	knot_binary_free(&key_params->public_value);
 
-	free(key_params->private_key);
+	knot_binary_free(&key_params->private_key);
 
 	memset(key_params, '\0', sizeof(knot_key_params_t));
 
@@ -464,13 +489,18 @@ int knot_free_key_params(knot_key_params_t *key_params)
  */
 knot_key_type_t knot_get_key_type(const knot_key_params_t *key_params)
 {
-	assert(key_params);
+	if (!key_params) {
+		return KNOT_EINVAL;
+	}
 
-	if (key_params->secret) {
+	if (key_params->secret.size > 0) {
 		return KNOT_KEY_TSIG;
 	}
 
-	if (key_params->modulus || key_params->prime || key_params->private_key) {
+	if (key_params->modulus.size > 0 ||
+	    key_params->prime.size > 0 ||
+	    key_params->private_key.size > 0
+	) {
 		return KNOT_KEY_DNSSEC;
 	}
 
@@ -480,68 +510,55 @@ knot_key_type_t knot_get_key_type(const knot_key_params_t *key_params)
 }
 
 /*!
- * \brief Creates TSIG key from function arguments.
- *
- * \param name       Key name (aka owner name).
- * \param algorithm  Algorithm number.
- * \param b64secret  Shared secret encoded in Base64.
- * \param key        Output TSIG key.
- *
- * \return Error code, KNOT_EOK when succeeded.
+ * \brief Creates TSIG key.
  */
-static int knot_tsig_create_key_from_args(knot_dname_t *name, int algorithm,
-                                          const char *b64secret,
-                                          knot_tsig_key_t *key)
+int knot_tsig_create_key(const char *name, int algorithm,
+                         const char *b64secret, knot_tsig_key_t *key)
 {
-	if (!name || !b64secret || !key)
+	if (!name || !b64secret || !key) {
 		return KNOT_EINVAL;
+	}
+
+	knot_dname_t *dname;
+	dname = knot_dname_from_str(name, strlen(name));
+	if (!dname) {
+		return KNOT_ENOMEM;
+	}
 
 	knot_binary_t secret;
 	int result = knot_binary_from_base64(b64secret, &secret);
-
-	if (result != KNOT_EOK)
+	if (result != KNOT_EOK) {
+		knot_dname_free(&dname);
 		return result;
+	}
 
-	knot_dname_retain(name);
-
-	key->name = name;
-	key->secret = secret;
+	key->name = dname;
 	key->algorithm = algorithm;
+	key->secret = secret;
 
 	return KNOT_EOK;
 }
 
-/*!
- * \brief Creates TSIG key.
- */
-int knot_tsig_create_key(const char *name, int algorithm,
-                         const char *b64secret, knot_tsig_key_t *key)
-{
-	knot_dname_t *dname;
-	dname = knot_dname_new_from_nonfqdn_str(name, strlen(name), NULL);
-	if (!dname)
-		return KNOT_ENOMEM;
-
-	int res;
-	res = knot_tsig_create_key_from_args(dname, algorithm, b64secret, key);
-
-	knot_dname_release(dname);
-
-	return res;
-}
-
-
 /*!
  * \brief Creates TSIG key from key parameters.
  */
 int knot_tsig_key_from_params(const knot_key_params_t *params,
                               knot_tsig_key_t *key)
 {
-	if (!params)
+	if (!params || !params->name || params->secret.size == 0) {
 		return KNOT_EINVAL;
+	}
+
+	int result = knot_binary_dup(&params->secret, &key->secret);
+	if (result != KNOT_EOK) {
+		return result;
+	}
 
-	return knot_tsig_create_key_from_args(params->name, params->algorithm,
-					      params->secret, key);
+	key->name = knot_dname_copy(params->name);
+
+	key->algorithm = params->algorithm;
+
+	return KNOT_EOK;
 }
 
 /*!
@@ -549,11 +566,11 @@ int knot_tsig_key_from_params(const knot_key_params_t *params,
  */
 int knot_tsig_key_free(knot_tsig_key_t *key)
 {
-	if (!key)
+	if (!key) {
 		return KNOT_EINVAL;
+	}
 
-	knot_dname_release(key->name);
-
+	knot_dname_free(&key->name);
 	knot_binary_free(&key->secret);
 	memset(key, '\0', sizeof(knot_tsig_key_t));
 
diff --git a/src/libknot/sign/key.h b/src/libknot/dnssec/key.h
similarity index 80%
rename from src/libknot/sign/key.h
rename to src/libknot/dnssec/key.h
index 54d110109874ff09053c1d80b0df518baae2509e..d2880b50c47411a7ef081b760ecbc16f216cefac 100644
--- a/src/libknot/sign/key.h
+++ b/src/libknot/dnssec/key.h
@@ -18,17 +18,19 @@
  *
  * \author Jan Vcelak <jan.vcelak@nic.cz>
  *
- * \brief Interface for loding of keys.
+ * \brief Interface for loading of DNSSEC keys.
  *
  * \addtogroup dnssec
  * @{
  */
 
-#ifndef _KNOT_SIGN_KEY_H_
-#define _KNOT_SIGN_KEY_H_
+#ifndef _KNOT_DNSSEC_KEY_H_
+#define _KNOT_DNSSEC_KEY_H_
 
 #include <stdint.h>
+#include <time.h>
 #include "dname.h"
+#include "binary.h"
 #include "tsig.h"
 
 /*----------------------------------------------------------------------------*/
@@ -36,43 +38,52 @@
 /*!
  * \brief Key attributes loaded from keyfile.
  */
-struct knot_key_params {
+typedef struct {
+	// common parameters
 	knot_dname_t *name;
+	knot_binary_t rdata;
 	int algorithm;
 	uint16_t keytag;
-	// parameters for symmetric cryptography
-	char *secret;
-	// parameters for public key cryptography
-	// RSA
-	char *modulus;
-	char *public_exponent;
-	char *private_exponent;
-	char *prime_one;
-	char *prime_two;
-	char *exponent_one;
-	char *exponent_two;
-	char *coefficient;
-	// DH, DSA
-	char *prime;
-	char *generator;
-	char *subprime;
-	char *base;
-	char *private_value;
-	char *public_value;
-	// EC
-	char *private_key;
-};
+	uint16_t flags;
 
-typedef struct knot_key_params knot_key_params_t;
+	// shared key
+	knot_binary_t secret;
+
+	// RSA
+	knot_binary_t modulus;
+	knot_binary_t public_exponent;
+	knot_binary_t private_exponent;
+	knot_binary_t prime_one;
+	knot_binary_t prime_two;
+	knot_binary_t exponent_one;
+	knot_binary_t exponent_two;
+	knot_binary_t coefficient;
+
+	// DSA
+	knot_binary_t prime;
+	knot_binary_t subprime;
+	knot_binary_t base;
+	knot_binary_t private_value;
+	knot_binary_t public_value;
 
-enum knot_key_type {
+	// EC
+	knot_binary_t private_key;
+
+	// key lifetime
+	//time_t time_created;
+	//time_t time_publish;
+	time_t time_activate;
+	//time_t time_revoke;
+	time_t time_inactive;
+	//time_t time_delete;
+} knot_key_params_t;
+
+typedef enum {
 	KNOT_KEY_UNKNOWN = 0,
 	KNOT_KEY_DNSSEC, //!< DNSSEC key. Described in RFC 2535 and RFC 4034.
 	KNOT_KEY_TSIG,   //!< Transaction Signature. Described in RFC 2845.
 	KNOT_KEY_TKEY    //!< Transaction Key. Described in RFC 2930.
-};
-
-typedef enum knot_key_type knot_key_type_t;
+} knot_key_type_t;
 
 /*----------------------------------------------------------------------------*/
 
@@ -157,12 +168,14 @@ int knot_tsig_key_from_params(const knot_key_params_t *params,
 /*!
  * \brief Frees TSIG key.
  *
+ * The structure itself is not freed.
+ *
  * \param key  TSIG key structure to be freed.
  *
  * \return Error code, KNOT_EOK when succeeded.
  */
 int knot_tsig_key_free(knot_tsig_key_t *key);
 
-#endif // _KNOT_SIGN_KEY_H_
+#endif // _KNOT_DNSSEC_KEY_H_
 
 /*! @} */
diff --git a/src/libknot/dnssec/nsec-bitmap.h b/src/libknot/dnssec/nsec-bitmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..f96287ce4f7e464f0df4321224b333d38ef3bcad
--- /dev/null
+++ b/src/libknot/dnssec/nsec-bitmap.h
@@ -0,0 +1,133 @@
+/*  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 nsec-bitmap.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \brief RR bitmap used in NSEC/NSEC3 records (RFC 4034).
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_ZONE_NSEC_BITMAP_H_
+#define _KNOT_DNSSEC_ZONE_NSEC_BITMAP_H_
+
+#include <stdint.h>
+#include <string.h>
+#include "libknot/zone/node.h"
+#include "libknot/rrset.h"
+
+#define BITMAP_WINDOW_SIZE 256
+#define BITMAP_WINDOW_BYTES (BITMAP_WINDOW_SIZE/CHAR_BIT)
+#define BITMAP_WINDOW_COUNT 256
+
+/*!
+ * \brief One window of a bitmap.
+ */
+typedef struct {
+	uint8_t used;
+	uint8_t data[BITMAP_WINDOW_BYTES];
+} bitmap_window_t;
+
+/*!
+ * \brief Bitmap of RR types.
+ */
+typedef struct {
+	int used;
+	bitmap_window_t windows[BITMAP_WINDOW_COUNT];
+} bitmap_t;
+
+/*!
+ * \brief Add one RR type into the bitmap.
+ */
+inline static void bitmap_add_type(bitmap_t *bitmap, uint16_t type)
+{
+	int win = type / BITMAP_WINDOW_SIZE;
+	int bit = type % BITMAP_WINDOW_SIZE;
+
+	if (bitmap->used <= win) {
+		bitmap->used = win + 1;
+	}
+
+	int win_byte = bit / CHAR_BIT;
+	int win_bit  = bit % CHAR_BIT;
+
+	bitmap_window_t *window = &bitmap->windows[win];
+	window->data[win_byte] |= 0x80 >> win_bit;
+	if (window->used <= win_byte) {
+		window->used = win_byte + 1;
+	}
+}
+
+/*!
+ * \brief Add all RR types from a node into the bitmap.
+ */
+inline static void bitmap_add_node_rrsets(bitmap_t *bitmap,
+                                          const knot_node_t *node)
+{
+	knot_rrset_t **node_rrsets = knot_node_get_rrsets_no_copy(node);
+	for (int i = 0; i < node->rrset_count; i++) {
+		bitmap_add_type(bitmap, node_rrsets[i]->type);
+	}
+}
+
+/*!
+ * \brief Compute the size of the bitmap in NSEC RDATA format.
+ */
+inline static size_t bitmap_size(const bitmap_t *bitmap)
+{
+	size_t result = 0;
+
+	for (int i = 0; i < bitmap->used; i++) {
+		int used = bitmap->windows[i].used;
+		if (used == 0) {
+			continue;
+		}
+
+		result += 2 + used; // windows number, window size, data
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Write bitmap in NSEC RDATA format.
+ */
+inline static void bitmap_write(const bitmap_t *bitmap, uint8_t *output)
+{
+	uint8_t *write_ptr = output;
+	for (int win = 0; win < bitmap->used; win++) {
+		int used = bitmap->windows[win].used;
+		if (used == 0) {
+			continue;
+		}
+
+		*write_ptr = (uint8_t)win;
+		write_ptr += 1;
+
+		*write_ptr = (uint8_t)used;
+		write_ptr += 1;
+
+		memcpy(write_ptr, bitmap->windows[win].data, used);
+		write_ptr += used;
+	}
+}
+
+#endif // _KNOT_DNSSEC_ZONE_NSEC_BITMAP_H_
+
+/*! @} */
diff --git a/src/libknot/dnssec/nsec3.c b/src/libknot/dnssec/nsec3.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7e849506b626701e7207a15e1e3cf3f5946c1e3
--- /dev/null
+++ b/src/libknot/dnssec/nsec3.c
@@ -0,0 +1,167 @@
+/*  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 <assert.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "common/descriptor.h"
+#include "common/memdup.h"
+#include "libknot/dnssec/algorithm.h"
+#include "libknot/dnssec/nsec3.h"
+#include "libknot/rdata.h"
+#include "util/tolower.h"
+
+/*!
+ * \brief Compute NSEC3 SHA1 hash.
+ *
+ * \param[in]  salt         Salt.
+ * \param[in]  salt_length  Salt length.
+ * \param[in]  iterations   Interation count of the SHA1 computation.
+ * \param[in]  data         Input data to be hashed.
+ * \param[in]  data_size    Input data size.
+ * \param[out] digest       Result of the computation (will be allocated).
+ * \param[out] digest_size  Size of the result.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int nsec3_sha1(const uint8_t *salt, uint8_t salt_length,
+                      uint16_t iterations, const uint8_t *data,
+                      size_t data_size, uint8_t **digest, size_t *digest_size)
+{
+	assert(data);
+	assert(digest);
+	assert(digest_size);
+
+	if (!salt) {
+		return KNOT_EINVAL;
+	}
+
+	EVP_MD_CTX mdctx;
+	EVP_MD_CTX_init(&mdctx);
+
+	unsigned int result_size = 0;
+	uint8_t *result = (uint8_t *)malloc(EVP_MD_size(EVP_sha1()));
+	if (result == NULL) {
+		EVP_MD_CTX_cleanup(&mdctx);
+		return KNOT_ENOMEM;
+	}
+
+	uint8_t *data_low = knot_strtolower(data, data_size);
+	if (data_low == NULL) {
+		free(result);
+		EVP_MD_CTX_cleanup(&mdctx);
+		return KNOT_ENOMEM;
+	}
+
+	const uint8_t *in = data_low;
+	unsigned int in_size = data_size;
+
+	for (int i = 0; i <= iterations; i++) {
+		EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
+
+		int success_ops =
+			EVP_DigestUpdate(&mdctx, in, in_size) +
+			EVP_DigestUpdate(&mdctx, salt, salt_length) +
+			EVP_DigestFinal_ex(&mdctx, result, &result_size);
+
+		if (success_ops != 3) {
+			EVP_MD_CTX_cleanup(&mdctx);
+			free(result);
+			free(data_low);
+			return KNOT_NSEC3_ECOMPUTE_HASH;
+		}
+
+		in = result;
+		in_size = result_size;
+	}
+
+	EVP_MD_CTX_cleanup(&mdctx);
+	free(data_low);
+
+	*digest = result;
+	*digest_size = (size_t)result_size;
+
+	return KNOT_EOK;
+}
+
+/* - public API -------------------------------------------------------------*/
+
+/*!
+ * \brief Initialize the structure with NSEC3 params from NSEC3PARAM RR set.
+ */
+int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
+                                const knot_rrset_t *rrset)
+{
+	if (params == NULL || rrset == NULL || rrset->rdata_count == 0) {
+		return KNOT_EINVAL;
+	}
+
+	assert(rrset->type == KNOT_RRTYPE_NSEC3PARAM);
+
+	knot_nsec3_params_t result = { 0 };
+
+	result.algorithm   = knot_rdata_nsec3param_algorithm(rrset, 0);
+	result.iterations  = knot_rdata_nsec3param_iterations(rrset, 0);
+	result.flags       = knot_rdata_nsec3param_flags(rrset, 0);
+	result.salt_length = knot_rdata_nsec3param_salt_length(rrset, 0);
+
+	if (result.salt_length > 0) {
+		result.salt = knot_memdup(knot_rdata_nsec3param_salt(rrset, 0),
+		                          result.salt_length);
+		if (!result.salt) {
+			return KNOT_ENOMEM;
+		}
+	} else {
+		result.salt = NULL;
+	}
+
+	knot_nsec3_params_free(params);
+	*params = result;
+
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Clean up structure with NSEC3 params (do not deallocate).
+ */
+void knot_nsec3_params_free(knot_nsec3_params_t *params)
+{
+	free(params->salt);
+}
+
+/*!
+ * \brief Compute NSEC3 hash for given data.
+ */
+int knot_nsec3_hash(const knot_nsec3_params_t *params, const uint8_t *data,
+                    size_t data_size, uint8_t **digest, size_t *digest_size)
+{
+	if (!params || !data || !digest || !digest_size) {
+		return KNOT_EINVAL;
+	}
+
+	if (params->algorithm != 1) {
+		return KNOT_ENOTSUP;
+	}
+
+	return nsec3_sha1(params->salt, params->salt_length, params->iterations,
+	                  data, data_size, digest, digest_size);
+}
diff --git a/src/libknot/dnssec/nsec3.h b/src/libknot/dnssec/nsec3.h
new file mode 100644
index 0000000000000000000000000000000000000000..96f4d3e6392f077b7499b42f449998e7e3e141c9
--- /dev/null
+++ b/src/libknot/dnssec/nsec3.h
@@ -0,0 +1,121 @@
+/*!
+ * \file nsec3.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \brief Functions for computation of NSEC3 hashes.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOT_DNSSEC_NSEC3_H_
+#define _KNOT_DNSSEC_NSEC3_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libknot/dnssec/algorithm.h"
+#include "rrset.h"
+
+/*---------------------------------------------------------------------------*/
+
+
+/*!
+ * \brief Get length of the raw NSEC3 hash.
+ *
+ * \param algorithm  NSEC3 hash algorithm.
+ *
+ * \return Length of the hash, 0 for unknown hash algorithm.
+ */
+inline static size_t knot_nsec3_hash_length(uint8_t algorithm)
+{
+	if (algorithm == KNOT_NSEC3_ALGORITHM_SHA1) {
+		return 20;
+	} else {
+		return 0;
+	}
+}
+
+/*!
+ * \brief Get length of the NSEC3 hash encoded in Base32 encoding.
+ *
+ * \param algorithm  NSEC3 hash algorithm.
+ *
+ * \return Length of the hash, 0 for unknown hash algorithm.
+ */
+inline static size_t knot_nsec3_hash_b32_length(uint8_t algorithm)
+{
+	if (algorithm == KNOT_NSEC3_ALGORITHM_SHA1) {
+		return 32;
+	} else {
+		return 0;
+	}
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Structure representing the NSEC3PARAM resource record.
+ */
+typedef struct {
+	uint8_t algorithm;    //!< Hash algorithm.
+	uint8_t flags;        //!< Flags.
+	uint16_t iterations;  //!< Additional iterations of the hash function.
+	uint8_t salt_length;  //!< Length of the salt field in bytes.
+	uint8_t *salt;        //!< Salt used in hashing.
+} knot_nsec3_params_t;
+
+/*---------------------------------------------------------------------------*/
+
+/*!
+ * \brief Initialize the structure with NSEC3 params from NSEC3PARAM RR set.
+ *
+ * \param params      Structure to initialize.
+ * \param nsec3param  The NSEC3PARAM RR set.
+ *
+ * \return Error code, KNOT_EOK on success.
+ */
+int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
+                                const knot_rrset_t *rrset);
+/*!
+ * \brief Clean up structure with NSEC3 params (do not deallocate).
+ *
+ * \param params Structure with NSEC3 params.
+ */
+void knot_nsec3_params_free(knot_nsec3_params_t *params);
+
+/*!
+ * \brief Compute NSEC3 hash for given data.
+ *
+ * \param[in]  params       NSEC3 parameters.
+ * \param[in]  data         Data to compute hash for.
+ * \param[in]  size         Size of the data.
+ * \param[out] digest       Computed hash.
+ * \param[out] digest_size  Size of the computed hash.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_nsec3_hash(const knot_nsec3_params_t *params, const uint8_t *data,
+                    size_t size, uint8_t **digest, size_t *digest_size);
+
+#endif // _KNOT_DNSSEC_NSEC3_H_
+
+/*! @} */
diff --git a/src/libknot/dnssec/policy.h b/src/libknot/dnssec/policy.h
new file mode 100644
index 0000000000000000000000000000000000000000..1db5d0c6e83480b074a53a46215cb9677372ea28
--- /dev/null
+++ b/src/libknot/dnssec/policy.h
@@ -0,0 +1,56 @@
+/*  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 policy.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \brief Policy for handling of DNSSEC signatures and keys.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_POLICY_H_
+#define _KNOT_DNSSEC_POLICY_H_
+
+typedef enum knot_update_serial {
+	KNOT_SOA_SERIAL_INC = 1 << 0,
+	KNOT_SOA_SERIAL_KEEP = 1 << 1
+} knot_update_serial_t;
+
+typedef struct {
+	uint32_t now;               //! Current time.
+	uint32_t sign_lifetime;     //! Signature life time.
+	uint32_t sign_refresh;      //! Sig. refresh time before expiration.
+	bool forced_sign;           //! Drop valid signatures as well.
+	knot_update_serial_t soa_up;//! Policy for serial updating.
+} knot_dnssec_policy_t;
+
+#define KNOT_DNSSEC_DEFAULT_LIFETIME 2592000
+
+#define DEFAULT_DNSSEC_POLICY { .now = time_now(), \
+				.sign_lifetime = KNOT_DNSSEC_DEFAULT_LIFETIME, \
+				.sign_refresh = 7200, .forced_sign = false, \
+				.soa_up = KNOT_SOA_SERIAL_INC }
+#define FORCED_DNSSEC_POLICY {  .now = time_now(), \
+				.sign_lifetime = KNOT_DNSSEC_DEFAULT_LIFETIME, \
+				.sign_refresh = 7200, .forced_sign = true, \
+				.soa_up = KNOT_SOA_SERIAL_INC }
+
+#endif // _KNOT_DNSSEC_POLICY_H_
+
+/*! @} */
diff --git a/src/libknot/dnssec/rrset-sign.c b/src/libknot/dnssec/rrset-sign.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a402c9d72b1f18b31a68525ff23f25f3940f269
--- /dev/null
+++ b/src/libknot/dnssec/rrset-sign.c
@@ -0,0 +1,351 @@
+/*  Copyright (C) 2013 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 <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "common/errcode.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/policy.h"
+#include "libknot/dnssec/rrset-sign.h"
+#include "libknot/dnssec/sign.h"
+#include "libknot/rdata.h"
+#include "libknot/rrset.h"
+
+/*!
+ * \todo functions rrsig_rdata_size() and rrsig_write_rdata() were copied from
+ * SIG(0) implementation and modified; reuse this implementations in SIG(0).
+ */
+
+#define MAX_RR_WIREFORMAT_SIZE (64 * 1024)
+#define RRSIG_RDATA_SIGNER_OFFSET 18
+
+/*- Creating of RRSIG --------------------------------------------------------*/
+
+/*!
+ * \brief Get size of RRSIG RDATA for a given key.
+ *
+ * \param key  DNSSEC key to be used for creating the signature.
+ *
+ * \return RRSIG RDATA size in bytes.
+ */
+static size_t rrsig_rdata_size(const knot_dnssec_key_t *key)
+{
+	assert(key);
+
+	size_t size;
+
+	// static part
+
+	size = sizeof(uint16_t)		// type covered
+	     + sizeof(uint8_t)		// algorithm
+	     + sizeof(uint8_t)		// labels
+	     + sizeof(uint32_t)		// original TTL
+	     + sizeof(uint32_t)		// signature expiration
+	     + sizeof(uint32_t)		// signature inception
+	     + sizeof(uint16_t);	// key tag (footprint)
+
+	// variable part
+
+	assert(key->name);
+	size += knot_dname_size(key->name);
+	size += knot_dnssec_sign_size(key);
+
+	return size;
+}
+
+/*!
+ * \brief Write RRSIG RDATA except the signature itself.
+ *
+ * \param rdata         Pointer to RDATA.
+ * \param key           Key used for signing.
+ * \param covered       RR covered by the signature.
+ * \param sig_incepted  Timestamp of signature inception.
+ * \param sig_expires   Timestamp of signature expiration.
+ */
+static void rrsig_write_rdata(uint8_t *rdata,
+                              const knot_dnssec_key_t *key,
+                              const knot_rrset_t *covered,
+                              uint32_t sig_incepted,
+                              uint32_t sig_expires)
+{
+	assert(key);
+	assert(rdata);
+	assert(covered);
+	assert(sig_incepted < sig_expires);
+	assert(covered->owner);
+
+	uint8_t owner_labels = knot_dname_labels(covered->owner, NULL);
+	if (knot_dname_is_wildcard(covered->owner)) {
+		owner_labels -= 1;
+	}
+
+	uint8_t *w = rdata;
+
+	knot_wire_write_u16(w, covered->type);	// type covered
+	w += sizeof(uint16_t);
+	*w = key->algorithm;			// algorithm
+	w += sizeof(uint8_t);
+	*w = owner_labels;			// labels
+	w += sizeof(uint8_t);
+	knot_wire_write_u32(w, covered->ttl);	// original TTL
+	w += sizeof(uint32_t);
+	knot_wire_write_u32(w, sig_expires);	// signature expiration
+	w += sizeof(uint32_t);
+	knot_wire_write_u32(w, sig_incepted);	// signature inception
+	w += sizeof(uint32_t);
+	knot_wire_write_u16(w, key->keytag);	// key footprint
+	w += sizeof(uint16_t);
+
+	assert(w == rdata + RRSIG_RDATA_SIGNER_OFFSET);
+	assert(key->name);
+	memcpy(w, key->name, knot_dname_size(key->name)); // signer
+}
+
+/*!
+ * \brief Create RRSIG RDATA.
+ *
+ * \param rrsigs        RR set with RRSIGS.
+ * \param covered       RR covered by the signature.
+ * \param key           Key used for signing.
+ * \param sig_incepted  Timestamp of signature inception.
+ * \param sig_expires   Timestamp of signature expiration.
+ */
+static uint8_t *create_rrsigs_rdata(knot_rrset_t *rrsigs,
+                                    const knot_rrset_t *covered,
+                                    const knot_dnssec_key_t *key,
+                                    uint32_t sig_incepted,
+                                    uint32_t sig_expires)
+{
+	assert(rrsigs);
+	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
+	assert(covered);
+	assert(key);
+
+	uint8_t *rdata = knot_rrset_create_rdata(rrsigs, rrsig_rdata_size(key));
+	if (!rdata) {
+		return NULL;
+	}
+
+	rrsig_write_rdata(rdata, key, covered, sig_incepted, sig_expires);
+
+	return rdata;
+}
+
+/*- Computation of signatures ------------------------------------------------*/
+
+/*!
+ * \brief Add RRSIG RDATA without signature to signing context.
+ *
+ * Requires signer name in RDATA in canonical form.
+ *
+ * \param ctx   Signing context.
+ * \param rdata Pointer to RRSIG RDATA.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_ctx_add_self(knot_dnssec_sign_context_t *ctx,
+                             const uint8_t *rdata)
+{
+	assert(ctx);
+	assert(rdata);
+
+	const uint8_t *signer = rdata + RRSIG_RDATA_SIGNER_OFFSET;
+	size_t data_size = RRSIG_RDATA_SIGNER_OFFSET + knot_dname_size(signer);
+
+	return knot_dnssec_sign_add(ctx, rdata, data_size);
+}
+
+/*!
+ * \brief Add covered RRs to signing context.
+ *
+ * Requires all DNAMEs in canonical form and all RRs ordered canonically.
+ *
+ * \param ctx      Signing context.
+ * \param covered  Covered RRs.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_ctx_add_records(knot_dnssec_sign_context_t *ctx,
+                                const knot_rrset_t *covered)
+{
+	// huge block of rrsets can be optionally created
+	uint8_t *rrwf = malloc(MAX_RR_WIREFORMAT_SIZE);
+	if (!rrwf) {
+		return KNOT_ENOMEM;
+	}
+
+	int result = KNOT_EOK;
+
+	uint16_t rr_count = covered->rdata_count;
+	for (uint16_t i = 0; i < rr_count; i++) {
+		size_t rr_size;
+		result = knot_rrset_to_wire_one(covered, i, rrwf,
+		                                MAX_RR_WIREFORMAT_SIZE,
+		                                &rr_size, NULL);
+		if (result != KNOT_EOK) {
+			break;
+		}
+
+		result = knot_dnssec_sign_add(ctx, rrwf, rr_size);
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+	free(rrwf);
+
+	return result;
+}
+
+/*!
+ * \brief Add all data covered by signature into signing context.
+ *
+ * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature)
+ * and all matching RR records, which are ordered canonically.
+ *
+ * Requires all DNAMEs in canonical form and all RRs ordered canonically.
+ *
+ * \param ctx          Signing context.
+ * \param rrsig_rdata  RRSIG RDATA with populated fields except signature.
+ * \param covered      Covered RRs.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_ctx_add_data(knot_dnssec_sign_context_t *ctx,
+                             const uint8_t *rrsig_rdata,
+                             const knot_rrset_t *covered)
+{
+	int result = sign_ctx_add_self(ctx, rrsig_rdata);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	return sign_ctx_add_records(ctx, covered);
+}
+
+/*!
+ * \brief Create RRSIG RR for given RR set.
+ */
+int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered,
+                    const knot_dnssec_key_t *key,
+                    knot_dnssec_sign_context_t *sign_ctx,
+                    const knot_dnssec_policy_t *policy)
+{
+	if (!rrsigs || !covered || !key || !sign_ctx || !policy ||
+	    rrsigs->type != KNOT_RRTYPE_RRSIG ||
+	    (knot_dname_cmp(rrsigs->owner, covered->owner) != 0)
+	) {
+		return KNOT_EINVAL;
+	}
+
+	uint32_t sig_incept = policy->now;
+	uint32_t sig_expire = sig_incept + policy->sign_lifetime;
+
+	uint8_t *rdata = create_rrsigs_rdata(rrsigs, covered, key,
+	                                     sig_incept, sig_expire);
+	if (!rdata) {
+		return KNOT_ENOMEM;
+	}
+
+	int result = knot_dnssec_sign_new(sign_ctx);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	result = sign_ctx_add_data(sign_ctx, rdata, covered);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	uint8_t *rdata_signature = rdata + RRSIG_RDATA_SIGNER_OFFSET
+	                           + knot_dname_size(key->name);
+
+	return knot_dnssec_sign_write(sign_ctx, rdata_signature);
+}
+
+/*- Verification of signatures -----------------------------------------------*/
+
+/*!
+ * \brief Check if the signature is expired.
+ *
+ * \param rrsigs  RR set with RRSIGs.
+ * \param pos     Number of RR in the RR set.
+ * \param policy  DNSSEC policy.
+ *
+ * \return Signature is expired or should be replaced soon.
+ */
+static bool is_expired_signature(const knot_rrset_t *rrsigs, size_t pos,
+                                 const knot_dnssec_policy_t *policy)
+{
+	assert(rrsigs);
+	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
+	assert(policy);
+
+	uint32_t now = policy->now;
+	uint32_t refresh = policy->sign_refresh;
+	uint32_t expiration = knot_rdata_rrsig_sig_expiration(rrsigs, pos);
+
+	return (expiration - refresh) <= now;
+}
+
+/*!
+ * \brief Check if RRSIG signature is valid.
+ */
+int knot_is_valid_signature(const knot_rrset_t *covered,
+                            const knot_rrset_t *rrsigs, size_t pos,
+                            const knot_dnssec_key_t *key,
+                            knot_dnssec_sign_context_t *ctx,
+                            const knot_dnssec_policy_t *policy)
+{
+	if (!covered || !rrsigs || !key || !ctx || !policy) {
+		return KNOT_EINVAL;
+	}
+
+	if (is_expired_signature(rrsigs, pos, policy)) {
+		return KNOT_DNSSEC_EINVALID_SIGNATURE;
+	}
+
+	// identify fields in the signature being validated
+
+	uint8_t *rdata = knot_rrset_get_rdata(rrsigs, pos);
+	if (!rdata) {
+		return KNOT_EINVAL;
+	}
+
+	uint8_t *signature = NULL;
+	size_t signature_size = 0;
+	knot_rdata_rrsig_signature(rrsigs, pos, &signature, &signature_size);
+	if (!signature) {
+		return KNOT_EINVAL;
+	}
+
+	// perform the validation
+
+	int result = knot_dnssec_sign_new(ctx);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	result = sign_ctx_add_data(ctx, rdata, covered);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	return knot_dnssec_sign_verify(ctx, signature, signature_size);
+}
diff --git a/src/libknot/dnssec/rrset-sign.h b/src/libknot/dnssec/rrset-sign.h
new file mode 100644
index 0000000000000000000000000000000000000000..0658b5c1a2560a413b97686f9327cc632de2a77b
--- /dev/null
+++ b/src/libknot/dnssec/rrset-sign.h
@@ -0,0 +1,74 @@
+/*  Copyright (C) 2013 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 rrsig-sign.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ *
+ * \brief Interface for DNSSEC signing of RR sets.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_RRSET_SIGN_H_
+#define _KNOT_DNSSEC_RRSET_SIGN_H_
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include "libknot/dnssec/policy.h"
+#include "libknot/dnssec/sign.h"
+#include "libknot/rrset.h"
+
+/*!
+ * \brief Create RRSIG RR for given RR set.
+ *
+ * \param rrsigs    RR set with RRSIGs into which the result will be added.
+ * \param covered   RR set to create a new signature for.
+ * \param key       Signing key.
+ * \param sign_ctx  Signing context.
+ * \param policy    DNSSEC policy.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_sign_rrset(knot_rrset_t *rrsigs,
+                    const knot_rrset_t *covered,
+                    const knot_dnssec_key_t *key,
+                    knot_dnssec_sign_context_t *sign_ctx,
+                    const knot_dnssec_policy_t *policy);
+
+/*!
+ * \brief Check if RRSIG signature is valid.
+ *
+ * \param covered  RRs covered by the signature.
+ * \param rrsigs   RR set with RRSIGs.
+ * \param pos      Number of RRSIG RR in 'rrsigs' to be validated.
+ * \param key      Signing key.
+ * \param ctx      Signing context.
+ * \param policy   DNSSEC policy.
+ *
+ * \return Error code, KNOT_EOK if successful and the signature is valid.
+ * \retval KNOT_DNSSEC_EINVALID_SIGNATURE  The signature is invalid.
+ */
+int knot_is_valid_signature(const knot_rrset_t *covered,
+                            const knot_rrset_t *rrsigs, size_t pos,
+                            const knot_dnssec_key_t *key,
+                            knot_dnssec_sign_context_t *ctx,
+                            const knot_dnssec_policy_t *policy);
+
+#endif // _KNOT_DNSSEC_RRSET_SIGN_H_
+
+/*! @} */
diff --git a/src/libknot/sign/sig0.c b/src/libknot/dnssec/sig0.c
similarity index 94%
rename from src/libknot/sign/sig0.c
rename to src/libknot/dnssec/sig0.c
index d1f1ea93cce18eb18e13e971202e6291e8e8cc69..f78246d238fbb8c957162d25edfdf567db40e2e7 100644
--- a/src/libknot/sign/sig0.c
+++ b/src/libknot/dnssec/sig0.c
@@ -15,11 +15,11 @@
 */
 
 #include <config.h>
-#include "sign/dnssec.h"
-#include "sign/sig0.h"
-#include "common/errcode.h"
 #include <assert.h>
 #include <time.h>
+#include "common/errcode.h"
+#include "sig0.h"
+#include "sign.h"
 #include "util/wire.h"
 
 /*!
@@ -38,11 +38,10 @@
  */
 static knot_rrset_t *sig0_create_rrset(void)
 {
-	knot_dname_t *root = knot_dname_new_from_str(".", 1, NULL);
+	knot_dname_t *root = knot_dname_from_str(".", 1);
 	uint32_t ttl = 0;
 	knot_rrset_t *sig_record = knot_rrset_new(root, KNOT_RRTYPE_SIG,
 	                                          KNOT_CLASS_ANY, ttl);
-	knot_dname_release(root);
 
 	return sig_record;
 }
@@ -72,7 +71,8 @@ static size_t sig0_rdata_size(knot_dnssec_key_t *key)
 
 	// variable part
 
-	size += sizeof(knot_dname_t *); // pointer to signer
+	assert(key->name);
+	size += knot_dname_size(key->name); // signer
 	size += knot_dnssec_sign_size(key);
 
 	return size;
@@ -132,7 +132,8 @@ static int sig0_write_rdata(knot_dnssec_key_t *key, uint8_t *rdata)
 	w += sizeof(uint16_t);
 
 	assert(w == rdata + 18);
-	memcpy(w, &key->name, sizeof(knot_dname_t *)); // pointer to signer
+	assert(key->name);
+	memcpy(w, key->name, knot_dname_size(key->name)); // signer
 
 	return KNOT_EOK;
 }
@@ -192,7 +193,7 @@ int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size,
 
 	uint8_t *sig_rdata = sig0_create_rdata(sig_rrset, key);
 	if (!sig_rdata) {
-		knot_rrset_deep_free(&sig_rrset, 1, 0);
+		knot_rrset_deep_free(&sig_rrset, 1);
 		return KNOT_ENOMEM;
 	}
 
@@ -208,7 +209,7 @@ int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size,
 	int result = knot_rrset_to_wire(sig_rrset, wire_end, &wire_sig_size,
 	                                wire_avail_size, &written_rr_count,
 	                                NULL);
-	knot_rrset_deep_free(&sig_rrset, 1, 0);
+	knot_rrset_deep_free(&sig_rrset, 1);
 	if (result != KNOT_EOK) {
 		return result;
 	}
diff --git a/src/libknot/sign/sig0.h b/src/libknot/dnssec/sig0.h
similarity index 91%
rename from src/libknot/sign/sig0.h
rename to src/libknot/dnssec/sig0.h
index 29e9d4e3e7de0ae1aef81e59ee8ae15c5f1ea6d6..a226eb3c72ddbdaf64ec2ddd4ff1a9db785a045f 100644
--- a/src/libknot/sign/sig0.h
+++ b/src/libknot/dnssec/sig0.h
@@ -24,10 +24,10 @@
  * @{
  */
 
-#ifndef _KNOT_SIGN_SIG0_H_
-#define _KNOT_SIGN_SIG0_H_
+#ifndef _KNOT_DNSSEC_SIG0_H_
+#define _KNOT_DNSSEC_SIG0_H_
 
-#include "sign/dnssec.h"
+#include "libknot/dnssec/sign.h"
 
 /*!
  * \brief Sign a packet using SIG(0) mechanism.
@@ -42,6 +42,6 @@
 int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size,
                    knot_dnssec_key_t *key);
 
-#endif // _KNOT_SIGN_SIG0_H_
+#endif // _KNOT_DNSSEC_SIG0_H_
 
 /*! @} */
diff --git a/src/libknot/sign/dnssec.c b/src/libknot/dnssec/sign.c
similarity index 65%
rename from src/libknot/sign/dnssec.c
rename to src/libknot/dnssec/sign.c
index ed9a652344ef70693bc8a2530afe8a1d8262d733..ea83773193ed85ba1c319f45e3193817ce08a7f3 100644
--- a/src/libknot/sign/dnssec.c
+++ b/src/libknot/dnssec/sign.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -18,9 +18,9 @@
 #include "common.h"
 #include "common/descriptor.h"
 #include "common/errcode.h"
-#include "sign/bnutils.h"
-#include "sign/dnssec.h"
-#include "sign/key.h"
+#include "libknot/dnssec/algorithm.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/sign.h"
 #include <assert.h>
 #include <openssl/dsa.h>
 #include <openssl/opensslconf.h>
@@ -57,8 +57,18 @@ struct algorithm_functions {
 	int (*sign_add)(const knot_dnssec_sign_context_t *, const uint8_t *, size_t);
 	//! \brief Callback: finish the signing and write out the signature.
 	int (*sign_write)(const knot_dnssec_sign_context_t *, uint8_t *);
+	//! \brief Callback: finish the signing and validate the signature.
+	int (*sign_verify)(const knot_dnssec_sign_context_t *, const uint8_t *, size_t);
 };
 
+/**
+ * \brief Convert binary data to OpenSSL BIGNUM format.
+ */
+static BIGNUM *binary_to_bn(const knot_binary_t *bin)
+{
+	return BN_bin2bn((unsigned char *)bin->data, (int)bin->size, NULL);
+}
+
 /*- Algorithm independent ----------------------------------------------------*/
 
 /*!
@@ -90,8 +100,9 @@ static int any_sign_add(const knot_dnssec_sign_context_t *context,
 	assert(context);
 	assert(data);
 
-	if (!EVP_SignUpdate(context->digest_context, data, data_size))
+	if (!EVP_DigestUpdate(context->digest_context, data, data_size)) {
 		return KNOT_DNSSEC_ESIGN;
+	}
 
 	return KNOT_EOK;
 }
@@ -107,7 +118,7 @@ static int any_sign_add(const knot_dnssec_sign_context_t *context,
  *
  * \return Error code, KNOT_EOK if successful.
  */
-static int any_sign_finish(const knot_dnssec_sign_context_t *context,
+static int any_sign_write(const knot_dnssec_sign_context_t *context,
                            uint8_t **signature, size_t *signature_size)
 {
 	assert(context);
@@ -116,8 +127,9 @@ static int any_sign_finish(const knot_dnssec_sign_context_t *context,
 
 	size_t max_size = (size_t)EVP_PKEY_size(context->key->data->private_key);
 	uint8_t *output = calloc(1, max_size);
-	if (!output)
+	if (!output) {
 		return KNOT_ENOMEM;
+	}
 
 	unsigned int actual_size;
 	int result = EVP_SignFinal(context->digest_context, output,
@@ -135,6 +147,38 @@ static int any_sign_finish(const knot_dnssec_sign_context_t *context,
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Verify the DNSSEC signature for supplied data.
+ *
+ * \param context         DNSSEC signature context.
+ * \param signature       Pointer to signature.
+ * \param signature_size  Size of the signature.
+ *
+ * \return Error code.
+ * \retval KNOT_EOK                        The signature is valid.
+ * \retval KNOT_DNSSEC_EINVALID_SIGNATURE  The signature is invalid.
+ * \retval KNOT_DNSSEC_ESIGN               Some error occured.
+ */
+static int any_sign_verify(const knot_dnssec_sign_context_t *context,
+                            const uint8_t *signature, size_t signature_size)
+{
+	assert(context);
+	assert(signature);
+
+	int result = EVP_VerifyFinal(context->digest_context,
+	                             signature, signature_size,
+	                             context->key->data->private_key);
+
+	switch (result) {
+	case 1:
+		return KNOT_EOK;
+	case 0:
+		return KNOT_DNSSEC_EINVALID_SIGNATURE;
+	default:
+		return KNOT_DNSSEC_ESIGN;
+	};
+}
+
 /*- RSA specific -------------------------------------------------------------*/
 
 /*!
@@ -147,20 +191,22 @@ static int any_sign_finish(const knot_dnssec_sign_context_t *context,
  */
 static int rsa_create_pkey(const knot_key_params_t *params, EVP_PKEY *key)
 {
+	assert(params);
 	assert(key);
 
 	RSA *rsa = RSA_new();
-	if (rsa == NULL)
+	if (rsa == NULL) {
 		return KNOT_ENOMEM;
+	}
 
-	rsa->n    = knot_b64_to_bignum(params->modulus);
-	rsa->e    = knot_b64_to_bignum(params->public_exponent);
-	rsa->d    = knot_b64_to_bignum(params->private_exponent);
-	rsa->p    = knot_b64_to_bignum(params->prime_one);
-	rsa->q    = knot_b64_to_bignum(params->prime_two);
-	rsa->dmp1 = knot_b64_to_bignum(params->exponent_one);
-	rsa->dmq1 = knot_b64_to_bignum(params->exponent_two);
-	rsa->iqmp = knot_b64_to_bignum(params->coefficient);
+	rsa->n    = binary_to_bn(&params->modulus);
+	rsa->e    = binary_to_bn(&params->public_exponent);
+	rsa->d    = binary_to_bn(&params->private_exponent);
+	rsa->p    = binary_to_bn(&params->prime_one);
+	rsa->q    = binary_to_bn(&params->prime_two);
+	rsa->dmp1 = binary_to_bn(&params->exponent_one);
+	rsa->dmq1 = binary_to_bn(&params->exponent_two);
+	rsa->iqmp = binary_to_bn(&params->coefficient);
 
 	if (RSA_check_key(rsa) != 1) {
 		RSA_free(rsa);
@@ -194,7 +240,7 @@ static int rsa_sign_write(const knot_dnssec_sign_context_t *context,
 	size_t raw_signature_size;
 	const knot_dnssec_key_t *key = context->key;
 
-	result = any_sign_finish(context, &raw_signature, &raw_signature_size);
+	result = any_sign_write(context, &raw_signature, &raw_signature_size);
 	if (result != KNOT_EOK) {
 		return result;
 	}
@@ -218,17 +264,19 @@ static int rsa_sign_write(const knot_dnssec_sign_context_t *context,
  */
 static int dsa_create_pkey(const knot_key_params_t *params, EVP_PKEY *key)
 {
+	assert(params);
 	assert(key);
 
 	DSA *dsa = DSA_new();
-	if (dsa == NULL)
+	if (dsa == NULL) {
 		return KNOT_ENOMEM;
+	}
 
-	dsa->p        = knot_b64_to_bignum(params->prime);
-	dsa->q        = knot_b64_to_bignum(params->subprime);
-	dsa->g        = knot_b64_to_bignum(params->base);
-	dsa->priv_key = knot_b64_to_bignum(params->private_value);
-	dsa->pub_key  = knot_b64_to_bignum(params->public_value);
+	dsa->p        = binary_to_bn(&params->prime);
+	dsa->q        = binary_to_bn(&params->subprime);
+	dsa->g        = binary_to_bn(&params->base);
+	dsa->priv_key = binary_to_bn(&params->private_value);
+	dsa->pub_key  = binary_to_bn(&params->public_value);
 
 	if (!EVP_PKEY_assign_DSA(key, dsa)) {
 		DSA_free(dsa);
@@ -263,7 +311,7 @@ static int dsa_sign_write(const knot_dnssec_sign_context_t *context,
 	uint8_t *raw_signature;
 	size_t raw_signature_size;
 
-	result = any_sign_finish(context, &raw_signature, &raw_signature_size);
+	result = any_sign_write(context, &raw_signature, &raw_signature_size);
 	if (result != KNOT_EOK) {
 		return result;
 	}
@@ -292,7 +340,8 @@ static int dsa_sign_write(const knot_dnssec_sign_context_t *context,
 	uint8_t *signature_r = signature + 21 - BN_num_bytes(decoded->r);
 	uint8_t *signature_s = signature + 41 - BN_num_bytes(decoded->s);
 
-	*signature_t = 0x00; //! \todo How to compute T? (Only recommended.)
+	memset(signature, '\0', dsa_sign_size(context->key));
+	*signature_t = 0x00; //! \todo Take from public key. Only recommended.
 	BN_bn2bin(decoded->r, signature_r);
 	BN_bn2bin(decoded->s, signature_s);
 
@@ -301,16 +350,98 @@ static int dsa_sign_write(const knot_dnssec_sign_context_t *context,
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Verify the DNSSEC signature for supplied data and DSA algorithm.
+ * \see any_sign_verify
+ */
+static int dsa_sign_verify(const knot_dnssec_sign_context_t *context,
+                           const uint8_t *signature, size_t signature_size)
+{
+	assert(context);
+	assert(signature);
+
+	if (signature_size != dsa_sign_size(context->key)) {
+		return KNOT_EINVAL;
+	}
+
+	// see dsa_sign_write() for conversion details
+
+	// T (1 byte), R (20 bytes), S (20 bytes)
+	const uint8_t *signature_r = signature + 1;
+	const uint8_t *signature_s = signature + 21;
+
+	DSA_SIG *decoded = DSA_SIG_new();
+	if (!decoded) {
+		return KNOT_ENOMEM;
+	}
+
+	decoded->r = BN_bin2bn(signature_r, 20, NULL);
+	decoded->s = BN_bin2bn(signature_s, 20, NULL);
+
+	size_t max_size = EVP_PKEY_size(context->key->data->private_key);
+	uint8_t *raw_signature = malloc(max_size);
+	if (!raw_signature) {
+		DSA_SIG_free(decoded);
+		return KNOT_ENOMEM;
+	}
+
+	uint8_t *raw_write = raw_signature;
+	int raw_size = i2d_DSA_SIG(decoded, &raw_write);
+	if (raw_size < 0) {
+		free(raw_signature);
+		DSA_SIG_free(decoded);
+		return KNOT_DNSSEC_EDECODE_RAW_SIGNATURE;
+	}
+	assert(raw_write == raw_signature + raw_size);
+
+	int result = any_sign_verify(context, raw_signature, raw_size);
+
+	DSA_SIG_free(decoded);
+	free(raw_signature);
+
+	return result;
+}
+
 /*- EC specific --------------------------------------------------------------*/
 
 #ifndef OPENSSL_NO_ECDSA
 
+/*!
+ * \brief Decode ECDSA public key from RDATA and set it into EC key.
+ * \note DNSKEY format for ECDSA is described in RFC 6605 section 4.
+ */
+static int ecdsa_set_public_key(const knot_binary_t *rdata, EC_KEY *ec_key)
+{
+	assert(rdata);
+	assert(ec_key);
+
+	if (rdata->size % 2 != 0) {
+		return KNOT_EINVAL;
+	}
+
+	size_t param_size = rdata->size / 2;
+	uint8_t *x_ptr = rdata->data;
+	uint8_t *y_ptr = rdata->data + param_size;
+
+	BIGNUM *x = BN_bin2bn(x_ptr, param_size, NULL);
+	BIGNUM *y = BN_bin2bn(y_ptr, param_size, NULL);
+
+	if (EC_KEY_set_public_key_affine_coordinates(ec_key, x, y) != 1) {
+		BN_free(x);
+		BN_free(y);
+		return KNOT_DNSSEC_EINVALID_KEY;
+	}
+
+	return KNOT_EOK;
+}
+
 /*!
  * \brief Create ECDSA private key from key parameters.
  * \see rsa_create_pkey
  */
 static int ecdsa_create_pkey(const knot_key_params_t *params, EVP_PKEY *key)
 {
+	assert(params);
 	assert(key);
 
 	int curve;
@@ -323,12 +454,25 @@ static int ecdsa_create_pkey(const knot_key_params_t *params, EVP_PKEY *key)
 	}
 
 	EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve);
-	if (ec_key == NULL)
+	if (ec_key == NULL) {
 		return KNOT_ENOMEM;
+	}
+
+	int result = ecdsa_set_public_key(&params->rdata, ec_key);
+	if (result != KNOT_EOK) {
+		EC_KEY_free(ec_key);
+		return result;
+	}
 
-	EC_KEY_set_private_key(ec_key, knot_b64_to_bignum(params->private_key));
+	if (EC_KEY_set_private_key(ec_key, binary_to_bn(&params->private_key)) != 1) {
+		EC_KEY_free(ec_key);
+		return KNOT_DNSSEC_EINVALID_KEY;
+	}
 
-	// EC_KEY_check_key() could be added, but fails without public key
+	if (EC_KEY_check_key(ec_key) != 1) {
+		EC_KEY_free(ec_key);
+		return KNOT_DNSSEC_EINVALID_KEY;
+	}
 
 	if (!EVP_PKEY_assign_EC_KEY(key, ec_key)) {
 		EC_KEY_free(ec_key);
@@ -373,7 +517,7 @@ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context,
 	uint8_t *raw_signature;
 	size_t raw_signature_size;
 
-	result = any_sign_finish(context, &raw_signature, &raw_signature_size);
+	result = any_sign_write(context, &raw_signature, &raw_signature_size);
 	if (result != KNOT_EOK) {
 		return result;
 	}
@@ -400,8 +544,10 @@ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context,
 
 	uint8_t *signature_r;
 	uint8_t *signature_s;
-	size_t param_size = ecdsa_sign_size(context->key) / 2;
+	size_t signature_size = ecdsa_sign_size(context->key);
+	size_t param_size = signature_size / 2;
 
+	memset(signature, '\0', signature_size);
 	signature_r = signature + param_size - BN_num_bytes(decoded->r);
 	signature_s = signature + 2 * param_size - BN_num_bytes(decoded->s);
 
@@ -413,6 +559,58 @@ static int ecdsa_sign_write(const knot_dnssec_sign_context_t *context,
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Verify the DNSSEC signature for supplied data and ECDSA algorithm.
+ * \see any_sign_verify
+ */
+static int ecdsa_sign_verify(const knot_dnssec_sign_context_t *context,
+                             const uint8_t *signature, size_t signature_size)
+{
+	assert(context);
+	assert(signature);
+
+	if (signature_size != ecdsa_sign_size(context->key)) {
+		return KNOT_EINVAL;
+	}
+
+	// see ecdsa_sign_write() for conversion details
+
+	size_t parameter_size = signature_size / 2;
+	const uint8_t *signature_r = signature;
+	const uint8_t *signature_s = signature + parameter_size;
+
+	ECDSA_SIG *decoded = ECDSA_SIG_new();
+	if (!decoded) {
+		return KNOT_ENOMEM;
+	}
+
+	decoded->r = BN_bin2bn(signature_r, parameter_size, NULL);
+	decoded->s = BN_bin2bn(signature_s, parameter_size, NULL);
+
+	size_t max_size = EVP_PKEY_size(context->key->data->private_key);
+	uint8_t *raw_signature = malloc(max_size);
+	if (!raw_signature) {
+		ECDSA_SIG_free(decoded);
+		return KNOT_ENOMEM;
+	}
+
+	uint8_t *raw_write = raw_signature;
+	int raw_size = i2d_ECDSA_SIG(decoded, &raw_write);
+	if (raw_size < 0) {
+		free(raw_signature);
+		ECDSA_SIG_free(decoded);
+		return KNOT_DNSSEC_EDECODE_RAW_SIGNATURE;
+	}
+	assert(raw_write == raw_signature + raw_size);
+
+	int result = any_sign_verify(context, raw_signature, raw_size);
+
+	ECDSA_SIG_free(decoded);
+	free(raw_signature);
+
+	return result;
+}
+
 #endif
 
 /*- Algorithm specifications -------------------------------------------------*/
@@ -421,14 +619,16 @@ static const algorithm_functions_t rsa_functions = {
 	rsa_create_pkey,
 	any_sign_size,
 	any_sign_add,
-	rsa_sign_write
+	rsa_sign_write,
+	any_sign_verify
 };
 
 static const algorithm_functions_t dsa_functions = {
 	dsa_create_pkey,
 	dsa_sign_size,
 	any_sign_add,
-	dsa_sign_write
+	dsa_sign_write,
+	dsa_sign_verify
 };
 
 #ifndef OPENSSL_NO_ECDSA
@@ -436,7 +636,8 @@ static const algorithm_functions_t ecdsa_functions = {
 	ecdsa_create_pkey,
 	ecdsa_sign_size,
 	any_sign_add,
-	ecdsa_sign_write
+	ecdsa_sign_write,
+	ecdsa_sign_verify
 };
 #endif
 
@@ -515,11 +716,14 @@ static int create_pkey(const knot_key_params_t *params,
                        const algorithm_functions_t *functions,
                        EVP_PKEY **result_key)
 {
+	assert(params);
+	assert(functions);
         assert(result_key);
 
 	EVP_PKEY *private_key = EVP_PKEY_new();
-	if (!private_key)
+	if (!private_key) {
 		return KNOT_ENOMEM;
+	}
 
 	int result = functions->create_pkey(params, private_key);
 	if (result != KNOT_EOK) {
@@ -542,17 +746,20 @@ static int create_pkey(const knot_key_params_t *params,
 static int create_digest_context(const knot_dnssec_key_t *key,
                                  EVP_MD_CTX **result_context)
 {
+	assert(key);
 	assert(result_context);
 
 	const EVP_MD *digest_type = get_digest_type(key->algorithm);
-	if (digest_type == NULL)
+	if (digest_type == NULL) {
 		return KNOT_DNSSEC_ENOTSUP;
+	}
 
 	EVP_MD_CTX *context = EVP_MD_CTX_create();
-	if (!context)
+	if (!context) {
 		return KNOT_ENOMEM;
+	}
 
-	if (!EVP_SignInit_ex(context, digest_type, NULL)) {
+	if (!EVP_DigestInit_ex(context, digest_type, NULL)) {
 		EVP_MD_CTX_destroy(context);
 		return KNOT_DNSSEC_ECREATE_DIGEST_CONTEXT;
 	}
@@ -614,8 +821,9 @@ static int init_algorithm_data(const knot_key_params_t *params,
 	assert(data);
 
 	data->functions = get_implementation(params->algorithm);
-	if (!data->functions)
+	if (!data->functions) {
 		return KNOT_DNSSEC_ENOTSUP;
+	}
 
 	int result = create_pkey(params, data->functions, &data->private_key);
 	if (result != KNOT_EOK) {
@@ -634,24 +842,35 @@ static int init_algorithm_data(const knot_key_params_t *params,
 int knot_dnssec_key_from_params(const knot_key_params_t *params,
                                 knot_dnssec_key_t *key)
 {
-	if (!key || !params)
+	if (!key || !params) {
 		return KNOT_EINVAL;
+	}
 
-	knot_dname_t *name = knot_dname_deep_copy(params->name);
-	if (!name)
+	knot_dname_t *name = knot_dname_copy(params->name);
+	if (!name) {
 		return KNOT_ENOMEM;
+	}
 
 	knot_dnssec_key_data_t *data;
 	data = calloc(1, sizeof(knot_dnssec_key_data_t));
 	if (!data) {
-		knot_dname_release(name);
+		knot_dname_free(&name);
 		return KNOT_ENOMEM;
 	}
 
-	int result = init_algorithm_data(params, data);
+	knot_binary_t rdata_copy = { 0 };
+	int result = knot_binary_dup(&params->rdata, &rdata_copy);
+	if (result != KNOT_EOK) {
+		knot_dname_free(&name);
+		free(data);
+		return result;
+	}
+
+	result = init_algorithm_data(params, data);
 	if (result != KNOT_EOK) {
-		knot_dname_release(name);
+		knot_dname_free(&name);
 		free(data);
+		knot_binary_free(&rdata_copy);
 		return result;
 	}
 
@@ -659,6 +878,7 @@ int knot_dnssec_key_from_params(const knot_key_params_t *params,
 	key->keytag = params->keytag;
 	key->algorithm = params->algorithm;
 	key->data = data;
+	key->dnskey_rdata = rdata_copy;
 
 	return KNOT_EOK;
 }
@@ -668,17 +888,19 @@ int knot_dnssec_key_from_params(const knot_key_params_t *params,
  */
 int knot_dnssec_key_free(knot_dnssec_key_t *key)
 {
-	if (!key)
+	if (!key) {
 		return KNOT_EINVAL;
+	}
 
-	if (key->name)
-		knot_dname_release(key->name);
+	knot_dname_free(&key->name);
 
 	if (key->data) {
 		clean_algorithm_data(key->data);
 		free(key->data);
 	}
 
+	knot_binary_free(&key->dnskey_rdata);
+
 	memset(key, '\0', sizeof(knot_dnssec_key_t));
 
 	return KNOT_EOK;
@@ -691,12 +913,14 @@ int knot_dnssec_key_free(knot_dnssec_key_t *key)
  */
 knot_dnssec_sign_context_t *knot_dnssec_sign_init(const knot_dnssec_key_t *key)
 {
-	if (!key)
+	if (!key) {
 		return NULL;
+	}
 
 	knot_dnssec_sign_context_t *context = malloc(sizeof(*context));
-	if (!context)
+	if (!context) {
 		return NULL;
+	}
 
 	context->key = key;
 
@@ -713,8 +937,9 @@ knot_dnssec_sign_context_t *knot_dnssec_sign_init(const knot_dnssec_key_t *key)
  */
 void knot_dnssec_sign_free(knot_dnssec_sign_context_t *context)
 {
-	if (!context)
+	if (!context) {
 		return;
+	}
 
 	context->key = NULL;
 	destroy_digest_context(&context->digest_context);
@@ -724,33 +949,63 @@ void knot_dnssec_sign_free(knot_dnssec_sign_context_t *context)
 /*!
  * \brief Get DNSSEC signature size.
  */
-size_t knot_dnssec_sign_size(knot_dnssec_key_t *key)
+size_t knot_dnssec_sign_size(const knot_dnssec_key_t *key)
 {
-	if (!key)
+	if (!key) {
 		return 0;
+	}
 
 	return key->data->functions->sign_size(key);
 }
 
+/**
+ * \brief Clean DNSSEC signing context to start a new signature.
+ */
+int knot_dnssec_sign_new(knot_dnssec_sign_context_t *context)
+{
+	if (!context) {
+		return KNOT_EINVAL;
+	}
+
+	destroy_digest_context(&context->digest_context);
+	return create_digest_context(context->key, &context->digest_context);
+}
+
 /*!
- * \brief Add data into DNSSEC signature.
+ * \brief Add data to be covered by DNSSEC signature.
  */
 int knot_dnssec_sign_add(knot_dnssec_sign_context_t *context,
                          const uint8_t *data, size_t data_size)
 {
-	if (!context || !context->key || !data)
+	if (!context || !context->key || !data) {
 		return KNOT_EINVAL;
+	}
 
 	return context->key->data->functions->sign_add(context, data, data_size);
 }
 
 /**
- * \brief Finish DNSSEC signing and write out the signature.
+ * \brief Write down the DNSSEC signature for supplied data.
  */
 int knot_dnssec_sign_write(knot_dnssec_sign_context_t *context, uint8_t *signature)
 {
-	if (!context || !context->key || !signature)
+	if (!context || !context->key || !signature) {
 		return KNOT_EINVAL;
+	}
 
 	return context->key->data->functions->sign_write(context, signature);
 }
+
+/**
+ * \brief Verify the DNSSEC signature for supplied data.
+ */
+int knot_dnssec_sign_verify(knot_dnssec_sign_context_t *context,
+			    const uint8_t *signature, size_t signature_size)
+{
+	if (!context || !context->key || !signature) {
+		return KNOT_EINVAL;
+	}
+
+	return context->key->data->functions->sign_verify(context, signature,
+	                                                  signature_size);
+}
diff --git a/src/libknot/sign/dnssec.h b/src/libknot/dnssec/sign.h
similarity index 72%
rename from src/libknot/sign/dnssec.h
rename to src/libknot/dnssec/sign.h
index bb9fe22147a1494eac8951f82e00ddb837039998..a0601caeef0209f85e7203ca503f41332bca6d1c 100644
--- a/src/libknot/sign/dnssec.h
+++ b/src/libknot/dnssec/sign.h
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -24,11 +24,13 @@
  * @{
  */
 
-#ifndef _KNOT_SIGN_DNSSEC_H_
-#define _KNOT_SIGN_DNSSEC_H_
+#ifndef _KNOT_DNSSEC_SIGN_H_
+#define _KNOT_DNSSEC_SIGN_H_
 
-#include "sign/key.h"
 #include "common/descriptor.h"
+#include "libknot/binary.h"
+#include "libknot/dnssec/algorithm.h"
+#include "libknot/dnssec/key.h"
 
 /*!
  * \brief Algorithm private key data and algorithm implementation (internal).
@@ -50,6 +52,7 @@ typedef struct {
 	uint16_t keytag;                   //!< Key tag (for fast lookup).
 	knot_dnssec_algorithm_t algorithm; //!< Algorithm identification.
 	knot_dnssec_key_data_t *data;      //!< Private key data.
+	knot_binary_t dnskey_rdata;        //!< DNSKEY RDATA.
 } knot_dnssec_key_t;
 
 /*- DNSSEC private key manipulation ------------------------------------------*/
@@ -101,10 +104,21 @@ void knot_dnssec_sign_free(knot_dnssec_sign_context_t *context);
  *
  * \return DNSSEC signature size. Zero in case of error.
  */
-size_t knot_dnssec_sign_size(knot_dnssec_key_t *key);
+size_t knot_dnssec_sign_size(const knot_dnssec_key_t *key);
+
+/**
+ * \brief Clean DNSSEC signing context to start a new signature.
+ *
+ * Need not be called after knot_dnssec_sign_init().
+ *
+ * \param context	DNSSEC signing context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_sign_new(knot_dnssec_sign_context_t *context);
 
 /*!
- * \brief Add data into DNSSEC signature.
+ * \brief Add data to be covered by DNSSEC signature.
  *
  * \param context    DNSSEC signing context.
  * \param data       Pointer to data to be added.
@@ -116,7 +130,7 @@ int knot_dnssec_sign_add(knot_dnssec_sign_context_t *context,
                          const uint8_t *data, size_t data_size);
 
 /**
- * \brief Finish DNSSEC signing and write out the signature.
+ * \brief Write down the DNSSEC signature for supplied data.
  *
  * \param context    DNSSEC signing context.
  * \param signature  Pointer to signature to be written.
@@ -126,6 +140,20 @@ int knot_dnssec_sign_add(knot_dnssec_sign_context_t *context,
 int knot_dnssec_sign_write(knot_dnssec_sign_context_t *context,
                            uint8_t *signature);
 
-#endif // _KNOT_SIGN_DNSSEC_H_
+/**
+ * \brief Verify the DNSSEC signature for supplied data.
+ *
+ * \param context         DNSSEC signing context.
+ * \param signature       Signature.
+ * \param signature_size  Size of the signature.
+ *
+ * \return Error code.
+ * \retval KNOT_EOK                        The signature is valid.
+ * \retval KNOT_DNSSEC_EINVALID_SIGNATURE  The signature is not valid.
+ */
+int knot_dnssec_sign_verify(knot_dnssec_sign_context_t *context,
+                            const uint8_t *signature, size_t signature_size);
+
+#endif // _KNOT_DNSSEC_SIGN_H_
 
 /*! @} */
diff --git a/src/libknot/dnssec/zone-events.c b/src/libknot/dnssec/zone-events.c
new file mode 100644
index 0000000000000000000000000000000000000000..ae5d5cac95842307d2fc016309f612854d8d10b2
--- /dev/null
+++ b/src/libknot/dnssec/zone-events.c
@@ -0,0 +1,254 @@
+/*  Copyright (C) 2013 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 <assert.h>
+#include <time.h>
+#include "knot/conf/conf.h"
+#include "knot/server/zones.h"
+#include "libknot/dnssec/zone-events.h"
+#include "libknot/dnssec/zone-nsec.h"
+#include "libknot/dnssec/zone-sign.h"
+#include "libknot/dnssec/zone-keys.h"
+#include "libknot/dnssec/policy.h"
+#include "libknot/zone/zone.h"
+#include "libknot/util/debug.h"
+
+static uint32_t time_now(void)
+{
+	return (uint32_t)time(NULL);
+}
+
+static void init_default_policy(knot_dnssec_policy_t *p,
+                                knot_update_serial_t soa_up)
+{
+	knot_dnssec_policy_t p_image = DEFAULT_DNSSEC_POLICY;
+	memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
+	p->soa_up = soa_up;
+}
+
+static void init_forced_policy(knot_dnssec_policy_t *p,
+                               knot_update_serial_t soa_up)
+{
+	knot_dnssec_policy_t p_image = FORCED_DNSSEC_POLICY;
+	memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
+	p->soa_up = soa_up;
+}
+
+static int init_dnssec_structs(const knot_zone_t *zone,
+                               knot_zone_keys_t *zone_keys,
+                               knot_dnssec_policy_t *policy,
+                               knot_update_serial_t soa_up, bool force)
+{
+	assert(zone);
+	assert(zone_keys);
+	assert(policy);
+
+	// Read zone keys from disk
+	bool nsec3_enabled = is_nsec3_enabled(zone->contents);
+	int result = load_zone_keys(conf()->dnssec_keydir,
+	                            zone->contents->apex->owner,
+	                            nsec3_enabled, zone_keys);
+	if (result != KNOT_EOK) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_zone_error("DNSSEC keys could not be loaded (%s). "
+		               "Not signing the %s zone!\n",
+		               knot_strerror(result), zname);
+		free(zname);
+		free_zone_keys(zone_keys);
+		return result;
+	}
+
+	// Init sign policy
+	if (force) {
+		init_forced_policy(policy, soa_up);
+	} else {
+		init_default_policy(policy, soa_up);
+	}
+
+	// Override signature lifetime, if set in config
+	int sig_lf = ((zonedata_t *)knot_zone_data(zone))->conf->sig_lifetime;
+	if (sig_lf > 0) {
+		policy->sign_lifetime = sig_lf;
+	}
+
+	return KNOT_EOK;
+}
+
+static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
+                     knot_update_serial_t soa_up, uint32_t *expires_at)
+{
+	assert(zone);
+	assert(zone->contents);
+	assert(out_ch);
+
+	dbg_dnssec_verb("Changeset emtpy before generating NSEC chain: %d\n",
+	                knot_changeset_is_empty(out_ch));
+
+	conf_zone_t *zone_config = ((zonedata_t *)knot_zone_data(zone))->conf;
+	if (!zone_config->dnssec_enable) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_server_warning("DNSSEC not enabled for '%s'.\n", zname);
+		free(zname);
+		return KNOT_EOK;
+	}
+
+	// Init needed structs
+	knot_zone_keys_t zone_keys = { '\0' };
+	knot_dnssec_policy_t policy = { '\0' };
+	int result = init_dnssec_structs(zone, &zone_keys, &policy, soa_up,
+	                                 force);
+	if (result != KNOT_EOK) {
+		log_zone_error("Failed to init DNSSEC signer (%s)\n",
+		               knot_strerror(result));
+		return result;
+	}
+
+	// generate NSEC records
+	result = knot_zone_create_nsec_chain(zone->contents, out_ch,
+	                                     &zone_keys, &policy);
+	if (result != KNOT_EOK) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_zone_error("Could not create NSEC(3) chain (%s). "
+		               "Not signing the %s zone!\n",
+		               knot_strerror(result), zname);
+		free(zname);
+		free_zone_keys(&zone_keys);
+		return result;
+	}
+	dbg_dnssec_verb("Changeset emtpy after generating NSEC chain: %d\n",
+	                knot_changeset_is_empty(out_ch));
+
+	// add missing signatures
+	result = knot_zone_sign(zone->contents, &zone_keys, &policy, out_ch,
+	                        expires_at);
+	if (result != KNOT_EOK) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_zone_error("Error signing zone %s (%s).\n",
+		               zname, knot_strerror(result));
+		free(zname);
+		free_zone_keys(&zone_keys);
+		return result;
+	}
+	dbg_dnssec_verb("Changeset emtpy after signing: %d\n",
+	                knot_changeset_is_empty(out_ch));
+
+	// Check if only SOA changed
+	if (knot_changeset_is_empty(out_ch) &&
+	    !knot_zone_sign_soa_expired(zone->contents, &zone_keys, &policy)) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_server_info("No signing performed, zone %s is valid.\n",
+		                zname);
+		free(zname);
+		free_zone_keys(&zone_keys);
+		assert(knot_changeset_is_empty(out_ch));
+		return KNOT_EOK;
+	}
+
+	// update SOA if there were any changes
+	const knot_rrset_t *soa = knot_node_rrset(zone->contents->apex,
+	                                          KNOT_RRTYPE_SOA);
+	assert(soa);
+	result = knot_zone_sign_update_soa(soa, &zone_keys, &policy,
+	                                   out_ch);
+	if (result != KNOT_EOK) {
+		char *zname = knot_dname_to_str(zone->name);
+		log_server_error("Cannot update SOA record (%s)."
+		                 " Not signing the %s zone!\n",
+		                 knot_strerror(result), zname);
+		free(zname);
+		free_zone_keys(&zone_keys);
+		return result;
+	}
+
+	free_zone_keys(&zone_keys);
+
+	return KNOT_EOK;
+}
+
+int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
+                          knot_update_serial_t soa_up, uint32_t *expires_at)
+{
+	if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return zone_sign(zone, out_ch, false, soa_up, expires_at);
+}
+
+int knot_dnssec_zone_sign_force(knot_zone_t *zone,
+                                knot_changeset_t *out_ch, uint32_t *expires_at)
+{
+	if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_INC, expires_at);
+}
+
+int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
+                               const knot_changeset_t *in_ch,
+                               knot_changeset_t *out_ch,
+                               knot_update_serial_t soa_up)
+{
+	if (!conf()->dnssec_enable) {
+		return KNOT_EOK;
+	}
+
+	if (zone == NULL || in_ch == NULL || out_ch == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Init needed structures
+	knot_zone_keys_t zone_keys = { '\0' };
+	knot_dnssec_policy_t policy = { '\0' };
+	int ret = init_dnssec_structs(zone->zone, &zone_keys, &policy, soa_up,
+	                              false);
+	if (ret != KNOT_EOK) {
+		log_zone_error("Failed to init DNSSEC signer (%s)\n",
+		               knot_strerror(ret));
+		return ret;
+	}
+
+	// Fix NSEC(3) chain
+	ret = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy);
+	if (ret != KNOT_EOK) {
+		log_zone_error("Failed to fix NSEC(3) chain (%s)\n",
+		               knot_strerror(ret));
+		free_zone_keys(&zone_keys);
+		return ret;
+	}
+
+	// Sign added and removed RRSets in changeset
+	ret = knot_zone_sign_changeset(zone, in_ch, out_ch, &zone_keys,
+	                               &policy);
+	if (ret != KNOT_EOK) {
+		log_zone_error("Failed to sign changeset (%s)\n",
+		               knot_strerror(ret));
+		free_zone_keys(&zone_keys);
+		return ret;
+	}
+
+	// Update SOA RRSIGs
+	ret = knot_zone_sign_update_soa(in_ch->soa_to, &zone_keys, &policy,
+	                                out_ch);
+	if (ret != KNOT_EOK) {
+		log_zone_error("Failed to sign SOA RR (%s)\n",
+		               knot_strerror(ret));
+	}
+	free_zone_keys(&zone_keys);
+	return ret;
+}
diff --git a/src/libknot/dnssec/zone-events.h b/src/libknot/dnssec/zone-events.h
new file mode 100644
index 0000000000000000000000000000000000000000..7043be298a02b4ab8beae10e5ec04b0427ce039d
--- /dev/null
+++ b/src/libknot/dnssec/zone-events.h
@@ -0,0 +1,77 @@
+/*  Copyright (C) 2013 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 zone-events.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief DNSSEC operations triggered on zone events.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+#ifndef _KNOT_DNSSEC_ZONE_EVENTS_H_
+#define _KNOT_DNSSEC_ZONE_EVENTS_H_
+
+#include "libknot/zone/zone.h"
+#include "libknot/updates/changesets.h"
+#include "libknot/dnssec/policy.h"
+/*!
+ * \brief DNSSEC resign zone, store new records into changeset. Valid signatures
+ *        and NSEC(3) records will not be changed.
+ *
+ * \param zone        Zone to be signed.
+ * \param out_ch      New records will be added to this changeset.
+ * \param soa_up      SOA serial update policy.
+ * \param expires_at  Expiration time of the oldest signature in zone
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
+                          knot_update_serial_t soa_up, uint32_t *expires_at);
+
+/*!
+ * \brief DNSSEC sign zone, store new records into changeset. Even valid
+ *        signatures will be dropped.
+ *
+ * \param zone    Zone to be signed.
+ * \param out_ch  New records will be added to this changeset.
+ * \param expires_at  Expiration time of the oldest signature in zone
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_zone_sign_force(knot_zone_t *zone, knot_changeset_t *out_ch,
+                                uint32_t *expires_at);
+
+/*!
+ * \brief Sign changeset created by DDNS or zone-diff.
+ *
+ * \param zone    Contents of the updated zone (AFTER zone is switched).
+ * \param in_ch   Changeset created bvy DDNS or zone-diff
+ * \param out_ch  New records will be added to this changeset.
+ * \param soa_up  SOA serial update policy.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
+                               const knot_changeset_t *in_ch,
+                               knot_changeset_t *out_ch,
+                               knot_update_serial_t soa_up);
+
+#endif // _KNOT_DNSSEC_ZONE_EVENTS_H_
+/*! @} */
diff --git a/src/libknot/dnssec/zone-keys.c b/src/libknot/dnssec/zone-keys.c
new file mode 100644
index 0000000000000000000000000000000000000000..f83a2e01e4cc7c6aa35e8dff97ba91ed1d69111e
--- /dev/null
+++ b/src/libknot/dnssec/zone-keys.c
@@ -0,0 +1,238 @@
+/*  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 <assert.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include "common/errcode.h"
+#include "libknot/dname.h"
+#include "libknot/dnssec/algorithm.h"
+#include "libknot/dnssec/nsec3.h"
+#include "libknot/dnssec/sign.h"
+#include "libknot/dnssec/zone-keys.h"
+#include "libknot/util/debug.h"
+
+/*!
+ * \brief Free DNSSEC signing context for each key.
+ */
+static void free_sign_contexts(knot_zone_keys_t *keys)
+{
+	assert(keys);
+
+	for (int i = 0; i < keys->count; i++) {
+		knot_dnssec_sign_free(keys->contexts[i]);
+		keys->contexts[i] = NULL;
+	}
+}
+
+
+/*!
+ * \brief Initialize DNSSEC signing context for each key.
+ */
+static int init_sign_contexts(knot_zone_keys_t *keys)
+{
+	assert(keys);
+
+	for (int i = 0; i < keys->count; i++) {
+		keys->contexts[i] = knot_dnssec_sign_init(&keys->keys[i]);
+		if (keys->contexts[i] == NULL) {
+			free_sign_contexts(keys);
+			return KNOT_ENOMEM;
+		}
+	}
+
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Check if the key is in active period.
+ */
+static bool is_current_key(const knot_key_params_t *key)
+{
+	assert(key);
+
+	time_t now = time(NULL);
+
+	if (now < key->time_activate) {
+		return false;
+	}
+
+	if (key->time_inactive && now > key->time_inactive) {
+		return false;
+	}
+
+	return true;
+}
+
+/*!
+ * \brief Get zone key by a keytag.
+ */
+const knot_dnssec_key_t *get_zone_key(const knot_zone_keys_t *keys,
+                                      uint16_t keytag)
+{
+	if (!keys) {
+		return NULL;
+	}
+
+	const knot_dnssec_key_t *result = NULL;
+
+	for (int i = 0; i < keys->count; i++) {
+		if (keys->keys[i].keytag == keytag) {
+			result = &keys->keys[i];
+			break;
+		}
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Load zone keys from a key directory.
+ *
+ * \todo Maybe use dynamic list instead of fixed size array.
+ */
+int load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name,
+		   bool nsec3_enabled, knot_zone_keys_t *keys)
+{
+	if (!keydir_name || !zone_name || !keys) {
+		return KNOT_EINVAL;
+	}
+
+	DIR *keydir = opendir(keydir_name);
+	if (!keydir) {
+		return KNOT_DNSSEC_EINVALID_KEY;
+	}
+
+	struct dirent entry_buf = { 0 };
+	struct dirent *entry = NULL;
+	while (keys->count < KNOT_MAX_ZONE_KEYS &&
+	       readdir_r(keydir, &entry_buf, &entry) == 0 &&
+	       entry != NULL) {
+
+		char *suffix = strrchr(entry->d_name, '.');
+		if (!suffix) {
+			continue;
+		}
+
+		if (strcmp(suffix, ".private") != 0) {
+			continue;
+		}
+
+		size_t path_len = strlen(keydir_name) + 1 + strlen(entry->d_name);
+		char *path = malloc((path_len + 1) * sizeof(char));
+		if (!path) {
+			dbg_dnssec_detail("failed to allocate key path\n");
+			continue;
+		}
+
+		int written = snprintf(path, path_len + 1, "%s/%s",
+		                       keydir_name, entry->d_name);
+		UNUSED(written);
+		assert(written == path_len);
+
+		dbg_dnssec_detail("loading key '%s'\n", path);
+
+		knot_key_params_t params = { 0 };
+		int result = knot_load_key_params(path, &params);
+		free(path);
+
+		if (result != KNOT_EOK) {
+			dbg_dnssec_detail("failed to load key parameters (%s)\n",
+			                  knot_strerror(result));
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		if (!knot_dname_is_equal(zone_name, params.name)) {
+			dbg_dnssec_detail("skipping key, different zone name\n");
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		if (!is_current_key(&params)) {
+			dbg_dnssec_detail("skipping key, inactive period\n");
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		if (knot_get_key_type(&params) != KNOT_KEY_DNSSEC) {
+			dbg_dnssec_detail("skipping key, different purpose\n");
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		if (!knot_dnssec_algorithm_is_zonesign(params.algorithm,
+		                                       nsec3_enabled)
+		) {
+			dbg_dnssec_detail("skipping key, incompatible algorithm\n");
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		if (get_zone_key(keys, params.keytag) != NULL) {
+			dbg_dnssec_detail("skipping key, duplicate keytag\n");
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		result = knot_dnssec_key_from_params(&params,
+		                                     &keys->keys[keys->count]);
+		if (result != KNOT_EOK) {
+			dbg_dnssec_detail("cannot create DNSSEC key (%s)\n",
+			                  knot_strerror(result));
+			knot_free_key_params(&params);
+			continue;
+		}
+
+		dbg_dnssec_detail("key is valid, tag %d, %s\n", params.keytag,
+		                  (params.flags & 1 ? "KSK" : "ZSK"));
+
+		keys->is_ksk[keys->count] = params.flags & 1;
+		keys->count += 1;
+
+		knot_free_key_params(&params);
+	}
+
+	closedir(keydir);
+
+	if (keys->count == 0) {
+		return KNOT_DNSSEC_EINVALID_KEY;
+	}
+
+	int result = init_sign_contexts(keys);
+	if (result != KNOT_EOK) {
+		dbg_dnssec_detail("init_sign_contexts() failed (%s)\n",
+		                  knot_strerror(result));
+		free_zone_keys(keys);
+		return result;
+	}
+
+	return KNOT_EOK;
+}
+
+void free_zone_keys(knot_zone_keys_t *keys)
+{
+	if (!keys) {
+		return;
+	}
+
+	free_sign_contexts(keys);
+
+	for (int i = 0; i < keys->count; i++) {
+		knot_dnssec_key_free(&keys->keys[i]);
+	}
+}
diff --git a/src/libknot/dnssec/zone-keys.h b/src/libknot/dnssec/zone-keys.h
new file mode 100644
index 0000000000000000000000000000000000000000..edd380ecadca819b5649ac91e397f8fb43a9f6e8
--- /dev/null
+++ b/src/libknot/dnssec/zone-keys.h
@@ -0,0 +1,83 @@
+/*  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 zone-keys.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Loading of zone keys.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_ZONE_KEYS_H_
+#define _KNOT_DNSSEC_ZONE_KEYS_H_
+
+#include <stdbool.h>
+#include "libknot/dname.h"
+#include "libknot/dnssec/sign.h"
+
+/*!
+ * Maximal count of active keys for one zone.
+ */
+#define KNOT_MAX_ZONE_KEYS 8
+
+/*!
+ * \brief Keys used for zone signing.
+ */
+typedef struct {
+	unsigned count;
+	knot_dnssec_key_t keys[KNOT_MAX_ZONE_KEYS];
+	knot_dnssec_sign_context_t *contexts[KNOT_MAX_ZONE_KEYS];
+	bool is_ksk[KNOT_MAX_ZONE_KEYS];
+} knot_zone_keys_t;
+
+/*!
+ * \brief Load zone keys from a key directory.
+ *
+ * \param keydir_name    Name of the directory with DNSSEC keys.
+ * \param zone_name      Domain name of the zone.
+ * \param nsec3_enabled  NSEC3 enabled for zone (determines allowed algorithms).
+ * \param keys           Structure with loaded keys.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name,
+		   bool nsec3_enabled, knot_zone_keys_t *keys);
+/*!
+ * \brief Get zone key by a keytag.
+ *
+ * \param keys    Zone keys.
+ * \param keytag  Keytag to lookup a key for.
+ *
+ * \return Pointer to key or NULL if not found.
+ */
+const knot_dnssec_key_t *get_zone_key(const knot_zone_keys_t *keys,
+                                      uint16_t keytag);
+
+/*!
+ * \brief Free structure with zone keys and associated DNSSEC contexts.
+ *
+ * \param keys    Zone keys.
+ */
+void free_zone_keys(knot_zone_keys_t *keys);
+
+#endif // _KNOT_DNSSEC_ZONE_KEYS_H_
+
+/*! @} */
diff --git a/src/libknot/dnssec/zone-nsec.c b/src/libknot/dnssec/zone-nsec.c
new file mode 100644
index 0000000000000000000000000000000000000000..0830b7535403dae872d829b895cc6ccdd8b7f4c5
--- /dev/null
+++ b/src/libknot/dnssec/zone-nsec.c
@@ -0,0 +1,926 @@
+/*  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 <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+
+#include "common/base32hex.h"
+#include "common/descriptor.h"
+#include "libknot/dnssec/nsec-bitmap.h"
+#include "libknot/dnssec/nsec3.h"
+#include "libknot/dnssec/zone-nsec.h"
+#include "libknot/dnssec/zone-sign.h"
+#include "libknot/rdata.h"
+#include "libknot/util/debug.h"
+#include "libknot/util/utils.h"
+#include "libknot/zone/zone-contents.h"
+#include "libknot/zone/zone-diff.h"
+
+/*!
+ * \brief Parameters to be used in connect_nsec_nodes callback.
+ */
+typedef struct {
+	uint32_t ttl;
+	knot_changeset_t *changeset;
+	const knot_zone_contents_t *zone;
+} nsec_chain_iterate_data_t;
+
+#define NSEC_NODE_SKIP 1
+
+/* - NSEC chain iteration -------------------------------------------------- */
+
+typedef int (*chain_iterate_cb)(knot_node_t *, knot_node_t *, void *);
+
+/*!
+ * \brief Call a function for each piece of the chain formed by sorted nodes.
+ *
+ * \note If the callback function returns anything other than KNOT_EOK, the
+ *       iteration is terminated and the error code is propagated.
+ *
+ * \param nodes     Zone nodes.
+ * \param callback  Callback function.
+ * \param data      Custom data supplied to the callback function.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int chain_iterate(knot_zone_tree_t *nodes, chain_iterate_cb callback,
+                         void *data)
+{
+	assert(nodes);
+	assert(callback);
+
+	bool sorted = true;
+	hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted);
+
+	if (!it) {
+		return KNOT_ENOMEM;
+	}
+
+	if (hattrie_iter_finished(it)) {
+		hattrie_iter_free(it);
+		return KNOT_EINVAL;
+	}
+
+	/*!< \todo Remove direct hattrie calls. */
+	knot_node_t *first = (knot_node_t *)*hattrie_iter_val(it);
+	knot_node_t *previous = first;
+	knot_node_t *current = first;
+
+	hattrie_iter_next(it);
+
+	int result = KNOT_EOK;
+	while (!hattrie_iter_finished(it)) {
+		current = (knot_node_t *)*hattrie_iter_val(it);
+
+		result = callback(previous, current, data);
+		if (result == NSEC_NODE_SKIP) {
+			// No NSEC should be created for 'current' node, skip
+			;
+		} else if (result == KNOT_EOK) {
+			previous = current;
+		} else {
+			hattrie_iter_free(it);
+			return result;
+		}
+		hattrie_iter_next(it);
+	}
+
+	hattrie_iter_free(it);
+
+	return result == NSEC_NODE_SKIP ? callback(previous, first, data) :
+	                                  callback(current, first, data);
+}
+
+/*!
+ * \brief Add entry for removed NSEC to the changeset.
+ *
+ * \param oldrr      Old NSEC RR set to be removed (including RRSIG).
+ * \param changeset  Changeset to add the old RR into.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int changeset_remove_nsec(const knot_rrset_t *oldrr,
+                                 knot_changeset_t *changeset)
+{
+	assert(oldrr);
+	assert(changeset);
+
+	int result;
+
+	// extract copy of NSEC and RRSIG
+
+	knot_rrset_t *old_nsec = NULL;
+	knot_rrset_t *old_rrsigs = NULL;
+
+	result = knot_rrset_deep_copy(oldrr, &old_nsec);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	old_rrsigs = old_nsec->rrsigs;
+	old_nsec->rrsigs = NULL;
+
+	// update changeset
+
+	result = knot_changeset_add_rrset(changeset, old_nsec,
+	                                  KNOT_CHANGESET_REMOVE);
+	if (result != KNOT_EOK) {
+		knot_rrset_deep_free(&old_nsec, 1);
+		knot_rrset_deep_free(&old_rrsigs, 1);
+		return result;
+	}
+
+	if (old_rrsigs) {
+		result = knot_changeset_add_rrset(changeset, old_rrsigs,
+		                                  KNOT_CHANGESET_REMOVE);
+		if (result != KNOT_EOK) {
+			knot_rrset_deep_free(&old_rrsigs, 1);
+			return result;
+		}
+	}
+
+	return KNOT_EOK;
+}
+
+/* - NSEC nodes construction ----------------------------------------------- */
+
+/*!
+ * \brief Create NSEC RR set.
+ *
+ * \param from       Node that should contain the new RRSet
+ * \param to         Node that should be pointed to from 'from'
+ * \param ttl        Record TTL (SOA's minimun TTL).
+ * \param from_apex  Indicates that 'from' node is zone apex node.
+ *
+ * \return NSEC RR set, NULL on error.
+ */
+static knot_rrset_t *create_nsec_rrset(const knot_node_t *from,
+                                       const knot_node_t *to,
+                                       uint32_t ttl, bool from_apex)
+{
+	assert(from);
+	assert(to);
+
+	// Create new RRSet
+	knot_dname_t *owner_cpy = knot_dname_copy(from->owner);
+	knot_rrset_t *rrset = knot_rrset_new(owner_cpy,
+	                                     KNOT_RRTYPE_NSEC, KNOT_CLASS_IN,
+	                                     ttl);
+	if (!rrset) {
+		return NULL;
+	}
+
+	// Create bitmap
+	bitmap_t rr_types = { 0 };
+	bitmap_add_node_rrsets(&rr_types, from);
+	bitmap_add_type(&rr_types, KNOT_RRTYPE_NSEC);
+	bitmap_add_type(&rr_types, KNOT_RRTYPE_RRSIG);
+	if (from_apex) {
+		bitmap_add_type(&rr_types, KNOT_RRTYPE_DNSKEY);
+	}
+
+	// Create RDATA
+	assert(to->owner);
+	size_t next_owner_size = knot_dname_size(to->owner);
+	size_t rdata_size = next_owner_size + bitmap_size(&rr_types);
+	uint8_t *rdata = knot_rrset_create_rdata(rrset, rdata_size);
+	if (!rdata) {
+		knot_rrset_free(&rrset);
+		return NULL;
+	}
+
+	// Fill RDATA
+	memcpy(rdata, to->owner, next_owner_size);
+	bitmap_write(&rr_types, rdata + next_owner_size);
+
+	return rrset;
+}
+
+/*!
+ * \brief Connect two nodes by adding a NSEC RR into the first node.
+ *
+ * Callback function, signature chain_iterate_cb.
+ *
+ * \param a  First node.
+ * \param b  Second node (immediate follower of a).
+ * \param d  Pointer to nsec_chain_iterate_data_t holding parameters
+ *           including changeset.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int connect_nsec_nodes(knot_node_t *a, knot_node_t *b, void *d)
+{
+	assert(a);
+	assert(b);
+	assert(d);
+
+	if (b->rrset_count == 0 || knot_node_is_non_auth(b)) {
+		return NSEC_NODE_SKIP;
+	}
+
+	nsec_chain_iterate_data_t *data = (nsec_chain_iterate_data_t *)d;
+	knot_rrset_t *old_next_nsec = knot_node_get_rrset(b, KNOT_RRTYPE_NSEC);
+	int ret = 0;
+
+	/*!
+	 * If the node has no other RRSets than NSEC (and possibly RRSIG),
+	 * just remove the NSEC and its RRSIG, they are redundant
+	 */
+	if (old_next_nsec != NULL
+	    && knot_node_rrset_count(b) == KNOT_NODE_RRSET_COUNT_ONLY_NSEC) {
+		ret = changeset_remove_nsec(old_next_nsec, data->changeset);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
+		// Skip the 'b' node
+		return NSEC_NODE_SKIP;
+	}
+
+	// create new NSEC
+	bool a_is_apex = a == data->zone->apex;
+	knot_rrset_t *new_nsec = create_nsec_rrset(a, b, data->ttl, a_is_apex);
+	if (!new_nsec) {
+		dbg_dnssec_detail("Failed to create new NSEC.\n");
+		return KNOT_ENOMEM;
+	}
+
+	knot_rrset_t *old_nsec = knot_node_get_rrset(a, KNOT_RRTYPE_NSEC);
+	if (old_nsec != NULL) {
+		if (knot_rrset_equal(new_nsec, old_nsec,
+		                     KNOT_RRSET_COMPARE_WHOLE)) {
+			// current NSEC is valid, do nothing
+			dbg_dnssec_detail("NSECs equal.\n");
+			knot_rrset_deep_free(&new_nsec, 1);
+			return KNOT_EOK;
+		}
+
+		dbg_dnssec_detail("NSECs not equal, replacing.\n");
+		// current NSEC is invalid, replace it and drop RRSIG
+		// mark the node, so later we know this NSEC needs new RRSIGs
+		knot_node_set_replaced_nsec(a);
+		ret = changeset_remove_nsec(old_nsec, data->changeset);
+		if (ret != KNOT_EOK) {
+			knot_rrset_deep_free(&new_nsec, 1);
+			return ret;
+		}
+	}
+
+	dbg_dnssec_detail("Adding new NSEC to changeset.\n");
+	// Add new NSEC to the changeset (no matter if old was removed)
+	return knot_changeset_add_rrset(data->changeset, new_nsec,
+	                                KNOT_CHANGESET_ADD);
+}
+
+/*!
+ * \brief Create new NSEC chain, add differences from current into a changeset.
+ *
+ * \param zone       Zone.
+ * \param ttl        TTL for created NSEC records.
+ * \param changeset  Changeset the differences will be put into.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int create_nsec_chain(const knot_zone_contents_t *zone, uint32_t ttl,
+			     knot_changeset_t *changeset)
+{
+	assert(zone);
+	assert(zone->nodes);
+	assert(changeset);
+
+	nsec_chain_iterate_data_t data = { ttl, changeset, zone };
+
+	return chain_iterate(zone->nodes, connect_nsec_nodes, &data);
+}
+
+/* - NSEC3 nodes comparison ------------------------------------------------ */
+
+/*!
+ * \brief Perform some basic checks that the node is valid NSEC3 node.
+ */
+inline static bool valid_nsec3_node(const knot_node_t *node)
+{
+	assert(node);
+
+	if (node->rrset_count != 1) {
+		return false;
+	}
+
+	if (node->rrset_tree[0]->type != KNOT_RRTYPE_NSEC3) {
+		return false;
+	}
+
+	if (node->rrset_tree[0]->rdata_count != 1) {
+		return false;
+	}
+
+	return true;
+}
+
+/*!
+ * \brief Check if two nodes are equal.
+ */
+static bool are_nsec3_nodes_equal(const knot_node_t *a, const knot_node_t *b)
+{
+	assert(valid_nsec3_node(a));
+	assert(valid_nsec3_node(b));
+
+	knot_rrset_t *a_rrset = a->rrset_tree[0];
+	knot_rrset_t *b_rrset = b->rrset_tree[0];
+
+	return knot_rrset_equal(a_rrset, b_rrset, KNOT_RRSET_COMPARE_WHOLE);
+}
+
+/* - RRSIGs handling for NSEC3 --------------------------------------------- */
+
+/*!
+ * \brief Shallow copy NSEC3 signatures from the one node to the second one.
+ *
+ * Just sets the pointer, needed only for comparison.
+ */
+static void shallow_copy_signature(const knot_node_t *from, knot_node_t *to)
+{
+	assert(valid_nsec3_node(from));
+	assert(valid_nsec3_node(to));
+
+	knot_rrset_t *from_rrset = from->rrset_tree[0];
+	knot_rrset_t *to_rrset = to->rrset_tree[0];
+
+	assert(to_rrset->rrsigs == NULL);
+
+	to_rrset->rrsigs = from_rrset->rrsigs;
+}
+
+/*!
+ * \brief Reuse signatatures by shallow copying them from one tree to another.
+ */
+static void copy_signatures(const knot_zone_tree_t *from, knot_zone_tree_t *to)
+{
+	assert(from);
+	assert(to);
+
+	bool sorted = false;
+	/*! \todo Remove direct hattrie calls */
+	hattrie_iter_t *it = hattrie_iter_begin(from, sorted);
+
+	for (/* NOP */; !hattrie_iter_finished(it); hattrie_iter_next(it)) {
+		knot_node_t *node_from = (knot_node_t *)*hattrie_iter_val(it);
+		knot_node_t *node_to = NULL;
+
+		knot_zone_tree_get(to, node_from->owner, &node_to);
+		if (node_to == NULL) {
+			continue;
+		}
+
+		if (!are_nsec3_nodes_equal(node_from, node_to)) {
+			continue;
+		}
+
+		shallow_copy_signature(node_from, node_to);
+	}
+
+	hattrie_iter_free(it);
+}
+
+/* - NSEC3 names conversion ------------------------------------------------ */
+
+/*!
+ * \brief Create NSEC3 owner name from hash and zone apex.
+ *
+ * \param hash       Raw hash.
+ * \param hash_size  Size of the hash.
+ * \param zone_apex  Zone apex.
+ *
+ * \return NSEC3 owner name, NULL in case of error.
+ */
+static knot_dname_t *nsec3_hash_to_dname(const uint8_t *hash, size_t hash_size,
+					 const knot_dname_t *zone_apex)
+{
+	assert(hash);
+	assert(zone_apex);
+
+	// encode raw hash to first label
+
+	uint8_t label[KNOT_DNAME_MAX_LENGTH];
+	int32_t label_size;
+	label_size = base32hex_encode(hash, hash_size, label, sizeof(label));
+	if (label_size <= 0) {
+		return NULL;
+	}
+
+	// allocate result
+
+	size_t zone_apex_size = knot_dname_size(zone_apex);
+	size_t result_size = 1 + label_size + zone_apex_size;
+	knot_dname_t *result = malloc(result_size);
+	if (!result) {
+		return NULL;
+	}
+
+	// build the result
+
+	uint8_t *write = result;
+
+	*write = (uint8_t)label_size;
+	write += 1;
+	memcpy(write, label, label_size);
+	write += label_size;
+	memcpy(write, zone_apex, zone_apex_size);
+	write += zone_apex_size;
+
+	assert(write == result + result_size);
+	knot_dname_to_lower(result);
+
+	return result;
+}
+
+/* - NSEC3 nodes construction ---------------------------------------------- */
+
+/*!
+ * \brief Get NSEC3 RDATA size.
+ */
+static size_t nsec3_rdata_size(const knot_nsec3_params_t *params,
+                               const bitmap_t *rr_types)
+{
+	assert(params);
+	assert(rr_types);
+
+	return 6 + params->salt_length
+	       + knot_nsec3_hash_length(params->algorithm)
+	       + bitmap_size(rr_types);
+}
+
+/*!
+ * \brief Fill NSEC3 RDATA.
+ *
+ * \note Content of next hash field is not changed.
+ */
+static void nsec3_fill_rdata(uint8_t *rdata, const knot_nsec3_params_t *params,
+                             const bitmap_t *rr_types, uint32_t ttl)
+{
+	assert(rdata);
+	assert(params);
+	assert(rr_types);
+
+	uint8_t hash_length = knot_nsec3_hash_length(params->algorithm);
+
+	*rdata = params->algorithm;                       // hash algorithm
+	rdata += 1;
+	*rdata = 0;                                       // flags
+	rdata += 1;
+	knot_wire_write_u16(rdata, params->iterations);   // iterations
+	rdata += 2;
+	*rdata = params->salt_length;                     // salt length
+	rdata += 1;
+	memcpy(rdata, params->salt, params->salt_length); // salt
+	rdata += params->salt_length;
+	*rdata = hash_length;                             // hash length
+	rdata += 1;
+	/*memset(rdata, '\0', hash_len);*/                // hash (unknown)
+	rdata += hash_length;
+	bitmap_write(rr_types, rdata);                    // RR types bit map
+}
+
+/*!
+ * \brief Create NSEC3 RR set.
+ */
+static knot_rrset_t *create_nsec3_rrset(knot_dname_t *owner,
+                                        const knot_nsec3_params_t *params,
+                                        const bitmap_t *rr_types,
+                                        uint32_t ttl)
+{
+	assert(owner);
+	assert(params);
+	assert(rr_types);
+
+	knot_rrset_t *rrset;
+	rrset = knot_rrset_new(owner, KNOT_RRTYPE_NSEC3, KNOT_CLASS_IN, ttl);
+	if (!rrset) {
+		return NULL;
+	}
+
+	size_t rdata_size = nsec3_rdata_size(params, rr_types);
+	uint8_t *rdata = knot_rrset_create_rdata(rrset, rdata_size);
+	if (!rdata) {
+		knot_rrset_free(&rrset);
+		return NULL;
+	}
+
+	nsec3_fill_rdata(rdata, params, rr_types, ttl);
+
+	return rrset;
+}
+
+/*!
+ * \brief Create NSEC3 node.
+ */
+static knot_node_t *create_nsec3_node(knot_dname_t *owner,
+                                      const knot_nsec3_params_t *nsec3_params,
+                                      knot_node_t *apex_node,
+                                      const bitmap_t *rr_types,
+                                      uint32_t ttl)
+{
+	assert(owner);
+	assert(nsec3_params);
+	assert(apex_node);
+	assert(rr_types);
+
+	uint8_t flags = 0;
+	knot_node_t *new_node = knot_node_new(owner, apex_node, flags);
+	if (!new_node) {
+		return NULL;
+	}
+
+	knot_rrset_t *nsec3_rrset;
+	nsec3_rrset = create_nsec3_rrset(owner, nsec3_params, rr_types, ttl);
+	if (!nsec3_rrset) {
+		knot_node_free(&new_node);
+		return NULL;
+	}
+
+	if (knot_node_add_rrset_no_merge(new_node, nsec3_rrset) != KNOT_EOK) {
+		knot_rrset_free(&nsec3_rrset);
+		knot_node_free(&new_node);
+		return NULL;
+	}
+
+	return new_node;
+}
+
+/*!
+ * \brief Connect two nodes by filling 'hash' field of NSEC3 RDATA of the node.
+ *
+ * \param a     First node.
+ * \param b     Second node (immediate follower of a).
+ * \param data  Unused parameter.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int connect_nsec3_nodes(knot_node_t *a, knot_node_t *b, void *data)
+{
+	assert(a);
+	assert(b);
+	UNUSED(data);
+
+	assert(a->rrset_count == 1);
+
+	uint8_t algorithm = knot_rdata_nsec3_algorithm(a->rrset_tree[0], 0);
+	if (algorithm == 0) {
+		return KNOT_EINVAL;
+	}
+
+	uint8_t *raw_hash = NULL;
+	uint8_t raw_length = 0;
+	knot_rdata_nsec3_next_hashed(a->rrset_tree[0], 0, &raw_hash, &raw_length);
+	if (raw_hash == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	assert(raw_length == knot_nsec3_hash_length(algorithm));
+
+	uint8_t *b32_hash = (uint8_t *)knot_dname_to_str(b->owner);
+	size_t b32_length = knot_nsec3_hash_b32_length(algorithm);
+	if (!b32_hash) {
+		return KNOT_ENOMEM;
+	}
+
+	int32_t written = base32hex_decode(b32_hash, b32_length,
+	                                   raw_hash, raw_length);
+
+	free(b32_hash);
+
+	if (written != raw_length) {
+		return KNOT_EINVAL;
+	}
+
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Check whether at least one RR type in node should be signed
+ *
+ * \param node  Node for which the check is done.
+ *
+ * \return true/false.
+ */
+static bool node_should_be_signed(const knot_node_t *n)
+{
+	knot_rrset_t **node_rrsets = knot_node_get_rrsets_no_copy(n);
+	for (int i = 0; i < n->rrset_count; i++) {
+		if (knot_node_rr_should_be_signed(n, node_rrsets[i], NULL)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*!
+ * \brief Create new NSEC3 node for given regular node.
+ *
+ * \param node       Node for which the NSEC3 node is created.
+ * \param apex       Zone apex node.
+ * \param params     NSEC3 hash function parameters.
+ * \param ttl        TTL of the new NSEC3 node.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static knot_node_t *create_nsec3_node_for_node(knot_node_t *node,
+                                               knot_node_t *apex,
+                                               const knot_nsec3_params_t *params,
+                                               uint32_t ttl)
+{
+	assert(node);
+	assert(apex);
+	assert(params);
+
+	knot_dname_t *nsec3_owner;
+	nsec3_owner = create_nsec3_owner(node->owner, apex->owner, params);
+	if (!nsec3_owner) {
+		return NULL;
+	}
+
+	bitmap_t rr_types = { 0 };
+	bitmap_add_node_rrsets(&rr_types, node);
+	if (node->rrset_count > 0 && node_should_be_signed(node)) {
+		bitmap_add_type(&rr_types, KNOT_RRTYPE_RRSIG);
+	}
+	if (node == apex) {
+		bitmap_add_type(&rr_types, KNOT_RRTYPE_DNSKEY);
+	}
+
+	knot_node_t *nsec3_node;
+	nsec3_node = create_nsec3_node(nsec3_owner, params, apex, &rr_types, ttl);
+
+	return nsec3_node;
+}
+
+/*!
+ * \brief Create NSEC3 node for each regular node in the zone.
+ *
+ * \param zone         Zone.
+ * \param ttl          TTL for the created NSEC records.
+ * \param nsec3_nodes  Tree whereto new NSEC3 nodes will be added.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int create_nsec3_nodes(const knot_zone_contents_t *zone, uint32_t ttl,
+                              knot_zone_tree_t *nsec3_nodes)
+{
+	assert(zone);
+	assert(nsec3_nodes);
+
+	const knot_nsec3_params_t *params = &zone->nsec3_params;
+
+	assert(params);
+
+	int result = KNOT_EOK;
+
+	int sorted = false;
+	hattrie_iter_t *it = hattrie_iter_begin(zone->nodes, sorted);
+	while (!hattrie_iter_finished(it)) {
+		knot_node_t *node = (knot_node_t *)*hattrie_iter_val(it);
+
+		if (knot_node_is_non_auth(node)) {
+			hattrie_iter_next(it);
+			continue;
+		}
+
+		knot_node_t *nsec3_node;
+		nsec3_node = create_nsec3_node_for_node(node, zone->apex,
+		                                        params, ttl);
+		if (!nsec3_node) {
+			result = KNOT_ENOMEM;
+			break;
+		}
+
+		result = knot_zone_tree_insert(nsec3_nodes, nsec3_node);
+		if (result != KNOT_EOK) {
+			break;
+		}
+
+		hattrie_iter_next(it);
+	}
+
+	hattrie_iter_free(it);
+
+	return result;
+}
+
+/*!
+ * \brief Custom NSEC3 tree free function.
+ *
+ * - Leaves RRSIGs, as these are only referenced (shallow copied).
+ * - Deep frees NSEC3 RRs, as these nodes were created.
+ *
+ */
+static void free_nsec3_tree(knot_zone_tree_t *nodes)
+{
+	assert(nodes);
+
+	bool sorted = false;
+	hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted);
+	for (/* NOP */; !hattrie_iter_finished(it); hattrie_iter_next(it)) {
+		knot_node_t *node = (knot_node_t *)*hattrie_iter_val(it);
+
+		for (int i = 0; i < node->rrset_count; i++) {
+			// referenced RRSIGs from old NSEC3 tree
+			node->rrset_tree[i]->rrsigs = NULL;
+			// newly allocated NSEC3 nodes
+			knot_rrset_deep_free(&node->rrset_tree[i], 1);
+		}
+
+		knot_node_free(&node);
+	}
+
+	hattrie_iter_free(it);
+	knot_zone_tree_free(&nodes);
+}
+
+/*!
+ * \brief Create new NSEC3 chain, add differences from current into a changeset.
+ */
+static int create_nsec3_chain(const knot_zone_contents_t *zone, uint32_t ttl,
+			      knot_changeset_t *changeset)
+{
+	assert(zone);
+	assert(changeset);
+
+	int result;
+
+	knot_zone_tree_t *nsec3_nodes = knot_zone_tree_create();
+	if (!nsec3_nodes) {
+		return KNOT_ENOMEM;
+	}
+
+	result = create_nsec3_nodes(zone, ttl, nsec3_nodes);
+	if (result != KNOT_EOK) {
+		free_nsec3_tree(nsec3_nodes);
+		return result;
+	}
+
+	result = chain_iterate(nsec3_nodes, connect_nsec3_nodes, NULL);
+	if (result != KNOT_EOK) {
+		free_nsec3_tree(nsec3_nodes);
+		return result;
+	}
+
+	copy_signatures(zone->nsec3_nodes, nsec3_nodes);
+
+	result = knot_zone_tree_add_diff(zone->nsec3_nodes, nsec3_nodes,
+	                                 changeset);
+
+	free_nsec3_tree(nsec3_nodes);
+
+	return result;
+}
+
+static int delete_nsec3_chain(const knot_zone_contents_t *zone,
+                              knot_changeset_t *changeset)
+{
+	assert(zone);
+	assert(zone->nsec3_nodes);
+	assert(changeset);
+
+	if (knot_zone_tree_is_empty(zone->nsec3_nodes)) {
+		return KNOT_EOK;
+	}
+
+	dbg_dnssec_detail("deleting NSEC3 chain\n");
+	knot_zone_tree_t *empty_tree = knot_zone_tree_create();
+	if (!empty_tree) {
+		return KNOT_ENOMEM;
+	}
+
+	int result = knot_zone_tree_add_diff(zone->nsec3_nodes, empty_tree,
+	                                     changeset);
+
+	knot_zone_tree_free(&empty_tree);
+
+	return result;
+}
+
+/* - helper functions ------------------------------------------------------ */
+
+/*!
+ * \brief Check if NSEC3 is enabled for given zone.
+ */
+bool is_nsec3_enabled(const knot_zone_contents_t *zone)
+{
+	if (!zone) {
+		return false;
+	}
+
+	return zone->nsec3_params.algorithm != 0;
+}
+
+/*!
+ * \brief Get minimum TTL from zone SOA.
+ * \note Value should be used for NSEC records.
+ */
+static bool get_zone_soa_min_ttl(const knot_zone_contents_t *zone,
+                                 uint32_t *ttl)
+{
+	assert(zone);
+	assert(zone->apex);
+	assert(ttl);
+
+	knot_node_t *apex = zone->apex;
+	knot_rrset_t *soa = knot_node_get_rrset(apex, KNOT_RRTYPE_SOA);
+	if (!soa) {
+		return false;
+	}
+
+	uint32_t result =  knot_rdata_soa_minimum(soa);
+	if (result == 0) {
+		return false;
+	}
+
+	*ttl = result;
+	return true;
+}
+
+/* - public API ------------------------------------------------------------ */
+
+/*!
+ * \brief Create NSEC3 owner name from regular owner name.
+ */
+knot_dname_t *create_nsec3_owner(const knot_dname_t *owner,
+				 const knot_dname_t *zone_apex,
+                                 const knot_nsec3_params_t *params)
+{
+	if (owner == NULL || zone_apex == NULL || params == NULL) {
+		return NULL;
+	}
+
+	uint8_t *hash = NULL;
+	size_t hash_size = 0;
+	int owner_size = knot_dname_size(owner);
+
+	if (owner_size < 0) {
+		return NULL;
+	}
+
+	if (knot_nsec3_hash(params, owner, owner_size, &hash, &hash_size) != KNOT_EOK) {
+		return NULL;
+	}
+
+	knot_dname_t *result = nsec3_hash_to_dname(hash, hash_size, zone_apex);
+	free(hash);
+
+	return result;
+}
+
+/*!
+ * \brief Create NSEC or NSEC3 chain in the zone.
+ */
+int knot_zone_create_nsec_chain(const knot_zone_contents_t *zone,
+                                knot_changeset_t *changeset,
+                                const knot_zone_keys_t *zone_keys,
+                                const knot_dnssec_policy_t *policy)
+{
+	if (!zone || !changeset) {
+		return KNOT_EINVAL;
+	}
+
+	uint32_t nsec_ttl = 0;
+	if (!get_zone_soa_min_ttl(zone, &nsec_ttl)) {
+		return KNOT_EINVAL;
+	}
+
+	int result;
+	bool nsec3_enabled = is_nsec3_enabled(zone);
+
+	if (nsec3_enabled) {
+		result = create_nsec3_chain(zone, nsec_ttl, changeset);
+	} else {
+		result = create_nsec_chain(zone, nsec_ttl, changeset);
+	}
+
+	if (result == KNOT_EOK && !nsec3_enabled) {
+		result = delete_nsec3_chain(zone, changeset);
+	}
+
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	// Sign newly created records right away
+	return knot_zone_sign_nsecs_in_changeset(zone_keys, policy, changeset);
+}
diff --git a/src/libknot/dnssec/zone-nsec.h b/src/libknot/dnssec/zone-nsec.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fe8113effd32101f9018d6f858e39f1cd597455
--- /dev/null
+++ b/src/libknot/dnssec/zone-nsec.h
@@ -0,0 +1,75 @@
+/*  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 zone-sign.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Interface for generating of NSEC/NSEC3 records in zone.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_ZONE_NSEC_H_
+#define _KNOT_DNSSEC_ZONE_NSEC_H_
+
+#include <stdbool.h>
+#include "libknot/updates/changesets.h"
+#include "libknot/dnssec/policy.h"
+#include "libknot/dnssec/zone-keys.h"
+#include "libknot/zone/zone-contents.h"
+
+/*!
+ * Check if NSEC3 is enabled for the given zone.
+ *
+ * \param zone  Zone to be checked.
+ *
+ * \return NSEC3 is enabled.
+ */
+bool is_nsec3_enabled(const knot_zone_contents_t *zone);
+
+/*!
+ * \brief Create NSEC3 owner name from regular owner name.
+ *
+ * \param owner      Node owner name.
+ * \param zone_apex  Zone apex name.
+ * \param params     Params for NSEC3 hashing function.
+ *
+ * \return NSEC3 owner name, NULL in case of error.
+ */
+knot_dname_t *create_nsec3_owner(const knot_dname_t *owner,
+                                 const knot_dname_t *zone_apex,
+                                 const knot_nsec3_params_t *params);
+
+/*!
+ * \brief Create NSEC or NSEC3 chain in the zone.
+ *
+ * \param zone       Zone for which the NSEC(3) chain will be created.
+ * \param changeset  Changeset into which the changes will be added.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_create_nsec_chain(const knot_zone_contents_t *zone,
+                                knot_changeset_t *changeset,
+                                const knot_zone_keys_t *zone_keys,
+                                const knot_dnssec_policy_t *policy);
+
+#endif // _KNOT_DNSSEC_ZONE_NSEC_H_
+
+/*! @} */
diff --git a/src/libknot/dnssec/zone-sign.c b/src/libknot/dnssec/zone-sign.c
new file mode 100644
index 0000000000000000000000000000000000000000..867ce69802258a2eeadd1c4b283a5c5e4b72df39
--- /dev/null
+++ b/src/libknot/dnssec/zone-sign.c
@@ -0,0 +1,1135 @@
+/*  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 <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+#include "common/descriptor.h"
+#include "common/errcode.h"
+#include "common/hattrie/hat-trie.h"
+#include "common/hattrie/ahtable.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/dnssec/zone-sign.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/policy.h"
+#include "libknot/dnssec/rrset-sign.h"
+#include "libknot/dnssec/sign.h"
+#include "libknot/dnssec/zone-keys.h"
+#include "libknot/rrset.h"
+#include "libknot/updates/changesets.h"
+#include "libknot/util/debug.h"
+#include "libknot/zone/node.h"
+#include "libknot/zone/zone-contents.h"
+
+/*- private API - common functions -------------------------------------------*/
+
+/*!
+ * \brief Create empty RRSIG RR set for a given RR set to be covered.
+ */
+static knot_rrset_t *create_empty_rrsigs_for(const knot_rrset_t *covered)
+{
+	assert(covered);
+
+	knot_dname_t *owner_copy = knot_dname_copy(covered->owner);
+
+	return knot_rrset_new(owner_copy, KNOT_RRTYPE_RRSIG, covered->rclass,
+	                      covered->ttl);
+}
+
+/*- private API - signing of in-zone nodes -----------------------------------*/
+
+/*!
+ * \brief Check if there is a valid signature for a given RR set and key.
+ *
+ * \param covered  RR set with covered records.
+ * \param rrsigs   RR set with RRSIGs.
+ * \param key      Signing key.
+ * \param ctx      Signing context.
+ * \param policy   DNSSEC policy.
+ *
+ * \return The signature exists and is valid.
+ */
+static bool valid_signature_exists(const knot_rrset_t *covered,
+				   const knot_rrset_t *rrsigs,
+				   const knot_dnssec_key_t *key,
+				   knot_dnssec_sign_context_t *ctx,
+				   const knot_dnssec_policy_t *policy)
+{
+	assert(key);
+
+	if (!rrsigs) {
+		return false;
+	}
+
+	for (int i = 0; i < rrsigs->rdata_count; i++) {
+		uint16_t keytag = knot_rdata_rrsig_key_tag(rrsigs, i);
+		if (keytag != key->keytag) {
+			continue;
+		}
+
+		return knot_is_valid_signature(covered, rrsigs, i, key, ctx,
+		                               policy) == KNOT_EOK;
+	}
+
+	return false;
+}
+
+/*!
+ * \brief Check if valid signature exist for all keys for a given RR set.
+ *
+ * \param covered    RR set with covered records.
+ * \param rrsigs     RR set with RRSIGs.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ *
+ * \return Valid signature exists for every key.
+ */
+static bool all_signatures_exist(const knot_rrset_t *covered,
+                                 const knot_rrset_t *rrsigs,
+                                 const knot_zone_keys_t *zone_keys,
+                                 const knot_dnssec_policy_t *policy)
+{
+	assert(covered);
+	assert(zone_keys);
+
+	bool use_ksk = covered->type == KNOT_RRTYPE_DNSKEY;
+	for (int i = 0; i < zone_keys->count; i++) {
+		if (zone_keys->is_ksk[i] && !use_ksk) {
+			continue;
+		}
+
+		const knot_dnssec_key_t *key = &zone_keys->keys[i];
+		knot_dnssec_sign_context_t *ctx = zone_keys->contexts[i];
+
+		if (!valid_signature_exists(covered, rrsigs, key, ctx, policy)) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+/*!
+ * \brief Get key and signing context for given RRSIG.
+ *
+ * \param[in]  rrsigs  RR set with RRSIGs.
+ * \param[in]  keys    Zone keys.
+ * \param[out] key     Signing key, set to NULL if no matching key found.
+ * \param[out] ctx     Signing context, set to NULL if no matching key found.
+ */
+static void get_matching_key_and_ctx(const knot_rrset_t *rrsigs, size_t pos,
+				     const knot_zone_keys_t *keys,
+				     const knot_dnssec_key_t **key,
+				     knot_dnssec_sign_context_t **ctx)
+{
+	assert(rrsigs && rrsigs->type == KNOT_RRTYPE_RRSIG);
+	assert(keys);
+	assert(key);
+	assert(ctx);
+
+	uint16_t keytag = knot_rdata_rrsig_key_tag(rrsigs, pos);
+
+	for (int i = 0; i < keys->count; i++) {
+		const knot_dnssec_key_t *found_key = &keys->keys[i];
+		if (keytag != found_key->keytag) {
+			continue;
+		}
+
+		*ctx = keys->contexts[i];
+		*key = &keys->keys[i];
+		return;
+	}
+
+	*ctx = NULL;
+	*key = NULL;
+}
+
+static void update_zone_expiration_with(const knot_rrset_t *rrsig, size_t pos,
+                                        const knot_dnssec_policy_t *policy,
+                                        uint32_t *exp)
+{
+	assert(rrsig && exp);
+	const uint32_t rrsig_exp = knot_rdata_rrsig_sig_expiration(rrsig, pos) -
+	                           policy->sign_refresh;
+	if (rrsig_exp < *exp) {
+		*exp = rrsig_exp;
+	}
+}
+
+/*!
+ * \brief Add expired or invalid RRSIGs into the changeset for removal.
+ *
+ * \param covered    RR set with covered records.
+ * \param rrsigs     RR set with RRSIGs.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int remove_expired_rrsigs(const knot_rrset_t *covered,
+                                 const knot_rrset_t *rrsigs,
+                                 const knot_zone_keys_t *zone_keys,
+                                 const knot_dnssec_policy_t *policy,
+                                 knot_changeset_t *changeset,
+                                 uint32_t *expires_at)
+{
+	assert(changeset);
+
+	if (!rrsigs) {
+		return KNOT_EOK;
+	}
+
+	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
+
+	knot_rrset_t *to_remove = NULL;
+	int result = KNOT_EOK;
+
+	for (int i = 0; i < rrsigs->rdata_count; i++) {
+		const knot_dnssec_key_t *key = NULL;
+		knot_dnssec_sign_context_t *ctx = NULL;
+		get_matching_key_and_ctx(rrsigs, i, zone_keys, &key, &ctx);
+
+		if (key && ctx) {
+			result = knot_is_valid_signature(covered, rrsigs, i,
+			                                 key, ctx, policy);
+			if (result == KNOT_EOK) {
+				update_zone_expiration_with(rrsigs, i, policy,
+				                            expires_at);
+				continue; // valid signature
+			}
+
+			if (result != KNOT_DNSSEC_EINVALID_SIGNATURE) {
+				break;
+			}
+		}
+
+		if (to_remove == NULL) {
+			to_remove = create_empty_rrsigs_for(rrsigs);
+			if (to_remove == NULL) {
+				result = KNOT_ENOMEM;
+				break;
+			}
+		}
+
+		result = knot_rrset_add_rr_from_rrset(to_remove, rrsigs, i);
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+	if (to_remove != NULL && result == KNOT_EOK) {
+		result = knot_changeset_add_rrset(changeset, to_remove,
+		                                  KNOT_CHANGESET_REMOVE);
+	}
+
+	if (to_remove != NULL && result != KNOT_EOK) {
+		int free_owners = true;
+		knot_rrset_deep_free(&to_remove, free_owners);
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Add missing RRSIGs into the changeset for adding.
+ *
+ * \param covered    RR set with covered records.
+ * \param rrsigs     RR set with RRSIGs.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int add_missing_rrsigs(const knot_rrset_t *covered,
+                              const knot_rrset_t *rrsigs,
+                              const knot_zone_keys_t *zone_keys,
+                              const knot_dnssec_policy_t *policy,
+                              knot_changeset_t *changeset)
+{
+	assert(covered);
+	assert(zone_keys);
+	assert(changeset);
+
+	int result = KNOT_EOK;
+	knot_rrset_t *to_add = NULL;
+	bool use_ksk = covered->type == KNOT_RRTYPE_DNSKEY;
+
+	for (int i = 0; i < zone_keys->count; i++) {
+		if (zone_keys->is_ksk[i] && !use_ksk) {
+			continue;
+		}
+
+		const knot_dnssec_key_t *key = &zone_keys->keys[i];
+		knot_dnssec_sign_context_t *ctx = zone_keys->contexts[i];
+		if (valid_signature_exists(covered, rrsigs, key, ctx, policy)) {
+			continue;
+		}
+
+		if (to_add == NULL) {
+			to_add = create_empty_rrsigs_for(covered);
+			if (to_add == NULL) {
+				return KNOT_ENOMEM;
+			}
+		}
+
+		result = knot_sign_rrset(to_add, covered, key, ctx, policy);
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+	if (to_add != NULL && result == KNOT_EOK) {
+		result = knot_changeset_add_rrset(changeset, to_add,
+		                                  KNOT_CHANGESET_ADD);
+	}
+
+	if (to_add != NULL && result != KNOT_EOK) {
+		int free_owners = true;
+		knot_rrset_deep_free(&to_add, free_owners);
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Add all RRSIGs into the changeset for removal.
+ *
+ * \param covered    RR set with covered records.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int remove_rrset_rrsigs(const knot_rrset_t *rrset,
+                               knot_changeset_t *changeset)
+{
+	assert(rrset);
+	assert(changeset);
+
+	if (!rrset->rrsigs) {
+		return KNOT_EOK;
+	}
+
+	knot_rrset_t *to_remove = NULL;
+	int result = knot_rrset_deep_copy(rrset->rrsigs, &to_remove);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	return knot_changeset_add_rrset(changeset, to_remove,
+	                                KNOT_CHANGESET_REMOVE);
+}
+
+/*!
+ * \brief Drop all existing and create new RRSIGs for covered records.
+ *
+ * \param covered    RR set with covered records.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int force_resign_rrset(const knot_rrset_t *covered,
+                              const knot_zone_keys_t *zone_keys,
+                              const knot_dnssec_policy_t *policy,
+                              knot_changeset_t *changeset)
+{
+	assert(covered);
+
+	if (covered->rrsigs) {
+		int result = remove_rrset_rrsigs(covered, changeset);
+		if (result != KNOT_EOK) {
+			return result;
+		}
+	}
+
+	return add_missing_rrsigs(covered, NULL, zone_keys, policy, changeset);
+}
+
+/*!
+ * \brief Drop all expired and create new RRSIGs for covered records.
+ *
+ * \param covered    RR set with covered records.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int resign_rrset(const knot_rrset_t *covered,
+                        const knot_zone_keys_t *zone_keys,
+                        const knot_dnssec_policy_t *policy,
+                        knot_changeset_t *changeset,
+                        uint32_t *expires_at)
+{
+	assert(covered);
+
+	// TODO this function creates some signatures twice (for checking)
+	// maybe merge the two functions into one
+	int result = remove_expired_rrsigs(covered, covered->rrsigs, zone_keys,
+	                                   policy, changeset, expires_at);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	return add_missing_rrsigs(covered, covered->rrsigs, zone_keys, policy,
+	                          changeset);
+}
+
+/*!
+ * \brief Update RRSIGs in a given node by updating changeset.
+ *
+ * \param node       Node to be signed.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_node_rrsets(const knot_node_t *node,
+                            const knot_zone_keys_t *zone_keys,
+                            const knot_dnssec_policy_t *policy,
+                            knot_changeset_t *changeset,
+                            uint32_t *expires_at)
+{
+	assert(node);
+	assert(policy);
+
+	int result = KNOT_EOK;
+
+	for (int i = 0; i < node->rrset_count; i++) {
+		const knot_rrset_t *rrset = node->rrset_tree[i];
+		if (!knot_node_rr_should_be_signed(node, rrset, NULL)) {
+			continue;
+		}
+
+		// Remove standalone RRSIGs (without the RRSet they sign)
+		if (rrset->rdata_count == 0 && rrset->rrsigs->rdata_count != 0) {
+			result = remove_rrset_rrsigs(rrset, changeset);
+			if (result != KNOT_EOK) {
+				break;
+			}
+		}
+
+		if (policy->forced_sign) {
+			result = force_resign_rrset(rrset, zone_keys, policy,
+			         changeset);
+		} else {
+			result = resign_rrset(rrset, zone_keys, policy,
+			                      changeset, expires_at);
+		}
+
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Struct to carry data for 'sign_data' callback function.
+ */
+typedef struct node_sign_args {
+	const knot_zone_keys_t *zone_keys;
+	const knot_dnssec_policy_t *policy;
+	knot_changeset_t *changeset;
+	uint32_t expires_at;
+	int result;
+} node_sign_args_t;
+
+/*!
+ * \brief Sign node (callback function).
+ *
+ * \param node  Node to be signed.
+ * \param data  Callback data, node_sign_args_t.
+ */
+static void sign_node(knot_node_t **node, void *data)
+{
+	assert(node && *node);
+	node_sign_args_t *args = (node_sign_args_t *)data;
+	assert(data);
+
+	if (args->result != KNOT_EOK) {
+		return;
+	}
+
+	if ((*node)->rrset_count == 0) {
+		return;
+	}
+
+	if (knot_node_is_non_auth(*node)) {
+		return;
+	}
+
+	args->result = sign_node_rrsets(*node, args->zone_keys, args->policy,
+	                                args->changeset, &args->expires_at);
+	knot_node_clear_replaced_nsec(*node);
+}
+
+/*!
+ * \brief Update RRSIGs in a given zone tree by updating changeset.
+ *
+ * \param tree        Zone tree to be signed.
+ * \param zone_keys   Zone keys.
+ * \param policy      DNSSEC policy.
+ * \param changeset   Changeset to be updated.
+ * \param expires_at  Expiration time of the oldest signature in zone.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int zone_tree_sign(knot_zone_tree_t *tree,
+                          const knot_zone_keys_t *zone_keys,
+                          const knot_dnssec_policy_t *policy,
+                          knot_changeset_t *changeset,
+                          uint32_t *expires_at)
+{
+	assert(tree);
+	assert(zone_keys);
+	assert(policy);
+	assert(changeset);
+
+	node_sign_args_t args = {.zone_keys = zone_keys, .policy = policy,
+	                         .changeset = changeset, .result = KNOT_EOK,
+	                         .expires_at = time(NULL) + (policy->sign_lifetime -
+	                                       policy->sign_refresh)};
+	knot_zone_tree_apply(tree, sign_node, &args);
+	*expires_at = args.expires_at;
+	return args.result;
+}
+
+/*- private API - signing of NSEC(3) in changeset ----------------------------*/
+
+/*!
+ * \brief Struct to carry data for 'add_rrsigs_for_nsec' callback function.
+ */
+typedef struct {
+	const knot_zone_contents_t *zone;
+	const knot_zone_keys_t *zone_keys;
+	const knot_dnssec_policy_t *policy;
+	knot_changeset_t *changeset;
+	ahtable_t *signed_table;
+} changeset_signing_data_t;
+
+/*!
+ * \brief Sign NSEC nodes in changeset (callback function).
+ *
+ * \param node  Node to be signed, silently skipped if not NSEC/NSEC3.
+ * \param data  Callback data, changeset_signing_data_t.
+ */
+static int add_rrsigs_for_nsec(knot_rrset_t *rrset, void *data)
+{
+	if (rrset == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	assert(data);
+
+	int result = KNOT_EOK;
+	changeset_signing_data_t *nsec_data = (changeset_signing_data_t *)data;
+
+	if (rrset->type == KNOT_RRTYPE_NSEC ||
+	    rrset->type == KNOT_RRTYPE_NSEC3
+	) {
+		result = add_missing_rrsigs(rrset, NULL, nsec_data->zone_keys,
+		                            nsec_data->policy,
+		                            nsec_data->changeset);
+	}
+
+	if (result != KNOT_EOK) {
+		dbg_dnssec_detail("add_rrsigs_for_nsec() for NSEC failed\n");
+	}
+
+	return result;
+}
+
+/*- private API - DNSKEY handling --------------------------------------------*/
+
+/*!
+ * \brief Check if DNSKEY RDATA match with DNSSEC key.
+ *
+ * \param key         DNSSEC key.
+ * \param rdata       DNSKEY RDATA.
+ * \param rdata_size  DNSKEY RDATA size.
+ *
+ * \return DNSKEY RDATA match with DNSSEC key.
+ */
+static bool dnskey_rdata_match(const knot_dnssec_key_t *key,
+                               const uint8_t *rdata, size_t rdata_size)
+{
+	assert(key);
+	assert(rdata);
+
+	return key->dnskey_rdata.size == rdata_size &&
+	       memcmp(key->dnskey_rdata.data, rdata, rdata_size) == 0;
+}
+
+/*!
+ * \brief Check if DNSKEY (key struct given) exists in zone.
+ *
+ * \param dnskeys  DNSKEYS RR set in zone apex.
+ * \param key      Key to be searched for.
+ *
+ * \return DNSKEY exists in the zone.
+ */
+static bool dnskey_exists_in_zone(const knot_rrset_t *dnskeys,
+                                  const knot_dnssec_key_t *key)
+{
+	assert(dnskeys);
+	assert(key);
+
+	for (int i = 0; i < dnskeys->rdata_count; i++) {
+		uint8_t *rdata = knot_rrset_get_rdata(dnskeys, i);
+		size_t rdata_size = rrset_rdata_item_size(dnskeys, i);
+
+		if (dnskey_rdata_match(key, rdata, rdata_size)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*!
+ * \brief Remove invalid DNSKEYs from the zone by updating the changeset.
+ *
+ * Invalid DNSKEY has wrong TTL, or the same keytag as some zone key
+ * but different RDATA.
+ *
+ * \param soa        RR set with SOA (to get TTL value from).
+ * \param dnskeys    RR set with DNSKEYs.
+ * \param zone_keys  Zone keys.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int remove_invalid_dnskeys(const knot_rrset_t *soa,
+                                  const knot_rrset_t *dnskeys,
+                                  const knot_zone_keys_t *zone_keys,
+                                  knot_changeset_t *changeset)
+{
+	assert(soa);
+	assert(soa->type == KNOT_RRTYPE_SOA);
+	assert(changeset);
+
+	if (!dnskeys) {
+		return KNOT_EOK;
+	}
+	assert(dnskeys->type == KNOT_RRTYPE_DNSKEY);
+
+	knot_rrset_t *to_remove = NULL;
+	int result = KNOT_EOK;
+
+	if (dnskeys->ttl != soa->ttl) {
+		dbg_dnssec_detail("removing DNSKEYs (SOA TTL differs)\n");
+		result = knot_rrset_deep_copy_no_sig(dnskeys, &to_remove);
+		goto done;
+	}
+
+	for (int i = 0; i < dnskeys->rdata_count; i++) {
+		uint8_t *rdata = knot_rrset_get_rdata(dnskeys, i);
+		size_t rdata_size = rrset_rdata_item_size(dnskeys, i);
+		uint16_t keytag = knot_keytag(rdata, rdata_size);
+		const knot_dnssec_key_t *key = get_zone_key(zone_keys, keytag);
+		if (key == NULL) {
+			dbg_dnssec_detail("keeping unknown DNSKEY with tag "
+			                  "%d\n", keytag);
+			continue;
+		}
+
+		if (dnskey_rdata_match(key, rdata, rdata_size)) {
+			dbg_dnssec_detail("keeping known DNSKEY with tag "
+			                  "%d\n", keytag);
+			continue;
+		}
+
+		dbg_dnssec_detail("removing DNSKEY with tag %d\n", keytag);
+
+		if (to_remove == NULL) {
+			to_remove = knot_rrset_new_from(dnskeys);
+			if (to_remove == NULL) {
+				result = KNOT_ENOMEM;
+				break;
+			}
+		}
+
+		result = knot_rrset_add_rr_from_rrset(to_remove, dnskeys, i);
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+done:
+
+	if (to_remove != NULL && result == KNOT_EOK) {
+		result = knot_changeset_add_rrset(changeset, to_remove,
+		                                  KNOT_CHANGESET_REMOVE);
+	}
+
+	if (to_remove != NULL && result != KNOT_EOK) {
+		knot_rrset_deep_free(&to_remove, 1);
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Create DNSKEY RR set from SOA RR set.
+ *
+ * \param soa  RR set with zone SOA.
+ *
+ * \return Empty DNSKEY RR set.
+ */
+static knot_rrset_t *create_dnskey_rrset_from_soa(const knot_rrset_t *soa)
+{
+	assert(soa);
+
+	knot_dname_t *owner = knot_dname_copy(soa->owner);
+	if (!owner) {
+		return NULL;
+	}
+
+	return knot_rrset_new(owner, KNOT_RRTYPE_DNSKEY, soa->rclass, soa->ttl);
+}
+
+/*!
+ * \brief Add missing DNSKEYs into the zone by updating the changeset.
+ *
+ * \param soa        RR set with SOA (to get TTL value from).
+ * \param dnskeys    RR set with DNSKEYs.
+ * \param zone_keys  Zone keys.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int add_missing_dnskeys(const knot_rrset_t *soa,
+                               const knot_rrset_t *dnskeys,
+                               const knot_zone_keys_t *zone_keys,
+                               knot_changeset_t *changeset)
+{
+	assert(soa);
+	assert(soa->type == KNOT_RRTYPE_SOA);
+	assert(!dnskeys || dnskeys->type == KNOT_RRTYPE_DNSKEY);
+	assert(zone_keys);
+	assert(changeset);
+
+	knot_rrset_t *to_add = NULL;
+	int result = KNOT_EOK;
+	bool add_all = dnskeys == NULL || dnskeys->ttl != soa->ttl;
+
+	for (int i = 0; i < zone_keys->count; i++) {
+		const knot_dnssec_key_t *key = &zone_keys->keys[i];
+		if (!add_all && dnskey_exists_in_zone(dnskeys, key)) {
+			continue;
+		}
+
+		dbg_dnssec_detail("adding DNSKEY with tag %d\n", key->keytag);
+
+		if (to_add == NULL) {
+			to_add = create_dnskey_rrset_from_soa(soa);
+			if (to_add == NULL) {
+				return KNOT_ENOMEM;
+			}
+		}
+
+		result = knot_rrset_add_rdata(to_add, key->dnskey_rdata.data,
+		                              key->dnskey_rdata.size);
+		if (result != KNOT_EOK) {
+			break;
+		}
+	}
+
+	if (to_add != NULL && result == KNOT_EOK) {
+		result = knot_changeset_add_rrset(changeset, to_add,
+		                                  KNOT_CHANGESET_ADD);
+	}
+
+	if (to_add != NULL && result != KNOT_EOK) {
+		knot_rrset_deep_free(&to_add, 1);
+	}
+
+	return result;
+}
+
+/*!
+ * \brief Refresh DNSKEY RRSIGs in the zone by updating the changeset.
+ *
+ * \param dnskeys    RR set with DNSKEYs.
+ * \param soa        RR set with SOA.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int update_dnskeys_rrsigs(const knot_rrset_t *dnskeys,
+                                 const knot_rrset_t *soa,
+                                 const knot_zone_keys_t *zone_keys,
+                                 const knot_dnssec_policy_t *policy,
+                                 knot_changeset_t *changeset)
+{
+	assert(zone_keys);
+	assert(changeset);
+
+	int result;
+
+	// We know how the DNSKEYs in zone should look like after applying
+	// the changeset. RRSIGs can be then built easily.
+
+	knot_rrset_t *new_dnskeys = create_dnskey_rrset_from_soa(soa);
+	if (!new_dnskeys) {
+		return KNOT_ENOMEM;
+	}
+
+	// add unknown keys from zone
+	for (int i = 0; dnskeys && i < dnskeys->rdata_count; i++) {
+		uint16_t keytag = knot_rdata_rrsig_key_tag(dnskeys, i);
+		if (get_zone_key(zone_keys, keytag) != NULL) {
+			continue;
+		}
+
+		result = knot_rrset_add_rr_from_rrset(new_dnskeys, dnskeys, i);
+		if (result != KNOT_EOK) {
+			goto fail;
+		}
+	}
+
+	// add known keys from key database
+	for (int i = 0; i < zone_keys->count; i++) {
+		const knot_dnssec_key_t *key = &zone_keys->keys[i];
+		const knot_binary_t *rdata = &key->dnskey_rdata;
+		result = knot_rrset_add_rdata(new_dnskeys, rdata->data,
+		                              rdata->size);
+		if (result != KNOT_EOK) {
+			goto fail;
+		}
+	}
+
+	result = knot_rrset_sort_rdata(new_dnskeys);
+	if (result != KNOT_EOK) {
+		goto fail;
+	}
+
+	result = add_missing_rrsigs(new_dnskeys, NULL, zone_keys, policy,
+	                            changeset);
+	if (result != KNOT_EOK) {
+		goto fail;
+	}
+
+	if (dnskeys) {
+		result = remove_rrset_rrsigs(dnskeys, changeset);
+	}
+
+fail:
+
+	knot_rrset_deep_free(&new_dnskeys, 1);
+
+	return result;
+}
+
+/*!
+ * \brief Update DNSKEY records in the zone by updating the changeset.
+ *
+ * \param zone       Zone to be updated.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int update_dnskeys(const knot_zone_contents_t *zone,
+                          const knot_zone_keys_t *zone_keys,
+                          const knot_dnssec_policy_t *policy,
+                          knot_changeset_t *changeset)
+{
+	assert(zone);
+	assert(zone->apex);
+	assert(changeset);
+
+	const knot_node_t *apex = zone->apex;
+	const knot_rrset_t *dnskeys = knot_node_rrset(apex, KNOT_RRTYPE_DNSKEY);
+	const knot_rrset_t *soa = knot_node_rrset(apex, KNOT_RRTYPE_SOA);
+
+	if (!soa) {
+		return KNOT_EINVAL;
+	}
+
+	int result;
+	size_t changes_before = knot_changeset_size(changeset);
+
+	result = remove_invalid_dnskeys(soa, dnskeys, zone_keys, changeset);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	result = add_missing_dnskeys(soa, dnskeys, zone_keys, changeset);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	bool modified = knot_changeset_size(changeset) != changes_before;
+
+	if (!modified && dnskeys &&
+	    all_signatures_exist(dnskeys, dnskeys->rrsigs, zone_keys, policy)
+	) {
+		return KNOT_EOK;
+	}
+
+	dbg_dnssec_detail("Creating new signatures for DNSKEYs\n");
+	return update_dnskeys_rrsigs(dnskeys, soa, zone_keys, policy, changeset);
+}
+
+/*!
+ * \brief Wrapper function for changeset signing - to be used with changeset
+ *        apply functions.
+ *
+ * \param chg_rrset  RRSet to be signed (potentially)
+ * \param data       Signing data
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_changeset_wrap(knot_rrset_t *chg_rrset, void *data)
+{
+	changeset_signing_data_t *args = (changeset_signing_data_t *)data;
+	// Find RR's node in zone, find out if we need to sign this RR
+	const knot_node_t *node =
+		knot_zone_contents_find_node(args->zone, chg_rrset->owner);
+	// If node is not in zone, all its RRSIGs were dropped - no-op
+	if (node) {
+		const knot_rrset_t *zone_rrset =
+			knot_node_rrset(node, chg_rrset->type);
+		if (knot_node_rr_should_be_signed(node, zone_rrset,
+		                                 args->signed_table)) {
+			return force_resign_rrset(zone_rrset, args->zone_keys,
+			                          args->policy, args->changeset);
+		} else if (zone_rrset && zone_rrset->rrsigs != NULL) {
+			/*!
+			 * If RRSet in zone DOES have RRSIGs although we
+			 * should not sign it, DDNS-caused change to node/rr
+			 * occured and we have to drop all RRSIGs.
+			 */
+			return remove_rrset_rrsigs(zone_rrset, args->changeset);
+		}
+	}
+	return KNOT_EOK;
+}
+
+/*- public API ---------------------------------------------------------------*/
+
+/*!
+ * \brief Update zone signatures and store performed changes in changeset.
+ */
+int knot_zone_sign(const knot_zone_contents_t *zone,
+                   const knot_zone_keys_t *zone_keys,
+                   const knot_dnssec_policy_t *policy,
+                   knot_changeset_t *changeset,
+                   uint32_t *expires_at)
+{
+	if (!zone || !zone_keys || !policy || !changeset) {
+		return KNOT_EINVAL;
+	}
+
+	int result;
+
+	result = update_dnskeys(zone, zone_keys, policy, changeset);
+	if (result != KNOT_EOK) {
+		dbg_dnssec_detail("update_dnskeys() failed\n");
+		return result;
+	}
+
+	uint32_t normal_tree_expiration = 0;
+	result = zone_tree_sign(zone->nodes, zone_keys, policy, changeset,
+	                        &normal_tree_expiration);
+	if (result != KNOT_EOK) {
+		dbg_dnssec_detail("zone_tree_sign() on normal nodes failed\n");
+		return result;
+	}
+
+	uint32_t nsec3_tree_expiration = 0;
+	result = zone_tree_sign(zone->nsec3_nodes, zone_keys, policy,
+	                        changeset, &nsec3_tree_expiration);
+	if (result != KNOT_EOK) {
+		dbg_dnssec_detail("zone_tree_sign() on nsec3 nodes failed\n");
+		return result;
+	}
+
+	// We need the earlier value of these two
+	*expires_at = normal_tree_expiration <= nsec3_tree_expiration ?
+	              normal_tree_expiration : nsec3_tree_expiration;
+
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Check if zone SOA signatures are expired.
+ */
+bool knot_zone_sign_soa_expired(const knot_zone_contents_t *zone,
+                                const knot_zone_keys_t *zone_keys,
+                                const knot_dnssec_policy_t *policy)
+{
+	if (!zone || !zone_keys || !policy) {
+		return KNOT_EINVAL;
+	}
+
+	const knot_rrset_t *soa = knot_node_rrset(zone->apex, KNOT_RRTYPE_SOA);
+	assert(soa);
+
+	return !all_signatures_exist(soa, soa->rrsigs, zone_keys, policy);
+}
+
+/*!
+ * \brief Update and sign SOA and store performed changes in changeset.
+ */
+int knot_zone_sign_update_soa(const knot_rrset_t *soa,
+                              const knot_zone_keys_t *zone_keys,
+                              const knot_dnssec_policy_t *policy,
+                              knot_changeset_t *changeset)
+{
+	if (!soa || !zone_keys || !policy || !changeset) {
+		return KNOT_EINVAL;
+	}
+
+	dbg_dnssec_verb("Updating SOA...\n");
+
+	uint32_t serial = knot_rdata_soa_serial(soa);
+	if (serial == UINT32_MAX && policy->soa_up == KNOT_SOA_SERIAL_INC) {
+		// TODO: this is wrong, the value should be 'rewound' to 0 in this case
+		return KNOT_EINVAL;
+	}
+
+	uint32_t new_serial = serial;
+	if (policy->soa_up == KNOT_SOA_SERIAL_INC) {
+		new_serial += 1;
+	} else {
+		assert(policy->soa_up == KNOT_SOA_SERIAL_KEEP);
+	}
+
+	int result;
+
+	// remove signatures for old SOA (if there are any)
+
+	if (soa->rrsigs) {
+		knot_rrset_t *soa_copy = NULL;
+		result = knot_rrset_deep_copy_no_sig(soa->rrsigs, &soa_copy);
+		if (result != KNOT_EOK) {
+			return result;
+		}
+		result = knot_changeset_add_rrset(changeset, soa_copy,
+		                                  KNOT_CHANGESET_REMOVE);
+		if (result != KNOT_EOK) {
+			knot_rrset_deep_free(&soa_copy, 1);
+			return result;
+		}
+	}
+
+	// copy old SOA and create new SOA with updated serial
+
+	knot_rrset_t *soa_from = NULL;
+	knot_rrset_t *soa_to = NULL;
+
+	result = knot_rrset_deep_copy_no_sig(soa, &soa_from);
+	if (result != KNOT_EOK) {
+		return result;
+	}
+
+	result = knot_rrset_deep_copy_no_sig(soa, &soa_to);
+	if (result != KNOT_EOK) {
+		knot_rrset_deep_free(&soa_from, 1);
+		return result;
+	}
+
+	knot_rdata_soa_serial_set(soa_to, new_serial);
+
+	// add signatures for new SOA
+
+	result = add_missing_rrsigs(soa_to, NULL, zone_keys, policy, changeset);
+	if (result != KNOT_EOK) {
+		knot_rrset_deep_free(&soa_from, 1);
+		knot_rrset_deep_free(&soa_to, 1);
+		return result;
+	}
+
+	// save the result
+
+	changeset->soa_from = soa_from;
+	changeset->soa_to = soa_to;
+	changeset->serial_from = serial;
+	changeset->serial_to = new_serial;
+
+	return KNOT_EOK;
+}
+
+/*!
+ * \brief Sign changeset created by DDNS or zone-diff.
+ */
+int knot_zone_sign_changeset(const knot_zone_contents_t *zone,
+                             const knot_changeset_t *in_ch,
+                             knot_changeset_t *out_ch,
+                             const knot_zone_keys_t *zone_keys,
+                             const knot_dnssec_policy_t *policy)
+{
+	if (zone == NULL || in_ch == NULL || out_ch == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Create args for wrapper function - ahtable for duplicate sigs
+	changeset_signing_data_t args = { .zone = zone, .zone_keys = zone_keys,
+	                                  .policy = policy,
+	                                  .changeset = out_ch,
+	                                  .signed_table = ahtable_create()};
+
+	// Sign all RRs that are new in changeset
+	int ret = knot_changeset_apply((knot_changeset_t *)in_ch,
+	                               KNOT_CHANGESET_ADD,
+	                               sign_changeset_wrap, &args);
+
+	// Sign all RRs that are removed in changeset
+	if (ret == KNOT_EOK) {
+		ret = knot_changeset_apply((knot_changeset_t *)in_ch,
+		                           KNOT_CHANGESET_REMOVE,
+		                           sign_changeset_wrap, &args);
+	}
+
+	ahtable_free(args.signed_table);
+
+	return ret;
+}
+
+/*!
+ * \brief Sign NSEC/NSEC3 nodes in changeset and update the changeset.
+ */
+int knot_zone_sign_nsecs_in_changeset(const knot_zone_keys_t *zone_keys,
+                                      const knot_dnssec_policy_t *policy,
+                                      knot_changeset_t *changeset)
+{
+	assert(zone_keys);
+	assert(policy);
+	assert(changeset);
+
+	changeset_signing_data_t data = {.zone = NULL,
+	                                 .zone_keys = zone_keys,
+	                                 .policy = policy,
+	                                 .changeset = changeset };
+
+	return knot_changeset_apply(changeset, KNOT_CHANGESET_ADD,
+	                            add_rrsigs_for_nsec, &data);
+}
diff --git a/src/libknot/dnssec/zone-sign.h b/src/libknot/dnssec/zone-sign.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc9ed920e8581bdc1ce8b0c3972bab3220ec10ee
--- /dev/null
+++ b/src/libknot/dnssec/zone-sign.h
@@ -0,0 +1,117 @@
+/*  Copyright (C) 2013 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 zone-sign.h
+ *
+ * \author Jan Vcelak <jan.vcelak@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Interface for DNSSEC signing of zones.
+ *
+ * \addtogroup dnssec
+ * @{
+ */
+
+#ifndef _KNOT_DNSSEC_ZONE_SIGN_H_
+#define _KNOT_DNSSEC_ZONE_SIGN_H_
+
+#include "common/hattrie/ahtable.h"
+#include "libknot/updates/changesets.h"
+#include "libknot/zone/zone-contents.h"
+#include "libknot/dnssec/zone-keys.h"
+#include "libknot/dnssec/policy.h"
+
+/*!
+ * \brief Update zone signatures and store performed changes in changeset.
+ *
+ * Updates RRSIGs, NSEC(3)s, and DNSKEYs.
+ *
+ * \param zone        Zone to be signed.
+ * \param zone_keys   Zone keys.
+ * \param policy      DNSSEC policy.
+ * \param changeset   Changeset to be updated.
+ * \param expires_at  Pointer to expiration time of the oldest signature in zone
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign(const knot_zone_contents_t *zone,
+                   const knot_zone_keys_t *zone_keys,
+                   const knot_dnssec_policy_t *policy,
+                   knot_changeset_t *out_ch, uint32_t *expires_at);
+
+/*!
+ * \brief Update and sign SOA and store performed changes in changeset.
+ *
+ * \param zone       Zone including SOA to be updated.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign_update_soa(const knot_rrset_t *soa,
+                              const knot_zone_keys_t *zone_keys,
+                              const knot_dnssec_policy_t *policy,
+                              knot_changeset_t *changeset);
+
+/*!
+ * \brief Check if zone SOA signatures are expired.
+ *
+ * \param zone       Zone to be signed.
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return True if zone SOA signatures need update, false othewise.
+ */
+bool knot_zone_sign_soa_expired(const knot_zone_contents_t *zone,
+                                const knot_zone_keys_t *zone_keys,
+                                const knot_dnssec_policy_t *policy);
+
+/*!
+ * \brief Sign changeset created by DDNS or zone-diff.
+ *
+ * \param zone Contents of the updated zone (AFTER zone is switched).
+ * \param in_ch Changeset created bvy DDNS or zone-diff
+ * \param out_ch New records will be added to this changeset.
+ * \param zone_keys Keys to use for signing.
+ * \param policy DNSSEC signing policy.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign_changeset(const knot_zone_contents_t *zone,
+                             const knot_changeset_t *in_ch,
+                             knot_changeset_t *out_ch,
+                             const knot_zone_keys_t *zone_keys,
+                             const knot_dnssec_policy_t *policy);
+
+/*!
+ * \brief Sign NSEC/NSEC3 nodes in changeset and update the changeset.
+ *
+ * \param zone_keys  Zone keys.
+ * \param policy     DNSSEC policy.
+ * \param changeset  Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign_nsecs_in_changeset(const knot_zone_keys_t *zone_keys,
+                                      const knot_dnssec_policy_t *policy,
+                                      knot_changeset_t *changeset);
+
+#endif // _KNOT_DNSSEC_ZONE_SIGN_H_
+
+/*! @} */
diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h
index c6a8c152f5eb1736baa7313008dbffe058546e11..4cd785d5d609558e20c4310f17aa32c52c20bbb3 100644
--- a/src/libknot/libknot.h
+++ b/src/libknot/libknot.h
@@ -30,14 +30,11 @@
 #include "consts.h"
 #include "dname.h"
 #include "edns.h"
-#include "nsec3.h"
 #include "packet/packet.h"
 #include "packet/query.h"
 #include "packet/response.h"
 #include "rrset.h"
 #include "rrset-dump.h"
-#include "sign/key.h"
-#include "sign/sig0.h"
 #include "tsig.h"
 #include "tsig-op.h"
 #include "util/tolower.h"
@@ -46,6 +43,7 @@
 #include "zone/node.h"
 #include "zone/zone.h"
 #include "zone/zonedb.h"
+#include "rdata.h"
 
 #endif
 
diff --git a/src/libknot/nameserver/chaos.c b/src/libknot/nameserver/chaos.c
index dd002832b6346909bfc2a41a949526e0ddd22230..44ca6adb7af9597ba37a8a822008acd3ba4f0a22 100644
--- a/src/libknot/nameserver/chaos.c
+++ b/src/libknot/nameserver/chaos.c
@@ -62,19 +62,18 @@ static knot_rrset_t *create_txt_rrset(const knot_dname_t *owner,
 	if (response_len > 255)
 		response_len = 255;
 
-	knot_dname_t *rowner = knot_dname_deep_copy(owner);
+	knot_dname_t *rowner = knot_dname_copy(owner);
 	if (!rowner)
 		return NULL;
 
 	knot_rrset_t *rrset;
 	rrset = knot_rrset_new(rowner, KNOT_RRTYPE_TXT, KNOT_CLASS_CH, 0);
-	knot_dname_release(rowner);
 	if (!rrset)
 		return NULL;
 
 	uint8_t *rdata = knot_rrset_create_rdata(rrset, response_len + 1);
 	if (!rdata) {
-		knot_rrset_deep_free(&rrset, 1, 0);
+		knot_rrset_deep_free(&rrset, 1);
 		return NULL;
 	}
 
@@ -104,17 +103,17 @@ static int answer_txt(knot_nameserver_t *nameserver, knot_packet_t *response,
 
 	int result = knot_response_add_rrset_answer(response, rrset, 1, 0, 0);
 	if (result != KNOT_EOK) {
-		knot_rrset_deep_free(&rrset, 1, 0);
+		knot_rrset_deep_free(&rrset, 1);
 		return KNOT_RCODE_SERVFAIL;
 	}
 
 	result = ns_response_to_wire(response, response_wire, response_size);
 	if (result != KNOT_EOK) {
-		knot_rrset_deep_free(&rrset, 1, 0);
+		knot_rrset_deep_free(&rrset, 1);
 		return KNOT_RCODE_SERVFAIL;
 	}
 
-	knot_rrset_deep_free(&rrset, 1, 0);
+	knot_rrset_deep_free(&rrset, 1);
 	knot_response_set_rcode(response, KNOT_RCODE_NOERROR);
 
 	return KNOT_RCODE_NOERROR;
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 9da91f412172dd986b74eabb88344cc2cdaab004..287b34e512368395fdda2182a612f1a8acccac5d 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -37,6 +37,8 @@
 #include "updates/changesets.h"
 #include "updates/ddns.h"
 #include "tsig-op.h"
+#include "libknot/rdata.h"
+#include "libknot/dnssec/zone-nsec.h"
 
 /*----------------------------------------------------------------------------*/
 
@@ -81,11 +83,8 @@ static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb,
 	 * records are only present in a parent zone.
 	 */
 	if (qtype == KNOT_RRTYPE_DS) {
-		/*! \todo Optimize, do not deep copy dname. */
-		knot_dname_t *name = knot_dname_left_chop(qname);
-		zone = knot_zonedb_find_zone_for_name(zdb, name);
-		/* Directly discard. */
-		knot_dname_free(&name);
+		const knot_dname_t *parent = knot_wire_next_label(qname, NULL);
+		zone = knot_zonedb_find_zone_for_name(zdb, parent);
 		/* If zone does not exist, search for its parent zone,
 		   this will later result to NODATA answer. */
 		if (zone == NULL) {
@@ -115,15 +114,13 @@ static knot_rrset_t *ns_synth_from_wildcard(
 	const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname)
 {
 	knot_rrset_t *rrset = NULL;
-	int ret = knot_rrset_deep_copy(wildcard_rrset, &rrset, 1);
+	int ret = knot_rrset_deep_copy(wildcard_rrset, &rrset);
 	if (ret != KNOT_EOK) {
 		dbg_ns("ns: ns_synth_from_wildcard: Could not copy RRSet.\n");
 		return NULL;
 	}
 
-	knot_dname_t *dname_copy = knot_dname_deep_copy(qname);
-	knot_rrset_set_owner(rrset, dname_copy);
-	knot_dname_release(dname_copy);
+	knot_rrset_set_owner(rrset, qname);
 
 	return rrset;
 }
@@ -164,7 +161,7 @@ dbg_ns_exec_verb(
 		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);
+			knot_rrset_deep_free(&synth_rrset, 1);
 			return ret;
 		}
 		*rrset = synth_rrset;
@@ -232,27 +229,27 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp,
 
 /* Wrapper functions for lists. */
 typedef struct chain_node {
-	node n;
+	node_t n;
 	const knot_node_t *kn_node;
 } chain_node_t;
 
-static int cname_chain_add(list *chain, const knot_node_t *kn_node)
+static int cname_chain_add(list_t *chain, const knot_node_t *kn_node)
 {
 	assert(chain != NULL);
 	chain_node_t *new_node = malloc(sizeof(chain_node_t));
 	CHECK_ALLOC_LOG(new_node, KNOT_ENOMEM);
 
 	new_node->kn_node = kn_node;
-	add_tail(chain, (node *)new_node);
+	add_tail(chain, (node_t *)new_node);
 
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
-static int cname_chain_contains(const list *chain, const knot_node_t *kn_node)
+static int cname_chain_contains(const list_t *chain, const knot_node_t *kn_node)
 {
-	node *n = NULL;
+	node_t *n = NULL;
 	WALK_LIST(n, *chain) {
 		chain_node_t *l_node = (chain_node_t *)n;
 		if (l_node->kn_node == kn_node) {
@@ -265,7 +262,7 @@ static int cname_chain_contains(const list *chain, const knot_node_t *kn_node)
 
 /*----------------------------------------------------------------------------*/
 
-static void cname_chain_free(list *chain)
+static void cname_chain_free(list_t *chain)
 {
 	WALK_LIST_FREE(*chain);
 }
@@ -304,7 +301,7 @@ static int ns_follow_cname(const knot_node_t **node,
 	 */
 	int stop = 0;
 
-	list cname_chain;
+	list_t cname_chain;
 	init_list(&cname_chain);
 
 	while (*node != NULL
@@ -351,7 +348,7 @@ static int ns_follow_cname(const knot_node_t **node,
 				dbg_ns("Failed to add synthetized RRSet (CNAME "
 				       "follow) to the tmp RRSets in response."
 				       "\n");
-				knot_rrset_deep_free(&rrset, 1, 1);
+				knot_rrset_deep_free(&rrset, 1);
 				cname_chain_free(&cname_chain);
 				return ret;
 			}
@@ -414,10 +411,18 @@ dbg_ns_exec_verb(
 
 		// get the name from the CNAME RDATA
 		const knot_dname_t *cname =
-			knot_rrset_rdata_cname_name(cname_rrset);
+			knot_rdata_cname_name(cname_rrset);
 		dbg_ns_detail("CNAME name from RDATA: %p\n", cname);
-		// change the node to the node of that name
-		*node = knot_dname_node(cname);
+
+		/* Attempt to find mentioned name in zone. */
+		rcu_read_lock();
+		const knot_zone_t *zone = resp->zone;
+		knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+		const knot_node_t *encloser = NULL, *prev = NULL;
+		knot_zone_contents_find_dname(contents, cname, node, &encloser, &prev);
+		if (*node == NULL && encloser && encloser->wildcard_child)
+			*node = encloser->wildcard_child;
+		rcu_read_unlock();
 		dbg_ns_detail("This name's node: %p\n", *node);
 
 		// save the new name which should be used for replacing wildcard
@@ -493,21 +498,26 @@ dbg_ns_exec_verb(
 
 			dbg_ns_detail("  Type: %u\n", knot_rrset_type(rrset));
 
-			ret = ns_check_wildcard(name, resp, &rrset);
-			if (ret != KNOT_EOK) {
-				dbg_ns("Failed to process wildcard.\n");
-				break;
-			}
+			if (knot_rrset_rdata_rr_count(rrset) > 0
+			    || knot_rrset_type(rrset) == KNOT_RRTYPE_APL) {
 
-			ret = knot_response_add_rrset_answer(resp, rrset, 1,
-			                                     0, 1);
-			if (ret != KNOT_EOK) {
-				dbg_ns("Failed add Answer RRSet: %s\n",
-				       knot_strerror(ret));
-				break;
-			}
+				ret = ns_check_wildcard(name, resp, &rrset);
+				if (ret != KNOT_EOK) {
+					dbg_ns("Failed to process wildcard.\n");
+					break;
+				}
 
-			*added += 1;
+				ret = knot_response_add_rrset_answer(resp,
+				                                     rrset, 1,
+				                                     0, 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);
@@ -623,7 +633,7 @@ dbg_ns_exec_verb(
 static int ns_put_additional_for_rrset(knot_packet_t *resp,
                                        const knot_rrset_t *rrset)
 {
-	const knot_node_t *node = NULL;
+	const knot_node_t *node = NULL, *encloser = NULL, *prev = NULL;
 
 	int ret = 0;
 
@@ -632,7 +642,7 @@ static int ns_put_additional_for_rrset(knot_packet_t *resp,
 	for (uint16_t i = 0; i < knot_rrset_rdata_rr_count(rrset); i++) {
 		dbg_ns_verb("Getting name from RDATA, type %u..\n",
 		            knot_rrset_type(rrset));
-		const knot_dname_t *dname = knot_rrset_rdata_name(rrset, i);
+		const knot_dname_t *dname = knot_rdata_name(rrset, i);
 		assert(dname);
 dbg_ns_exec_detail(
 		char *name = knot_dname_to_str(dname);
@@ -640,15 +650,15 @@ dbg_ns_exec_detail(
 		free(name);
 );
 		assert(dname != NULL);
-		node = knot_dname_node(dname);
-
-		dbg_ns_detail("Node saved in RDATA dname: %p\n", node);
-		if (node != NULL && node->owner != dname) {
-			// 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)));
-		}
+
+		/* Attempt to find mentioned name in zone. */
+		rcu_read_lock();
+		const knot_zone_t *zone = resp->zone;
+		knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+		knot_zone_contents_find_dname(contents, dname, &node, &encloser, &prev);
+		if (node == NULL && encloser && encloser->wildcard_child)
+			node = encloser->wildcard_child;
+		rcu_read_unlock();
 
 		knot_rrset_t *rrset_add;
 
@@ -867,10 +877,10 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone,
 
 	// if SOA's TTL is larger than MINIMUM, copy the RRSet and set
 	// MINIMUM as TTL
-	uint32_t min = knot_rrset_rdata_soa_minimum(soa_rrset);
+	uint32_t min = knot_rdata_soa_minimum(soa_rrset);
 	if (min < knot_rrset_ttl(soa_rrset)) {
 		knot_rrset_t *soa_copy = NULL;
-		ret = knot_rrset_deep_copy(soa_rrset, &soa_copy, 1);
+		ret = knot_rrset_deep_copy(soa_rrset, &soa_copy);
 
 		if (ret != KNOT_EOK) {
 			return ret;
@@ -883,7 +893,7 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone,
 		/* Need to add it as temporary, so it get's freed. */
 		ret = knot_packet_add_tmp_rrset(resp, soa_copy);
 		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&soa_copy, 1, 1);
+			knot_rrset_deep_free(&soa_copy, 1);
 			return ret;
 		}
 	}
@@ -917,8 +927,8 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone,
 static knot_dname_t *ns_next_closer(const knot_dname_t *closest_encloser,
                                       const knot_dname_t *name)
 {
-	int ce_labels = knot_dname_label_count(closest_encloser);
-	int qname_labels = knot_dname_label_count(name);
+	int ce_labels = knot_dname_labels(closest_encloser, NULL);
+	int qname_labels = knot_dname_labels(name, NULL);
 
 	assert(ce_labels < qname_labels);
 
@@ -927,16 +937,11 @@ static knot_dname_t *ns_next_closer(const knot_dname_t *closest_encloser,
 	       == ce_labels);
 
 	// chop some labels from the qname
-	knot_dname_t *next_closer = knot_dname_deep_copy(name);
-	if (next_closer == NULL) {
-		return NULL;
-	}
-
 	for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) {
-		knot_dname_left_chop_no_copy(next_closer);
+		name = knot_wire_next_label(name, NULL);
 	}
 
-	return next_closer;
+	return knot_dname_copy(name);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1127,7 +1132,7 @@ dbg_ns_exec_verb(
 );
 		ret = ns_put_covering_nsec3(zone, new_next_closer, resp);
 
-		knot_dname_release(new_next_closer);
+		knot_dname_free(&new_next_closer);
 	} else {
 		ret = ns_put_covering_nsec3(zone, next_closer, resp);
 	}
@@ -1147,16 +1152,14 @@ static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name)
 {
 	assert(name != NULL);
 
-	knot_dname_t *wildcard = knot_dname_new_from_str("*", 1, NULL);
+	knot_dname_t *wildcard = knot_dname_from_str("*", 1);
 	if (wildcard == NULL) {
 		return NULL;
 	}
 
-	if (knot_dname_cat(wildcard, name) == NULL) {
-		/* Directly discard dname. */
-		knot_dname_free(&wildcard);
+	wildcard = knot_dname_cat(wildcard, name);
+	if (wildcard == NULL)
 		return NULL;
-	}
 
 dbg_ns_exec_verb(
 	char *name = knot_dname_to_str(wildcard);
@@ -1300,6 +1303,10 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
 		previous = knot_zone_contents_find_previous(zone, qname);
 		assert(previous != NULL);
 
+		/*!
+		 * \todo isn't this handled in adjusting?
+		 * knot_zone_contents_adjust_node_in_tree_ptr()
+		 */
 		while (!knot_node_is_auth(previous)) {
 			previous = knot_node_previous(previous);
 		}
@@ -1328,12 +1335,12 @@ dbg_ns_exec_verb(
 	}
 
 	rrset = knot_rrset_get_rrsigs(rrset);
-	assert(rrset != NULL);
+	//assert(rrset != NULL);
 	ret = knot_response_add_rrset_authority(resp, rrset, 1, 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;
+		//return ret;
 	}
 	// 2) NSEC proving that there is no wildcard covering the name
 	// this is only different from 1) if the wildcard would be
@@ -1349,7 +1356,7 @@ dbg_ns_exec_verb(
 
 	const knot_node_t *prev_new = previous;
 
-	while (knot_dname_compare(knot_node_owner(prev_new),
+	while (knot_dname_cmp(knot_node_owner(prev_new),
 				    wildcard) > 0) {
 dbg_ns_exec_verb(
 		char *name = knot_dname_to_str(knot_node_owner(prev_new));
@@ -1359,7 +1366,7 @@ dbg_ns_exec_verb(
 		assert(prev_new != knot_zone_contents_apex(zone));
 		prev_new = knot_node_previous(prev_new);
 	}
-	assert(knot_dname_compare(knot_node_owner(prev_new),
+	assert(knot_dname_cmp(knot_node_owner(prev_new),
 	                            wildcard) < 0);
 
 dbg_ns_exec_verb(
@@ -1392,7 +1399,7 @@ dbg_ns_exec_verb(
 		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 ret;
 		}
 	}
 
@@ -1525,7 +1532,7 @@ dbg_ns_exec_verb(
 
 
 	/* Duplicate from ns_next_close(), safe to discard. */
-	knot_dname_release(next_closer);
+	knot_dname_free(&next_closer);
 
 	return ret;
 }
@@ -1556,6 +1563,10 @@ static int ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
 		previous = knot_zone_contents_find_previous(zone, qname);
 		assert(previous != NULL);
 
+		/*!
+		 * \todo isn't this handled in adjusting?
+		 * knot_zone_contents_adjust_node_in_tree_ptr()
+		 */
 		while (!knot_node_is_auth(previous)) {
 			previous = knot_node_previous(previous);
 		}
@@ -1572,7 +1583,7 @@ static int ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
 		                                        1);
 		if (ret == KNOT_EOK) {
 			rrset = knot_rrset_get_rrsigs(rrset);
-			assert(rrset != NULL);
+			//assert(rrset != NULL);
 			ret = knot_response_add_rrset_authority(resp, rrset, 1,
 			                                        0, 1);
 		}
@@ -1656,7 +1667,7 @@ static int ns_put_nsec_nsec3_wildcard_answer(const knot_node_t *node,
 	if (DNSSEC_ENABLED
 	    && knot_query_dnssec_requested(knot_packet_query(resp))
 	    && knot_dname_is_wildcard(knot_node_owner(node))
-	    && knot_dname_compare(qname, knot_node_owner(node)) != 0) {
+	    && knot_dname_cmp(qname, knot_node_owner(node)) != 0) {
 		dbg_ns_verb("Adding NSEC/NSEC3 for wildcard answer.\n");
 		if (knot_zone_contents_nsec3_enabled(zone)) {
 			ret = ns_put_nsec3_wildcard(zone, closest_encloser,
@@ -1725,7 +1736,7 @@ static inline int ns_referral(const knot_node_t *node,
 		node = knot_node_parent(node);
 	}
 
-	int at_deleg = !knot_dname_compare(qname, knot_node_owner(node));
+	int at_deleg = knot_dname_is_equal(qname, knot_node_owner(node));
 
 	int ret = KNOT_EOK;
 
@@ -1734,7 +1745,7 @@ static inline int ns_referral(const knot_node_t *node,
 		knot_rrset_t *ds_rrset = knot_node_get_rrset(node,
 		                                             KNOT_RRTYPE_DS);
 
-		if (ds_rrset) {
+		if (ds_rrset && knot_rrset_rdata_rr_count(ds_rrset) > 0) {
 			ret = knot_response_add_rrset_answer(resp, ds_rrset, 1,
 			                                     0, 1);
 			if (ret == KNOT_EOK && DNSSEC_ENABLED
@@ -1917,7 +1928,7 @@ static int ns_answer_from_node(const knot_node_t *node,
 	} else {  // else put authority NS
 		assert(closest_encloser == knot_node_parent(node)
 		      || !knot_dname_is_wildcard(knot_node_owner(node))
-		      || knot_dname_compare(qname, knot_node_owner(node)) == 0);
+		      || knot_dname_cmp(qname, knot_node_owner(node)) == 0);
 
 		ret = ns_put_nsec_nsec3_wildcard_answer(node, closest_encloser,
 		                                  previous, zone, qname, resp);
@@ -1949,7 +1960,7 @@ static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
 
 	// create new CNAME RRSet
 
-	knot_dname_t *owner = knot_dname_deep_copy(qname);
+	knot_dname_t *owner = knot_dname_copy(qname);
 	if (owner == NULL) {
 		return NULL;
 	}
@@ -1960,13 +1971,11 @@ static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
 		return NULL;
 	}
 
-	/* Release owner, as it's retained in rrset. */
-	knot_dname_release(owner);
-
-	// replace last labels of qname with DNAME
-	knot_dname_t *cname = knot_dname_replace_suffix(qname,
-	      knot_dname_size(knot_rrset_owner(dname_rrset)),
-	      knot_rrset_rdata_dname_target(dname_rrset));
+	/* Replace last labels of qname with DNAME. */
+	const knot_dname_t *dname_wire = knot_rrset_owner(dname_rrset);
+	size_t labels = knot_dname_labels(dname_wire, NULL);
+	const knot_dname_t *dname_tgt = knot_rdata_dname_target(dname_rrset);
+	knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt);
 	if (cname == NULL) {
 		knot_rrset_free(&cname_rrset);
 		return NULL;
@@ -1976,17 +1985,18 @@ dbg_ns_exec(
 	dbg_ns_verb("CNAME canonical name: %s.\n", name);
 	free(name);
 );
-	uint8_t *cname_rdata = knot_rrset_create_rdata(cname_rrset,
-	                                               sizeof(knot_dname_t *));
+	int cname_size = knot_dname_size(cname);
+	uint8_t *cname_rdata = knot_rrset_create_rdata(cname_rrset, cname_size);
 	if (cname_rdata == NULL) {
 		dbg_ns("ns: cname_from_dname: Cannot cerate CNAME RDATA.\n");
 		knot_rrset_free(&cname_rrset);
-		knot_dname_release(cname);
+		knot_dname_free(&cname);
 		return NULL;
 	}
 
 	/* Store DNAME into RDATA. */
-	memcpy(cname_rdata, &cname, sizeof(knot_dname_t *));
+	memcpy(cname_rdata, cname, cname_size);
+	knot_dname_free(&cname);
 
 	return cname_rrset;
 }
@@ -2006,10 +2016,10 @@ static int ns_dname_is_too_long(const knot_rrset_t *rrset,
                                 const knot_dname_t *qname)
 {
 	// TODO: add function for getting DNAME target
-	if (knot_dname_label_count(qname)
-	        - knot_dname_label_count(knot_rrset_owner(rrset))
-	        + knot_dname_label_count(knot_rrset_rdata_dname_target(rrset))
-	        > KNOT_MAX_DNAME_LENGTH) {
+	if (knot_dname_labels(qname, NULL)
+	        - knot_dname_labels(knot_rrset_owner(rrset), NULL)
+	        + knot_dname_labels(knot_rdata_dname_target(rrset), NULL)
+	        > KNOT_DNAME_MAXLEN) {
 		return 1;
 	} else {
 		return 0;
@@ -2074,7 +2084,7 @@ dbg_ns_exec_verb(
 	}
 
 	// get the next SNAME from the CNAME RDATA
-	const knot_dname_t *cname = knot_rrset_rdata_cname_name(synth_cname);
+	const knot_dname_t *cname = knot_rdata_cname_name(synth_cname);
 	dbg_ns_verb("CNAME name from RDATA: %p\n", cname);
 
 	// save the new name which should be used for replacing wildcard
@@ -2202,7 +2212,8 @@ have_node:
 		// DNAME?
 		knot_rrset_t *dname_rrset = knot_node_get_rrset(
 		                         closest_encloser, KNOT_RRTYPE_DNAME);
-		if (dname_rrset != NULL) {
+		if (dname_rrset != NULL
+		    && knot_rrset_rdata_rr_count(dname_rrset) > 0) {
 			ret = ns_process_dname(dname_rrset, &qname, resp);
 
 			knot_response_set_aa(resp);
@@ -2609,7 +2620,7 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
 
 	// Clean the response structure
 	dbg_ns_verb("Clearing response structure..\n");
-	knot_response_clear(xfr->response, 0);
+	knot_response_clear(xfr->response);
 
 	// increment the packet number
 	++xfr->packet_nr;
@@ -2678,8 +2689,14 @@ rrset:
 			continue;
 		}
 
-		ret = knot_response_add_rrset_answer(params->xfr->response,
-		                                       rrset, 0, 0, 0);
+		// Do not put empty RRSet
+		if (knot_rrset_rdata_rr_count(rrset) <= 0) {
+			rrset = knot_rrset_get_rrsigs(rrset);
+			goto rrsigs;
+		}
+
+		ret = knot_response_add_rrset_answer(
+				 params->xfr->response, rrset, 0, 0, 0);
 
 		if (ret == KNOT_ESPACE) {
 			// TODO: send the packet and clean the structure
@@ -2836,8 +2853,15 @@ static int ns_axfr_from_zone(knot_zone_contents_t *zone, knot_ns_xfr_t *xfr)
 
 static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, knot_rrset_t *rrset)
 {
-	int res = knot_response_add_rrset_answer(xfr->response, rrset,
-	                                         0, 0, 0);
+	int res;
+
+	if (knot_rrset_rdata_rr_count(rrset) > 0) {
+		res = knot_response_add_rrset_answer(xfr->response, rrset,
+	                                             0, 0, 0);
+	} else {
+		res = KNOT_ENORRSET;
+	}
+
 	if (res == KNOT_ESPACE) {
 		knot_response_set_rcode(xfr->response, KNOT_RCODE_NOERROR);
 		/*! \todo Probably rename the function. */
@@ -2848,7 +2872,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, knot_rrset_t *rrset)
 	}
 
 	if (res != KNOT_EOK) {
-		dbg_ns("Error putting origin SOA to IXFR reply: %s\n",
+		dbg_ns("Error putting RR to IXFR reply: %s\n",
 			 knot_strerror(res));
 		/*! \todo Probably send back AXFR instead. */
 		knot_response_set_rcode(xfr->response,
@@ -2862,7 +2886,8 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, knot_rrset_t *rrset)
 
 /*----------------------------------------------------------------------------*/
 
-static int ns_ixfr_put_changeset(knot_ns_xfr_t *xfr, const knot_changeset_t *chgset)
+static int ns_ixfr_put_changeset(knot_ns_xfr_t *xfr,
+                                 const knot_changeset_t *chgset)
 {
 	// 1) put origin SOA
 	int res = ns_ixfr_put_rrset(xfr, chgset->soa_from);
@@ -2871,22 +2896,25 @@ static int ns_ixfr_put_changeset(knot_ns_xfr_t *xfr, const knot_changeset_t *chg
 	}
 
 	// 2) put remove RRSets
-	for (int i = 0; i < chgset->remove_count; ++i) {
-		res = ns_ixfr_put_rrset(xfr, chgset->remove[i]);
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, chgset->remove) {
+		knot_rrset_t *rr_rem = rr_node->rr;
+		res = ns_ixfr_put_rrset(xfr, rr_rem);
 		if (res != KNOT_EOK) {
 			return res;
 		}
 	}
 
-	// 1) put target SOA
+	// 3) put target SOA
 	res = ns_ixfr_put_rrset(xfr, chgset->soa_to);
 	if (res != KNOT_EOK) {
 		return res;
 	}
 
-	// 2) put remove RRSets
-	for (int i = 0; i < chgset->add_count; ++i) {
-		res = ns_ixfr_put_rrset(xfr, chgset->add[i]);
+	// 4) put add RRSets
+	WALK_LIST(rr_node, chgset->add) {
+		knot_rrset_t *rr_add = rr_node->rr;
+		res = ns_ixfr_put_rrset(xfr, rr_add);
 		if (res != KNOT_EOK) {
 			return res;
 		}
@@ -2929,8 +2957,8 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
 	}
 
 	// 5) put the changesets into the response while they fit in
-	for (int i = 0; i < chgsets->count; ++i) {
-		knot_changeset_t *chs = chgsets->sets + i;
+	knot_changeset_t *chs = NULL;
+	WALK_LIST(chs, chgsets->sets) {
 		res = ns_ixfr_put_changeset(xfr, chs);
 		if (res != KNOT_EOK) {
 			// answer is sent
@@ -2939,12 +2967,12 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
 		} else {
 			log_zone_info("%s Serial %u -> %u.\n",
 			              xfr->msg,
-			              knot_rrset_rdata_soa_serial(chs->soa_from),
-			              knot_rrset_rdata_soa_serial(chs->soa_to));
+			              knot_rdata_soa_serial(chs->soa_from),
+			              knot_rdata_soa_serial(chs->soa_to));
 		}
 	}
 
-	if (chgsets->count > 0) {
+	if (!EMPTY_LIST(chgsets->sets)) {
 		res = ns_ixfr_put_rrset(xfr, zone_soa);
 	}
 
@@ -2984,7 +3012,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
 	// check if XFR QNAME and SOA correspond
 	if (knot_packet_qtype(xfr->query) != KNOT_RRTYPE_IXFR
 	    || knot_rrset_type(soa) != KNOT_RRTYPE_SOA
-	    || knot_dname_compare(qname, knot_rrset_owner(soa)) != 0) {
+	    || knot_dname_cmp(qname, knot_rrset_owner(soa)) != 0) {
 		// malformed packet
 		dbg_ns("IXFR query is malformed.\n");
 		knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
@@ -3000,12 +3028,12 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
 /*----------------------------------------------------------------------------*/
 
 static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp,
-                                    size_t max_size, int copy_question)
+                                    size_t max_size)
 {
 	assert(max_size >= 500);
 
 	// initialize response packet structure
-	*resp = knot_packet_new_mm(KNOT_PACKET_PREALLOC_RESPONSE, &query->mm);
+	*resp = knot_packet_new_mm(&query->mm);
 	if (*resp == NULL) {
 		dbg_ns("Failed to create packet structure.\n");
 		return KNOT_ENOMEM;
@@ -3019,7 +3047,7 @@ static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp,
 		return ret;
 	}
 
-	ret = knot_response_init_from_query(*resp, query, copy_question);
+	ret = knot_response_init_from_query(*resp, query);
 
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to init response structure.\n");
@@ -3060,7 +3088,7 @@ knot_nameserver_t *knot_ns_create()
 	}
 
 	// prepare empty response with SERVFAIL error
-	knot_packet_t *err = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+	knot_packet_t *err = knot_packet_new();
 	if (err == NULL) {
 		ERR_ALLOC_FAILED;
 		free(ns);
@@ -3264,36 +3292,25 @@ int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
 
 	size_t max_size = *rsize;
 	uint8_t flags1 = knot_wire_get_flags1(knot_packet_wireformat(query));
+	const size_t question_off = KNOT_WIRE_HEADER_SIZE;
 
 	// 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 (query->parsed > KNOT_WIRE_HEADER_SIZE + question_off) {
+
+		/* Append question only (do not rewrite header). */
+		size_t question_size = knot_packet_question_size(query);
+		question_size -= question_off;
+		if (max_size >= *rsize + question_size) {
 			if (response_wire != knot_packet_wireformat(query)) {
-				memcpy(response_wire + KNOT_WIRE_HEADER_SIZE,
-				       knot_packet_wireformat(query)
-				       + KNOT_WIRE_HEADER_SIZE, question_size);
+				memcpy(response_wire + question_off,
+				       knot_packet_wireformat(query) + question_off,
+				       question_size);
 			}
 			*rsize += question_size;
-
-			// adjust QDCOUNT
 			knot_wire_set_qdcount(response_wire, 1);
 		}
 	}
@@ -3411,7 +3428,7 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
 		resp_max_size = MAX_UDP_PAYLOAD;
 	}
 
-	ret = knot_ns_prepare_response(query, resp, resp_max_size, 1);
+	ret = knot_ns_prepare_response(query, resp, resp_max_size);
 	if (ret != KNOT_EOK) {
 		return KNOT_ERROR;
 	}
@@ -3458,6 +3475,10 @@ dbg_ns_exec_verb(
 	knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
 	*zone = ns_get_zone_for_qname(zonedb, qname, qtype);
 
+	/* Assign zone to packets. */
+	query->zone = *zone;
+	(*resp)->zone = *zone;
+
 	return KNOT_EOK;
 }
 
@@ -3550,7 +3571,7 @@ int knot_ns_prep_update_response(knot_nameserver_t *nameserver,
 		resp_max_size = MAX_UDP_PAYLOAD;
 	}
 
-	ret = knot_ns_prepare_response(query, resp, resp_max_size, 0);
+	ret = knot_ns_prepare_response(query, resp, resp_max_size);
 	if (ret != KNOT_EOK) {
 		return KNOT_ERROR;
 	}
@@ -3744,7 +3765,7 @@ dbg_ns_exec(
 int knot_ns_init_xfr_resp(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
 {
 	int ret = KNOT_EOK;
-	knot_packet_t *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+	knot_packet_t *resp = knot_packet_new_mm(&xfr->query->mm);
 	if (resp == NULL) {
 		dbg_ns("Failed to create packet structure.\n");
 		/*! \todo xfr->wire is not NULL, will fail on assert! */
@@ -3759,7 +3780,7 @@ int knot_ns_init_xfr_resp(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
 	resp->wireformat = xfr->wire;
 	resp->max_size = xfr->wire_size;
 
-	ret = knot_response_init_from_query(resp, xfr->query, 1);
+	ret = knot_response_init_from_query(resp, xfr->query);
 
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to init response structure.\n");
@@ -3833,9 +3854,9 @@ int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
 	}
 
 	// retrieve origin (xfr) serial and target (zone) serial
-	*serial_to = knot_rrset_rdata_soa_serial(zone_soa);
+	*serial_to = knot_rdata_soa_serial(zone_soa);
 	*serial_from =
-		knot_rrset_rdata_soa_serial(knot_packet_authority_rrset(xfr->query, 0));
+		knot_rdata_soa_serial(knot_packet_authority_rrset(xfr->query, 0));
 
 	return KNOT_EOK;
 }
@@ -4056,7 +4077,9 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver,
 		zone->zone = z;
 	}
 
+	rcu_read_unlock();
 	int ret = xfrin_switch_zone(z, zone, xfr->type);
+	rcu_read_lock();
 
 dbg_ns_exec_verb(
 	dbg_ns_verb("Zone db contents: (zone count: %zu)\n",
@@ -4117,7 +4140,7 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 		knot_zone_t *zone = xfr->zone;
 		if (zone == NULL) {
 			dbg_ns("No zone found for incoming IXFR!\n");
-			knot_free_changesets(
+			knot_changesets_free(
 				(knot_changesets_t **)(&xfr->data));
 			return KNOT_ENOZONE;
 		}
@@ -4141,8 +4164,8 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 			}
 
 			if (ns_serial_compare(
-			      knot_rrset_rdata_soa_serial(chgsets->first_soa),
-			      knot_rrset_rdata_soa_serial(zone_soa))
+			      knot_rdata_soa_serial(chgsets->first_soa),
+			      knot_rdata_soa_serial(zone_soa))
 			    > 0) {
 				if ((xfr->flags & XFR_FLAG_UDP) != 0) {
 					// IXFR over UDP
@@ -4152,7 +4175,7 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 					// fallback to AXFR
 					dbg_ns("ns_process_ixfrin: "
 					       "Fallback to AXFR.\n");
-					knot_free_changesets(
+					knot_changesets_free(
 					      (knot_changesets_t **)&xfr->data);
 					knot_packet_free(&xfr->query);
 					return KNOT_ENOIXFR;
@@ -4161,7 +4184,7 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 			} else {
 				// free changesets
 				dbg_ns("No update needed.\n");
-				knot_free_changesets(
+				knot_changesets_free(
 					(knot_changesets_t **)(&xfr->data));
 				return KNOT_ENOXFR;
 			}
@@ -4174,82 +4197,11 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 	return ret;
 }
 
-/*----------------------------------------------------------------------------*/
-/*
- * The given query is already fully parsed. But the parameter contains an
- * already prepared response structure.
- *
- * This function should process the contents, prepare prerequisities, prepare
- * changeset and return to the caller.
- */
-int knot_ns_process_update(const knot_packet_t *query,
-                           const knot_zone_contents_t *zone,
-                           knot_changeset_t *changeset, knot_rcode_t *rcode)
-{
-	assert(knot_packet_is_query(query));
-
-	dbg_ns("Processing Dynamic Update.\n");
-
-	*rcode = KNOT_RCODE_NOERROR;
-
-	// 1) Check zone
-	// Already done
-//	dbg_ns_verb("Checking zone for DDNS.\n");
-//	int ret = knot_ddns_check_zone(zone, query, rcode);
-//	if (ret != KNOT_EOK) {
-//		dbg_ns("Failed to check zone for update: "
-//		       "%s.\n", knot_strerror(ret));
-//		return ret;
-//	}
-
-	// 2) Convert prerequisities
-	// Already done
-//	dbg_ns_verb("Processing prerequisities.\n");
-//	knot_ddns_prereq_t *prereqs = NULL;
-//	int 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));
-//		return ret;
-//	}
-
-//	assert(prereqs != NULL);
-
-	// 3) Check prerequisities
-	/*! \todo Somehow ensure the zone will not be changed until the update
-	 *        is finished.
-	 */
-	// Already done
-//	dbg_ns_verb("Checking prerequisities.\n");
-//	ret = knot_ddns_check_prereqs(zone, &prereqs, rcode);
-//	if (ret != KNOT_EOK) {
-//		knot_ddns_prereqs_free(&prereqs);
-//		dbg_ns("Failed to check zone for update: "
-//		       "%s.\n", knot_strerror(ret));
-//		return ret;
-//	}
-
-	// 4) Convert update to changeset
-	dbg_ns_verb("Converting UPDATE packet to changeset.\n");
-	int ret = knot_ddns_process_update(zone, query, changeset, rcode);
-	if (ret != KNOT_EOK) {
-		dbg_ns("Failed to check zone for update: "
-		       "%s.\n", knot_strerror(ret));
-		return ret;
-	}
-
-	assert(changeset != NULL);
-
-	// Done in zones.c
-//	knot_ddns_prereqs_free(&prereqs);
-	return ret;
-}
-
 /*----------------------------------------------------------------------------*/
 /*
  * This function should:
  * 1) Create zone shallow copy and the changes structure.
- * 2) Call knot_ddns_process_update2().
+ * 2) Call knot_ddns_process_update().
  *    - If something went bad, call xfrin_rollback_update() and return an error.
  *    - If everything went OK, continue.
  * 3) Finalize the updated zone.
@@ -4257,25 +4209,22 @@ int knot_ns_process_update(const knot_packet_t *query,
  * NOTE: Mostly copied from xfrin_apply_changesets(). Should be refactored in
  *       order to get rid of duplicate code.
  */
-int knot_ns_process_update2(const knot_packet_t *query,
+int knot_ns_process_update(const knot_packet_t *query,
                             knot_zone_contents_t *old_contents,
                             knot_zone_contents_t **new_contents,
                             knot_changesets_t *chgs, knot_rcode_t *rcode)
 {
-	/*! \todo Implement. */
 	if (query == NULL || old_contents == NULL || chgs == NULL ||
-	    chgs->sets == NULL || new_contents == NULL || rcode == NULL) {
+	    EMPTY_LIST(chgs->sets) || new_contents == NULL || rcode == NULL) {
 		return KNOT_EINVAL;
 	}
 
 	dbg_ns("Applying UPDATE to zone...\n");
 
-	/* 1) Create zone shallow copy. */
+	// 1) Create zone shallow copy.
 	dbg_ns_verb("Creating shallow copy of the zone...\n");
 	knot_zone_contents_t *contents_copy = NULL;
-	knot_changes_t *changes = NULL;
-	int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy,
-	                                  &changes);
+	int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy);
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to prepare zone copy: %s\n",
 		          knot_strerror(ret));
@@ -4283,30 +4232,33 @@ int knot_ns_process_update2(const knot_packet_t *query,
 		return ret;
 	}
 
-	/* 2) Apply the UPDATE and create changesets. */
+	// 2) Apply the UPDATE and create changesets.
 	dbg_ns_verb("Applying the UPDATE and creating changeset...\n");
-	ret = knot_ddns_process_update2(contents_copy, query, &chgs->sets[0],
-	                                changes, rcode);
+	ret = knot_ddns_process_update(contents_copy, query,
+	                               knot_changesets_get_last(chgs),
+	                               chgs->changes, rcode);
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to apply UPDATE to the zone copy or no update"
 		       " made: %s\n", (ret < 0) ? knot_strerror(ret)
 		                                : "No change made.");
-		xfrin_rollback_update(old_contents, &contents_copy, &changes);
+		xfrin_rollback_update(old_contents, &contents_copy,
+		                      chgs->changes);
 		return ret;
 	}
 
+	// 3) Finalize zone
 	dbg_ns_verb("Finalizing updated zone...\n");
-	ret = xfrin_finalize_updated_zone(contents_copy, changes);
+	ret = xfrin_finalize_updated_zone(contents_copy, chgs->changes);
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to finalize updated zone: %s\n",
 		       knot_strerror(ret));
-		xfrin_rollback_update(old_contents, &contents_copy, &changes);
+		xfrin_rollback_update(old_contents, &contents_copy,
+		                      chgs->changes);
 		*rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
 		                             : KNOT_RCODE_SERVFAIL;
 		return ret;
 	}
 
-	chgs->changes = changes;
 	*new_contents = contents_copy;
 
 	return KNOT_EOK;
@@ -4339,8 +4291,7 @@ int knot_ns_process_forward_response(const knot_packet_t *response,
                                      uint16_t original_id,
                                      uint8_t *response_wire, size_t *size)
 {
-	// just copy the wireformat of the response and set the original ID
-
+	// copy the wireformat of the response and set the original ID
 	if (knot_packet_size(response) > *size) {
 		return KNOT_ESPACE;
 	}
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
index a0c9b2fee325d049eee4010c3f1d7d4df219cc20..1c7bb7af746c086b9e48264a5547c6dfc7030672 100644
--- a/src/libknot/nameserver/name-server.h
+++ b/src/libknot/nameserver/name-server.h
@@ -84,7 +84,7 @@ typedef int (*xfr_callback_t)(int session, sockaddr_t *addr,
  * Used for communication with XFR handler.
  */
 typedef struct knot_ns_xfr {
-	node n;
+	node_t n;
 	int type;
 	int flags;
 	sockaddr_t addr, saddr;
@@ -179,7 +179,8 @@ typedef enum knot_ns_xfr_type_t {
 	XFR_TYPE_SOA,     /*!< Pending SOA request. */
 	XFR_TYPE_NOTIFY,  /*!< Pending NOTIFY query. */
 	XFR_TYPE_UPDATE,  /*!< UPDATE request (incoming UPDATE). */
-	XFR_TYPE_FORWARD  /*!< UPDATE forward request. */
+	XFR_TYPE_FORWARD,  /*!< UPDATE forward request. */
+	XFR_TYPE_DNSSEC   /*!< DNSSEC changes. */
 } knot_ns_xfr_type_t;
 
 /*----------------------------------------------------------------------------*/
@@ -357,10 +358,6 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
                              knot_ns_xfr_t *xfr);
 
 int knot_ns_process_update(const knot_packet_t *query,
-                           const knot_zone_contents_t *zone,
-                           knot_changeset_t *changeset, knot_rcode_t *rcode);
-
-int knot_ns_process_update2(const knot_packet_t *query,
                             knot_zone_contents_t *old_contents,
                             knot_zone_contents_t **new_contents,
                             knot_changesets_t *chgs, knot_rcode_t *rcode);
diff --git a/src/libknot/nsec3.c b/src/libknot/nsec3.c
deleted file mode 100644
index 4fe332a1719fb40122537db8660a024b9330fc90..0000000000000000000000000000000000000000
--- a/src/libknot/nsec3.c
+++ /dev/null
@@ -1,262 +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 <stdint.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <sys/time.h>
-
-#include <openssl/evp.h>
-#include <openssl/sha.h>
-
-#include "nsec3.h"
-#include "common.h"
-#include "common/descriptor.h"
-#include "util/utils.h"
-#include "util/tolower.h"
-#include "util/debug.h"
-
-/*----------------------------------------------------------------------------*/
-
-int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
-                                  const knot_rrset_t *nsec3param)
-{
-	if (params == NULL || nsec3param == NULL ||
-	    knot_rrset_rdata_rr_count(nsec3param) == 0) {
-		return KNOT_EINVAL;
-	}
-
-	assert(knot_rrset_type(nsec3param) == KNOT_RRTYPE_NSEC3PARAM);
-
-	params->algorithm = knot_rrset_rdata_nsec3param_algorithm(nsec3param);
-	params->iterations = knot_rrset_rdata_nsec3param_iterations(nsec3param);
-	params->flags = knot_rrset_rdata_nsec3param_flags(nsec3param);
-	params->salt_length =
-		knot_rrset_rdata_nsec3param_salt_length(nsec3param);
-
-	if (params->salt_length > 0) {
-		/* It is called also on reload, so we need to free if exists. */
-		if (params->salt != NULL) {
-			free(params->salt);
-			params->salt = NULL;
-		}
-		params->salt = (uint8_t *)malloc(params->salt_length);
-		CHECK_ALLOC_LOG(params->salt, KNOT_ENOMEM);
-		memcpy(params->salt,
-		       knot_rrset_rdata_nsec3param_salt(nsec3param),
-		       params->salt_length);
-	} else {
-		params->salt = NULL;
-	}
-
-	dbg_nsec3("Parsed NSEC3PARAM:\n");
-	dbg_nsec3("Algorithm: %u\n", params->algorithm);
-	dbg_nsec3("Flags: %u\n", params->flags);
-	dbg_nsec3("Iterations: %u\n", params->iterations);
-	dbg_nsec3("Salt length: %u\n", params->salt_length);
-	dbg_nsec3("Salt: \n");
-	if (params->salt != NULL) {
-		dbg_nsec3_hex((char *)params->salt,
-		                       params->salt_length);
-		dbg_nsec3("\n");
-	} else {
-		dbg_nsec3("none\n");
-	}
-
-	return KNOT_EOK;
-}
-
-static uint8_t *knot_nsec3_to_lowercase(const uint8_t *data, size_t size)
-{
-	uint8_t *out = (uint8_t *)malloc(size);
-	CHECK_ALLOC_LOG(out, NULL);
-
-	for (size_t i = 0; i < size; ++i) {
-		out[i] = knot_tolower(data[i]);
-	}
-
-	return out;
-}
-
-/*----------------------------------------------------------------------------*/
-#if KNOT_NSEC3_SHA_USE_EVP
-int knot_nsec3_sha1(const knot_nsec3_params_t *params,
-                      const uint8_t *data, size_t size, uint8_t **digest,
-                      size_t *digest_size)
-{
-	if (digest == NULL || digest_size == NULL || data == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	uint8_t *salt = params->salt;
-	uint8_t salt_length = params->salt_length;
-	uint16_t iterations = params->iterations;
-
-	EVP_MD_CTX mdctx;
-	EVP_MD_CTX_init(&mdctx);
-
-	*digest = (uint8_t *)malloc(EVP_MD_size(EVP_sha1()));
-	if (*digest == NULL) {
-		ERR_ALLOC_FAILED;
-		return -1;
-	}
-
-	uint8_t *data_low = knot_nsec3_to_lowercase(data, size);
-	if (data_low == NULL) {
-		free(*digest);
-		return -1;
-	}
-
-	const uint8_t *in = data_low;
-	unsigned in_size = size;
-
-	int res = 0;
-
-#ifdef KNOT_NSEC3_DEBUG
-	unsigned long long total_time = 0;
-	unsigned long calls = 0;
-	long time = 0;
-#endif
-
-	for (int i = 0; i <= iterations; ++i) {
-#ifdef KNOT_NSEC3_DEBUG
-		perf_begin();
-#endif
-
-		EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
-
-		res = EVP_DigestUpdate(&mdctx, in, in_size);
-
-		if (salt_length > 0) {
-			res = EVP_DigestUpdate(&mdctx, salt, salt_length);
-		}
-
-		EVP_DigestFinal_ex(&mdctx, *digest, digest_size);
-		in = *digest;
-		in_size = *digest_size;
-
-#ifdef KNOT_NSEC3_DEBUG
-		perf_end(time);
-		total_time += time;
-		++calls;
-#endif
-
-		if (res != 1) {
-			dbg_nsec3("Error calculating SHA-1 hash.\n");
-			free(data_low);
-			free(*digest);
-			return -2;
-		}
-	}
-
-	EVP_MD_CTX_cleanup(&mdctx);
-
-	dbg_nsec3_verb("NSEC3 hashing: calls: %lu, avg time per call: %f."
-	               "\n", calls, (double)(total_time) / calls);
-
-	free(data_low);
-	return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-#else
-
-int knot_nsec3_sha1(const knot_nsec3_params_t *params,
-                      const uint8_t *data, size_t size, uint8_t **digest,
-                      size_t *digest_size)
-{
-	if (params == NULL || digest == NULL || digest_size == NULL
-	    || data == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	uint8_t *salt = params->salt;
-	uint8_t salt_length = params->salt_length;
-	uint16_t iterations = params->iterations;
-
-	dbg_nsec3_verb("Hashing: \n");
-	dbg_nsec3_verb("  Data: %.*s \n", (int)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_verb((char *)salt, salt_length);
-		dbg_nsec3_verb("\n");
-	} else {
-		dbg_nsec3_verb("none\n");
-	}
-
-	SHA_CTX ctx;
-
-	*digest = (uint8_t *)malloc(SHA_DIGEST_LENGTH);
-	if (*digest == NULL) {
-		ERR_ALLOC_FAILED;
-		return KNOT_ENOMEM;
-	}
-
-	uint8_t *data_low = knot_nsec3_to_lowercase(data, size);
-	if (data_low == NULL) {
-		free(*digest);
-		return KNOT_ENOMEM;
-	}
-
-	const uint8_t *in = data_low;
-	unsigned in_size = size;
-
-	int res = 0;
-
-	// other iterations
-	for (int i = 0; i <= iterations; ++i) {
-		SHA1_Init(&ctx);
-
-		res = SHA1_Update(&ctx, in, in_size);
-
-		if (salt_length > 0) {
-			res = SHA1_Update(&ctx, salt, salt_length);
-		}
-
-		SHA1_Final(*digest, &ctx);
-
-		in = *digest;
-		in_size = SHA_DIGEST_LENGTH;
-
-		if (res != 1) {
-			dbg_nsec3("Error calculating SHA-1 hash.\n");
-			free(data_low);
-			free(*digest);
-			return KNOT_ECRYPTO;
-		}
-	}
-
-	*digest_size = SHA_DIGEST_LENGTH;
-
-	dbg_nsec3_verb("Hash: %.*s\n", (int)*digest_size, *digest);
-	dbg_nsec3_hex_verb((const char *)*digest, *digest_size);
-	dbg_nsec3_verb("\n");
-
-	free(data_low);
-	return KNOT_EOK;
-}
-#endif
-
-/*----------------------------------------------------------------------------*/
-
-void knot_nsec3_params_free(knot_nsec3_params_t *params)
-{
-	free(params->salt);
-}
diff --git a/src/libknot/nsec3.h b/src/libknot/nsec3.h
deleted file mode 100644
index 7efd229c3c9b1d06699c49bf0e0ba11f5eee4218..0000000000000000000000000000000000000000
--- a/src/libknot/nsec3.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*!
- * \file nsec3.h
- *
- * \author Lubos Slovak <lubos.slovak@nic.cz>
- *
- * \brief Functions for calcularing NSEC3 hashes.
- *
- * \addtogroup libknot
- * @{
- */
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _KNOT_NSEC3_H_
-#define _KNOT_NSEC3_H_
-
-#include <stdint.h>
-#include <string.h>
-
-#include "rrset.h"
-
-#define KNOT_NSEC3_SHA_USE_EVP 0
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Structure representing the NSEC3PARAM resource record.
- */
-struct knot_nsec3_params {
-	uint8_t algorithm;    /*!< Hash algorithm. */
-	uint8_t flags;        /*!< Flags. */
-	uint16_t iterations;  /*!< Additional iterations of the hash function.*/
-	uint8_t salt_length;  /*!< Length of the salt field in bytes. */
-	uint8_t *salt;        /*!< Salt used in hashing. */
-};
-
-typedef struct knot_nsec3_params knot_nsec3_params_t;
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Initializes the NSEC3PARAM structure.
- *
- * \param params NSEC3PARAM structure to initialize.
- * \param nsec3param The NSEC3PARAM RRset.
- *
- * \retval KNOT_EOK on success (always).
- */
-int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
-                                  const knot_rrset_t *nsec3param);
-
-/*!
- * \brief Hashes the given data using the SHA1 hash and the given parameters.
- *
- * \param[in] params NSEC3PARAM structure with the required parameters for
- *                   hashing.
- * \param[in] data Data to hash.
- * \param[in] size Size of the data in bytes.
- * \param[out] digest Result will be store here.
- * \param[out] digest_size Size of the result in octets will be stored here.
- *
- * \retval KNOT_EOK if successful.
- * \retval KNOT_ENOMEM
- * \retval KNOT_EINVAL
- * \retval KNOT_ECRYPTO
- */
-int knot_nsec3_sha1(const knot_nsec3_params_t *params, const uint8_t *data,
-                      size_t size, uint8_t **digest, size_t *digest_size);
-
-/*!
- * \brief Properly cleans up (but does not deallocate) the NSEC3PARAM structure.
- *
- * \param params NSEC3PARAMS structure to clean up.
- */
-void knot_nsec3_params_free(knot_nsec3_params_t *params);
-
-/*----------------------------------------------------------------------------*/
-
-#endif /* _KNOT_NSEC3_H_ */
-
-/*! @} */
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 52ed72b7796b041cf7df35a64a7c546184acd5f4..2bb3e5afde8a7f7e89499fee2d7de88354f85c98 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -25,267 +25,33 @@
 #include "util/wire.h"
 #include "tsig.h"
 
-/*----------------------------------------------------------------------------*/
-
-#define DEFAULT_RRCOUNT_QUERY(type) DEFAULT_##type##COUNT_QUERY
-#define DEFAULT_RRCOUNT(type) DEFAULT_##type##COUNT
-
-#define DEFAULT_RRSET_COUNT(type, packet) \
-	((packet->prealloc_type == KNOT_PACKET_PREALLOC_NONE)  \
-		? 0  \
-		: (packet->prealloc_type == KNOT_PACKET_PREALLOC_QUERY)  \
-			? DEFAULT_##type##_QUERY  \
-			: DEFAULT_##type)
-
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
 /*----------------------------------------------------------------------------*/
-/*!
- * \brief Sets all the pointers in the packet structure to the respective
- *        parts of the pre-allocated space.
- */
-static void knot_packet_init_pointers_response(knot_packet_t *pkt)
-{
-	dbg_packet_detail("Packet pointer: %p\n", pkt);
-
-	char *pos = (char *)pkt + PREALLOC_PACKET;
-
-	// put QNAME directly after the structure
-	pkt->question.qname = (knot_dname_t *)pos;
-	pos += PREALLOC_QNAME_DNAME;
-
-	dbg_packet_detail("QNAME: %p\n", pkt->question.qname);
-
-	pkt->question.qname->name = (uint8_t *)pos;
-	pos += PREALLOC_QNAME_NAME;
-	pkt->question.qname->labels = (uint8_t *)pos;
-	pos += PREALLOC_QNAME_LABELS;
-
-	// then answer, authority and additional sections
-	if (DEFAULT_ANCOUNT == 0) {
-		pkt->answer = NULL;
-	} else {
-		pkt->answer = (const knot_rrset_t **)pos;
-		pos += DEFAULT_ANCOUNT * sizeof(const knot_rrset_t *);
-	}
-
-	if (DEFAULT_NSCOUNT == 0) {
-		pkt->authority = NULL;
-	} else {
-		pkt->authority = (const knot_rrset_t **)pos;
-		pos += DEFAULT_NSCOUNT * sizeof(const knot_rrset_t *);
-	}
-
-	if (DEFAULT_ARCOUNT == 0) {
-		pkt->additional = NULL;
-	} else {
-		pkt->additional = (const knot_rrset_t **)pos;
-		pos += DEFAULT_ARCOUNT * sizeof(const knot_rrset_t *);
-	}
-
-	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;
-	pkt->max_ar_rrsets = DEFAULT_ARCOUNT;
-
-	// wildcard nodes and SNAMEs associated with them
-	pkt->wildcard_nodes.nodes = (const knot_node_t **)pos;
-	pos += DEFAULT_WILDCARD_NODES * sizeof(const knot_node_t *);
-	pkt->wildcard_nodes.snames = (const knot_dname_t **)pos;
-	pos += DEFAULT_WILDCARD_NODES * sizeof(knot_dname_t *);
-
-	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;
-
-	pkt->tmp_rrsets = (const knot_rrset_t **)pos;
-	pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *);
-
-	dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets);
-
-	pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS;
-
-	assert((char *)pos == (char *)pkt + PREALLOC_RESPONSE);
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Sets all the pointers in the packet structure to the respective
- *        parts of the pre-allocated space.
- */
-static void knot_packet_init_pointers_query(knot_packet_t *pkt)
-{
-	dbg_packet_detail("Packet pointer: %p\n", pkt);
-
-	char *pos = (char *)pkt + PREALLOC_PACKET;
-
-	// put QNAME directly after the structure
-	pkt->question.qname = (knot_dname_t *)pos;
-	pos += PREALLOC_QNAME_DNAME;
-
-	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;
-
-
-	// then answer, authority and additional sections
-	if (DEFAULT_ANCOUNT_QUERY == 0) {
-		pkt->answer = NULL;
-	} else {
-		pkt->answer = (const knot_rrset_t **)pos;
-		pos += DEFAULT_ANCOUNT_QUERY * sizeof(const knot_rrset_t *);
-	}
-
-	if (DEFAULT_NSCOUNT_QUERY == 0) {
-		pkt->authority = NULL;
-	} else {
-		pkt->authority = (const knot_rrset_t **)pos;
-		pos += DEFAULT_NSCOUNT_QUERY * sizeof(const knot_rrset_t *);
-	}
-
-	if (DEFAULT_ARCOUNT_QUERY == 0) {
-		pkt->additional = NULL;
-	} else {
-		pkt->additional = (const knot_rrset_t **)pos;
-		pos += DEFAULT_ARCOUNT_QUERY * sizeof(const knot_rrset_t *);
-	}
-
-	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;
-	pkt->max_ar_rrsets = DEFAULT_ARCOUNT_QUERY;
-
-	pkt->tmp_rrsets = (const knot_rrset_t **)pos;
-	pos += DEFAULT_TMP_RRSETS_QUERY * sizeof(const knot_rrset_t *);
-
-	dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets);
-
-	pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY;
-
-	dbg_packet_detail("Allocated total: %u\n", PREALLOC_QUERY);
-
-	assert(pos == (char *)pkt + PREALLOC_QUERY);
-}
 
 /*----------------------------------------------------------------------------*/
-/*!
- * \brief Parses DNS header from the wire format.
- *
- * \note This function also adjusts the position (\a pos) and size of remaining
- *       bytes in the wire format (\a remaining) according to what was parsed
- *       (though it actually always parses the 12 bytes of the header).
- *
- * \param[in,out] pos Wire format to parse the header from.
- * \param[in,out] remaining Remaining size of the wire format.
- * \param[out] header Header structure to fill in.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EFEWDATA
- */
-static int knot_packet_parse_header(const uint8_t *wire, size_t *pos,
-                                      size_t size, knot_header_t *header)
-{
-	assert(wire != NULL);
-	assert(pos != NULL);
-	assert(header != NULL);
-
-	if (size - *pos < KNOT_WIRE_HEADER_SIZE) {
-		dbg_packet("Not enough data to parse header.\n");
-		return KNOT_EFEWDATA;
-	}
-
-	header->id = knot_wire_get_id(wire);
-	header->flags1 = knot_wire_get_flags1(wire);
-	header->flags2 = knot_wire_get_flags2(wire);
-
-	header->qdcount = knot_wire_get_qdcount(wire);
-	header->ancount = knot_wire_get_ancount(wire);
-	header->nscount = knot_wire_get_nscount(wire);
-	header->arcount = knot_wire_get_arcount(wire);
-
-	*pos += KNOT_WIRE_HEADER_SIZE;
-
-	return KNOT_EOK;
-}
 
 /*----------------------------------------------------------------------------*/
 /*!
- * \brief Parses DNS Question entry from the wire format.
- *
- * \note This function also adjusts the position (\a pos) and size of remaining
- *       bytes in the wire format (\a remaining) according to what was parsed.
- *
- * \param[in,out] pos Wire format to parse the Question from.
- * \param[in,out] remaining Remaining size of the wire format.
- * \param[out] question DNS Question structure to be filled.
- *
- * \retval KNOT_EOK
- * \retval KNOT_EFEWDATA
- * \retval KNOT_ENOMEM
+ * \brief Processes DNS Question entry from the wire format.
  */
-static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
-                                        size_t size,
-                                        knot_question_t *question, int alloc)
+static int knot_packet_parse_question(knot_packet_t *pkt)
 {
-	assert(pos != NULL);
-	assert(wire != NULL);
-	assert(question != NULL);
+	assert(pkt != NULL);
 
-	if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) {
-		dbg_packet("Not enough data to parse question.\n");
-		return KNOT_EFEWDATA;  // malformed
-	}
-
-	dbg_packet("Parsing Question starting on position %zu.\n", *pos);
+	dbg_packet("Parsing Question starting on position %zu.\n", pkt->parsed);
 
-	// domain name must end with 0, so just search for 0
-	int i = *pos;
-	while (i < size && wire[i] != 0) {
-		++i;
-	}
+	/* Process question. */
+	int len = knot_dname_wire_check(pkt->wireformat + pkt->parsed,
+	                                pkt->wireformat + pkt->size,
+	                                pkt->wireformat);
+	if (len <= 0)
+		return KNOT_EMALF;
 
-	if (size - i - 1 < 4) {
-		dbg_packet("Not enough data to parse question.\n");
-		return KNOT_EFEWDATA;  // no 0 found or not enough data left
-	}
-
-	dbg_packet_verb("Parsing dname starting on position %zu and "
-	                      "%zu bytes long.\n", *pos, i - *pos + 1);
-	dbg_packet_verb("Alloc: %d\n", alloc);
-	if (alloc) {
-		question->qname = knot_dname_parse_from_wire(wire, pos,
-		                                             i + 1,
-		                                             NULL, NULL);
-		if (question->qname == NULL) {
-			return KNOT_ENOMEM;
-		}
-		knot_dname_to_lower(question->qname);
-	} else {
-		assert(question->qname != NULL); /* When alloc=0, must be set. */
-		void *parsed = knot_dname_parse_from_wire(wire, pos,
-		                                     i + 1,
-	                                             NULL, question->qname);
-		if (!parsed) {
-			return KNOT_EMALF;
-		}
-		knot_dname_to_lower(question->qname);
-	}
-	question->qtype = knot_wire_read_u16(wire + i + 1);
-	question->qclass = knot_wire_read_u16(wire + i + 3);
-	*pos += 4;
+	/*! \todo Question case should be preserved. */
+	/* knot_dname_to_lower(question->qname); */
+	pkt->parsed += len + 2 * sizeof(uint16_t); /* Class + Type */
+	pkt->qname_size = len;
 
 	return KNOT_EOK;
 }
@@ -296,37 +62,24 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
  *
  * \param rrsets Space for RRSets.
  * \param max_count Size of the space available for the RRSets.
- * \param default_max_count Size of the space pre-allocated for the RRSets when
- *        the response structure was initialized.
- * \param step How much the space should be increased.
  *
  * \retval KNOT_EOK
  * \retval KNOT_ENOMEM
  */
-static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
-                                        short *max_count,
-                                        short default_max_count, short step)
+int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
+                                      short *max_count,
+                                      mm_ctx_t *mm)
 {
-	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;
-
-	short new_max_count = *max_count + step;
-	const knot_rrset_t **new_rrsets = (const knot_rrset_t **)malloc(
+	short new_max_count = *max_count + RRSET_ALLOC_STEP;
+	const knot_rrset_t **new_rrsets = mm->alloc(mm->ctx,
 		new_max_count * sizeof(knot_rrset_t *));
 	CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
-
-	memset(new_rrsets, 0, new_max_count * sizeof(knot_rrset_t *));
 	memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
 
+	mm->free(*rrsets);
 	*rrsets = new_rrsets;
 	*max_count = new_max_count;
 
-	if (free_old) {
-		free(old);
-	}
-
 	return KNOT_EOK;
 }
 
@@ -359,59 +112,18 @@ static int knot_packet_parse_rdata(knot_rrset_t *rr, const uint8_t *wire,
 		return ret;
 	}
 
-//	uint8_t* rd = knot_rrset_create_rdata(rr, rdlength);
-//	if (!rd) {
-//		return KNOT_ERROR;
-//	}
-//	uint8_t* np = rd + rdlength;
-
-//	const rdata_descriptor_t *desc = get_rdata_descriptor(knot_rrset_type(rr));
-//	if (!desc) {
-//		/*! \todo Free rdata mem ? Not essential, but nice. */
-//		return KNOT_EINVAL;
-//	}
-
-//	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
-//		const int id = desc->block_types[i];
-//		if (descriptor_item_is_dname(id)) {
-//			knot_dname_t *dn = NULL;
-//			dn = knot_dname_parse_from_wire(wire, pos, total_size, NULL, NULL);
-//			if (dn == NULL) {
-//				return KNOT_EMALF;
-//			}
-//			/* Store ptr in rdata. */
-//			*((knot_dname_t**)rd) = dn;
-//			rd += sizeof(knot_dname_t*);
-//		} else if (descriptor_item_is_fixed(id)) {
-//			memcpy(rd, wire + *pos, id);
-//			rd += id; /* Item represents fixed len here */
-//			*pos += id;
-//		} else if (descriptor_item_is_remainder(id)) {
-//			size_t rchunk = np - rd;
-//			memcpy(rd, wire + *pos, rchunk);
-//			rd += rchunk;
-//			*pos += rchunk;
-//		} else {
-//			//NAPTR
-//			assert(knot_rrset_type(rr) == KNOT_RRTYPE_NAPTR);
-//			assert(0);
-//		}
-
-//	}
-
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
 static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
-                                              size_t size)
+                                          size_t size)
 {
 	dbg_packet("Parsing RR from position: %zu, total size: %zu\n",
 	           *pos, size);
 
-	knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
-	                                                     NULL, NULL);
+	knot_dname_t *owner = knot_dname_parse(wire, pos, size);
 	dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner,
 	                  *pos);
 	if (owner == NULL) {
@@ -425,11 +137,10 @@ dbg_packet_exec_verb(
 	free(name);
 );
 
-	/*! @todo Get rid of the numerical constant. */
-	if (size - *pos < 10) {
+	if (size - *pos < KNOT_RR_HEADER_SIZE) {
 		dbg_packet("Malformed RR: Not enough data to parse RR"
 		           " header.\n");
-		knot_dname_release(owner);
+		knot_dname_free(&owner);
 		return NULL;
 	}
 
@@ -440,12 +151,8 @@ dbg_packet_exec_verb(
 	uint32_t ttl = knot_wire_read_u32(wire + *pos + 4);
 
 	knot_rrset_t *rrset = knot_rrset_new(owner, type, rclass, ttl);
-
-	/* Owner is either referenced in rrset or rrset creation failed. */
-	knot_dname_release(owner);
-
-	/* Check rrset allocation. */
 	if (rrset == NULL) {
+		knot_dname_free(&owner);
 		return NULL;
 	}
 
@@ -455,12 +162,12 @@ dbg_packet_exec_verb(
 	                    "rdlength %u\n", rrset->type, rrset->rclass,
 	                    rrset->ttl, rdlength);
 
-	*pos += 10;
+	*pos += KNOT_RR_HEADER_SIZE;
 
 	if (size - *pos < rdlength) {
 		dbg_packet("Malformed RR: Not enough data to parse RR"
 		           " RDATA (size: %zu, position: %zu).\n", size, *pos);
-		knot_rrset_deep_free(&rrset, 1, 1);
+		knot_rrset_deep_free(&rrset, 1);
 		return NULL;
 	}
 
@@ -477,7 +184,7 @@ dbg_packet_exec_verb(
 	int ret = knot_packet_parse_rdata(rrset, wire, pos, size, rdlength);
 	if (ret != KNOT_EOK) {
 		dbg_packet("Malformed RR: Could not parse RDATA.\n");
-		knot_rrset_deep_free(&rrset, 1, 1);
+		knot_rrset_deep_free(&rrset, 1);
 		return NULL;
 	}
 
@@ -490,8 +197,7 @@ 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_t *packet,
                                  knot_packet_flag_t flags)
 {
 	assert(rrset != NULL);
@@ -507,8 +213,8 @@ dbg_packet_exec_verb(
 );
 
 	if (*rrset_count == *max_rrsets
-	    && knot_packet_realloc_rrsets(rrsets, max_rrsets, default_rrsets,
-	                                    STEP_ANCOUNT) != KNOT_EOK) {
+	    && knot_packet_realloc_rrsets(rrsets, max_rrsets,
+	                                  &packet->mm) != KNOT_EOK) {
 		return KNOT_ENOMEM;
 	}
 
@@ -530,12 +236,12 @@ dbg_packet_exec_detail(
 );
 
 			if (knot_rrset_equal((*rrsets)[i], rrset,
-			                         KNOT_RRSET_COMPARE_HEADER)) {
+			                     KNOT_RRSET_COMPARE_HEADER)) {
 				/*! \todo Test this!!! */
 				// no duplicate checking here, the packet should
 				// look exactly as it came from wire
-				int rc = knot_rrset_merge(
-				    ((knot_rrset_t *)(*rrsets)[i]), rrset);
+				int rc =
+					knot_rrset_merge((knot_rrset_t *)(*rrsets)[i], rrset);
 				if (rc != KNOT_EOK) {
 					return rc;
 				}
@@ -557,7 +263,6 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
                                  uint16_t *parsed_rrs,
                                  const knot_rrset_t ***rrsets,
                                  short *rrset_count, short *max_rrsets,
-                                 short default_rrsets,
                                  knot_packet_t *packet,
                                  knot_packet_flag_t flags)
 {
@@ -590,15 +295,15 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
 		++(*parsed_rrs);
 
 		err = knot_packet_add_rrset(rrset, rrsets, rrset_count,
-		                            max_rrsets, default_rrsets, packet, flags);
+		                            max_rrsets, packet, flags);
 		if (err < 0) {
 			break;
 		} else if (err == 1) {	// merged, shallow data copy
 			dbg_packet_detail("RRSet merged, freeing.\n");
-			knot_rrset_deep_free(&rrset, 1, 0);
+			knot_rrset_deep_free(&rrset, 1);
 			continue;
 		} else if (err == 2) { // skipped
-			knot_rrset_deep_free(&rrset, 1, 1);
+			knot_rrset_deep_free(&rrset, 1);
 			continue;
 		}
 
@@ -607,7 +312,7 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
 			// remove the last RRSet from the list of RRSets
 			// - just decrement the count
 			--(*rrset_count);
-			knot_rrset_deep_free(&rrset, 1, 1);
+			knot_rrset_deep_free(&rrset, 1);
 			break;
 		}
 
@@ -642,29 +347,13 @@ 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_verb("Freeing additional space in packet.\n");
-	if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) {
-		dbg_packet_detail("Freeing QNAME.\n");
-		knot_dname_release(pkt->question.qname);
-	}
-
-	if (pkt->max_an_rrsets > DEFAULT_RRSET_COUNT(ANCOUNT, pkt)) {
-		free(pkt->answer);
-	}
-	if (pkt->max_ns_rrsets > DEFAULT_RRSET_COUNT(NSCOUNT, pkt)) {
-		free(pkt->authority);
-	}
-	if (pkt->max_ar_rrsets > DEFAULT_RRSET_COUNT(ARCOUNT, pkt)) {
-		free(pkt->additional);
-	}
-
-	if (pkt->wildcard_nodes.max > pkt->wildcard_nodes.default_count) {
-		free(pkt->wildcard_nodes.nodes);
-		free(pkt->wildcard_nodes.snames);
-	}
 
-	if (pkt->tmp_rrsets_max > DEFAULT_RRSET_COUNT(TMP_RRSETS, pkt)) {
-		free(pkt->tmp_rrsets);
-	}
+	pkt->mm.free(pkt->answer);
+	pkt->mm.free(pkt->authority);
+	pkt->mm.free(pkt->additional);
+	pkt->mm.free(pkt->wildcard_nodes.nodes);
+	pkt->mm.free(pkt->wildcard_nodes.snames);
+	pkt->mm.free(pkt->tmp_rrsets);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -684,9 +373,9 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, size_t *pos,
 
 	dbg_packet_verb("Parsing Answer RRs...\n");
 	if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
-	   packet->size, packet->header.ancount, &packet->parsed_an,
+	   packet->size, knot_wire_get_ancount(packet->wireformat), &packet->parsed_an,
 	   &packet->answer, &packet->an_rrsets, &packet->max_an_rrsets,
-	   DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet, flags)) != KNOT_EOK) {
+	                                 packet, flags)) != KNOT_EOK) {
 		return err;
 	}
 
@@ -697,9 +386,9 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, size_t *pos,
 
 	dbg_packet_verb("Parsing Authority RRs...\n");
 	if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
-	   packet->size, packet->header.nscount, &packet->parsed_ns,
+	   packet->size, knot_wire_get_nscount(packet->wireformat), &packet->parsed_ns,
 	   &packet->authority, &packet->ns_rrsets, &packet->max_ns_rrsets,
-	   DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet, flags)) != KNOT_EOK) {
+	   packet, flags)) != KNOT_EOK) {
 		return err;
 	}
 
@@ -710,9 +399,9 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, size_t *pos,
 
 	dbg_packet_verb("Parsing Additional RRs...\n");
 	if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
-	   packet->size, packet->header.arcount, &packet->parsed_ar,
+	   packet->size, knot_wire_get_arcount(packet->wireformat), &packet->parsed_ar,
 	   &packet->additional, &packet->ar_rrsets, &packet->max_ar_rrsets,
-	   DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet, flags)) != KNOT_EOK) {
+	   packet, flags)) != KNOT_EOK) {
 		return err;
 	}
 
@@ -755,42 +444,21 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet, size_t *pos,
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
 
-knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc)
+knot_packet_t *knot_packet_new()
 {
 	mm_ctx_t mm;
 	mm_ctx_init(&mm);
-	return knot_packet_new_mm(prealloc, &mm);
+	return knot_packet_new_mm(&mm);
 }
 
-knot_packet_t *knot_packet_new_mm(knot_packet_prealloc_type_t prealloc, mm_ctx_t *mm)
+knot_packet_t *knot_packet_new_mm(mm_ctx_t *mm)
 {
 	knot_packet_t *pkt = NULL;
-	void (*init_pointers)(knot_packet_t *pkt) = NULL;
-	size_t size = 0;
-
-	switch (prealloc) {
-	case KNOT_PACKET_PREALLOC_NONE:
-		size = sizeof(knot_packet_t);
-		break;
-	case KNOT_PACKET_PREALLOC_QUERY:
-		size = PREALLOC_QUERY;
-		init_pointers = knot_packet_init_pointers_query;
-		break;
-	case KNOT_PACKET_PREALLOC_RESPONSE:
-		size = PREALLOC_RESPONSE;
-		init_pointers = knot_packet_init_pointers_response;
-		break;
-	}
-
-	pkt = (knot_packet_t *)mm->alloc(mm->ctx, size);
+
+	pkt = (knot_packet_t *)mm->alloc(mm->ctx, sizeof(knot_packet_t));
 	CHECK_ALLOC_LOG(pkt, NULL);
-	memset(pkt, 0, size);
+	memset(pkt, 0, sizeof(knot_packet_t));
 	memcpy(&pkt->mm, mm, sizeof(mm_ctx_t));
-	if (init_pointers != NULL) {
-		init_pointers(pkt);
-	}
-
-	pkt->prealloc_type = prealloc;
 
 	// set EDNS version to not supported
 	pkt->opt_rr.version = EDNS_NOT_SUPPORTED;
@@ -801,55 +469,30 @@ knot_packet_t *knot_packet_new_mm(knot_packet_prealloc_type_t prealloc, mm_ctx_t
 /*----------------------------------------------------------------------------*/
 
 int knot_packet_parse_from_wire(knot_packet_t *packet,
-                                  const uint8_t *wireformat, size_t size,
-                                  int question_only, knot_packet_flag_t flags)
+                                const uint8_t *wireformat, size_t size,
+                                int question_only, knot_packet_flag_t flags)
 {
-	if (packet == NULL || wireformat == NULL) {
+	if (packet == NULL || wireformat == NULL || size < KNOT_WIRE_HEADER_SIZE)
 		return KNOT_EINVAL;
-	}
 
-	int err;
 
-	// save the wireformat in the packet
-	// TODO: can we just save the pointer, or we have to copy the data??
+	int err = KNOT_EOK;
+
 	assert(packet->wireformat == NULL);
 	packet->wireformat = (uint8_t*)wireformat;
 	packet->size = size;
-	packet->free_wireformat = 0;
-
-	if (size < 2) {
-		return KNOT_EMALF;
-	}
-
-	size_t pos = 0;
-
-	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;
-	}
-
-	packet->parsed = pos;
+	packet->flags &= ~KNOT_PF_FREE_WIRE;
+	packet->parsed = KNOT_WIRE_HEADER_SIZE;
 
-	dbg_packet_verb("Question (prealloc type: %d)...\n",
-	                packet->prealloc_type);
-
-	if (packet->header.qdcount > 1) {
+	uint16_t qdcount = knot_wire_get_qdcount(packet->wireformat);
+	if (qdcount == 1) {
+		if ((err = knot_packet_parse_question(packet)) != KNOT_EOK)
+			return err;
+	} else if (qdcount > 1) {
 		dbg_packet("QDCOUNT larger than 1, FORMERR.\n");
 		return KNOT_EMALF;
 	}
 
-	if (packet->header.qdcount == 1) {
-		if ((err = knot_packet_parse_question(wireformat, &pos, size,
-		             &packet->question, packet->prealloc_type
-		                                == KNOT_PACKET_PREALLOC_NONE)
-		     ) != KNOT_EOK) {
-			return err;
-		}
-		packet->parsed = pos;
-	}
-
 dbg_packet_exec_detail(
 	knot_packet_dump(packet);
 );
@@ -858,7 +501,6 @@ dbg_packet_exec_detail(
 		return KNOT_EOK;
 	}
 
-	/*! \todo Replace by call to parse_rest()? */
 	err = knot_packet_parse_rest(packet, flags);
 
 dbg_packet_exec_detail(
@@ -876,9 +518,9 @@ int knot_packet_parse_rest(knot_packet_t *packet, knot_packet_flag_t flags)
 		return KNOT_EINVAL;
 	}
 
-	if (packet->header.ancount == packet->parsed_an
-	    && packet->header.nscount == packet->parsed_ns
-	    && packet->header.arcount == packet->parsed_ar
+	if (knot_wire_get_ancount(packet->wireformat) == packet->parsed_an
+	    && knot_wire_get_nscount(packet->wireformat) == packet->parsed_ns
+	    && knot_wire_get_arcount(packet->wireformat) == packet->parsed_ar
 	    && packet->parsed == packet->size) {
 		return KNOT_EOK;
 	}
@@ -906,8 +548,8 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
 	*rr = NULL;
 
 	if (packet->parsed >= packet->size) {
-		assert(packet->an_rrsets <= packet->header.ancount);
-		if (packet->parsed_an != packet->header.ancount) {
+		assert(packet->an_rrsets <= knot_wire_get_ancount(packet->wireformat));
+		if (packet->parsed_an != knot_wire_get_ancount(packet->wireformat)) {
 			dbg_packet("Parsed less RRs than expected.\n");
 			return KNOT_EMALF;
 		} else {
@@ -916,7 +558,7 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
 		}
 	}
 
-	if (packet->parsed_an == packet->header.ancount) {
+	if (packet->parsed_an == knot_wire_get_ancount(packet->wireformat)) {
 		assert(packet->parsed < packet->size);
 		//dbg_packet("Trailing garbage, ignoring...\n");
 		// there may be other data in the packet
@@ -957,8 +599,8 @@ int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
 	*rr = NULL;
 
 	if (packet->parsed >= packet->size) {
-		assert(packet->ar_rrsets <= packet->header.arcount);
-		if (packet->parsed_ar != packet->header.arcount) {
+		assert(packet->ar_rrsets <= knot_wire_get_arcount(packet->wireformat));
+		if (packet->parsed_ar != knot_wire_get_arcount(packet->wireformat)) {
 			dbg_packet("Parsed less RRs than expected.\n");
 			return KNOT_EMALF;
 		} else {
@@ -967,7 +609,7 @@ int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
 		}
 	}
 
-	if (packet->parsed_ar == packet->header.arcount) {
+	if (packet->parsed_ar == knot_wire_get_arcount(packet->wireformat)) {
 		assert(packet->parsed < packet->size);
 		dbg_packet_verb("Trailing garbage, treating as malformed...\n");
 		return KNOT_EMALF;
@@ -1011,8 +653,7 @@ size_t knot_packet_max_size(const knot_packet_t *packet)
 
 size_t knot_packet_question_size(const knot_packet_t *packet)
 {
-	return (KNOT_WIRE_HEADER_SIZE + 4
-	        + knot_dname_size(packet->question.qname));
+	return KNOT_WIRE_HEADER_SIZE + packet->qname_size + 2 * sizeof(uint16_t);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1024,6 +665,16 @@ size_t knot_packet_parsed(const knot_packet_t *packet)
 
 /*----------------------------------------------------------------------------*/
 
+int knot_packet_set_size(knot_packet_t *packet, int size)
+{
+	if (packet == NULL || size > packet->max_size)
+		return KNOT_EINVAL;
+
+	return packet->size = size;
+}
+
+/*----------------------------------------------------------------------------*/
+
 int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
 {
 	if (packet == NULL || max_size <= 0) {
@@ -1038,22 +689,17 @@ int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
 			return KNOT_ENOMEM;
 		}
 
-		uint8_t *wire_old = packet->wireformat;
-
-		memcpy(wire_new, packet->wireformat, packet->max_size);
-		packet->wireformat = wire_new;
-
-		if (packet->max_size > 0 && packet->free_wireformat) {
-			if (packet->mm.free)
-				packet->mm.free(wire_old);
+		if (packet->max_size > 0) {
+			memcpy(wire_new, packet->wireformat, packet->max_size);
+			if (packet->flags & KNOT_PF_FREE_WIRE)
+				packet->mm.free(packet->wireformat);
 		}
 
-		packet->free_wireformat = 1;
+		packet->wireformat = wire_new;
+		packet->max_size = max_size;
+		packet->flags |= KNOT_PF_FREE_WIRE;
 	}
 
-	// set max size
-	packet->max_size = max_size;
-
 	return KNOT_EOK;
 }
 
@@ -1062,18 +708,7 @@ int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
 uint16_t knot_packet_id(const knot_packet_t *packet)
 {
 	assert(packet != NULL);
-	return packet->header.id;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_packet_set_id(knot_packet_t *packet, uint16_t id)
-{
-	if (packet == NULL) {
-		return;
-	}
-
-	packet->header.id = id;
+	return knot_wire_get_id(packet->wireformat);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1084,7 +719,7 @@ void knot_packet_set_random_id(knot_packet_t *packet)
 		return;
 	}
 
-	packet->header.id = knot_random_id();
+	knot_wire_set_id(packet->wireformat, knot_random_id());
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1092,15 +727,8 @@ void knot_packet_set_random_id(knot_packet_t *packet)
 uint8_t knot_packet_opcode(const knot_packet_t *packet)
 {
 	assert(packet != NULL);
-	return knot_wire_flags_get_opcode(packet->header.flags1);
-}
-
-/*----------------------------------------------------------------------------*/
-
-knot_question_t *knot_packet_question(knot_packet_t *packet)
-{
-	if (packet == NULL) return NULL;
-	return &packet->question;
+	uint8_t flags = knot_wire_get_flags1(packet->wireformat);
+	return knot_wire_flags_get_opcode(flags);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1111,7 +739,7 @@ const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
 		return NULL;
 	}
 
-	return packet->question.qname;
+	return packet->wireformat + KNOT_WIRE_HEADER_SIZE;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1119,15 +747,8 @@ const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
 uint16_t knot_packet_qtype(const knot_packet_t *packet)
 {
 	assert(packet != NULL);
-	return packet->question.qtype;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_packet_set_qtype(knot_packet_t *packet, uint16_t qtype)
-{
-	assert(packet != NULL);
-	packet->question.qtype = qtype;
+	unsigned off = KNOT_WIRE_HEADER_SIZE + packet->qname_size;
+	return knot_wire_read_u16(packet->wireformat + off);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1135,7 +756,8 @@ void knot_packet_set_qtype(knot_packet_t *packet, uint16_t qtype)
 uint16_t knot_packet_qclass(const knot_packet_t *packet)
 {
 	assert(packet != NULL);
-	return packet->question.qclass;
+	unsigned off = KNOT_WIRE_HEADER_SIZE + packet->qname_size + sizeof(uint16_t);
+	return knot_wire_read_u16(packet->wireformat + off);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1146,7 +768,8 @@ int knot_packet_is_query(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return (knot_wire_flags_get_qr(packet->header.flags1) == 0);
+	uint8_t flags = knot_wire_get_flags1(packet->wireformat);
+	return (knot_wire_flags_get_qr(flags) == 0);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1168,7 +791,8 @@ int knot_packet_rcode(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return knot_wire_flags_get_rcode(packet->header.flags2);
+	uint8_t flags = knot_wire_get_flags2(packet->wireformat);
+	return knot_wire_flags_get_rcode(flags);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1179,7 +803,8 @@ int knot_packet_tc(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return knot_wire_flags_get_tc(packet->header.flags1);
+	uint8_t flags = knot_wire_get_flags1(packet->wireformat);
+	return knot_wire_flags_get_tc(flags);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1190,7 +815,7 @@ int knot_packet_qdcount(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return packet->header.qdcount;
+	return knot_wire_get_qdcount(packet->wireformat);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1201,7 +826,7 @@ int knot_packet_ancount(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return packet->header.ancount;
+	return knot_wire_get_ancount(packet->wireformat);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1212,7 +837,7 @@ int knot_packet_nscount(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return packet->header.nscount;
+	return knot_wire_get_nscount(packet->wireformat);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1223,7 +848,7 @@ int knot_packet_arcount(const knot_packet_t *packet)
 		return KNOT_EINVAL;
 	}
 
-	return packet->header.arcount;
+	return knot_wire_get_arcount(packet->wireformat);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1356,12 +981,12 @@ int knot_packet_add_tmp_rrset(knot_packet_t *packet,
 		return KNOT_EINVAL;
 	}
 
-	if (packet->tmp_rrsets_count == packet->tmp_rrsets_max
-	    && knot_packet_realloc_rrsets(&packet->tmp_rrsets,
-			&packet->tmp_rrsets_max,
-			DEFAULT_RRSET_COUNT(TMP_RRSETS, packet),
-			STEP_TMP_RRSETS) != KNOT_EOK) {
-		return KNOT_ENOMEM;
+	if (packet->tmp_rrsets_count == packet->tmp_rrsets_max) {
+		int ret = knot_packet_realloc_rrsets(&packet->tmp_rrsets,
+		                                     &packet->tmp_rrsets_max,
+		                                     &packet->mm);
+		if (ret != KNOT_EOK)
+			return ret;
 	}
 
 	packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset;
@@ -1399,62 +1024,8 @@ dbg_packet_exec(
 		// function (for reallocating rrset array)
 		// TODO sort out freeing, this WILL leak.
 		knot_rrset_deep_free(
-			&(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), 1, 1);
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_packet_header_to_wire(const knot_header_t *header,
-                                  uint8_t **pos, size_t *size)
-{
-	if (header == NULL || pos == NULL || *pos == NULL || size == NULL) {
-		return;
+			&(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), 1);
 	}
-
-	knot_wire_set_id(*pos, header->id);
-	knot_wire_set_flags1(*pos, header->flags1);
-	knot_wire_set_flags2(*pos, header->flags2);
-	knot_wire_set_qdcount(*pos, header->qdcount);
-	knot_wire_set_ancount(*pos, header->ancount);
-	knot_wire_set_nscount(*pos, header->nscount);
-	knot_wire_set_arcount(*pos, header->arcount);
-
-	*pos += KNOT_WIRE_HEADER_SIZE;
-	*size += KNOT_WIRE_HEADER_SIZE;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int knot_packet_question_to_wire(knot_packet_t *packet)
-{
-	if (packet == NULL || packet->question.qname == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (packet->size > KNOT_WIRE_HEADER_SIZE) {
-		return KNOT_ERROR;
-	}
-
-	// TODO: get rid of the numeric constants
-	size_t qsize = 4 + knot_dname_size(packet->question.qname);
-	if (qsize > packet->max_size - KNOT_WIRE_HEADER_SIZE) {
-		return KNOT_ESPACE;
-	}
-
-	// create the wireformat of Question
-	uint8_t *pos = packet->wireformat + KNOT_WIRE_HEADER_SIZE;
-	memcpy(pos, knot_dname_name(packet->question.qname),
-	       knot_dname_size(packet->question.qname));
-
-	pos += knot_dname_size(packet->question.qname);
-	knot_wire_write_u16(pos, packet->question.qtype);
-	pos += 2;
-	knot_wire_write_u16(pos, packet->question.qclass);
-
-	packet->size += knot_dname_size(packet->question.qname) + 4;
-
-	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1469,7 +1040,7 @@ int knot_packet_edns_to_wire(knot_packet_t *packet)
 	                                  packet->wireformat + packet->size,
 	                                  packet->max_size - packet->size);
 
-	packet->header.arcount += 1;
+	knot_wire_add_arcount(packet->wireformat, 1);
 
 	return KNOT_EOK;
 }
@@ -1487,20 +1058,11 @@ int knot_packet_to_wire(knot_packet_t *packet,
 	assert(packet->size <= packet->max_size);
 
 	// if there are no additional RRSets, add EDNS OPT RR
-	if (packet->header.arcount == 0
+	if (knot_wire_get_arcount(packet->wireformat) == 0
 	    && packet->opt_rr.version != EDNS_NOT_SUPPORTED) {
 	    knot_packet_edns_to_wire(packet);
 	}
 
-	// set QDCOUNT (in response it is already set, in query it is needed)
-	knot_wire_set_qdcount(packet->wireformat, packet->header.qdcount);
-	// set ANCOUNT to the packet
-	knot_wire_set_ancount(packet->wireformat, packet->header.ancount);
-	// set NSCOUNT to the packet
-	knot_wire_set_nscount(packet->wireformat, packet->header.nscount);
-	// set ARCOUNT to the packet
-	knot_wire_set_arcount(packet->wireformat, packet->header.arcount);
-
 	//assert(response->size == size);
 	*wire = packet->wireformat;
 	*wire_size = packet->size;
@@ -1532,17 +1094,14 @@ void knot_packet_free(knot_packet_t **packet)
 	knot_packet_free_allocated_space(*packet);
 
 	// free the space for wireformat
-	if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) {
-		if ((*packet)->mm.free)
-			(*packet)->mm.free((*packet)->wireformat);
-	}
+	if ((*packet)->flags & KNOT_PF_FREE_WIRE)
+		(*packet)->mm.free((*packet)->wireformat);
 
 	// free EDNS options
 	knot_edns_free_options(&(*packet)->opt_rr);
 
 	dbg_packet("Freeing packet structure\n");
-	if ((*packet)->mm.free)
-		(*packet)->mm.free(*packet);
+	(*packet)->mm.free(*packet);
 	*packet = NULL;
 }
 
@@ -1567,36 +1126,34 @@ void knot_packet_dump(const knot_packet_t *packet)
 	}
 
 #ifdef KNOT_PACKET_DEBUG
+	uint8_t flags1 = knot_wire_get_flags1(packet->wireformat);
+	uint8_t flags2 = knot_wire_get_flags2(packet->wireformat);
 	dbg_packet("DNS packet:\n-----------------------------\n");
 
 	dbg_packet("\nHeader:\n");
-	dbg_packet("  ID: %u\n", packet->header.id);
+	dbg_packet("  ID: %u\n", knot_wire_get_id(packet->wireformat));
 	dbg_packet("  FLAGS: %s %s %s %s %s %s %s\n",
-	       knot_wire_flags_get_qr(packet->header.flags1) ? "qr" : "",
-	       knot_wire_flags_get_aa(packet->header.flags1) ? "aa" : "",
-	       knot_wire_flags_get_tc(packet->header.flags1) ? "tc" : "",
-	       knot_wire_flags_get_rd(packet->header.flags1) ? "rd" : "",
-	       knot_wire_flags_get_ra(packet->header.flags2) ? "ra" : "",
-	       knot_wire_flags_get_ad(packet->header.flags2) ? "ad" : "",
-	       knot_wire_flags_get_cd(packet->header.flags2) ? "cd" : "");
-	dbg_packet("  RCODE: %u\n", knot_wire_flags_get_rcode(
-	                   packet->header.flags2));
-	dbg_packet("  OPCODE: %u\n", knot_wire_flags_get_opcode(
-	                   packet->header.flags1));
-	dbg_packet("  QDCOUNT: %u\n", packet->header.qdcount);
-	dbg_packet("  ANCOUNT: %u\n", packet->header.ancount);
-	dbg_packet("  NSCOUNT: %u\n", packet->header.nscount);
-	dbg_packet("  ARCOUNT: %u\n", packet->header.arcount);
+	       knot_wire_flags_get_qr(flags1) ? "qr" : "",
+	       knot_wire_flags_get_aa(flags1) ? "aa" : "",
+	       knot_wire_flags_get_tc(flags1) ? "tc" : "",
+	       knot_wire_flags_get_rd(flags1) ? "rd" : "",
+	       knot_wire_flags_get_ra(flags2) ? "ra" : "",
+	       knot_wire_flags_get_ad(flags2) ? "ad" : "",
+	       knot_wire_flags_get_cd(flags2) ? "cd" : "");
+	dbg_packet("  RCODE: %u\n", knot_wire_flags_get_rcode(flags2));
+	dbg_packet("  OPCODE: %u\n", knot_wire_flags_get_opcode(flags1));
+	dbg_packet("  QDCOUNT: %u\n", knot_wire_get_qdcount(packet->wireformat));
+	dbg_packet("  ANCOUNT: %u\n", knot_wire_get_ancount(packet->wireformat));
+	dbg_packet("  NSCOUNT: %u\n", knot_wire_get_nscount(packet->wireformat));
+	dbg_packet("  ARCOUNT: %u\n", knot_wire_get_arcount(packet->wireformat));
 
 	if (knot_packet_qdcount(packet) > 0) {
 		dbg_packet("\nQuestion:\n");
-		char *qname = knot_dname_to_str(packet->question.qname);
+		char *qname = knot_dname_to_str(knot_packet_qname(packet));
 		dbg_packet("  QNAME: %s\n", qname);
 		free(qname);
-		dbg_packet("  QTYPE: %u (%u)\n", packet->question.qtype,
-		           packet->question.qtype);
-		dbg_packet("  QCLASS: %u (%u)\n", packet->question.qclass,
-		           packet->question.qclass);
+		dbg_packet("  QTYPE: %u\n", knot_packet_qtype(packet));
+		dbg_packet("  QCLASS: %u\n", knot_packet_qclass(packet));
 	}
 
 	dbg_packet("\nAnswer RRSets:\n");
@@ -1622,7 +1179,7 @@ void knot_packet_dump(const knot_packet_t *packet)
 static int knot_packet_free_section(const knot_rrset_t **s, short count) {
 	/*! \todo The API is really incompatible here. */
 	for (short i = 0; i < count; ++i)
-		knot_rrset_deep_free((knot_rrset_t **)s + i, 1, 1);
+		knot_rrset_deep_free((knot_rrset_t **)s + i, 1);
 	return count;
 }
 
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 0921fba430a8c3bc1fb37974543eb84da80f8a04..e86afc9e1bb1ad3ebaf395169d96ad92afb77cb4 100644
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -38,50 +38,19 @@
 
 /*----------------------------------------------------------------------------*/
 
+/* How many RRs pointers to alloc in one step. */
+#define RRSET_ALLOC_STEP 8
+
 struct knot_wildcard_nodes {
 	const knot_node_t **nodes; /*!< Wildcard nodes from CNAME processing. */
 	const knot_dname_t **snames;  /*!< SNAMEs related to the nodes. */
 	short count;             /*!< Count of items in the previous arrays. */
 	short max;               /*!< Capacity of the structure (allocated). */
-	short default_count;
 };
 
 typedef struct knot_wildcard_nodes knot_wildcard_nodes_t;
 
 /*----------------------------------------------------------------------------*/
-/*!
- * \brief Structure representing the DNS packet header.
- */
-struct knot_header {
-	uint16_t id;      /*!< ID stored in host byte order. */
-	uint8_t flags1;   /*!< First octet of header flags. */
-	uint8_t flags2;   /*!< Second octet of header flags. */
-	uint16_t qdcount; /*!< Number of Question RRs, in host byte order. */
-	uint16_t ancount; /*!< Number of Answer RRs, in host byte order. */
-	uint16_t nscount; /*!< Number of Authority RRs, in host byte order. */
-	uint16_t arcount; /*!< Number of Additional RRs, in host byte order. */
-};
-
-typedef struct knot_header knot_header_t;
-
-/*!
- * \brief Structure representing one Question entry in the DNS packet.
- */
-struct knot_question {
-	knot_dname_t *qname;  /*!< Question domain name. */
-	uint16_t qtype;         /*!< Question TYPE. */
-	uint16_t qclass;        /*!< Question CLASS. */
-};
-
-typedef struct knot_question knot_question_t;
-
-enum knot_packet_prealloc_type {
-	KNOT_PACKET_PREALLOC_NONE,
-	KNOT_PACKET_PREALLOC_QUERY,
-	KNOT_PACKET_PREALLOC_RESPONSE
-};
-
-typedef enum knot_packet_prealloc_type knot_packet_prealloc_type_t;
 
 /* Maximum number of compressed names. */
 #define COMPR_MAXLEN 64
@@ -98,21 +67,8 @@ typedef struct {
 /*----------------------------------------------------------------------------*/
 /*!
  * \brief Structure representing a DNS packet.
- *
- * \note QNAME, Answer, Authority and Additonal sections are by default put to
- *       preallocated space after the structure with default sizes. If the
- *       space is not enough, more space is allocated dynamically.
  */
 struct knot_packet {
-	/*! \brief DNS header. */
-	knot_header_t header;
-
-	/*!
-	 * \brief Question section.
-	 *
-	 * \note Only one Question is supported!
-	 */
-	knot_question_t question;
 
 	const knot_rrset_t **answer;      /*!< Answer RRSets. */
 	const knot_rrset_t **authority;   /*!< Authority RRSets. */
@@ -130,12 +86,12 @@ struct knot_packet {
 
 	uint8_t *wireformat;  /*!< Wire format of the packet. */
 
-	short free_wireformat;
 	size_t parsed;
 	uint16_t parsed_an;
 	uint16_t parsed_ns;
 	uint16_t parsed_ar;
 
+	uint8_t qname_size; /*!< QNAME size. */
 	size_t size;      /*!< Current wire size of the packet. */
 	size_t max_size;  /*!< Maximum allowed size of the packet. */
 
@@ -152,8 +108,6 @@ struct knot_packet {
 
 	struct knot_packet *query; /*!< Associated query. */
 
-	knot_packet_prealloc_type_t prealloc_type;
-
 	size_t tsig_size;	/*!< Space to reserve for the TSIG RR. */
 	knot_rrset_t *tsig_rr;  /*!< TSIG RR stored in the packet. */
 	uint16_t flags;         /*!< Packet flags. */
@@ -169,88 +123,14 @@ typedef struct knot_packet knot_packet_t;
  * \brief Packet flags.
  */
 enum {
-	KNOT_PF_NULL     = 0 << 0, /*!< No flags. */
-	KNOT_PF_QUERY    = 1 << 0, /*!< Packet is query. */
-	KNOT_PF_WILDCARD = 1 << 1, /*!< Query to wildcard name. */
-	KNOT_PF_RESPONSE = 1 << 2  /*!< Packet is response. */
-};
-
-/*!
- * \brief Default sizes for response structure parts and steps for increasing
- *        them.
- */
-enum {
-	DEFAULT_ANCOUNT = 6,         /*!< Default count of Answer RRSets. */
-	DEFAULT_NSCOUNT = 8,         /*!< Default count of Authority RRSets. */
-	DEFAULT_ARCOUNT = 28,        /*!< Default count of Additional RRSets. */
-
-	DEFAULT_ANCOUNT_QUERY = 1,   /*!< Default count of Answer RRSets. */
-	DEFAULT_NSCOUNT_QUERY = 0,   /*!< Default count of Authority RRSets. */
-	DEFAULT_ARCOUNT_QUERY = 1,  /*!< Default count of Additional RRSets. */
-	/*!
-	 * \brief Default count of all domain names in response.
-	 *
-	 * Used for compression table.
-	 */
-	DEFAULT_DOMAINS_IN_RESPONSE = 22,
-
-	/*! \brief Default count of temporary RRSets stored in response. */
-	DEFAULT_TMP_RRSETS = 5,
-
-	/*! \brief Default count of wildcard nodes saved for later processing.*/
-	DEFAULT_WILDCARD_NODES = 1,
-
-	/*! \brief Default count of temporary RRSets stored in query. */
-	DEFAULT_TMP_RRSETS_QUERY = 2,
-
-	STEP_ANCOUNT = 6, /*!< Step for increasing space for Answer RRSets. */
-	STEP_NSCOUNT = 8, /*!< Step for increasing space for Authority RRSets.*/
-	STEP_ARCOUNT = 8,/*!< Step for increasing space for Additional RRSets.*/
-	STEP_DOMAINS = 10,   /*!< Step for resizing compression table. */
-	STEP_TMP_RRSETS = 5,  /*!< Step for increasing temorary RRSets count. */
-	STEP_WILDCARD_NODES = 2
+	KNOT_PF_NULL      = 0 << 0, /*!< No flags. */
+	KNOT_PF_QUERY     = 1 << 0, /*!< Packet is query. */
+	KNOT_PF_WILDCARD  = 1 << 1, /*!< Query to wildcard name. */
+	KNOT_PF_RESPONSE  = 1 << 2, /*!< Packet is response. */
+	KNOT_PF_FREE_WIRE = 1 << 3  /*!< Free wire. */
 };
 
 /*----------------------------------------------------------------------------*/
-#define PREALLOC_RRSETS(count) (count * sizeof(knot_rrset_t *))
-
-/*! \brief Sizes for preallocated space in the response structure. */
-enum {
-	/*! \brief Size of the response structure itself. */
-	PREALLOC_PACKET = sizeof(knot_packet_t),
-	/*! \brief Space for QNAME dname structure. */
-	PREALLOC_QNAME_DNAME = sizeof(knot_dname_t),
-	/*! \brief Space for QNAME name (maximum domain name size). */
-	PREALLOC_QNAME_NAME = 256,
-	/*! \brief Space for QNAME labels (maximum label count). */
-	PREALLOC_QNAME_LABELS = 127,
-	/*! \brief Total space for QNAME. */
-	PREALLOC_QNAME = PREALLOC_QNAME_DNAME
-	                 + PREALLOC_QNAME_NAME
-	                 + PREALLOC_QNAME_LABELS,
-
-	PREALLOC_WC_NODES =
-		DEFAULT_WILDCARD_NODES * sizeof(knot_node_t *),
-	PREALLOC_WC_SNAMES =
-		DEFAULT_WILDCARD_NODES * sizeof(knot_dname_t *),
-	PREALLOC_WC = PREALLOC_WC_NODES + PREALLOC_WC_SNAMES,
-
-	PREALLOC_QUERY = PREALLOC_PACKET
-	                 + PREALLOC_QNAME
-	                 + PREALLOC_RRSETS(DEFAULT_ANCOUNT_QUERY)
-	                 + PREALLOC_RRSETS(DEFAULT_NSCOUNT_QUERY)
-	                 + PREALLOC_RRSETS(DEFAULT_ARCOUNT_QUERY)
-	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS_QUERY),
-
-	/*! \brief Total preallocated size for the response. */
-	PREALLOC_RESPONSE = PREALLOC_PACKET
-	                 + PREALLOC_QNAME
-	                 + PREALLOC_RRSETS(DEFAULT_ANCOUNT)
-	                 + PREALLOC_RRSETS(DEFAULT_NSCOUNT)
-	                 + PREALLOC_RRSETS(DEFAULT_ARCOUNT)
-	                 + PREALLOC_WC
-	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS)
-};
 
 /*! \brief Flags which control packet parsing. */
 typedef enum {
@@ -264,18 +144,14 @@ typedef enum {
 /*!
  * \brief Creates new empty packet structure.
  *
- * \param prealloc What space should be preallocated in the structure.
- *
  * \return New packet structure or NULL if an error occured.
  */
-knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc);
+knot_packet_t *knot_packet_new();
 
 /*!
  * \brief Memory managed version of new packet create.
- *
- * See knot_packet_new() for info about parameters and output.
  */
-knot_packet_t *knot_packet_new_mm(knot_packet_prealloc_type_t prealloc, mm_ctx_t *mm);
+knot_packet_t *knot_packet_new_mm(mm_ctx_t *mm);
 
 /*!
  * \brief Parses the DNS packet from wire format.
@@ -335,9 +211,9 @@ size_t knot_packet_parsed(const knot_packet_t *packet);
  */
 int knot_packet_set_max_size(knot_packet_t *packet, int max_size);
 
-uint16_t knot_packet_id(const knot_packet_t *packet);
+int knot_packet_set_size(knot_packet_t *packet, int size);
 
-void knot_packet_set_id(knot_packet_t *packet, uint16_t id);
+uint16_t knot_packet_id(const knot_packet_t *packet);
 
 void knot_packet_set_random_id(knot_packet_t *packet);
 
@@ -350,15 +226,6 @@ void knot_packet_set_random_id(knot_packet_t *packet);
  */
 uint8_t knot_packet_opcode(const knot_packet_t *packet);
 
-/*!
- * \brief Return question section from the packet.
- *
- * \param packet Packet instance.
- *
- * \return pointer to question section.
- */
-knot_question_t *knot_packet_question(knot_packet_t *packet);
-
 /*!
  * \brief Returns the QNAME from the packet.
  *
@@ -515,19 +382,6 @@ int knot_packet_add_tmp_rrset(knot_packet_t *response,
 
 void knot_packet_free_tmp_rrsets(knot_packet_t *pkt);
 
-/*!
- * \brief Converts the header structure to wire format.
- *
- * \note This function also adjusts the position (\a pos) according to
- *       the size of the converted wire format.
- *
- * \param[in] header DNS header structure to convert.
- * \param[out] pos Position where to put the converted header. The space has
- *                 to be allocated before calling this function.
- * \param[out] size Size of the wire format of the header in bytes.
- */
-void knot_packet_header_to_wire(const knot_header_t *header,
-                                  uint8_t **pos, size_t *size);
 
 int knot_packet_question_to_wire(knot_packet_t *packet);
 
@@ -577,6 +431,13 @@ void knot_packet_dump(const knot_packet_t *packet);
  */
 int knot_packet_free_rrsets(knot_packet_t *packet);
 
+/*!
+ * \brief (Temporary) realloc RRs array size.
+ */
+int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
+                               short *max_count,
+                               mm_ctx_t *mm);
+
 #endif /* _KNOT_PACKET_H_ */
 
 /*! @} */
diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c
index 4bededc9f113524ad015d75c14a290e4dc04e835..7ddf5295f2e5c91e99bdbebb5c712ee1c1048f80 100644
--- a/src/libknot/packet/query.c
+++ b/src/libknot/packet/query.c
@@ -62,31 +62,46 @@ int knot_query_init(knot_packet_t *query)
 	if (query == NULL) {
 		return KNOT_EINVAL;
 	}
-	// set the qr bit to 0
-	knot_wire_flags_clear_qr(&query->header.flags1);
-
-	uint8_t *pos = query->wireformat;
-	knot_packet_header_to_wire(&query->header, &pos, &query->size);
 
+	query->size = KNOT_WIRE_HEADER_SIZE;
+	memset(query->wireformat, 0, query->size);
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
 int knot_query_set_question(knot_packet_t *query,
-                              const knot_question_t *question)
+                            const knot_dname_t *qname,
+                            uint16_t qclass, uint16_t qtype)
 {
-	if (query == NULL || question == NULL) {
+	if (query == NULL || qname == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	query->question.qname = question->qname;
-	query->question.qclass = question->qclass;
-	query->question.qtype = question->qtype;
-	query->header.qdcount = 1;
+	assert(query->size == KNOT_WIRE_HEADER_SIZE);
+
+	/* Copy name wireformat. */
+	uint8_t *dst = query->wireformat + KNOT_WIRE_HEADER_SIZE;
+	int qname_len = knot_dname_to_wire(dst, qname, query->max_size - query->size);
+	assert(qname_len == knot_dname_size(qname));
+	size_t question_len = 2 * sizeof(uint16_t) + qname_len;
+
+	/* Check size limits. */
+	if (qname_len < 0 || query->size + question_len > query->max_size)
+		return KNOT_ESPACE;
+
+	/* Copy QTYPE & QCLASS */
+	dst += qname_len;
+	knot_wire_write_u16(dst, qtype);
+	dst += sizeof(uint16_t);
+	knot_wire_write_u16(dst, qclass);
 
-	// convert the Question to wire format right away
-	return knot_packet_question_to_wire(query);
+	/* Update question count and sizes. */
+	knot_wire_set_qdcount(query->wireformat, 1);
+	query->size += question_len;
+	query->qname_size = qname_len;
+
+	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -96,8 +111,7 @@ int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode)
 	if (query == NULL) {
 		return KNOT_EINVAL;
 	}
-	// set the OPCODE in the structure
-	knot_wire_flags_set_opcode(&query->header.flags1, opcode);
+
 	// set the OPCODE in the wire format
 	knot_wire_set_opcode(query->wireformat, opcode);
 
@@ -151,7 +165,7 @@ int knot_query_add_rrset_authority(knot_packet_t *query,
 	}
 	query->size += written;
 	++query->ns_rrsets;
-	++query->header.nscount;
+	knot_wire_add_nscount(query->wireformat, 1);
 
 	return KNOT_EOK;
 }
diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h
index ee5418a2818a1f53cc01658016ca4389cd7aebf6..0c021731ce66cf9daa3337431b9a1f7df8dc4a5d 100644
--- a/src/libknot/packet/query.h
+++ b/src/libknot/packet/query.h
@@ -64,7 +64,8 @@ int knot_query_edns_supported(const knot_packet_t *query);
 int knot_query_init(knot_packet_t *query);
 
 int knot_query_set_question(knot_packet_t *query,
-                              const knot_question_t *question);
+                            const knot_dname_t *qname,
+                            uint16_t qclass, uint16_t qtype);
 
 int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode);
 
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
index ddd594c43928a2d4e5ad44a178fc5f7b01b29a87..6367f720f9eabb723481108b09ccdc94a8a492f6 100644
--- a/src/libknot/packet/response.c
+++ b/src/libknot/packet/response.c
@@ -33,7 +33,8 @@
  *
  * Update current best score.
  */
-static bool knot_response_compr_score(uint8_t *n, uint8_t *p, uint8_t labels,
+static bool knot_response_compr_score(const uint8_t *n, const uint8_t *p,
+                                      uint8_t labels,
                                       uint8_t *wire, knot_compr_ptr_t *match)
 {
 	uint16_t score = 0;
@@ -65,22 +66,6 @@ static bool knot_response_compr_score(uint8_t *n, uint8_t *p, uint8_t labels,
 	return false;
 }
 
-/*!
- * \brief Align name and reference to a common number of suffix labels.
- */
-static uint8_t knot_response_compr_align(uint8_t **name, uint8_t nlabels,
-                                         uint8_t **ref, uint8_t reflabels,
-                                         uint8_t *wire)
-{
-	for (unsigned j = nlabels; j < reflabels; ++j)
-		*ref = knot_wire_next_label(*ref, wire);
-
-	for (unsigned j = reflabels; j < nlabels; ++j)
-		*name = knot_wire_next_label(*name, wire);
-
-	return (nlabels < reflabels) ? nlabels : reflabels;
-}
-
 int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
                                  uint8_t *dst, size_t max)
 {
@@ -89,13 +74,18 @@ int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
 	}
 
 	/* Do not compress small dnames. */
-	uint8_t *name = dname->name;
-	if (dname->size <= 2) {
-                if (dname->size > max)
-                        return KNOT_ESPACE;
-                memcpy(dst, name, dname->size);
-                return dname->size;
+	int name_labels = knot_dname_labels(dname, NULL);
+	if (name_labels < 0) {
+		return name_labels; // error code
 	}
+	if (*dname == '\0') {
+		if (max < 1)
+			return KNOT_ESPACE;
+		*dst = *dname;
+		return 1;
+	}
+
+	assert(name_labels >= 0 && name_labels <= KNOT_DNAME_MAXLABELS);
 
 	/* Align and compare name and pointer in the compression table. */
 	unsigned i = 0;
@@ -103,32 +93,32 @@ int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
 	unsigned match_id = 0;
 	knot_compr_ptr_t match = { 0, 0 };
 	for (; i < COMPR_MAXLEN && compr->table[i].off > 0; ++i) {
-		uint8_t *name = dname->name;
-		uint8_t *ref = compr->wire + compr->table[i].off;
-		lbcount = knot_response_compr_align(&name, dname->label_count,
-		                                    &ref, compr->table[i].lbcount,
-		                                    compr->wire);
+		const uint8_t *sample = dname;
+		const uint8_t *ref = compr->wire + compr->table[i].off;
+		lbcount = knot_dname_align(&sample, name_labels,
+		                           &ref, compr->table[i].lbcount,
+		                           compr->wire);
 
-		if (knot_response_compr_score(name, ref, lbcount, compr->wire,
+		if (knot_response_compr_score(sample, ref, lbcount, compr->wire,
 		                              &match)) {
 			match_id = i;
-			if (match.lbcount == dname->label_count)
+			if (match.lbcount == name_labels)
 				break; /* Best match, break. */
 		}
 	}
 
 	/* Write non-matching prefix. */
 	unsigned written = 0;
-	for (unsigned j = match.lbcount; j < dname->label_count; ++j) {
-		if (written + *name + 1 > max)
+	for (unsigned j = match.lbcount; j < name_labels; ++j) {
+		if (written + *dname + 1 > max)
 			return KNOT_ESPACE;
-		memcpy(dst + written, name, *name + 1);
-		written += *name + 1;
-		name = knot_wire_next_label(name, compr->wire);
+		memcpy(dst + written, dname, *dname + 1);
+		written += *dname + 1;
+		dname = (knot_dname_t *)knot_wire_next_label(dname, compr->wire);
 	}
 
 	/* Write out pointer covering suffix. */
-	if (*name != '\0') {
+	if (*dname != '\0') {
 		if (written + sizeof(uint16_t) > max)
 			return KNOT_ESPACE;
 		knot_wire_put_pointer(dst + written, match.off);
@@ -149,7 +139,7 @@ int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
 	}
 
 	/* Do not insert if exceeds bounds or full match. */
-	if (match.lbcount == dname->label_count ||
+	if (match.lbcount == name_labels ||
 	    compr->wire_pos > KNOT_WIRE_PTR_MAX)
 		return written;
 
@@ -163,7 +153,7 @@ int knot_response_compress_dname(const knot_dname_t *dname, knot_compr_t *compr,
 	/* Store in dname table. */
 	if (compr->table[i].off == 0) {
 		compr->table[i].off = (uint16_t)compr->wire_pos;
-		compr->table[i].lbcount = dname->label_count;
+		compr->table[i].lbcount = name_labels;
 	}
 
 	return written;
@@ -227,50 +217,12 @@ dbg_response_exec(
 		                  resp->size);
 	} else if (tc) {
 		dbg_response_verb("Setting TC bit.\n");
-		knot_wire_flags_set_tc(&resp->header.flags1);
 		knot_wire_set_tc(resp->wireformat);
 	}
 
 	return rr_count > 0 ? rr_count : ret;
 }
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Reallocate space for RRSets.
- *
- * \param rrsets Space for RRSets.
- * \param max_count Size of the space available for the RRSets.
- * \param default_max_count Size of the space pre-allocated for the RRSets when
- *        the response structure was initialized.
- * \param step How much the space should be increased.
- *
- * \retval KNOT_EOK
- * \retval KNOT_ENOMEM
- */
-static int knot_response_realloc_rrsets(const knot_rrset_t ***rrsets,
-                                          short *max_count,
-                                          short default_max_count, short step)
-{
-	int free_old = (*max_count) != default_max_count;
-	const knot_rrset_t **old = *rrsets;
-
-	short new_max_count = *max_count + step;
-	const knot_rrset_t **new_rrsets = (const knot_rrset_t **)malloc(
-		new_max_count * sizeof(knot_rrset_t *));
-	CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
-
-	memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
-
-	*rrsets = new_rrsets;
-	*max_count = new_max_count;
-
-	if (free_old) {
-		free(old);
-	}
-
-	return KNOT_EOK;
-}
-
 /*----------------------------------------------------------------------------*/
 /*!
  * \brief Reallocate space for Wildcard nodes.
@@ -281,40 +233,32 @@ static int knot_response_realloc_rrsets(const knot_rrset_t ***rrsets,
 static int knot_response_realloc_wc_nodes(const knot_node_t ***nodes,
                                           const knot_dname_t ***snames,
                                           short *max_count,
-                                          short default_max_count, short step)
+                                          mm_ctx_t *mm)
 {
-	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;
-	const knot_dname_t **old_snames = *snames;
+	short new_max_count = *max_count + RRSET_ALLOC_STEP;
 
-	short new_max_count = *max_count + step;
-
-	const knot_node_t **new_nodes = (const knot_node_t **)malloc(
+	const knot_node_t **new_nodes = mm->alloc(mm->ctx,
 		new_max_count * sizeof(knot_node_t *));
 	CHECK_ALLOC_LOG(new_nodes, KNOT_ENOMEM);
 
-	const knot_dname_t **new_snames = (const knot_dname_t **)malloc(
+	const knot_dname_t **new_snames = mm->alloc(mm->ctx,
 	                        new_max_count * sizeof(knot_dname_t *));
 	if (new_snames == NULL) {
-		free(new_nodes);
+		mm->free(new_nodes);
 		return KNOT_ENOMEM;
 	}
 
 	memcpy(new_nodes, *nodes, (*max_count) * sizeof(knot_node_t *));
 	memcpy(new_snames, *snames, (*max_count) * sizeof(knot_dname_t *));
 
+	mm->free(*nodes);
+	mm->free(*snames);
+
 	*nodes = new_nodes;
 	*snames = new_snames;
 	*max_count = new_max_count;
 
-	if (free_old) {
-		free(old_nodes);
-		free(old_snames);
-	}
-
 	return KNOT_EOK;
 }
 
@@ -332,96 +276,66 @@ int knot_response_init(knot_packet_t *response)
 		return KNOT_ESPACE;
 	}
 
-	// set the qr bit to 1
-	knot_wire_flags_set_qr(&response->header.flags1);
+	/* Empty packet header. */
+	memset(response->wireformat, 0, KNOT_WIRE_HEADER_SIZE);
+	response->size = KNOT_WIRE_HEADER_SIZE;
 
-	uint8_t *pos = response->wireformat;
-	knot_packet_header_to_wire(&response->header, &pos,
-	                                &response->size);
+	/* Set the qr bit to 1. */
+	uint8_t flags = 0;
+	knot_wire_flags_set_qr(&flags);
+	knot_wire_set_flags1(response->wireformat, flags);
 
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_response_init_from_query(knot_packet_t *response,
-                                  knot_packet_t *query,
-                                  int copy_question)
+int knot_response_init_from_query(knot_packet_t *response, knot_packet_t *query)
 {
-
 	if (response == NULL || query == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	// copy the header from the query
-	memcpy(&response->header, &query->header, sizeof(knot_header_t));
-
-	/*! \todo Constant. */
-	size_t to_copy = 12;
-
-	if (copy_question) {
-		// copy the Question section (but do not copy the QNAME)
-		memcpy(&response->question, &query->question,
-		       sizeof(knot_question_t));
-
-		/* Insert QNAME into compression table. */
-		response->compression[0].off = KNOT_WIRE_HEADER_SIZE;
-		response->compression[0].lbcount = response->question.qname->label_count;
-
-
-		/*! \todo Constant. */
-		to_copy += 4 + knot_dname_size(response->question.qname);
-	} else {
-		response->header.qdcount = 0;
-		knot_wire_set_qdcount(response->wireformat, 0);
-	}
-
-	assert(response->max_size >= to_copy);
+	/* Header + question size. */
+	size_t to_copy = knot_packet_question_size(query);
+	assert(to_copy <= response->max_size);
 	if (response->wireformat != query->wireformat) {
 		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);
+	/* Insert QNAME into compression table. */
+	uint8_t *qname = response->wireformat + KNOT_WIRE_HEADER_SIZE;
+	response->compression[0].off = KNOT_WIRE_HEADER_SIZE;
+	response->compression[0].lbcount = knot_dname_labels(qname, NULL);
 
-	// clear TC flag
-	knot_wire_flags_clear_tc(&response->header.flags1);
+	/* Update size and flags. */
+	knot_wire_set_qdcount(response->wireformat, 1);
+	knot_wire_set_qr(response->wireformat);
 	knot_wire_clear_tc(response->wireformat);
-
-	// clear AD flag
-	knot_wire_flags_clear_ad(&response->header.flags2);
-	knot_wire_clear_ad(response->wireformat);
-
-	// clear RA flag
-	knot_wire_flags_clear_ra(&response->header.flags2);
 	knot_wire_clear_ad(response->wireformat);
+	knot_wire_clear_ra(response->wireformat);
 
-	// set counts to 0
-	response->header.ancount = 0;
+	/* Reset RR counts */
 	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;
-
+	response->size = to_copy;
+	response->qname_size = query->qname_size;
 	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
 
-void knot_response_clear(knot_packet_t *resp, int clear_question)
+void knot_response_clear(knot_packet_t *resp)
 {
 	if (resp == NULL) {
 		return;
 	}
 
-	resp->size = (clear_question) ? KNOT_WIRE_HEADER_SIZE
-	              : KNOT_WIRE_HEADER_SIZE + 4
-	                + knot_dname_size(resp->question.qname);
+	/* Keep question. */
+	resp->size = knot_packet_question_size(resp);
 	resp->an_rrsets = 0;
 	resp->ns_rrsets = 0;
 	resp->ar_rrsets = 0;
@@ -440,9 +354,9 @@ void knot_response_clear(knot_packet_t *resp, int clear_question)
 	 *        the list of wildcard nodes should be cleared here.
 	 */
 
-	resp->header.ancount = 0;
-	resp->header.nscount = 0;
-	resp->header.arcount = 0;
+	knot_wire_set_ancount(resp->wireformat, 0);
+	knot_wire_set_nscount(resp->wireformat, 0);
+	knot_wire_set_arcount(resp->wireformat, 0);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -512,12 +426,12 @@ int knot_response_add_rrset_answer(knot_packet_t *response,
 	}
 
 	dbg_response_verb("add_rrset_answer()\n");
-	assert(response->header.arcount == 0);
-	assert(response->header.nscount == 0);
+	assert(knot_wire_get_arcount(response->wireformat) == 0);
+	assert(knot_wire_get_nscount(response->wireformat) == 0);
 
 	if (response->an_rrsets == response->max_an_rrsets
-	    && knot_response_realloc_rrsets(&response->answer,
-	          &response->max_an_rrsets, DEFAULT_ANCOUNT, STEP_ANCOUNT)
+	    && knot_packet_realloc_rrsets(&response->answer,
+	          &response->max_an_rrsets, &response->mm)
 	       != KNOT_EOK) {
 		return KNOT_ENOMEM;
 	}
@@ -540,7 +454,7 @@ int knot_response_add_rrset_answer(knot_packet_t *response,
 	                                        rrset, tc);
 
 	if (rrs >= 0) {
-		response->header.ancount += rrs;
+		knot_wire_add_ancount(response->wireformat, rrs);
 
 		if (rotate) {
 			// do round-robin rotation of the RRSet
@@ -564,11 +478,11 @@ int knot_response_add_rrset_authority(knot_packet_t *response,
 		return KNOT_EINVAL;
 	}
 
-	assert(response->header.arcount == 0);
+	assert(knot_wire_get_arcount(response->wireformat) == 0);
 
 	if (response->ns_rrsets == response->max_ns_rrsets
-	    && knot_response_realloc_rrsets(&response->authority,
-			&response->max_ns_rrsets, DEFAULT_NSCOUNT, STEP_NSCOUNT)
+	    && knot_packet_realloc_rrsets(&response->authority,
+			&response->max_ns_rrsets, &response->mm)
 		!= 0) {
 		return KNOT_ENOMEM;
 	}
@@ -589,7 +503,7 @@ int knot_response_add_rrset_authority(knot_packet_t *response,
 	                                        rrset, tc);
 
 	if (rrs >= 0) {
-		response->header.nscount += rrs;
+		knot_wire_add_nscount(response->wireformat, rrs);
 
 		if (rotate) {
 			// do round-robin rotation of the RRSet
@@ -616,15 +530,15 @@ int knot_response_add_rrset_additional(knot_packet_t *response,
 	int ret;
 
 	// if this is the first additional RRSet, add EDNS OPT RR first
-	if (response->header.arcount == 0
+	if (knot_wire_get_arcount(response->wireformat) == 0
 	    && response->opt_rr.version != EDNS_NOT_SUPPORTED
 	    && (ret = knot_packet_edns_to_wire(response)) != KNOT_EOK) {
 		return ret;
 	}
 
 	if (response->ar_rrsets == response->max_ar_rrsets
-	    && knot_response_realloc_rrsets(&response->additional,
-			&response->max_ar_rrsets, DEFAULT_ARCOUNT, STEP_ARCOUNT)
+	    && knot_packet_realloc_rrsets(&response->additional,
+			&response->max_ar_rrsets, &response->mm)
 		!= 0) {
 		return KNOT_ENOMEM;
 	}
@@ -644,7 +558,7 @@ int knot_response_add_rrset_additional(knot_packet_t *response,
 	                                        tc);
 
 	if (rrs >= 0) {
-		response->header.arcount += rrs;
+		knot_wire_add_arcount(response->wireformat, rrs);
 
 		if (rotate) {
 			// do round-robin rotation of the RRSet
@@ -665,8 +579,9 @@ void knot_response_set_rcode(knot_packet_t *response, short rcode)
 		return;
 	}
 
-	knot_wire_flags_set_rcode(&response->header.flags2, rcode);
-	knot_wire_set_rcode(response->wireformat, rcode);
+	uint8_t flags = knot_wire_get_flags2(response->wireformat);
+	knot_wire_flags_set_rcode(&flags, rcode);
+	knot_wire_set_flags2(response->wireformat, flags);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -677,7 +592,6 @@ void knot_response_set_aa(knot_packet_t *response)
 		return;
 	}
 
-	knot_wire_flags_set_aa(&response->header.flags1);
 	knot_wire_set_aa(response->wireformat);
 }
 
@@ -689,7 +603,6 @@ void knot_response_set_tc(knot_packet_t *response)
 		return;
 	}
 
-	knot_wire_flags_set_tc(&response->header.flags1);
 	knot_wire_set_tc(response->wireformat);
 }
 
@@ -720,8 +633,7 @@ int knot_response_add_wildcard_node(knot_packet_t *response,
 	    && knot_response_realloc_wc_nodes(&response->wildcard_nodes.nodes,
 	                                      &response->wildcard_nodes.snames,
 	                                      &response->wildcard_nodes.max,
-	                                      DEFAULT_WILDCARD_NODES,
-	                                     STEP_WILDCARD_NODES) != KNOT_EOK) {
+	                                      &response->mm) != KNOT_EOK) {
 		return KNOT_ENOMEM;
 	}
 
diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h
index 799a88c1e928cbd23f658090c805f1cbd61fb290..e499b52bed7b80ca3265ee2c191b07859c0c004c 100644
--- a/src/libknot/packet/response.h
+++ b/src/libknot/packet/response.h
@@ -105,9 +105,7 @@ int knot_response_init(knot_packet_t *response);
  *
  * \retval KNOT_EOK
  */
-int knot_response_init_from_query(knot_packet_t *response,
-                                  knot_packet_t *query,
-                                  int copy_question);
+int knot_response_init_from_query(knot_packet_t *response, knot_packet_t *query);
 
 /*!
  * \brief Clears the response structure for reuse.
@@ -119,7 +117,7 @@ int knot_response_init_from_query(knot_packet_t *response,
  *
  * \todo Replace the use of this function with something else maybe?
  */
-void knot_response_clear(knot_packet_t *resp, int clear_question);
+void knot_response_clear(knot_packet_t *resp);
 
 /*!
  * \brief Sets the OPT RR of the response.
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
new file mode 100644
index 0000000000000000000000000000000000000000..980b3293bda734e24aa5247e2030d62162efb4d5
--- /dev/null
+++ b/src/libknot/rdata.h
@@ -0,0 +1,511 @@
+/*!
+ * \file rdata.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for manipulating RDATA contents.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOT_RDATA_H_
+#define _KNOT_RDATA_H_
+
+#include "common/descriptor.h"
+#include "dname.h"
+#include "rrset.h"
+#include "util/utils.h"
+
+static inline
+const knot_dname_t *knot_rdata_cname_name(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return NULL;
+	}
+
+	return rrset->rdata;
+}
+
+static inline
+const knot_dname_t *knot_rdata_dname_target(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return NULL;
+	}
+
+	return rrset->rdata;
+}
+
+static inline
+const knot_dname_t *knot_rdata_soa_primary_ns(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return NULL;
+	}
+
+	return rrset->rdata;
+}
+
+static inline
+const knot_dname_t *knot_rdata_soa_mailbox(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return NULL;
+	}
+
+	return rrset->rdata + knot_dname_size(rrset->rdata);
+}
+
+static inline
+size_t knot_rrset_rdata_soa_names_len(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_dname_size(knot_rdata_soa_primary_ns(rrset))
+	       + knot_dname_size(knot_rdata_soa_mailbox(rrset));
+}
+
+static inline
+uint32_t knot_rdata_soa_serial(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(rrset->rdata
+	                          + knot_rrset_rdata_soa_names_len(rrset));
+}
+
+static inline
+void knot_rdata_soa_serial_set(knot_rrset_t *rrset, uint32_t serial)
+{
+	if (rrset == NULL) {
+		return;
+	}
+
+	// the number is in network byte order, transform it
+	knot_wire_write_u32(rrset->rdata
+	                    + knot_rrset_rdata_soa_names_len(rrset), serial);
+}
+
+static inline
+uint32_t knot_rdata_soa_refresh(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(rrset->rdata
+	                          + knot_rrset_rdata_soa_names_len(rrset) + 4);
+}
+
+static inline
+uint32_t knot_rdata_soa_retry(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(rrset->rdata
+	                          + knot_rrset_rdata_soa_names_len(rrset) + 8);
+}
+
+static inline
+uint32_t knot_rdata_soa_expire(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(rrset->rdata
+	                          + knot_rrset_rdata_soa_names_len(rrset) + 12);
+}
+
+static inline
+uint32_t knot_rdata_soa_minimum(const knot_rrset_t *rrset)
+{
+	if (rrset == NULL) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(rrset->rdata
+	                          + knot_rrset_rdata_soa_names_len(rrset) + 16);
+}
+
+static inline
+uint16_t knot_rdata_rrsig_type_covered(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return 0;
+	}
+
+	return knot_wire_read_u16(knot_rrset_get_rdata(rrset, pos));
+}
+
+static inline
+uint8_t knot_rdata_rrsig_algorithm(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(knot_rrset_get_rdata(rrset, pos) + 2);
+}
+
+static inline
+uint8_t knot_rdata_rrsig_labels(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(knot_rrset_get_rdata(rrset, pos) + 3);
+}
+
+static inline
+uint32_t knot_rdata_rrsig_original_ttl(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, pos) + 4);
+}
+
+static inline
+uint32_t knot_rdata_rrsig_sig_expiration(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, pos) + 8);
+}
+
+static inline
+uint32_t knot_rdata_rrsig_sig_inception(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, pos) + 12);
+}
+
+static inline
+uint16_t knot_rdata_rrsig_key_tag(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u16(knot_rrset_get_rdata(rrset, pos) + 16);
+}
+
+static inline
+const knot_dname_t *knot_rdata_rrsig_signer_name(const knot_rrset_t *rrset,
+                                                 size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return NULL;
+	}
+
+	return knot_rrset_get_rdata(rrset, pos) + 18;
+}
+
+static inline
+void knot_rdata_rrsig_signature(const knot_rrset_t *rrset, size_t pos,
+                                uint8_t **signature, size_t *signature_size)
+{
+	if (!signature || !signature_size) {
+		return;
+	}
+
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		*signature = NULL;
+		*signature_size = 0;
+		return;
+	}
+
+	uint8_t *rdata = knot_rrset_get_rdata(rrset, pos);
+	uint8_t *signer = rdata + 18;
+	size_t total_size = rrset_rdata_item_size(rrset, pos);
+	size_t header_size = 18 + knot_dname_size(signer);
+
+	*signature = rdata + header_size;
+	*signature_size = total_size - header_size;
+}
+
+static inline
+uint16_t knot_rdata_dnskey_flags(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u16(knot_rrset_get_rdata(rrset, pos));
+}
+
+static inline
+uint8_t knot_rdata_dnskey_proto(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(knot_rrset_get_rdata(rrset, pos) + 2);
+}
+
+static inline
+uint8_t knot_rdata_dnskey_alg(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(knot_rrset_get_rdata(rrset, pos) + 3);
+}
+
+static inline
+void knot_rdata_dnskey_key(const knot_rrset_t *rrset, size_t pos, uint8_t **key,
+                           uint16_t *key_size)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return;
+	}
+
+	*key = knot_rrset_get_rdata(rrset, pos) + 4;
+	*key_size = rrset_rdata_item_size(rrset, pos) - 4;
+}
+
+static inline
+const knot_dname_t *knot_rdata_nsec_next(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos);
+}
+
+static inline
+void knot_rdata_nsec_bitmap(const knot_rrset_t *rrset, size_t rr_pos,
+                            uint8_t **bitmap, uint16_t *size)
+{
+	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
+		return;
+	}
+
+	uint8_t *rdata = knot_rrset_get_rdata(rrset, rr_pos);
+	int next_size = knot_dname_size(rdata);
+
+	*bitmap = rdata + next_size;
+	*size = rrset_rdata_item_size(rrset, rr_pos) - next_size;
+}
+
+static inline
+uint8_t knot_rdata_nsec3_algorithm(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos));
+}
+
+static inline
+uint8_t knot_rdata_nsec3_flags(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos) + 1);
+}
+
+static inline
+uint16_t knot_rdata_nsec3_iterations(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return knot_wire_read_u16(rrset_rdata_pointer(rrset, pos) + 2);
+}
+
+static inline
+uint8_t knot_rdata_nsec3_salt_length(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos) + 4);
+}
+
+static inline
+const uint8_t *knot_rdata_nsec3_salt(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos) + 5;
+}
+
+static inline
+void knot_rdata_nsec3_next_hashed(const knot_rrset_t *rrset, size_t pos,
+                                  uint8_t **name, uint8_t *name_size)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return;
+	}
+
+	uint8_t salt_size = knot_rdata_nsec3_salt_length(rrset, pos);
+	*name_size = *(knot_rrset_get_rdata(rrset, pos) + 4 + salt_size + 1);
+	*name = knot_rrset_get_rdata(rrset, pos) + 4 + salt_size + 2;
+}
+
+static inline
+void knot_rdata_nsec3_bitmap(const knot_rrset_t *rrset, size_t pos,
+                             uint8_t **bitmap, uint16_t *size)
+{
+	if (rrset == NULL || pos >= rrset->rdata_count) {
+		return;
+	}
+
+	/* Bitmap is last, skip all the items. */
+	size_t offset = 6; //hash alg., flags, iterations, salt len., hash len.
+	offset += knot_rdata_nsec3_salt_length(rrset, pos); //salt
+
+	uint8_t *next_hashed = NULL;
+	uint8_t next_hashed_size = 0;
+	knot_rdata_nsec3_next_hashed(rrset, pos, &next_hashed,
+	                             &next_hashed_size);
+	offset += next_hashed_size; //hash
+
+	*bitmap = knot_rrset_get_rdata(rrset, pos) + offset;
+	*size = rrset_rdata_item_size(rrset, pos) - offset;
+}
+
+static inline
+uint8_t knot_rdata_nsec3param_algorithm(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos));
+}
+
+static inline
+uint8_t knot_rdata_nsec3param_flags(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos) + 1);
+}
+
+static inline
+uint16_t knot_rdata_nsec3param_iterations(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return 0;
+	}
+
+	return knot_wire_read_u16(rrset_rdata_pointer(rrset, pos) + 2);
+}
+
+static inline
+uint8_t knot_rdata_nsec3param_salt_length(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return 0;
+	}
+
+	return *(rrset_rdata_pointer(rrset, pos) + 4);
+}
+
+static inline
+const uint8_t *knot_rdata_nsec3param_salt(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos) + 5;
+}
+
+static inline
+const knot_dname_t *knot_rdata_ns_name(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos);
+}
+
+static inline
+const knot_dname_t *knot_rdata_mx_name(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos) + 2;
+}
+
+static inline
+const knot_dname_t *knot_rdata_srv_name(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	return rrset_rdata_pointer(rrset, pos) + 6;
+}
+
+static inline
+const knot_dname_t *knot_rdata_name(const knot_rrset_t *rrset, size_t pos)
+{
+	if (rrset == NULL || rrset->rdata_count <= pos) {
+		return NULL;
+	}
+
+	switch (rrset->type) {
+		case KNOT_RRTYPE_NS:
+			return knot_rdata_ns_name(rrset, pos);
+		case KNOT_RRTYPE_MX:
+			return knot_rdata_mx_name(rrset, pos);
+		case KNOT_RRTYPE_SRV:
+			return knot_rdata_srv_name(rrset, pos);
+		case KNOT_RRTYPE_CNAME:
+			return knot_rdata_cname_name(rrset);
+	}
+
+	return NULL;
+}
+
+#endif /* _KNOT_RDATA_H_ */
+/*! @} */
diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c
index c8c7fe75264c7d86aa9e1579c8bf2f9b28045ec1..d350683d34bb82dcb9296314601e5e12a051cdfe 100644
--- a/src/libknot/rrset-dump.c
+++ b/src/libknot/rrset-dump.c
@@ -59,7 +59,7 @@ const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT = {
 	.verbose = false,
 	.reduce = true,
 	.human_ttl = false,
-	.human_tmstamp = false
+	.human_tmstamp = true
 };
 
 static void dump_string(rrset_dump_params_t *p, const char *str)
@@ -819,75 +819,21 @@ static void wire_bitmap_to_str(rrset_dump_params_t *p)
 
 static void wire_dname_to_str(rrset_dump_params_t *p)
 {
-	knot_dname_t *dname;
-	uint8_t label_len;
-	size_t  in_len = 0;
-	size_t  out_len = 0;
-
 	p->ret = -1;
 
-	// Compute dname length.
-	do {
-		// Read label length.
-		if (p->in_max < 1) {
-			return;
-		}
-		label_len = *(p->in);
-		in_len++;
-		p->in++;
-		p->in_max--;
-
-		if (label_len > p->in_max) {
-			return;
-		}
-		in_len += label_len;
-		p->in += label_len;
-		p->in_max -= label_len;
-	} while (label_len > 0);
-
-	// Create dname.
-	dname = knot_dname_new_from_wire(p->in - in_len, in_len, NULL);
-	if (dname == NULL) {
-		return;
-	}
-
-	// Write dname string.
-	char *dname_str = knot_dname_to_str(dname);
-	knot_dname_release(dname);
-	int ret = snprintf(p->out, p->out_max, "%s", dname_str);
-	free(dname_str);
-	if (ret < 0 || (size_t)ret >= p->out_max) {
+	int in_len = knot_dname_size(p->in);
+	if (in_len < 0) {
 		return;
 	}
-	out_len = ret;
-
-	// Fill in output.
-	p->out += out_len;
-	p->out_max -= out_len;
-	p->total += out_len;
-	p->ret = 0;
-}
 
-static void ptr_dname_to_str(rrset_dump_params_t *p)
-{
-	knot_dname_t *dname;
-	size_t in_len = sizeof(knot_dname_t *);
 	size_t out_len = 0;
 
-	p->ret = -1;
-
-	// Check input size.
 	if (in_len > p->in_max) {
 		return;
 	}
 
-	// Fill in input data.
-	if (memcpy(&dname, p->in, in_len) == NULL) {
-		return;
-	}
-
 	// Write dname string.
-	char *dname_str = knot_dname_to_str(dname);
+	char *dname_str = knot_dname_to_str(p->in);
 	int ret = snprintf(p->out, p->out_max, "%s", dname_str);
 	free(dname_str);
 	if (ret < 0 || (size_t)ret >= p->out_max) {
@@ -1357,7 +1303,7 @@ static void wire_unknown_to_str(rrset_dump_params_t *p)
 #define DUMP_NUM16	wire_num16_to_str(&p); CHECK_RET(p);
 #define DUMP_NUM32	wire_num32_to_str(&p); CHECK_RET(p);
 #define DUMP_NUM48	wire_num48_to_str(&p); CHECK_RET(p);
-#define DUMP_DNAME	ptr_dname_to_str(&p); CHECK_RET(p);
+#define DUMP_DNAME	wire_dname_to_str(&p); CHECK_RET(p);
 #define DUMP_TIME	wire_ttl_to_str(&p); CHECK_RET(p);
 #define DUMP_TIMESTAMP	wire_timestamp_to_str(&p); CHECK_RET(p);
 #define DUMP_IPV4	wire_ipv4_to_str(&p); CHECK_RET(p);
@@ -2032,8 +1978,9 @@ int knot_rrset_txt_dump(const knot_rrset_t      *rrset,
 	size_t len = 0;
 	int    ret;
 
-	// If the rrset is empty, dump header only.
-	if (rrset->rdata_count == 0) {
+	// Only APL RR may have empty RDATA, in this case, dump only header
+	if (rrset->rdata_count == 0
+	    && knot_rrset_type(rrset) == KNOT_RRTYPE_APL) {
 		// Dump rdata owner, class, ttl and type.
 		ret = knot_rrset_txt_dump_header(rrset, 0, dst + len,
 		                                 maxlen - len, style);
diff --git a/src/libknot/rrset-dump.h b/src/libknot/rrset-dump.h
index ece39e8e3261325edfabf44ed34776d2b5af0b39..2ee69fd541f7e78ff16a340572272349b27ade31 100644
--- a/src/libknot/rrset-dump.h
+++ b/src/libknot/rrset-dump.h
@@ -52,6 +52,9 @@ typedef struct {
 /*! \brief Default dump style. */
 extern const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT;
 
+/*! \brief DNSSEC-friendly dump style. */
+extern const knot_dump_style_t KNOT_DUMP_STYLE_DNSSEC;
+
 /*!
  * \brief Dumps rrset header.
  *
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index 74b91585e3936763f395fc9eab62340b202d7b22..34cf15d6390c5c1ea15e4d528676d499a50bf402 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -15,50 +15,29 @@
  */
 
 #include <config.h>
-#include <stdint.h>
-#include <stdlib.h>
 #include <assert.h>
-#include <stdio.h>
 #include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 #include "consts.h"
 #include "common.h"
 #include "common/mempattern.h"
 #include "rrset.h"
+#include "libknot/rrset-dump.h"
 #include "common/descriptor.h"
 #include "util/debug.h"
 #include "util/utils.h"
 #include "packet/response.h"
 #include "util/wire.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
 
-/*----------------------------------------------------------------------------*/
-/* Non-API functions                                                          */
-/*----------------------------------------------------------------------------*/
-
-static int rrset_retain_dnames_in_rr(knot_dname_t **dname, void *data)
-{
-	UNUSED(data);
-	if (dname == NULL || *dname == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	knot_dname_retain(*dname);
-	return KNOT_EOK;
-}
-
-static int rrset_release_dnames_in_rr(knot_dname_t **dname, void *data)
-{
-	UNUSED(data);
-	if (dname == NULL || *dname == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	knot_dname_release(*dname);
-	return KNOT_EOK;
-}
+static const unsigned int RR_CHANGE_TOO_BIG = 16;
 
-static uint32_t rrset_rdata_offset(const knot_rrset_t *rrset,
-                                   size_t pos)
+static uint32_t rrset_rdata_offset(const knot_rrset_t *rrset, size_t pos)
 {
 	if (rrset == NULL || rrset->rdata_indices == NULL ||
 	    pos >= rrset->rdata_count || pos == 0) {
@@ -69,8 +48,7 @@ static uint32_t rrset_rdata_offset(const knot_rrset_t *rrset,
 	return rrset->rdata_indices[pos - 1];
 }
 
-static uint8_t *rrset_rdata_pointer(const knot_rrset_t *rrset,
-                                    size_t pos)
+uint8_t *rrset_rdata_pointer(const knot_rrset_t *rrset, size_t pos)
 {
 	if (rrset == NULL || rrset->rdata == NULL
 	    || pos >= rrset->rdata_count) {
@@ -123,22 +101,17 @@ dbg_rrset_exec_detail(
 		int item = desc->block_types[i];
 		const uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
 		if (descriptor_item_is_dname(item)) {
-			knot_dname_t *dname;
-			memcpy(&dname, rdata + offset, sizeof(knot_dname_t *));
+			const knot_dname_t *dname = rdata + offset;
 			char *name = knot_dname_to_str(dname);
-			if (dname == NULL) {
-				dbg_rrset_detail("DNAME error.\n");
-				return;
-			}
 			dbg_rrset_detail("block=%d: (%p) DNAME=%s\n",
 			        i, dname, name);
 			free(name);
-			offset += sizeof(knot_dname_t *);
+			offset += knot_dname_size(dname);
 		} else if (descriptor_item_is_fixed(item)) {
 			dbg_rrset_detail("block=%d Raw data (size=%d):\n",
-			        i, item);
-                dbg_rrset_hex_detail((char *)(rdata + offset), item);
-			offset += item;
+			                 i, item);
+			dbg_rrset_hex_detail((char *)(rdata + offset), item);
+			                      offset += item;
 		} else if (descriptor_item_is_remainder(item)) {
 			dbg_rrset_detail("block=%d Remainder (size=%zu):\n",
 			        i, rrset_rdata_item_size(rrset,
@@ -169,12 +142,10 @@ static size_t rrset_rdata_remainder_size(const knot_rrset_t *rrset,
 	return ret;
 }
 
-/* [code-review] Please document at least parameters & return values. */
-static int rrset_rdata_compare_one(const knot_rrset_t *rrset1,
-                                   const knot_rrset_t *rrset2,
-                                   size_t pos1, size_t pos2)
+int rrset_rdata_compare_one(const knot_rrset_t *rrset1,
+                            const knot_rrset_t *rrset2,
+                            size_t pos1, size_t pos2)
 {
-	/* [code-review] Just to be sure. */
 	assert(rrset1 != NULL);
 	assert(rrset2 != NULL);
 
@@ -185,14 +156,20 @@ static int rrset_rdata_compare_one(const knot_rrset_t *rrset1,
 	int cmp = 0;
 	size_t offset = 0;
 
+	// TODO: this can be much simpler: Get data for memcmp and sizes in ifs
+	// compare only once
 	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
 		if (descriptor_item_is_dname(desc->block_types[i])) {
-			knot_dname_t *dname1 = NULL;
-			memcpy(&dname1, r1 + offset, sizeof(knot_dname_t *));
-			knot_dname_t *dname2 = NULL;
-			memcpy(&dname2, r2 + offset, sizeof(knot_dname_t *));
-			cmp = knot_dname_compare(dname1, dname2);
-			offset += sizeof(knot_dname_t *);
+			const knot_dname_t *dname1 = r1 + offset;
+			int size1 = knot_dname_size(dname1);
+			const knot_dname_t *dname2 = r2 + offset;
+			int size2 = knot_dname_size(dname2);
+			cmp = memcmp(dname1, dname2,
+			             size1 <= size2 ? size1 : size2);
+			if (cmp == 0 && size1 != size2) {
+				cmp = size1 < size2 ? -1 : 1;
+			}
+			offset += knot_dname_size(dname1);
 		} else if (descriptor_item_is_fixed(desc->block_types[i])) {
 			cmp = memcmp(r1 + offset, r2 + offset,
 			             desc->block_types[i]);
@@ -261,8 +238,8 @@ static int knot_rrset_header_to_wire(const knot_rrset_t *rrset,
 		owner_len = compr->owner.size;
 	// Use rrset owner.
 	} else {
-		owner = knot_dname_name(rrset->owner);
-		owner_len = knot_dname_size(rrset->owner);
+		owner = rrset->owner;
+		owner_len = knot_dname_size(owner);
 	}
 
 	dbg_response("Max size: %zu, compressed owner: %s, owner size: %u\n",
@@ -275,8 +252,7 @@ static int knot_rrset_header_to_wire(const knot_rrset_t *rrset,
 	}
 
 	// Write owner, type, class and ttl to wire.
-	memcpy(*pos, owner, owner_len);
-	*pos += owner_len;
+	*pos += knot_dname_to_wire(*pos, owner, KNOT_DNAME_MAXLEN);
 
 	dbg_rrset_detail("  Type: %u\n", rrset->type);
 	knot_wire_write_u16(*pos, rrset->type);
@@ -297,7 +273,7 @@ static int knot_rrset_header_to_wire(const knot_rrset_t *rrset,
 
 /* [code-review] Split to more functions, this one's too long. */
 static int knot_rrset_rdata_to_wire_one(const knot_rrset_t *rrset,
-                                        size_t rdata_pos, uint8_t **pos,
+                                        uint16_t rdata_pos, uint8_t **pos,
                                         size_t max_size, size_t *rr_size,
                                         knot_compr_t *compr)
 {
@@ -336,9 +312,7 @@ static int knot_rrset_rdata_to_wire_one(const knot_rrset_t *rrset,
 	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) {
 		int item = desc->block_types[i];
 		if (compr && descriptor_item_is_compr_dname(item)) {
-			knot_dname_t *dname;
-			memcpy(&dname, rdata + offset, sizeof(knot_dname_t *));
-			assert(dname);
+			const knot_dname_t *dname = rdata + offset;
 			int ret = knot_response_compress_dname(dname,
 			            compr, *pos,
 			            max_size - size - rdlength);
@@ -354,31 +328,27 @@ dbg_response_exec_detail(
 );
 			*pos += ret;
 			rdlength += ret;
-			offset += sizeof(knot_dname_t *);
+			offset += knot_dname_size(dname);
 			compr->wire_pos += ret;
 		} else if (descriptor_item_is_dname(item)) {
-			knot_dname_t *dname;
-			memcpy(&dname, rdata + offset, sizeof(knot_dname_t *));
-			assert(dname && dname->name);
-			if (size + rdlength + knot_dname_size(dname) > max_size) {
-				dbg_rrset("rr: to_wire: DNAME does not fit into RR.\n");
-				return KNOT_ESPACE;
-			}
+			const knot_dname_t *dname = rdata + offset;
 dbg_rrset_exec_detail(
 			char *name = knot_dname_to_str(dname);
 			dbg_rrset_detail("Saving this DNAME=%s\n", name);
 			free(name);
 );
 			// save whole domain name
-			memcpy(*pos, knot_dname_name(dname),
-			       knot_dname_size(dname));
+			size_t maxb = max_size - size - rdlength;
+			int dname_size = knot_dname_to_wire(*pos, dname, maxb);
+			if (dname_size < 0)
+				return KNOT_ESPACE;
 			dbg_rrset_detail("Uncompressed dname size: %d\n",
-			                 knot_dname_size(dname));
-			*pos += knot_dname_size(dname);
-			rdlength += knot_dname_size(dname);
-			offset += sizeof(knot_dname_t *);
+			                 dname_size);
+			*pos += dname_size;
+			rdlength += dname_size;
+			offset += dname_size;
 			if (compr) {
-				compr->wire_pos += knot_dname_size(dname);
+				compr->wire_pos += dname_size;
 			}
 		} else if (descriptor_item_is_fixed(item)) {
 			dbg_rrset_detail("Saving static chunk, size=%d\n",
@@ -419,7 +389,7 @@ dbg_rrset_exec_detail(
 			assert(rrset->type == KNOT_RRTYPE_NAPTR);
 			/* Store the binary chunk. */
 			uint16_t chunk_size =
-			rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
+			    rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos);
 			if (size + rdlength + chunk_size > max_size) {
 				dbg_rrset("rr: to_wire: NAPTR chunk does not "
 				          "fit to wire.\n");
@@ -455,12 +425,12 @@ static int knot_rrset_to_wire_aux(const knot_rrset_t *rrset, uint8_t **pos,
 	assert(*pos != NULL);
 
 	dbg_rrset_detail("Max size: %zu, owner: %p, owner size: %d\n",
-	                 max_size, rrset->owner, rrset->owner->size);
+	                 max_size, rrset, knot_dname_size(rrset->owner));
 
 	knot_compr_t compr_info;
 	if (comp) {
 		dbg_response_detail("Compressing RR owner: %s.\n",
-		                    rrset->owner->name);
+		                    rrset->owner);
 		compr_info.table = comp->compressed_dnames;
 		compr_info.wire = comp->wire;
 		compr_info.wire_pos = comp->wire_pos;
@@ -549,51 +519,6 @@ static int knot_rrset_rdata_store_binary(uint8_t *rdata, size_t *offset,
 	return KNOT_EOK;
 }
 
-static int rrset_type_multiple_dnames(const knot_rrset_t *rrset)
-{
-	if (rrset->type == KNOT_RRTYPE_SOA || rrset->type == KNOT_RRTYPE_MINFO ||
-	    rrset->type == KNOT_RRTYPE_RP) {
-		return 1;
-	} else {
-		return 0;
-	}
-}
-
-static int rrset_find_rr_pos_for_pointer(const knot_rrset_t *rrset,
-                                         knot_dname_t **p, size_t *pos)
-{
-	if (p == NULL) {
-		return 0;
-	}
-
-	/* [code-review] Added check of 'p' validity - whether it
-	 * points to the RDATA array of 'rrset'.
-	 */
-	if ((size_t)p < (size_t)rrset->rdata
-	    || (size_t)p > (size_t)rrset->rdata
-	                   + rrset_rdata_size_total(rrset)) {
-		// 'p' is not within the RDATA array
-		return KNOT_ERANGE;
-	}
-
-	size_t offset = (size_t)p - (size_t)rrset->rdata;
-
-	if (offset < rrset_rdata_item_size(rrset, 0)) {
-		return 0;
-	}
-	for (uint16_t i = 0; i < rrset->rdata_count; ++i) {
-		if (rrset_rdata_offset(rrset, i) > offset) {
-			*pos = i - 1;
-			return KNOT_EOK;
-		} else if (rrset_rdata_offset(rrset, i) == offset) {
-			*pos = i;
-			return KNOT_EOK;
-		}
-	}
-	*pos = rrset->rdata_count - 1;
-	return KNOT_EOK;
-}
-
 static size_t rrset_binary_size_one(const knot_rrset_t *rrset,
                                       size_t rdata_pos)
 {
@@ -606,11 +531,10 @@ static size_t rrset_binary_size_one(const knot_rrset_t *rrset,
 		int item = desc->block_types[i];
 		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
 		if (descriptor_item_is_dname(item)) {
-			knot_dname_t *dname;
-			memcpy(&dname, rdata + offset, sizeof(knot_dname_t *));
-			assert(dname);
-			offset += sizeof(knot_dname_t *);
-			size += knot_dname_size(dname) + 1; // extra 1 - we need a size
+			const knot_dname_t *dname = rdata + offset;
+			int dname_size = knot_dname_size(dname);
+			offset += dname_size;
+			size += dname_size;
 		} else if (descriptor_item_is_fixed(item)) {
 			offset += item;
 			size += item;
@@ -645,14 +569,11 @@ static void rrset_serialize_rr(const knot_rrset_t *rrset, size_t rdata_pos,
 		int item = desc->block_types[i];
 		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
 		if (descriptor_item_is_dname(item)) {
-			knot_dname_t *dname;
-			memcpy(&dname, rdata + offset, sizeof(knot_dname_t *));
-			assert(dname);
-			memcpy(stream + *size, &dname->size, 1);
-			*size += 1;
-			memcpy(stream + *size, dname->name, dname->size);
-			offset += sizeof(knot_dname_t *);
-			*size += dname->size;
+			const knot_dname_t *dname = rdata + offset;
+			int dname_len = knot_dname_to_wire(stream + *size, dname,
+			                                   KNOT_DNAME_MAXLEN);
+			*size += dname_len;
+			offset += dname_len;
 		} else if (descriptor_item_is_fixed(item)) {
 			memcpy(stream + *size, rdata + offset, item);
 			offset += item;
@@ -693,21 +614,12 @@ static int rrset_deserialize_rr(knot_rrset_t *rrset, size_t rdata_pos,
 		int item = desc->block_types[i];
 		uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos);
 		if (descriptor_item_is_dname(item)) {
-			/* Read dname size. */
-			uint8_t dname_size = 0;
-			memcpy(&dname_size, stream + stream_offset, 1);
-			stream_offset += 1;
-			knot_dname_t *dname =
-				knot_dname_new_from_wire(stream + stream_offset,
-			                                 dname_size, NULL);
-			if (dname == NULL) {
-				return KNOT_ERROR;
-			}
-			assert(dname->size == dname_size);
-			memcpy(rdata + rdata_offset, &dname,
-			       sizeof(knot_dname_t *));
+			const knot_dname_t *dname = stream + stream_offset;
+			int dname_size = knot_dname_size(dname);
+
+			memcpy(rdata + rdata_offset, dname, dname_size);
 			stream_offset += dname_size;
-			rdata_offset += sizeof(knot_dname_t *);
+			rdata_offset += dname_size;
 		} else if (descriptor_item_is_fixed(item)) {
 			memcpy(rdata + rdata_offset, stream + stream_offset, item);
 			rdata_offset += item;
@@ -740,15 +652,6 @@ int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
 		return KNOT_EINVAL;
 	}
 
-	/* Handle DNAMEs inside RDATA. */
-	int ret = rrset_rr_dnames_apply(rrset, pos, rrset_release_dnames_in_rr,
-	                                NULL);
-	if (ret != KNOT_EOK) {
-		dbg_rrset("rr: remove_rdata_pos: Could not release DNAMEs "
-		          "within RDATA (%s).\n", knot_strerror(ret));
-		return ret;
-	}
-
 	/* Reorganize the actual RDATA array. */
 	uint8_t *rdata_to_remove = rrset_rdata_pointer(rrset, pos);
 	dbg_rrset_detail("rr: remove_rdata_pos: Removing data=%p on "
@@ -770,7 +673,6 @@ int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
 	uint32_t removed_size = rrset_rdata_item_size(rrset, pos);
 	uint32_t new_size = rrset_rdata_size_total(rrset) - removed_size;
 
-	/*! \todo Realloc might not be needed. Only if the RRSet is large. */
 	if (new_size == 0) {
 		assert(rrset->rdata_count == 1);
 		free(rrset->rdata);
@@ -778,18 +680,12 @@ int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
 		free(rrset->rdata_indices);
 		rrset->rdata_indices = NULL;
 	} else {
-		/* [code-review] Should not be done always - as said in the TODO
-		 *               above. But also, why here and not in the part
-		 *               handling the RDATA array?
-		 */
+		/* Resize RDATA array, the change might be worth reallocation.*/
 		rrset->rdata = xrealloc(rrset->rdata, new_size);
 		/*
 		 * Handle RDATA indices. All indices larger than the removed one
 		 * have to be adjusted. Last index will be changed later.
 		 */
-		/* [code-review] The upper bound should be rdata_count - 2, it
-		 *               has not yet been adjusted.
-		 */
 		for (uint16_t i = pos; i < rrset->rdata_count - 1; ++i) {
 			rrset->rdata_indices[i] = rrset->rdata_indices[i + 1] - removed_size;
 		}
@@ -797,7 +693,7 @@ int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
 		/* Save size of the whole RDATA array. Note: probably not needed! */
 		rrset->rdata_indices[rrset->rdata_count - 2] = new_size;
 
-		/* Resize indices, might not be needed, but we'll do it to be proper. */
+		/* Resize indices, not always needed, but we'll do it to be proper. */
 		rrset->rdata_indices =
 			xrealloc(rrset->rdata_indices,
 		                 (rrset->rdata_count - 1) * sizeof(uint32_t));
@@ -811,10 +707,6 @@ int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos)
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-/* API functions                                                              */
-/*----------------------------------------------------------------------------*/
-
 uint32_t rrset_rdata_size_total(const knot_rrset_t *rrset)
 {
 	if (rrset == NULL || rrset->rdata_indices == NULL ||
@@ -822,22 +714,23 @@ uint32_t rrset_rdata_size_total(const knot_rrset_t *rrset)
 		return 0;
 	}
 
-	/* Last index denotes end of all RRs. */
+	// Last index denotes end of all RRs.
 	return (rrset->rdata_indices[rrset->rdata_count - 1]);
 }
 
 knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
                              uint16_t rclass, uint32_t ttl)
 {
-	knot_rrset_t *ret = xmalloc(sizeof(knot_rrset_t));
+	knot_rrset_t *ret = malloc(sizeof(knot_rrset_t));
+	if (ret == NULL) {
+		ERR_ALLOC_FAILED;
+		return NULL;
+	}
 
 	ret->rdata = NULL;
 	ret->rdata_count = 0;
 	ret->rdata_indices = NULL;
 
-	/* Retain reference to owner. */
-	knot_dname_retain(owner);
-
 	ret->owner = owner;
 	ret->type = type;
 	ret->rclass = rclass;
@@ -847,6 +740,20 @@ knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
 	return ret;
 }
 
+knot_rrset_t *knot_rrset_new_from(const knot_rrset_t *tpl)
+{
+	if (!tpl) {
+		return NULL;
+	}
+
+	knot_dname_t *owner = knot_dname_copy(tpl->owner);
+	if (!owner) {
+		return NULL;
+	}
+
+	return knot_rrset_new(owner, tpl->type, tpl->rclass, tpl->ttl);
+}
+
 int knot_rrset_add_rdata(knot_rrset_t *rrset,
                          const uint8_t *rdata, uint16_t size)
 {
@@ -860,6 +767,76 @@ int knot_rrset_add_rdata(knot_rrset_t *rrset,
 	return KNOT_EOK;
 }
 
+static uint8_t* knot_rrset_create_rdata_at_pos(knot_rrset_t *rrset,
+                                               size_t pos, uint16_t size)
+{
+	if (rrset == NULL || size == 0 || pos > rrset->rdata_count) {
+		return NULL;
+	}
+	if (pos == rrset->rdata_count) {
+		return knot_rrset_create_rdata(rrset, size);
+	}
+
+	uint32_t total_size = rrset_rdata_size_total(rrset);
+
+	// Realloc actual data.
+	void *tmp = realloc(rrset->rdata, total_size + size);
+	if (tmp) {
+		rrset->rdata = tmp;
+	} else {
+		ERR_ALLOC_FAILED;
+		return NULL;
+	}
+
+	/*
+	 * Move already existing data to from position we want to add to.
+	 * But only if we don't want to add new item after last item.
+	 */
+	uint8_t *old_pointer = rrset_rdata_pointer(rrset, pos);
+	assert(old_pointer);
+	memmove(old_pointer + size, old_pointer,
+	        rrset_rdata_size_total(rrset) - rrset_rdata_offset(rrset, pos));
+	
+	// Realloc indices. We will allocate exact size to save space.
+	tmp = realloc(rrset->rdata_indices,
+	              (rrset->rdata_count + 1) * sizeof(uint32_t));
+	if (tmp) {
+		rrset->rdata_indices = tmp;
+	} else {
+		ERR_ALLOC_FAILED;
+		return NULL;
+	}
+	// Move indices.
+	memmove(rrset->rdata_indices + pos + 1, rrset->rdata_indices + pos,
+	        (rrset->rdata_count - pos) * sizeof(uint32_t));
+	// Init new index
+	rrset->rdata_indices[pos] = pos ? rrset->rdata_indices[pos - 1] : 0;
+	++rrset->rdata_count;
+	// Adjust all following items
+	for (uint16_t i = pos; i < rrset->rdata_count; ++i) {
+		rrset->rdata_indices[i] += size;
+	}
+
+	// Return pointer from correct position (now contains bogus data).
+	return old_pointer;
+}
+
+int knot_rrset_add_rdata_at_pos(knot_rrset_t *rrset, size_t pos,
+                                const uint8_t *rdata, uint16_t size)
+{
+	if (rrset == NULL || rdata == NULL || size == 0) {
+		return KNOT_EINVAL;
+	}
+
+	uint8_t *p = knot_rrset_create_rdata_at_pos(rrset, pos, size);
+	if (p == NULL) {
+		return KNOT_ERROR;
+	}
+	memcpy(p, rdata, size);
+
+	return KNOT_EOK;
+}
+
 /*----------------------------------------------------------------------------*/
 
 uint8_t* knot_rrset_create_rdata(knot_rrset_t *rrset, uint16_t size)
@@ -871,8 +848,6 @@ uint8_t* knot_rrset_create_rdata(knot_rrset_t *rrset, uint16_t size)
 	uint32_t total_size = rrset_rdata_size_total(rrset);
 
 	/* Realloc indices. We will allocate exact size to save space. */
-	/* TODO this sucks big time - allocation of length 1. */
-	/* But another variable holding allocated count is out of question. What now?*/
 	rrset->rdata_indices = xrealloc(rrset->rdata_indices,
 	                                (rrset->rdata_count + 1) * sizeof(uint32_t));
 
@@ -916,8 +891,6 @@ uint16_t rrset_rdata_item_size(const knot_rrset_t *rrset,
 	return rrset->rdata_indices[pos] - rrset->rdata_indices[pos - 1];
 }
 
-/*----------------------------------------------------------------------------*/
-
 int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
 {
 	if (rrset == NULL) {
@@ -928,13 +901,11 @@ int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
 int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
                           knot_rrset_dupl_handling_t dupl)
 {
-	if (rrset == NULL || rrsigs == NULL
-	    || knot_dname_compare_non_canon(rrset->owner, rrsigs->owner) != 0) {
+	if (rrset == NULL || rrsigs == NULL ||
+	    !knot_dname_is_equal(rrset->owner, rrsigs->owner)) {
 		return KNOT_EINVAL;
 	}
 
@@ -942,8 +913,8 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
 	if (rrset->rrsigs != NULL) {
 		if (dupl == KNOT_RRSET_DUPL_MERGE) {
 			int merged, deleted_rrs;
-			rc = knot_rrset_merge_no_dupl(rrset->rrsigs, rrsigs,
-			                              &merged, &deleted_rrs);
+			rc = knot_rrset_merge_sort(rrset->rrsigs, rrsigs,
+			                           &merged, &deleted_rrs);
 			if (rc != KNOT_EOK) {
 				return rc;
 			} else if (merged || deleted_rrs) {
@@ -966,33 +937,36 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
 const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset)
 {
 	return rrset->owner;
 }
 
-/*----------------------------------------------------------------------------*/
-
 knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset)
 {
 	return rrset->owner;
 }
 
-/*----------------------------------------------------------------------------*/
-
-void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t *owner)
+int knot_rrset_set_owner(knot_rrset_t *rrset, const knot_dname_t *owner)
 {
-	if (rrset) {
-		/* Retain new owner and release old owner. */
-		knot_dname_retain(owner);
-		knot_dname_release(rrset->owner);
-		rrset->owner = owner;
+	if (rrset == NULL) {
+		return KNOT_EINVAL;
 	}
-}
 
-/*----------------------------------------------------------------------------*/
+	/* Copy the new owner. */
+	knot_dname_t *owner_copy = NULL;
+	if (owner) {
+		owner_copy = knot_dname_copy(owner);
+		if (owner_copy == NULL) {
+			return KNOT_ENOMEM;
+		}
+	}
+
+	/* Free old owner and assign. */
+	knot_dname_free(&rrset->owner);
+	rrset->owner = owner_copy;
+	return KNOT_EOK;
+}
 
 void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl)
 {
@@ -1001,36 +975,26 @@ void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl)
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
 uint16_t knot_rrset_type(const knot_rrset_t *rrset)
 {
 	return rrset->type;
 }
 
-/*----------------------------------------------------------------------------*/
-
 uint16_t knot_rrset_class(const knot_rrset_t *rrset)
 {
 	return rrset->rclass;
 }
 
-/*----------------------------------------------------------------------------*/
-
 uint32_t knot_rrset_ttl(const knot_rrset_t *rrset)
 {
 	return rrset->ttl;
 }
 
-/*----------------------------------------------------------------------------*/
-
 uint8_t *knot_rrset_get_rdata(const knot_rrset_t *rrset, size_t rdata_pos)
 {
 	return rrset_rdata_pointer(rrset, rdata_pos);
 }
 
-/*----------------------------------------------------------------------------*/
-
 uint16_t knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
 {
 	if (rrset != NULL) {
@@ -1040,8 +1004,6 @@ uint16_t knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
 const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
 {
 	if (rrset == NULL) {
@@ -1051,8 +1013,6 @@ const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
 knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
 {
 	if (rrset == NULL) {
@@ -1062,6 +1022,9 @@ knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
 	}
 }
 
+/*!
+ * \brief Compare two RR sets, order of RDATA is not significant.
+ */
 int knot_rrset_rdata_equal(const knot_rrset_t *r1, const knot_rrset_t *r2)
 {
 	if (r1 == NULL || r2 == NULL || (r1->type != r2->type) ||
@@ -1069,38 +1032,27 @@ int knot_rrset_rdata_equal(const knot_rrset_t *r1, const knot_rrset_t *r2)
 		return KNOT_EINVAL;
 	}
 
-	// compare RDATA sets (order is not significant)
-
-	// find all RDATA from r1 in r2
-	int found = 0;
-	for (uint16_t i = 0; i < r1->rdata_count; i++) {
-		found = 0;
-		for (uint16_t j = 0; j < r2->rdata_count && !found; j++) {
-			found = !rrset_rdata_compare_one(r1, r2, i, j);
-		}
-	}
-
-	if (!found) {
+	if (r1->rdata_count != r2->rdata_count) {
 		return 0;
 	}
 
-	// other way around
-	for (uint16_t i = 0; i < r2->rdata_count; i++) {
-		found = 0;
-		for (uint16_t j = 0; j < r1->rdata_count && !found; j++) {
-			found = !rrset_rdata_compare_one(r1, r2, j, i);
+	for (uint16_t i = 0; i < r1->rdata_count; i++) {
+		bool found = false;
+		for (uint16_t j = 0; j < r2->rdata_count; j++) {
+			if (rrset_rdata_compare_one(r1, r2, i, j) == 0) {
+				found = true;
+				break;
+			}
 		}
-	}
 
-	if (!found) {
-		return 0;
+		if (!found) {
+			return 0;
+		}
 	}
 
 	return 1;
 }
 
-/*----------------------------------------------------------------------------*/
-
 int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
                        size_t max_size, uint16_t *rr_count, void *data)
 {
@@ -1138,15 +1090,25 @@ dbg_rrset_exec_detail(
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
+int knot_rrset_to_wire_one(const knot_rrset_t *rrset, uint16_t rr_number,
+                           uint8_t *wire, size_t max_size, size_t *outsize,
+                           void *compr)
+{
+	if (!rrset || !wire || !outsize)
+		return KNOT_EINVAL;
+
+	uint8_t *pos = wire;
+	return knot_rrset_rdata_to_wire_one(rrset, rr_number, &pos, max_size,
+					    outsize, (knot_compr_t *)compr);
+}
 
 int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset,
                                    const uint8_t *wire, size_t *pos,
                                    size_t total_size, size_t rdlength)
 {
-	int obsolete = 0;
-
-	/* [code-review] Missing parameter checks. */
+	if (rrset == NULL || wire == NULL || pos == NULL) {
+		return KNOT_EINVAL;
+	}
 
 	if (rdlength == 0) {
 		/* Nothing to parse, */
@@ -1157,70 +1119,55 @@ int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset,
 	                 " wire_size=%zu, type=%d.\n", rdlength, total_size,
 	                 rrset->type);
 
-	size_t extra_dname_size = 0;
 	const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type);
 
 	/* Check for obsolete record. */
 	if (desc->type_name == NULL) {
 		desc = get_obsolete_rdata_descriptor(rrset->type);
-		if (desc->type_name != NULL) {
-			obsolete = 1;
-		}
-	}
-
-	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
-		if (descriptor_item_is_dname(desc->block_types[i])) {
-			if (obsolete) {
-				extra_dname_size += KNOT_MAX_DNAME_LENGTH;
-			} else {
-				extra_dname_size += sizeof(knot_dname_t *);
-			}
-		}
 	}
 
-	uint8_t rdata_buffer[rdlength + extra_dname_size];
-	memset(rdata_buffer, 0, rdlength + extra_dname_size);
-	dbg_rrset_detail("rr: parse_rdata_wire: Added %zu bytes to buffer to "
-	                 "store RDATA DNAME pointers.\n", extra_dname_size);
+	/*! \todo This estimate is very rough - just to have enough space for
+	 *        possible unpacked dname. Should be later replaced by exact
+	 *        size counting.
+	 */
+	uint8_t rdata_buffer[rdlength + KNOT_DNAME_MAXLEN];
+	memset(rdata_buffer, 0, rdlength + KNOT_DNAME_MAXLEN);
 
 	size_t offset = 0; // offset within in-memory RDATA
 	size_t parsed = 0; // actual count of parsed octets
 	const size_t packet_offset = *pos;
 
+	/*! \todo [RRSet refactor]
+	 *        This could be A LOT simpler - copy it as a whole,
+	 *        unpack dnames and just do some format checks if necessary.
+	 *        But it's questionable, if copying the memory when unpacking
+	 *        dnames, wouldn't be too expensive.
+	 */
+
 	for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END &&
 	     parsed < rdlength; ++i) {
-		size_t pos2 = 0; //used for DNAME parsing
 		const int item = desc->block_types[i];
 		if (descriptor_item_is_dname(item)) {
-			pos2 = *pos;
-			knot_dname_t *dname = knot_dname_parse_from_wire(
-					wire, &pos2, total_size, NULL, NULL);
-			if (dname == NULL) {
-				return KNOT_EMALF;
+			int wire_size = knot_dname_size(wire + *pos);
+			int unpacked_size = knot_dname_unpack(
+				rdata_buffer + offset, wire + *pos,
+				KNOT_DNAME_MAXLEN, wire);
+			if (unpacked_size == KNOT_EINVAL) {
+				return KNOT_ERROR;
 			}
-			knot_dname_to_lower(dname);
 
-			dbg_rrset_detail("rr: parse_rdata_wire: Parsed DNAME, "
-			                 "length=%zu.\n", pos2 - *pos);
+			parsed += wire_size;
+
 dbg_rrset_exec_detail(
-			char *name = knot_dname_to_str(dname);
+			dbg_rrset_detail("rr: parse_rdata_wire: Parsed DNAME, "
+			                 "length=%d.\n", wire_size);
+			char *name = knot_dname_to_str(rdata_buffer + offset);
 			dbg_rrset_detail("rr: parse_rdata_wire: Parsed "
 			                 "DNAME=%s\n", name);
 			free(name);
 );
-			if (obsolete) {
-				memcpy(rdata_buffer + offset,
-				       knot_dname_name(dname),
-				       knot_dname_size(dname));
-				offset += knot_dname_size(dname);
-				knot_dname_free(&dname);
-			} else {
-				memcpy(rdata_buffer + offset, &dname,
-				       sizeof(knot_dname_t *));
-				offset += sizeof(knot_dname_t *);
-			}
-			parsed += pos2 - *pos;
-			*pos = pos2;
+			*pos += wire_size;
+			offset += unpacked_size;
 		} else if (descriptor_item_is_fixed(item)) {
 			dbg_rrset_detail("rr: parse_rdata_wire: Saving static "
 			                 "chunk of size=%u\n", item);
@@ -1319,27 +1266,20 @@ int knot_rrset_equal(const knot_rrset_t *r1,
 		return r1 == r2;
 	}
 
-	int res = knot_dname_compare_non_canon(r1->owner, r2->owner);
-	if (res != 0) {
-		return 0;
-	}
 
-	if (r1->rclass == r2->rclass &&
-	    r1->type == r2->type) {
-		res = 1;
-	} else {
-		res = 0;
-	}
+	if (!knot_dname_is_equal(r1->owner, r2->owner))
+		return false;
 
-	if (cmp == KNOT_RRSET_COMPARE_WHOLE && res == 1) {
-		res = knot_rrset_rdata_equal(r1, r2);
-	}
+	if (r1->rclass != r2->rclass || r1->type != r2->type)
+		return false;
+
+	if (cmp == KNOT_RRSET_COMPARE_WHOLE)
+		return knot_rrset_rdata_equal(r1, r2);
 
-	return res;
+	return true;
 }
 
-int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
-                         int copy_rdata_dnames)
+int knot_rrset_deep_copy_no_sig(const knot_rrset_t *from, knot_rrset_t **to)
 {
 	if (from == NULL || to == NULL) {
 		return KNOT_EINVAL;
@@ -1350,7 +1290,7 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
 
 	*to = xmalloc(sizeof(knot_rrset_t));
 
-	(*to)->owner = knot_dname_deep_copy(from->owner);
+	(*to)->owner = knot_dname_copy(from->owner);
 	if ((*to)->owner == NULL) {
 		free(*to);
 		*to = NULL;
@@ -1361,16 +1301,7 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
 	(*to)->ttl = from->ttl;
 	(*to)->type = from->type;
 	(*to)->rdata_count = from->rdata_count;
-	if (from->rrsigs != NULL) {
-		int ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs,
-		                           copy_rdata_dnames);
-		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(to, 1, 0);
-			return ret;
-		}
-	} else {
-		(*to)->rrsigs = NULL;
-	}
+	(*to)->rrsigs = NULL;
 
 	/* Just copy arrays - actual data + indices. */
 	(*to)->rdata = xmalloc(rrset_rdata_size_total(from));
@@ -1379,54 +1310,46 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
 	(*to)->rdata_indices = xmalloc(sizeof(uint32_t) * from->rdata_count);
 	memcpy((*to)->rdata_indices, from->rdata_indices,
 	       sizeof(uint32_t) * from->rdata_count);
-	/* Here comes the hard part. */
-	if (copy_rdata_dnames) {
-		knot_dname_t **dname_from = NULL;
-		knot_dname_t **dname_to = NULL;
-		knot_dname_t *dname_copy = NULL;
-		while ((dname_from = knot_rrset_get_next_dname(from, dname_from))) {
-dbg_rrset_exec_detail(
-			char *name = knot_dname_to_str(*dname_from);
-			dbg_rrset_detail("rrset: deep_copy: copying RDATA DNAME"
-			                 "=%s\n", name);
-			free(name);
-);
-			size_t off = (uint8_t*)dname_from - from->rdata;
-			dname_to = (knot_dname_t **)((*to)->rdata + off);
-			/* These pointers have to be the same. */
-			assert(*dname_from == *dname_to);
-			dname_copy = knot_dname_deep_copy(*dname_from);
-			if (dname_copy == NULL) {
-				dbg_rrset("rrset: deep_copy: Cannot copy RDATA"
-				          " dname.\n");
-				/*! \todo This will leak. Is it worth fixing? */
-				/* [code-review] Why will it leak? */
-				knot_rrset_deep_free(&(*to)->rrsigs, 1,
-				                     copy_rdata_dnames);
-				free((*to)->rdata);
-				free((*to)->rdata_indices);
-				free(*to);
-				return KNOT_ENOMEM;
-			}
 
-			*dname_to = dname_copy;
+	return KNOT_EOK;
+}
+
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+	int result = knot_rrset_deep_copy_no_sig(from, to);
+
+	if (result == KNOT_EOK && from->rrsigs != NULL) {
+		result = knot_rrset_deep_copy_no_sig(from->rrsigs,
+		                                     &(*to)->rrsigs);
+		if (result != KNOT_EOK) {
+			knot_rrset_deep_free(to, 1);
 		}
 	}
 
-	return KNOT_EOK;
+	return result;
 }
 
 /*----------------------------------------------------------------------------*/
 
 int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to)
 {
-	*to = (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
-	CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+	if (!from || !to) {
+		return KNOT_EINVAL;
+	}
+
+	knot_rrset_t *result = (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+	if (!result) {
+		return KNOT_ENOMEM;
+	}
 
-	memcpy(*to, from, sizeof(knot_rrset_t));
+	memcpy(result, from, sizeof(knot_rrset_t));
+	result->owner = knot_dname_copy(result->owner);
+	if (!result->owner) {
+		free(result);
+		return KNOT_ENOMEM;
+	}
 
-	/* Retain owner. */
-	knot_dname_retain((*to)->owner);
+	*to = result;
 
 	return KNOT_EOK;
 }
@@ -1447,729 +1370,223 @@ void knot_rrset_free(knot_rrset_t **rrset)
 		return;
 	}
 
-	/*! \todo Shouldn't we always release owner reference? */
-	knot_dname_release((*rrset)->owner);
+	knot_dname_free(&(*rrset)->owner);
 
 	free(*rrset);
 	*rrset = NULL;
 }
 
-void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
-                          int free_rdata_dnames)
+void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner)
 {
+	/*! \bug The number of different frees in rrset is too damn high! */
 	if (rrset == NULL || *rrset == NULL) {
 		return;
 	}
 
 	if ((*rrset)->rrsigs != NULL) {
-		knot_rrset_deep_free(&(*rrset)->rrsigs, free_owner, free_rdata_dnames);
-	}
-
-	if (free_rdata_dnames) {
-		rrset_dnames_apply(*rrset, rrset_release_dnames_in_rr,
-	                           NULL);
+		knot_rrset_deep_free(&(*rrset)->rrsigs, free_owner);
 	}
 
 	free((*rrset)->rdata);
 	free((*rrset)->rdata_indices);
 
 	if (free_owner) {
-		knot_dname_release((*rrset)->owner);
+		knot_dname_free(&(*rrset)->owner);
 	}
 
 	free(*rrset);
 	*rrset = NULL;
 }
 
-void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner,
-                                 int free_rdata_dnames)
+void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner)
 {
 	if (rrset == NULL || *rrset == NULL) {
 		return;
 	}
 
-	if (free_rdata_dnames) {
-		int ret = rrset_dnames_apply(*rrset, rrset_release_dnames_in_rr,
-		                             NULL);
-		if (ret != KNOT_EOK) {
-			dbg_rrset("rr: deep_free: Could not free DNAMEs in RDATA.\n");
-		}
-	}
-
 	free((*rrset)->rdata);
 	free((*rrset)->rdata_indices);
 
 	if (free_owner) {
-		knot_dname_release((*rrset)->owner);
+		knot_dname_free(&(*rrset)->owner);
 	}
 
 	free(*rrset);
 	*rrset = NULL;
 }
 
-int knot_rrset_merge(knot_rrset_t *rrset1, const knot_rrset_t *rrset2)
+static int knot_rrset_add_rr_n(knot_rrset_t *rrset, const knot_rrset_t *rr,
+                               size_t pos)
 {
-	if (rrset1 == NULL || rrset2 == NULL) {
+	if (rrset == NULL || rr == NULL) {
 		return KNOT_EINVAL;
 	}
-
-	/* Check, that we really merge RRSets? */
-	if (rrset1->type != rrset2->type ||
-	    rrset1->rclass != rrset2->rclass ||
-	    (knot_dname_compare_non_canon(rrset1->owner, rrset2->owner) != 0)) {
+	if (!knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) {
+		// Adding to a different header
 		return KNOT_EINVAL;
 	}
 
-	/* Merging empty RRSets is OK. */
-	if (rrset1->rdata_count == 0 && rrset2->rdata_count == 0) {
-		return KNOT_EOK;
+	uint8_t *new_rdata =
+		knot_rrset_create_rdata(rrset,
+	                                rrset_rdata_item_size(rr, pos));
+	if (new_rdata == NULL) {
+		return KNOT_ERROR;
 	}
 
-	/* Add all RDATAs from rrset2 to rrset1 (i.e. concatenate two arrays) */
-
-	/*! \note The following code should work for
-	 *        all the cases i.e. R1 or R2 are empty.
-	 */
-
-	/* Reallocate actual RDATA array. */
-	rrset1->rdata = xrealloc(rrset1->rdata, rrset_rdata_size_total(rrset1) +
-	                         rrset_rdata_size_total(rrset2));
-
-	/* The space is ready, copy the actual data. */
-	memcpy(rrset1->rdata + rrset_rdata_size_total(rrset1),
-	       rrset2->rdata, rrset_rdata_size_total(rrset2));
-
-	/* Indices have to be readjusted. But space has to be made first. */
-	rrset1->rdata_indices =
-		xrealloc(rrset1->rdata_indices,
-	        (rrset1->rdata_count + rrset2->rdata_count) *
-	         sizeof(uint32_t));
+	memcpy(new_rdata, rrset_rdata_pointer(rr, pos),
+	       rrset_rdata_item_size(rr, pos));
 
-	uint32_t rrset1_total_size = rrset_rdata_size_total(rrset1);
-	uint32_t rrset2_total_size = rrset_rdata_size_total(rrset2);
-
-	/*
-	 * Move the indices. Discard the last item in the first array, as it
-	 * contains total length of the data, which is now different.
-	 */
-	memcpy(rrset1->rdata_indices + rrset1->rdata_count,
-	       rrset2->rdata_indices, rrset2->rdata_count);
+	return KNOT_EOK;
+}
 
-	/* Go through the second part of index array and adjust offsets. */
-	for (uint16_t i = 0; i < rrset2->rdata_count - 1; i++) {
-		rrset1->rdata_indices[rrset1->rdata_count + i] +=
-			rrset1_total_size;
+int knot_rrset_merge(knot_rrset_t *rrset1, const knot_rrset_t *rrset2)
+{
+	for (uint16_t i = 0; i < rrset2->rdata_count; ++i) {
+		int ret = knot_rrset_add_rr_n(rrset1, rrset2, i);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
-	rrset1->rdata_indices[rrset1->rdata_count + rrset2->rdata_count - 1] =
-		rrset1_total_size + rrset2_total_size;
-
-	rrset1->rdata_count += rrset2->rdata_count;
-
 	return KNOT_EOK;
 }
 
-int knot_rrset_merge_no_dupl(knot_rrset_t *rrset1, const knot_rrset_t *rrset2,
-                             int *merged, int *deleted_rrs)
+static int knot_rrset_add_rr_sort_n(knot_rrset_t *rrset, const knot_rrset_t *rr,
+                                    int *merged, int *deleted, size_t pos)
 {
-	if (rrset1 == NULL || rrset2 == NULL) {
-		dbg_rrset("rrset: merge_no_dupl: NULL arguments.");
+	if (rrset == NULL || rr == NULL) {
+		dbg_rrset("rrset: add_rr_sort: NULL arguments.");
 		return KNOT_EINVAL;
 	}
 
 dbg_rrset_exec_detail(
-	char *name = knot_dname_to_str(rrset1->owner);
-	dbg_rrset_detail("rrset: merge_no_dupl: Merging %s.\n", name);
+	char *name = knot_dname_to_str(rrset->owner);
+	dbg_rrset_detail("rrset: add_rr_sort: Merging %s.\n", name);
 	free(name);
 );
 
-	if ((knot_dname_compare_non_canon(rrset1->owner, rrset2->owner) != 0)
-	    || rrset1->rclass != rrset2->rclass
-	    || rrset1->type != rrset2->type) {
-		dbg_rrset("rrset: merge_no_dupl: Trying to merge "
+	if ((!knot_dname_is_equal(rrset->owner, rr->owner))
+	    || rrset->rclass != rr->rclass
+	    || rrset->type != rr->type) {
+		dbg_rrset("rrset: add_rr_sort: Trying to merge "
 		          "different RRs.\n");
 		return KNOT_EINVAL;
 	}
 
-	*deleted_rrs = 0;
-	*merged = 0;
-	/* For each item in second RRSet, make sure it is not duplicated. */
-	for (uint16_t i = 0; i < rrset2->rdata_count; i++) {
-		int duplicated = 0;
-		/* Compare with all RRs in the first RRSet. */
-		for (uint16_t j = 0; j < rrset1->rdata_count && !duplicated;
-		     j++) {
-			duplicated = !rrset_rdata_compare_one(rrset1, rrset2,
-			                                      j, i);
-		}
-
-		if (!duplicated) {
-			*merged += 1; // = need to shallow free rrset2
-			// This index goes to merged RRSet.
-			int ret = knot_rrset_add_rdata(rrset1,
-			                               rrset_rdata_pointer(rrset2, i),
-			                               rrset_rdata_item_size(rrset2, i));
-			if (ret != KNOT_EOK) {
-				dbg_rrset("rrset: merge_no_dupl: Could not "
-				          "add RDATA to RRSet. (%s)\n",
-				          knot_strerror(ret));
-				return ret;
-			}
+	int found = 0;
+	int duplicated = 0;
+	// Compare RR with all RRs in the first RRSet.
+	size_t insert_to = 0;
+	for (uint16_t j = 0; j < rrset->rdata_count && (!duplicated && !found); ++j) {
+		int cmp = rrset_rdata_compare_one(rrset, rr, j, pos);
+		if (cmp == 0) {
+			// Duplication - no need to merge this RR
+			duplicated = 1;
+		} else if (cmp > 0) {
+			// Found position to insert
+			found = 1;
 		} else {
-			*deleted_rrs += 1; // = need to shallow free rrset2
+			// Not yet - it might be next position
+			insert_to = j + 1;
 		}
 	}
 
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr)
-{
-	assert(rr != NULL);
-
-	/* Is NSEC3 or non-empty RRSIG covering NSEC3. */
-	return ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
-	        || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
-	            && knot_rrset_rdata_rrsig_type_covered(rr)
-	            == KNOT_RRTYPE_NSEC3));
-}
-
-/*----------------------------------------------------------------------------*/
-
-const knot_dname_t *knot_rrset_rdata_cname_name(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-
-	knot_dname_t *dname;
-	memcpy(&dname, rrset->rdata, sizeof(knot_dname_t *));
-	return dname;
-}
-
-/*----------------------------------------------------------------------------*/
-
-const knot_dname_t *knot_rrset_rdata_dname_target(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-	knot_dname_t *dname;
-	memcpy(&dname, rrset->rdata, sizeof(knot_dname_t *));
-	return dname;
-}
-
-/*---------------------------------------------------------------------------*/
-
-const knot_dname_t *knot_rrset_rdata_soa_primary_ns(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-	knot_dname_t *dname;
-	memcpy(&dname, rrset->rdata, sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_soa_mailbox(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-	knot_dname_t *dname;
-	memcpy(&dname, rrset->rdata + sizeof(knot_dname_t *),
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_rp_first_dname(const knot_rrset_t *rrset,
-                                                    size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return NULL;
-	}
-
-	knot_dname_t *dname;
-	memcpy(&dname, knot_rrset_get_rdata(rrset, pos), sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_rp_second_dname(const knot_rrset_t *rrset,
-                                                     size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return NULL;
-	}
-
-	knot_dname_t *dname;
-	memcpy(&dname, knot_rrset_get_rdata(rrset, pos) + sizeof(knot_dname_t *),
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_minfo_first_dname(const knot_rrset_t *rrset,
-                                                       size_t pos)
-{
-	return knot_rrset_rdata_rp_first_dname(rrset, pos);
-}
-
-const knot_dname_t *knot_rrset_rdata_minfo_second_dname(const knot_rrset_t *rrset,
-                                                        size_t pos)
-{
-	return knot_rrset_rdata_rp_second_dname(rrset, pos);
-}
-
-uint32_t knot_rrset_rdata_soa_serial(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(rrset->rdata + sizeof(knot_dname_t *) * 2);
-}
-
-/*---------------------------------------------------------------------------*/
-
-void knot_rrset_rdata_soa_serial_set(knot_rrset_t *rrset, uint32_t serial)
-{
-	if (rrset == NULL) {
-		return;
-	}
-
-	// the number is in network byte order, transform it
-	knot_wire_write_u32(rrset->rdata + sizeof(knot_dname_t *) * 2,
-	                    serial);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint32_t knot_rrset_rdata_soa_refresh(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(rrset->rdata +
-	                          sizeof(knot_dname_t *) * 2 + 4);
-}
-
-/*---------------------------------------------------------------------------*/
-
-
-uint32_t knot_rrset_rdata_soa_retry(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(rrset->rdata +
-	                          sizeof(knot_dname_t *) * 2 + 8);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint32_t knot_rrset_rdata_soa_expire(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(rrset->rdata +
-	                          sizeof(knot_dname_t *) * 2 + 12);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint32_t knot_rrset_rdata_soa_minimum(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(rrset->rdata +
-	                          sizeof(knot_dname_t *) * 2 + 16);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint16_t knot_rrset_rdata_rrsig_type_covered(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
-	}
-
-	return knot_wire_read_u16(rrset->rdata);
-}
-
-uint8_t knot_rrset_rdata_rrsig_algorithm(const knot_rrset_t *rrset,
-                                         size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(knot_rrset_get_rdata(rrset, rr_pos) + 2);
-}
-
-uint8_t knot_rrset_rdata_rrsig_labels(const knot_rrset_t *rrset,
-                                      size_t rr_pos)
-
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(knot_rrset_get_rdata(rrset, rr_pos) + 3);
-}
-
-uint32_t knot_rrset_rdata_rrsig_original_ttl(const knot_rrset_t *rrset,
-                                             size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, rr_pos) + 4);
-}
-
-uint32_t knot_rrset_rdata_rrsig_sig_expiration(const knot_rrset_t *rrset,
-                                               size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, rr_pos) + 8);
-}
-
-uint32_t knot_rrset_rdata_rrsig_sig_inception(const knot_rrset_t *rrset,
-                                              size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return knot_wire_read_u32(knot_rrset_get_rdata(rrset, rr_pos) + 12);
-}
-
-uint16_t knot_rrset_rdata_rrsig_key_tag(const knot_rrset_t *rrset,
-                                        size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return knot_wire_read_u16(knot_rrset_get_rdata(rrset, rr_pos) + 16);
-}
-
-const knot_dname_t *knot_rrset_rdata_rrsig_signer_name(const knot_rrset_t *rrset,
-                                                       size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return NULL;
-	}
-
-	const knot_dname_t *dname = NULL;
-	memcpy(&dname, knot_rrset_get_rdata(rrset, rr_pos) + 18,
-	       sizeof(knot_dname_t *));
-
-	return dname;
-}
-
-uint16_t knot_rrset_rdata_dnskey_flags(const knot_rrset_t *rrset, size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return knot_wire_read_u16(knot_rrset_get_rdata(rrset, rr_pos));
-}
-
-uint8_t knot_rrset_rdata_dnskey_proto(const knot_rrset_t *rrset, size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(knot_rrset_get_rdata(rrset, rr_pos) + 2);
-}
-
-uint8_t knot_rrset_rdata_dnskey_alg(const knot_rrset_t *rrset, size_t rr_pos)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(knot_rrset_get_rdata(rrset, rr_pos) + 3);
-}
-
-void knot_rrset_rdata_dnskey_key(const knot_rrset_t *rrset, size_t rr_pos,
-                                 uint8_t **key, uint16_t *key_size)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return;
-	}
-
-	*key = knot_rrset_get_rdata(rrset, rr_pos) + 4;
-	*key_size = rrset_rdata_item_size(rrset, rr_pos) - 4;
-}
-
-const knot_dname_t *knot_rrset_rdata_nsec_next(const knot_rrset_t *rrset,
-                                               size_t rr_pos)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-
-	const knot_dname_t *dname;
-	memcpy(&dname, rrset_rdata_pointer(rrset, rr_pos),
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-void knot_rrset_rdata_nsec_bitmap(const knot_rrset_t *rrset, size_t rr_pos,
-                                  uint8_t **bitmap, uint16_t *size)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return;
-	}
-
-	*bitmap = knot_rrset_get_rdata(rrset, rr_pos) + sizeof(knot_dname_t *);
-	*size = rrset_rdata_item_size(rrset, rr_pos) - sizeof(knot_dname_t *);
-}
-
-void knot_rrset_rdata_nsec3_bitmap(const knot_rrset_t *rrset, size_t rr_pos,
-                                   uint8_t **bitmap, uint16_t *size)
-{
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return;
-	}
-
-	/* Bitmap is last, skip all the items. */
-	size_t offset = 1; //hash alg.
-	offset += 1; //flags
-	offset += 2; //iterations
-	offset += 1; //salt lenght
-	offset += knot_rrset_rdata_nsec3_salt_length(rrset, rr_pos); //sal
-	uint8_t *next_hashed = NULL;
-	uint8_t next_hashed_size = 0;
-	knot_rrset_rdata_nsec3_next_hashed(rrset, rr_pos, &next_hashed,
-	                                   &next_hashed_size);
-	offset += 1; //hash length
-	offset += next_hashed_size; //hash
-	*bitmap = knot_rrset_get_rdata(rrset, rr_pos) + offset;
-	*size = rrset_rdata_item_size(rrset, rr_pos) - offset;
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint8_t knot_rrset_rdata_nsec3_algorithm(const knot_rrset_t *rrset,
-                                         size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(rrset_rdata_pointer(rrset, pos));
-}
-
-uint8_t knot_rrset_rdata_nsec3_flags(const knot_rrset_t *rrset,
-                                     size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return 0;
-	}
-
-	return *(rrset_rdata_pointer(rrset, pos) + 1);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint16_t knot_rrset_rdata_nsec3_iterations(const knot_rrset_t *rrset,
-                                           size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return 0;
+	if (!duplicated) {
+		*merged += 1; // = need to shallow free rrset2
+		// Insert RR to RRSet
+		int ret = knot_rrset_add_rdata_at_pos(rrset, insert_to,
+			                  rrset_rdata_pointer(rr, pos),
+			                  rrset_rdata_item_size(rr, pos));
+		if (ret != KNOT_EOK) {
+			dbg_rrset("rrset: add_rr: Could not "
+			          "add RDATA to RRSet. (%s)\n",
+			          knot_strerror(ret));
+			return ret;
+		}
+	} else {
+		assert(!found);
+		*deleted += 1; // = need to shallow free rr
 	}
 
-	return knot_wire_read_u16(rrset_rdata_pointer(rrset, pos) + 2);
+	return KNOT_EOK;
 }
 
-/*---------------------------------------------------------------------------*/
-
-uint8_t knot_rrset_rdata_nsec3param_flags(const knot_rrset_t *rrset)
+int knot_rrset_merge_sort(knot_rrset_t *rrset1, const knot_rrset_t *rrset2,
+                          int *merged_rrs, int *deleted_rrs)
 {
-	if (rrset == NULL) {
-		return 0;
-	}
+	int result = KNOT_EOK;
+	int merged = 0;
+	int deleted = 0;
 
-	return *(rrset_rdata_pointer(rrset, 0) + 1);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint8_t knot_rrset_rdata_nsec3param_algorithm(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
+	for (uint16_t i = 0; i < rrset2->rdata_count; ++i) {
+		result = knot_rrset_add_rr_sort_n(rrset1, rrset2, &merged,
+		                                   &deleted, i);
+		if (result != KNOT_EOK) {
+			break;
+		}
 	}
 
-	return *(rrset_rdata_pointer(rrset, 0));
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint16_t knot_rrset_rdata_nsec3param_iterations(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
+	if (merged_rrs) {
+		*merged_rrs = merged;
 	}
 
-	return knot_wire_read_u16(rrset_rdata_pointer(rrset, 0) + 2);
-}
-
-/*---------------------------------------------------------------------------*/
-
-uint8_t knot_rrset_rdata_nsec3param_salt_length(const knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return 0;
+	if (deleted_rrs) {
+		*deleted_rrs = deleted;
 	}
 
-	return *(rrset_rdata_pointer(rrset, 0) + 4);
+	return result;
 }
 
-/*---------------------------------------------------------------------------*/
-
-const uint8_t *knot_rrset_rdata_nsec3param_salt(const knot_rrset_t *rrset)
+/*!
+ * \todo Not optimal, rewrite!
+ */
+int knot_rrset_sort_rdata(knot_rrset_t *rrset)
 {
-	if (rrset == NULL) {
-		return NULL;
+	if (!rrset) {
+		return KNOT_EINVAL;
 	}
 
-	return rrset_rdata_pointer(rrset, 0) + 5;
-}
-
-/*---------------------------------------------------------------------------*/
+	// 1. create temporary rrset
+	// 2. sort-merge given rrset into temporary rrset
+	// 3. swap the contents, free the temporary
 
-
-uint8_t knot_rrset_rdata_nsec3_salt_length(const knot_rrset_t *rrset,
-                                           size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return 0;
+	knot_rrset_t *sorted = knot_rrset_new(rrset->owner, rrset->type,
+	                                      rrset->rclass, rrset->ttl);
+	if (!sorted) {
+		return KNOT_ENOMEM;
 	}
 
-	return *(rrset_rdata_pointer(rrset, pos) + 4);
-}
-
-void knot_rrset_rdata_nsec3_next_hashed(const knot_rrset_t *rrset, size_t pos,
-                                        uint8_t **name, uint8_t *name_size)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return;
+	int result = knot_rrset_merge_sort(sorted, rrset, NULL, NULL);
+	if (result != KNOT_EOK) {
+		knot_rrset_deep_free(&sorted, 1);
+		return result;
 	}
 
-	uint8_t salt_size = knot_rrset_rdata_nsec3_salt_length(rrset, pos);
-	*name_size = *(knot_rrset_get_rdata(rrset, pos) + 4 + salt_size + 1);
-	*name = knot_rrset_get_rdata(rrset, pos) + 4 + salt_size + 2;
-}
-
-/*---------------------------------------------------------------------------*/
-
-const uint8_t *knot_rrset_rdata_nsec3_salt(const knot_rrset_t *rrset,
-                                           size_t pos)
-{
-	if (rrset == NULL || pos >= rrset->rdata_count) {
-		return NULL;
-	}
+	/*!
+	 * \todo Pointers to pointers to pointers to pointers FTW!
+	 * Tell me how to rewrite this to use our awesome
+	 * knot_rrset_deep_free() API and I will buy you a beer!
+	 */
+	free(rrset->rdata);
+	free(rrset->rdata_indices);
+	*rrset = *sorted;
+	free(sorted);
 
-	return rrset_rdata_pointer(rrset, pos) + 5;
+	return KNOT_EOK;
 }
 
-knot_dname_t **knot_rrset_get_next_rr_dname(const knot_rrset_t *rrset,
-                                            knot_dname_t **prev_dname,
-                                            size_t rr_pos)
+bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr)
 {
-	if (rrset == NULL || rr_pos >= rrset->rdata_count) {
-		return NULL;
-	}
-
-	uint8_t *rdata = rrset_rdata_pointer(rrset, rr_pos);
-	if (rrset_type_multiple_dnames(rrset)) {
-		if (prev_dname == NULL) {
-			/* The very first DNAME. */
-			/* [code-review] How do you know the dname is the first
-			 * item in the RDATA?
-			 */
-			return (knot_dname_t **)rdata;
-		}
-		assert((size_t)prev_dname >= (size_t)rdata);
-		if ((size_t)prev_dname - (size_t)rdata == sizeof(knot_dname_t *)) {
-			/* No DNAMEs left to return. */
-			return NULL;
-		} else {
-			/* Return second DNAME from RR. */
-			assert((size_t)prev_dname == (size_t)rdata);
-			return (knot_dname_t **)(rdata + sizeof(knot_dname_t *));
-		}
-	} else {
-		/*
-		 * Return DNAME from normal RR, if any.
-		 * Find DNAME in blocks. No need to check remainder.
-		 */
-		if (prev_dname) {
-			/* Nothing left to return. */
-			return NULL;
-		}
-		size_t offset = 0;
-		const rdata_descriptor_t *desc =
-			get_rdata_descriptor(rrset->type);
-		for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
-			if (descriptor_item_is_dname(desc->block_types[i])) {
-				return (knot_dname_t **)(rdata + offset);
-			} else if (descriptor_item_is_fixed(desc->block_types[i])) {
-				offset += desc->block_types[i];
-			} else if (!descriptor_item_is_remainder(desc->block_types[i])) {
-				assert(rrset->type == KNOT_RRTYPE_NAPTR);
-				offset +=
-					rrset_rdata_naptr_bin_chunk_size(rrset,
-				                                         rr_pos);
-			}
-		}
-	}
+	assert(rr != NULL);
 
-	return NULL;
+	/* Is NSEC3 or non-empty RRSIG covering NSEC3. */
+	return ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
+	        || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
+	            && knot_rdata_rrsig_type_covered(rr, 0)
+	            == KNOT_RRTYPE_NSEC3));
 }
 
-knot_dname_t **knot_rrset_get_next_dname(const knot_rrset_t *rrset,
-                                         knot_dname_t **prev_dname)
-{
-	if (rrset == NULL || rrset->rdata_count == 0) {
-		return NULL;
-	}
-
-	/* 1) Find in which RR is the given dname. */
-	size_t pos = 0;
-	int ret = rrset_find_rr_pos_for_pointer(rrset, prev_dname, &pos);
-	if (ret != KNOT_EOK) {
-		return NULL;
-	}
-
-	/* 2) Try to get next dname from the RR. */
-	knot_dname_t **next =
-		knot_rrset_get_next_rr_dname(rrset, prev_dname, pos);
-
-	/* 3) If not found and there is a next RR to search in, try it. */
-	if (next == NULL && pos < rrset->rdata_count - 1) {
-		// prev_dname = NULL because in this RR we haven't searched yet
-		next = knot_rrset_get_next_rr_dname(rrset, NULL, pos + 1);
-	}
-
-	return next;
-}
+/*----------------------------------------------------------------------------*/
 
 void knot_rrset_dump(const knot_rrset_t *rrset)
 {
@@ -2199,9 +1616,8 @@ dbg_rrset_exec_detail(
 	        rrset_rdata_size_total(rrset));
 
 	for (uint16_t i = 0; i < rrset->rdata_count; i++) {
-		dbg_rrset_detail("%d=%d ", i, rrset_rdata_offset(rrset, i));
+		dbg_rrset_detail("%d=%d\n", i, rrset_rdata_offset(rrset, i));
 	}
-	dbg_rrset_detail("\n");
 
 	if (knot_rrset_rdata_rr_count(rrset) == 0) {
 		dbg_rrset_detail("NO RDATA\n");
@@ -2219,13 +1635,12 @@ uint64_t rrset_binary_size(const knot_rrset_t *rrset)
 		return 0;
 	}
 	uint64_t size = sizeof(uint64_t) + // size at the beginning
-	              1 + // owner size
 	              knot_dname_size(knot_rrset_owner(rrset)) + // owner data
 	              sizeof(uint16_t) + // type
 	              sizeof(uint16_t) + // class
 	              sizeof(uint32_t) + // ttl
 	              sizeof(uint16_t) +  //RR count
-	              sizeof(uint32_t) * rrset->rdata_count; //RR indices
+	              sizeof(uint32_t) * rrset->rdata_count; // RR indices
 	for (uint16_t i = 0; i < rrset->rdata_count; i++) {
 		/* Space to store length of one RR. */
 		size += sizeof(uint32_t);
@@ -2253,12 +1668,8 @@ int rrset_serialize(const knot_rrset_t *rrset, uint8_t *stream, size_t *size)
 	memcpy(stream + offset, rrset->rdata_indices,
 	       rrset->rdata_count * sizeof(uint32_t));
 	offset += sizeof(uint32_t) * rrset->rdata_count;
-	/* Save owner. Size first. */
-	memcpy(stream + offset, &rrset->owner->size, 1);
-	++offset;
-	memcpy(stream + offset, knot_dname_name(rrset->owner),
-	       knot_dname_size(rrset->owner));
-	offset += knot_dname_size(rrset->owner);
+	/* Save owner. */
+	offset += knot_dname_to_wire(stream + offset, rrset->owner, rrset_length - offset);
 
 	/* Save static data. */
 	memcpy(stream + offset, &rrset->type, sizeof(uint16_t));
@@ -2334,10 +1745,8 @@ int rrset_deserialize(uint8_t *stream, size_t *stream_size,
 	       rdata_count * sizeof(uint32_t));
 	offset += rdata_count * sizeof(uint32_t);
 	/* Read owner from the stream. */
-	uint8_t owner_size = *(stream + offset);
-	offset += 1;
-	knot_dname_t *owner = knot_dname_new_from_wire(stream + offset,
-	                                               owner_size, NULL);
+	unsigned owner_size = knot_dname_size(stream + offset);
+	knot_dname_t *owner = knot_dname_copy_part(stream + offset, owner_size);
 	assert(owner);
 	offset += owner_size;
 	/* Read type. */
@@ -2356,9 +1765,10 @@ int rrset_deserialize(uint8_t *stream, size_t *stream_size,
 	/* Create new RRSet. */
 	*rrset = knot_rrset_new(owner, type, rclass, ttl);
 	if (*rrset == NULL) {
+		knot_dname_free(&owner);
+		free(rdata_indices);
 		return KNOT_ENOMEM;
 	}
-	knot_dname_release(owner);
 
 	(*rrset)->rdata_indices = rdata_indices;
 	(*rrset)->rdata_count = rdata_count;
@@ -2380,7 +1790,6 @@ int rrset_deserialize(uint8_t *stream, size_t *stream_size,
 		if (ret != KNOT_EOK) {
 			free((*rrset)->rdata);
 			free(rdata_indices);
-			knot_dname_release(owner);
 			return ret;
 		}
 		/* TODO handle malformations. */
@@ -2399,66 +1808,6 @@ dbg_rrset_exec_detail(
 	return KNOT_EOK;
 }
 
-const knot_dname_t *knot_rrset_rdata_ns_name(const knot_rrset_t *rrset,
-                                             size_t rdata_pos)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-
-	const knot_dname_t *dname;
-	memcpy(&dname, rrset_rdata_pointer(rrset, rdata_pos),
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_mx_name(const knot_rrset_t *rrset,
-                                             size_t rdata_pos)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-
-	knot_dname_t *dname;
-	memcpy(&dname, rrset_rdata_pointer(rrset, rdata_pos) + 2,
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_srv_name(const knot_rrset_t *rrset,
-                                              size_t rdata_pos)
-{
-	if (rrset == NULL) {
-		return NULL;
-	}
-
-	knot_dname_t *dname;
-	memcpy(&dname, rrset_rdata_pointer(rrset, rdata_pos) + 6,
-	       sizeof(knot_dname_t *));
-	return dname;
-}
-
-const knot_dname_t *knot_rrset_rdata_name(const knot_rrset_t *rrset,
-                                          size_t rdata_pos)
-{
-	if (rrset == NULL || rrset->rdata_count <= rdata_pos) {
-		return NULL;
-	}
-
-	switch (rrset->type) {
-		case KNOT_RRTYPE_NS:
-			return knot_rrset_rdata_ns_name(rrset, rdata_pos);
-		case KNOT_RRTYPE_MX:
-			return knot_rrset_rdata_mx_name(rrset, rdata_pos);
-		case KNOT_RRTYPE_SRV:
-			return knot_rrset_rdata_srv_name(rrset, rdata_pos);
-		case KNOT_RRTYPE_CNAME:
-			return knot_rrset_rdata_cname_name(rrset);
-	}
-
-	return NULL;
-}
-
 int knot_rrset_find_rr_pos(const knot_rrset_t *rr_search_in,
                            const knot_rrset_t *rr_reference, size_t pos,
                            size_t *pos_out)
@@ -2475,10 +1824,9 @@ int knot_rrset_find_rr_pos(const knot_rrset_t *rr_search_in,
 	return found ? KNOT_EOK : KNOT_ENOENT;
 }
 
-int knot_rrset_remove_rr(knot_rrset_t *rrset,
-                         const knot_rrset_t *rr_from, size_t rdata_pos)
+static int knot_rrset_remove_rr(knot_rrset_t *rrset,
+                                const knot_rrset_t *rr_from, size_t rdata_pos)
 {
-	/* [code-review] Missing parameter checks. */
 	/*
 	 * Position in first and second rrset can differ, we have
 	 * to search for position first.
@@ -2506,7 +1854,7 @@ int knot_rrset_remove_rr(knot_rrset_t *rrset,
 	return KNOT_EOK;
 }
 
-int knot_rrset_rdata_reset(knot_rrset_t *rrset)
+static int knot_rrset_rdata_reset(knot_rrset_t *rrset)
 {
 	if (rrset == NULL) {
 		return KNOT_EINVAL;
@@ -2519,46 +1867,6 @@ int knot_rrset_rdata_reset(knot_rrset_t *rrset)
 	return KNOT_EOK;
 }
 
-int rrset_rr_dnames_apply(knot_rrset_t *rrset, size_t rdata_pos,
-                          int (*func)(knot_dname_t **, void *), void *data)
-{
-	if (rrset == NULL || rdata_pos >= rrset->rdata_count || func == NULL) {
-		return KNOT_EINVAL;
-	}
-
-
-	knot_dname_t **dname = NULL;
-	while ((dname = knot_rrset_get_next_rr_dname(rrset, dname,
-	                                             rdata_pos))) {
-		assert(dname && *dname);
-		int ret = func(dname, data);
-		if (ret != KNOT_EOK) {
-			dbg_rrset("rr: rr_dnames_apply: Function could not be"
-			          "applied (%s).\n", knot_strerror(ret));
-			return ret;
-		}
-	}
-
-	return KNOT_EOK;
-}
-
-int rrset_dnames_apply(knot_rrset_t *rrset, int (*func)(knot_dname_t **, void *),
-                       void *data)
-{
-	if (rrset == NULL || rrset->rdata_count == 0 || func == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	for (uint16_t i = 0; i < rrset->rdata_count; ++i) {
-		int ret = rrset_rr_dnames_apply(rrset, i, func, data);
-		if (ret != KNOT_EOK) {
-			return ret;
-		}
-	}
-
-	return KNOT_EOK;
-}
-
 int knot_rrset_add_rr_from_rrset(knot_rrset_t *dest, const knot_rrset_t *source,
                                  size_t rdata_pos)
 {
@@ -2579,15 +1887,6 @@ int knot_rrset_add_rr_from_rrset(knot_rrset_t *dest, const knot_rrset_t *source,
 	/* Copy actual data. */
 	memcpy(rdata, rrset_rdata_pointer(source, rdata_pos), item_size);
 
-	/* Retain DNAMEs inside RDATA. */
-	int ret = rrset_rr_dnames_apply((knot_rrset_t *)source, rdata_pos,
-	                                rrset_retain_dnames_in_rr, NULL);
-	if (ret != KNOT_EOK) {
-		dbg_rrset("rr: add_rr_from_rrset: Could not retain DNAMEs"
-		          " in RR (%s).\n", knot_strerror(ret));
-		return ret;
-	}
-
 	return KNOT_EOK;
 }
 
@@ -2595,7 +1894,9 @@ int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from,
                                      const knot_rrset_t *what,
                                      knot_rrset_t **rr_deleted, int ddns_check)
 {
-	/* [code-review] Missing parameter checks. */
+	if (from == NULL || what == NULL || rr_deleted == NULL) {
+		return KNOT_EINVAL;
+	}
 
 	knot_rrset_t *return_rr = NULL;
 	int ret = knot_rrset_shallow_copy(what, &return_rr);
@@ -2637,7 +1938,7 @@ int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from,
 			/* RR was removed, can be added to 'return' RRSet. */
 			ret = knot_rrset_add_rr_from_rrset(return_rr, what, i);
 			if (ret != KNOT_EOK) {
-				knot_rrset_deep_free(&return_rr, 0, 0);
+				knot_rrset_deep_free(&return_rr, 0);
 				dbg_xfrin("xfr: Could not add RR (%s).\n",
 				          knot_strerror(ret));
 				return ret;
@@ -2650,7 +1951,7 @@ int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from,
 			dbg_rrset("rrset: remove_using_rrset: "
 			          "RRSet removal failed (%s).\n",
 			          knot_strerror(ret));
-			knot_rrset_deep_free(&return_rr, 0, 0);
+			knot_rrset_deep_free(&return_rr, 0);
 			return ret;
 		}
 	}
@@ -2664,20 +1965,20 @@ int knot_rrset_remove_rr_using_rrset_del(knot_rrset_t *from,
 {
 	knot_rrset_t *rr_removed = NULL;
 	int ret = knot_rrset_remove_rr_using_rrset(from, what, &rr_removed, 0);
-	knot_rrset_deep_free(&rr_removed, 1, 1);
+	knot_rrset_deep_free(&rr_removed, 1);
 	return ret;
-//	for (uint16_t i = 0; i < what->rdata_count; ++i) {
-//		int ret = knot_rrset_remove_rr(from, what, i);
-//		if (ret != KNOT_ENOENT || ret != KNOT_EOK) {
-//			/* NOENT is OK, but other errors are not. */
-//			dbg_rrset("rrset: remove_rr_using_rrset: "
-//			          "RRSet removal failed (%s).\n",
-//			          knot_strerror(ret));
-//			return ret;
-//		}
-//	}
-
-//	return KNOT_EOK;
+	for (uint16_t i = 0; i < what->rdata_count; ++i) {
+		int ret = knot_rrset_remove_rr(from, what, i);
+		if (ret != KNOT_ENOENT || ret != KNOT_EOK) {
+			/* NOENT is OK, but other errors are not. */
+			dbg_rrset("rrset: remove_rr_using_rrset: "
+			          "RRSet removal failed (%s).\n",
+			          knot_strerror(ret));
+			return ret;
+		}
+	}
+
+	return KNOT_EOK;
 }
 
 void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass)
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
index 05deb3726b12b7a1488257a6cc10fe4fb9aade1a..e77d51e227e040543f6cab26c3c0fcb7ed7a3fe5 100644
--- a/src/libknot/rrset.h
+++ b/src/libknot/rrset.h
@@ -9,7 +9,7 @@
  * \addtogroup libknot
  * @{
  */
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -51,16 +51,13 @@ struct knot_rrset {
 	uint16_t type; /*!< TYPE of the RRset. */
 	uint16_t rclass; /*!< CLASS of the RRSet. */
 	uint32_t ttl; /*!< TTL of the RRSet. */
-
-	/* [code-review] It would be fine to better describe the format of this
-	 * array and the meaning of the indices. Maybe even draw some simple
-	 * image :-)
+	/*! \brief RDATA array (for all RRs). DNAMEs stored as full,
+	 *         uncompressed wire. Binary data stored in wireformat order.
 	 */
-	uint8_t *rdata; /*!< RDATA array (All RRs). */
+	uint8_t *rdata; /*!< RDATA array (All RRs). DNAMEs stored as full wire. */
 	/*! \brief Beginnings of RRs - first one does not contain 0, last
 	 *         last one holds total length of all RRs together
 	 */
-	/* [code-review] Does this have to be 32b integer? Isn't 16b enough? */
 	uint32_t *rdata_indices; /*!< Indices to beginnings of RRs (without 0)*/
 	uint16_t rdata_count; /*!< Count of RRs in this RRSet. */
 	struct knot_rrset *rrsigs; /*!< Set of RRSIGs covering this RRSet. */
@@ -102,6 +99,17 @@ uint32_t rrset_rdata_size_total(const knot_rrset_t *rrset);
 knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
                                  uint16_t rclass, uint32_t ttl);
 
+/*!
+ * \brief Creates a new RRSet according to given template RRSet.
+ *
+ * OWNER, TYPE, CLASS, and TTL values from template RRSet are used.
+ *
+ * \param tmp  RRSet template.
+ *
+ * \return New RRSet, NULL if an error occured.
+ */
+knot_rrset_t *knot_rrset_new_from(const knot_rrset_t *tpl);
+
 /*!
  * \brief Adds the given RDATA to the RRSet.
  *
@@ -137,7 +145,7 @@ uint8_t* knot_rrset_create_rdata(knot_rrset_t *rrset, const uint16_t size);
  * \retval 0 on error.
  * \return Item size on success.
  */
-/* [code-review] Misleading name, maybe remove the word 'item'. */
+/* [code-review] Misleading name, maybe remove the word 'item'. And add knot. */
 uint16_t rrset_rdata_item_size(const knot_rrset_t *rrset,
                                size_t pos);
 
@@ -194,7 +202,7 @@ knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset);
  * \param rrset Specified RRSet.
  * \param owner New owner dname.
  */
-void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner);
+int knot_rrset_set_owner(knot_rrset_t *rrset, const knot_dname_t *owner);
 
 /*!
  * \brief Sets rrset TTL to given TTL.
@@ -282,8 +290,9 @@ int knot_rrset_equal(const knot_rrset_t *r1,
                      const knot_rrset_t *r2,
                      knot_rrset_compare_type_t cmp);
 
-int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
-                         int copy_rdata_dnames);
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to);
+
+int knot_rrset_deep_copy_no_sig(const knot_rrset_t *from, knot_rrset_t **to);
 
 int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to);
 
@@ -321,15 +330,20 @@ void knot_rrset_free(knot_rrset_t **rrset);
  *                          present in RDATA. Set to 0 otherwise. (See
  *                          knot_rdata_deep_free().)
  */
-void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
-                          int free_rdata_dnames);
+void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner);
 
-void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner,
-                                 int free_rdata_dnames);
+void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner);
 
 int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
                        size_t max_size, uint16_t *rr_count, void *comp_data);
 
+/*!
+ * \brief Write one RR from RRSet.
+ */
+int knot_rrset_to_wire_one(const knot_rrset_t *rrset, uint16_t rr_number,
+                           uint8_t *wire, size_t max_size, size_t *outsize,
+                           void *comp_data);
+
 /*!
  * \brief Merges two RRSets.
  *
@@ -348,19 +362,18 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
  *         Owner, Type, Class or TTL does not match.
  */
 int knot_rrset_merge(knot_rrset_t *rrset1, const knot_rrset_t *rrset2);
-
+/*! \brief Merges without with duplicate check, with sort. */
+int knot_rrset_merge_sort(knot_rrset_t *rrset1, const knot_rrset_t *rrset2,
+                          int *merged, int *deleted_rrs);
 
 /*!
- * \brief Merges two RRSets, but will only merge unique items.
- *
- * \param r1 Pointer to RRSet to be merged into.
- * \param r2 Poitner to RRSet to be merged.
+ * \brief Sort RDATA in RRSet to be in caonical order.
+ * \todo Not optimal, rewrite!
  *
- * \retval KNOT_EOK
- * \retval KNOT_EINVAL if the RRSets could not be merged, because their
- *         Owner, Type, Class or TTL does not match.
+ * \param rrset  RRSet to be sorted.
+ * \return Error code, KNOT_EOK when successful.
  */
-int knot_rrset_merge_no_dupl(knot_rrset_t *rrset1, const knot_rrset_t *rrset2, int *merged, int *deleted_rrs);
+int knot_rrset_sort_rdata(knot_rrset_t *rrset);
 
 /*!
  * \brief Return true if the RRSet is an NSEC3 related type.
@@ -369,136 +382,6 @@ int knot_rrset_merge_no_dupl(knot_rrset_t *rrset1, const knot_rrset_t *rrset2, i
  */
 bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr);
 
-
-//TODO test
-const knot_dname_t *knot_rrset_rdata_cname_name(const knot_rrset_t *rrset);
-//TODO test
-const knot_dname_t *knot_rrset_rdata_dname_target(const knot_rrset_t *rrset);
-//TODO test
-void knot_rrset_rdata_set_cname_name(knot_rrset_t *rrset,
-                                     const knot_dname_t *name);
-//TODO test
-void knot_rrset_rdata_set_dname_target(knot_rrset_t *rrset,
-                                       const knot_dname_t *target);
-//TODO test
-const knot_dname_t *knot_rrset_rdata_soa_primary_ns(const knot_rrset_t *rrset);
-//TODO test
-const knot_dname_t *knot_rrset_rdata_soa_mailbox(const knot_rrset_t *rrset);
-uint32_t knot_rrset_rdata_soa_serial(const knot_rrset_t *rrset);
-//TODO test
-void knot_rrset_rdata_soa_serial_set(knot_rrset_t *rrset, uint32_t serial);
-//TODO test
-uint32_t knot_rrset_rdata_soa_refresh(const knot_rrset_t *rrset);
-//TODO test
-uint32_t knot_rrset_rdata_soa_retry(const knot_rrset_t *rrset);
-//TODO test
-uint32_t knot_rrset_rdata_soa_expire(const knot_rrset_t *rrset);
-//TODO test
-uint32_t knot_rrset_rdata_soa_minimum(const knot_rrset_t *rrset);
-//TODO test
-uint16_t knot_rrset_rdata_rrsig_type_covered(const knot_rrset_t *rrset);
-/* TODO not all of these need to have rr_pos as a parameter. */
-uint8_t knot_rrset_rdata_rrsig_algorithm(const knot_rrset_t *rrset,
-                                         size_t rr_pos);
-uint8_t knot_rrset_rdata_rrsig_labels(const knot_rrset_t *rrset, size_t rr_pos);
-uint32_t knot_rrset_rdata_rrsig_original_ttl(const knot_rrset_t *rrset,
-                                             size_t rr_pos);
-uint32_t knot_rrset_rdata_rrsig_sig_expiration(const knot_rrset_t *rrset,
-                                               size_t rr_pos);
-uint32_t knot_rrset_rdata_rrsig_sig_inception(const knot_rrset_t *rrset,
-                                              size_t rr_pos);
-uint16_t knot_rrset_rdata_rrsig_key_tag(const knot_rrset_t *rrset,
-                                        size_t rr_pos);
-const knot_dname_t *knot_rrset_rdata_rrsig_signer_name(const knot_rrset_t *rrset,
-                                                       size_t rr_pos);
-
-uint16_t knot_rrset_rdata_dnskey_flags(const knot_rrset_t *rrset, size_t rr_pos);
-uint8_t knot_rrset_rdata_dnskey_proto(const knot_rrset_t *rrset, size_t rr_pos);
-uint8_t knot_rrset_rdata_dnskey_alg(const knot_rrset_t *rrset, size_t rr_pos);
-void knot_rrset_rdata_dnskey_key(const knot_rrset_t *rrset, size_t rr_pos,
-                                 uint8_t **key, uint16_t *key_size);
-const knot_dname_t *knot_rrset_rdata_nsec_next(const knot_rrset_t *rrset,
-                                               size_t rr_pos);
-void knot_rrset_rdata_nsec_bitmap(const knot_rrset_t *rrset, size_t rr_pos,
-                                  uint8_t **bitmap, uint16_t *size);
-
-void knot_rrset_rdata_nsec3_bitmap(const knot_rrset_t *rrset, size_t rr_pos,
-                                   uint8_t **bitmap, uint16_t *size);
-//TODO test
-uint8_t knot_rrset_rdata_nsec3_algorithm(const knot_rrset_t *rrset,
-                                         size_t pos);
-uint8_t knot_rrset_rdata_nsec3_flags(const knot_rrset_t *rrset,
-                                     size_t pos);
-//TODO test
-uint16_t knot_rrset_rdata_nsec3_iterations(const knot_rrset_t *rrset,
-                                           size_t pos);
-//TODO test
-uint8_t knot_rrset_rdata_nsec3_salt_length(const knot_rrset_t *rrset,
-                                           size_t pos);
-// TODO same as salt, size and data
-void knot_rrset_rdata_nsec3_next_hashed(const knot_rrset_t *rrset, size_t pos,
-                                        uint8_t **name, uint8_t *name_size);
-//TODO test
-const uint8_t *knot_rrset_rdata_nsec3_salt(const knot_rrset_t *rrset,
-                                           size_t pos);
-//TODO test
-uint8_t knot_rrset_rdata_nsec3param_algorithm(const knot_rrset_t *rrset);
-//TODO test
-uint8_t knot_rrset_rdata_nsec3param_flags(const knot_rrset_t *rrset);
-//TODO test
-uint16_t knot_rrset_rdata_nsec3param_iterations(const knot_rrset_t *rrset);
-//TODO test
-uint8_t knot_rrset_rdata_nsec3param_salt_length(const knot_rrset_t *rrset);
-//TODO test
-const uint8_t *knot_rrset_rdata_nsec3param_salt(const knot_rrset_t *rrset);
-
-const knot_dname_t *knot_rrset_rdata_rp_first_dname(const knot_rrset_t *rrset,
-                                                    size_t pos);
-const knot_dname_t *knot_rrset_rdata_rp_second_dname(const knot_rrset_t *rrset,
-                                                     size_t pos);
-const knot_dname_t *knot_rrset_rdata_minfo_first_dname(const knot_rrset_t *rrset,
-                                                       size_t pos);
-const knot_dname_t *knot_rrset_rdata_minfo_second_dname(const knot_rrset_t *rrset,
-                                                        size_t pos);
-
-/*!
- * \brief Find next dname in rrset relative to prev.
- *
- * \param rrset Inspected rrset.
- * \param prev_dname Pointer to previous dname.
- * \return next dname or NULL.
- */
-/* [code-review] Emphasize that the 'prev' pointer must point into the RDATA
- * array of the given RRSet.
- */
-knot_dname_t **knot_rrset_get_next_dname(const knot_rrset_t *rrset,
-                                                 knot_dname_t **prev);
-
-/*!
- * \brief Find next dname in RR relative to previous one.
- *
- * \param rrset Inspected rrset.
- * \param prev_dname Previous dname. This must be a pointer to the rrset's
- *                   RDATA array.
- * \param rr_pos Position of RR.
- * \return Next dname or NULL if there is no other dname in the RR.
- */
-knot_dname_t **knot_rrset_get_next_rr_dname(const knot_rrset_t *rrset,
-                                            knot_dname_t **prev_dname,
-                                            size_t rr_pos);
-
-const knot_dname_t *knot_rrset_rdata_ns_name(const knot_rrset_t *rrset,
-                                             size_t rdata_pos);
-
-const knot_dname_t *knot_rrset_rdata_mx_name(const knot_rrset_t *rrset,
-                                             size_t rdata_pos);
-
-const knot_dname_t *knot_rrset_rdata_srv_name(const knot_rrset_t *rrset,
-                                              size_t rdata_pos);
-
-const knot_dname_t *knot_rrset_rdata_name(const knot_rrset_t *rrset,
-                                          size_t rdata_pos);
-
 void knot_rrset_dump(const knot_rrset_t *rrset);
 
 //TODO test
@@ -513,36 +396,34 @@ int rrset_serialize_alloc(const knot_rrset_t *rrset, uint8_t **stream,
 int rrset_deserialize(uint8_t *stream, size_t *stream_size,
                       knot_rrset_t **rrset);
 
-int knot_rrset_remove_rr(knot_rrset_t *dest,
-                         const knot_rrset_t *source, size_t rdata_pos);
-
-int knot_rrset_rdata_reset(knot_rrset_t *rrset);
-
+/* \brief Adds RR on 'pos' position from 'source' to 'dest' */
 int knot_rrset_add_rr_from_rrset(knot_rrset_t *dest, const knot_rrset_t *source,
                                  size_t rdata_pos);
-
+/* \brief Removes RRs contained in 'what' RRSet from 'from' RRSet.
+ *        Deleted RRs are returned in 'rr_deleted' */
 int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from,
                                      const knot_rrset_t *what,
                                      knot_rrset_t **rr_deleted, int ddns_check);
-
+/* \brief Removes RRs contained in 'what' RRSet from 'from' RRSet. */
 int knot_rrset_remove_rr_using_rrset_del(knot_rrset_t *from,
                                          const knot_rrset_t *what);
-
+/* \brief Finds RR at 'pos' position in 'rr_reference' RRSet in 
+         'rr_search_in' RRSet. Position returned in 'pos_out'. */
 int knot_rrset_find_rr_pos(const knot_rrset_t *rr_search_in,
                            const knot_rrset_t *rr_reference, size_t pos,
                            size_t *pos_out);
-
-int rrset_rr_dnames_apply(knot_rrset_t *rrset, size_t rdata_pos,
-                          int (*func)(knot_dname_t **, void *), void *data);
-
-int rrset_dnames_apply(knot_rrset_t *rrset, int (*func)(knot_dname_t **, void *),
-                       void *data);
-
+/* \brief Creates one RR from wire, stores it into 'rrset'. */
 int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset,
                                    const uint8_t *wire, size_t *pos,
                                    size_t total_size, size_t rdlength);
 
 int knot_rrset_ds_check(const knot_rrset_t *rrset);
+
+uint8_t *rrset_rdata_pointer(const knot_rrset_t *rrset, size_t pos);
+
+int rrset_rdata_compare_one(const knot_rrset_t *rrset1,
+                            const knot_rrset_t *rrset2,
+                            size_t pos1, size_t pos2);
 #endif /* _KNOT_RRSET_H_ */
 
 /*! @} */
diff --git a/src/libknot/sign/bnutils.c b/src/libknot/sign/bnutils.c
deleted file mode 100644
index c328f4436eb161a4e530b8fc2b218bfa200106d5..0000000000000000000000000000000000000000
--- a/src/libknot/sign/bnutils.c
+++ /dev/null
@@ -1,44 +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 <stddef.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "sign/bnutils.h"
-#include "common/base64.h"
-
-/*!
- * \brief Convert Base64 encoded number into OpenSSL BIGNUM format.
- */
-BIGNUM *knot_b64_to_bignum(const char *input)
-{
-	size_t size = strlen(input);
-	uint8_t *decoded;
-	int32_t decoded_size;
-	BIGNUM *result;
-
-	decoded_size = base64_decode_alloc((uint8_t *)input, size, &decoded);
-	if (decoded_size < 0) {
-		return NULL;
-	}
-
-	result = BN_bin2bn((unsigned char *)decoded, (int)decoded_size, NULL);
-	free(decoded);
-
-	return result;
-}
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
index a009d3e9f8401b356817e36ee5e6522193f75c46..482ebd5489db2b7e1f70e694109a44a2fbcea4bf 100644
--- a/src/libknot/tsig-op.c
+++ b/src/libknot/tsig-op.c
@@ -29,7 +29,7 @@
 #include "util/wire.h"
 #include "util/debug.h"
 #include "consts.h"
-#include "sign/key.h"
+#include "libknot/dnssec/key.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
@@ -72,7 +72,7 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
 		return KNOT_EMALF;
 	}
 
-	if (knot_dname_compare(tsig_name, tsig_key->name) != 0) {
+	if (knot_dname_cmp(tsig_name, tsig_key->name) != 0) {
 		/*!< \todo which error. */
 		dbg_tsig("TSIG: unknown key: %s\n", name);
 		free(name);
@@ -204,11 +204,7 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
 
 	int offset = 0;
 
-	memcpy(wire + offset, knot_dname_name(tsig_owner),
-	       sizeof(uint8_t) * knot_dname_size(tsig_owner));
-	dbg_tsig_verb("TSIG: write variables: written owner (tsig alg): \n");
-	dbg_tsig_hex_verb((char *)(wire + offset), knot_dname_size(tsig_owner));
-	offset += knot_dname_size(tsig_owner);
+	offset += knot_dname_to_wire(wire + offset, tsig_owner, KNOT_DNAME_MAXLEN);
 
 	/*!< \todo which order? */
 
@@ -233,26 +229,15 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
 		return KNOT_EINVAL;
 	}
 
-	/* The algorithm name must be in canonical form, i.e. in lowercase. */
-	if (knot_dname_to_lower_copy(alg_name, (char *)(wire + offset),
-	        knot_dname_size(alg_name)) != KNOT_EOK)
-	{
+	/* Te algorithm name must be in canonical form, i.e. in lowercase. */
+	uint8_t *alg_name_wire = wire + offset;
+	offset += knot_dname_to_wire(alg_name_wire, alg_name, KNOT_DNAME_MAXLEN);
+	if (knot_dname_to_lower(alg_name_wire) != KNOT_EOK) {
 		dbg_tsig("TSIG: write variables: cannot convert algorithm "
 		         "to lowercase.\n");
 		return KNOT_EINVAL;
 	}
 
-#if defined(KNOT_TSIG_DEBUG) && defined(DEBUG_ENABLE_VERBOSE)
-//	char *_algstr = knot_dname_to_str(alg_name);
-	dbg_tsig_verb("TSIG: write variables: written alg name: %s\n",
-	              wire + offset);
-//	free(_algstr);
-#endif
-
-//	memcpy(wire + offset, knot_dname_name(alg_name),
-//	       sizeof(uint8_t) * knot_dname_size(alg_name));
-	offset += knot_dname_size(alg_name);
-
 	/* Following data are written in network order. */
 	/* Time signed. */
 	knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr));
@@ -465,7 +450,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
 		return KNOT_EINVAL;
 	}
 
-	knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name);
+	knot_dname_t *key_name_copy = knot_dname_copy(key->name);
 	if (!key_name_copy) {
 		dbg_tsig("TSIG: key_name_copy = NULL\n");
 		return KNOT_ENOMEM;
@@ -474,19 +459,18 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
 	knot_rrset_t *tmp_tsig =
 		knot_rrset_new(key_name_copy,
 			       KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
-	/* Should be retained by rrsig or freed, release. */
-	knot_dname_release(key_name_copy);
 	if (!tmp_tsig) {
 		dbg_tsig("TSIG: tmp_tsig = NULL\n");
+		knot_dname_free(&key_name_copy);
 		return KNOT_ENOMEM;
 	}
 
 	/* Create rdata for TSIG RR. */
-	tsig_create_rdata(tmp_tsig, knot_tsig_digest_length(key->algorithm),
-	                  (tsig_rcode == KNOT_RCODE_BADTIME)
-	                    ? tsig_rcode
-	                    : 0);
-	tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+	uint16_t rdata_rcode = 0;
+	if (tsig_rcode == KNOT_RCODE_BADTIME)
+		rdata_rcode = tsig_rcode;
+	tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm),
+	                  knot_tsig_digest_length(key->algorithm), rdata_rcode);
 
 	/* Distinguish BADTIME response. */
 	if (tsig_rcode == KNOT_RCODE_BADTIME) {
@@ -529,7 +513,7 @@ dbg_rrset_exec_detail(
 	if (ret != KNOT_EOK) {
 		dbg_tsig("TSIG: could not create wire or sign wire: %s\n",
 		         knot_strerror(ret));
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		return ret;
 	}
 
@@ -547,11 +531,11 @@ dbg_rrset_exec_detail(
 	if (ret != KNOT_EOK) {
 		dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
 		*digest_len = 0;
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		return ret;
 	}
 
-	knot_rrset_deep_free(&tmp_tsig, 1, 1);
+	knot_rrset_deep_free(&tmp_tsig, 1);
 
 	dbg_tsig("TSIG: written TSIG RR (wire len %zu)\n", tsig_wire_len);
 	*msg_len += tsig_wire_len;
@@ -580,15 +564,17 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 	size_t digest_tmp_len = 0;
 
 	/* Create tmp TSIG. */
-	knot_rrset_t *tmp_tsig =
-		knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+	knot_dname_t *owner_copy = knot_dname_copy(key->name);
+	knot_rrset_t *tmp_tsig = knot_rrset_new(owner_copy,
+	                                        KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
 	if (!tmp_tsig) {
+		knot_dname_free(&owner_copy);
 		return KNOT_ENOMEM;
 	}
 
 	/* Create rdata for TSIG RR. */
-	tsig_create_rdata(tmp_tsig, knot_tsig_digest_length(key->algorithm), 0);
-	tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+	tsig_create_rdata(tmp_tsig, tsig_alg_to_dname(key->algorithm),
+	                  knot_tsig_digest_length(key->algorithm), 0);
 	tsig_rdata_store_current_time(tmp_tsig);
 	tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
 
@@ -598,7 +584,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 	uint8_t *wire = malloc(wire_len);
 	if (!wire) {
 		ERR_ALLOC_FAILED;
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		return KNOT_ENOMEM;
 	}
 	memset(wire, 0, wire_len);
@@ -627,13 +613,13 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 	/* No matter how the function did, this data is no longer needed. */
 	free(wire);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		*digest_len = 0;
 		return ret;
 	}
 
 	if (digest_tmp_len > *digest_len) {
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		*digest_len = 0;
 		return KNOT_ESPACE;
 	}
@@ -656,18 +642,18 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 	                         &tsig_wire_size, msg_max_len - *msg_len,
 	                         &rr_count, NULL);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		*digest_len = 0;
 		return ret;
 	}
 
 	/* This should not happen, at least one rr has to be converted. */
 	if (rr_count == 0) {
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		return KNOT_EINVAL;
 	}
 
-	knot_rrset_deep_free(&tmp_tsig, 1, 1);
+	knot_rrset_deep_free(&tmp_tsig, 1);
 
 	*msg_len += tsig_wire_size;
 	uint16_t arcount = knot_wire_get_arcount(msg);
@@ -849,7 +835,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 
 	/*! \todo What key to use, when we do not sign? Does this even work? */
 	knot_dname_t *key_name =
-			knot_dname_deep_copy(knot_rrset_owner(tsig_rr));
+			knot_dname_copy(knot_rrset_owner(tsig_rr));
 	if (key_name == NULL) {
 		dbg_tsig("TSIG: failed to copy owner\n");
 		return KNOT_ERROR;
@@ -863,39 +849,14 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 		return KNOT_ENOMEM;
 	}
 
-	/* Already referenced in tmp_tsig, release. */
-	knot_dname_release(key_name);
-
-	knot_dname_t *alg_name =
-			knot_dname_deep_copy(tsig_rdata_alg_name(tsig_rr));
-	if (alg_name == NULL) {
-		dbg_tsig("TSIG: failed to copy alg name\n");
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
-		return KNOT_ERROR;
-	}
-
-
-	/* Create rdata for TSIG RR. */
-	knot_tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
-	if (alg == KNOT_TSIG_ALG_NULL) {
-		dbg_tsig("TSIG: refusing to use NULL algorithm\n");
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
-		knot_dname_free(&alg_name);
-		return KNOT_ERROR;
-	}
-
 	assert(tsig_rcode != KNOT_RCODE_BADTIME);
-	tsig_create_rdata(tmp_tsig, 0, tsig_rcode); /* No digest. */
-
-	tsig_rdata_set_alg_name(tmp_tsig, alg_name);
+	tsig_create_rdata(tmp_tsig, tsig_rdata_alg_name(tsig_rr), 0, tsig_rcode);
 	tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(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() */
 
 	/* Set original ID */
 	tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
@@ -914,12 +875,12 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
 	                         &rr_count, NULL);
 	if (ret != KNOT_EOK) {
 		dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
-		knot_rrset_deep_free(&tmp_tsig, 1, 1);
+		knot_rrset_deep_free(&tmp_tsig, 1);
 		return ret;
 	}
 
 	/* key_name already referenced in RRSet, no need to free separately. */
-	knot_rrset_deep_free(&tmp_tsig, 1, 1);
+	knot_rrset_deep_free(&tmp_tsig, 1);
 
 	*msg_len += tsig_wire_len;
 
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
index daa096de5b3a6815af5174e57c3be43660341ba4..1cbdcacf0220f1102e0d8aa51c1a419263fe6205 100644
--- a/src/libknot/tsig-op.h
+++ b/src/libknot/tsig-op.h
@@ -31,7 +31,7 @@
 
 #include "tsig.h"
 #include "rrset.h"
-#include "sign/key.h"
+#include "libknot/dnssec/key.h"
 
 /*!
  * \brief Generate TSIG signature of a message.
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
index fd5970985951738b31c06c9e1cc3ee183b53177c..4d3d9b9ea68fe65419ab788c8efcd991d2919692 100644
--- a/src/libknot/tsig.c
+++ b/src/libknot/tsig.c
@@ -43,11 +43,10 @@ typedef enum tsig_off_t {
 	TSIG_OTHER_O
 } tsig_off_t;
 
-/* Helpers for r offset calculation. */
-#define TSIG_NAMELEN (sizeof(knot_dname_t*))
+/* Helpers for RDATA offset calculation. */
 #define TSIG_OTHER_MAXLEN (3 * sizeof(uint16_t))
-#define TSIG_OFF_MACLEN (TSIG_NAMELEN + 4 * sizeof(uint16_t))
-#define TSIG_FIXED_RDLEN (TSIG_NAMELEN + 11 * sizeof(uint16_t))
+#define TSIG_OFF_MACLEN (4 * sizeof(uint16_t))
+#define TSIG_FIXED_RDLEN (11 * sizeof(uint16_t))
 
 /*!
  * \brief Seek offset of a TSIG RR field.
@@ -64,12 +63,13 @@ static uint8_t* tsig_rdata_seek(const knot_rrset_t *rr, tsig_off_t id, size_t nb
 		return NULL;
 	}
 
-	/* Check if fixed part is readable. */
+	/* TSIG RR names should be already sanitized on parse. */
+	int alg_len = knot_dname_size(rd);
 	uint16_t lim = rrset_rdata_item_size(rr, 0);
-	if (lim < TSIG_NAMELEN + 5 * sizeof(uint16_t)) {
+	if (lim < alg_len + 5 * sizeof(uint16_t)) {
 		dbg_tsig("TSIG: rdata: not enough items "
 		         "(has %"PRIu16", min %zu).\n",
-		         lim, TSIG_NAMELEN + 5 * sizeof(uint16_t));
+		         lim, alg_len + 5 * sizeof(uint16_t));
 		return NULL;
 	}
 
@@ -77,25 +77,25 @@ static uint8_t* tsig_rdata_seek(const knot_rrset_t *rr, tsig_off_t id, size_t nb
 	uint8_t *bp = rd;
 	switch(id) {
 	case TSIG_ALGNAME_O: break;
-	case TSIG_TSIGNED_O: rd += TSIG_NAMELEN; break;
-	case TSIG_FUDGE_O: rd += TSIG_NAMELEN + 3 * sizeof(uint16_t); break;
-	case TSIG_MACLEN_O: rd += TSIG_NAMELEN + 4 * sizeof(uint16_t); break;
-	case TSIG_MAC_O: rd += TSIG_NAMELEN + 5 * sizeof(uint16_t); break;
+	case TSIG_TSIGNED_O: rd += alg_len; break;
+	case TSIG_FUDGE_O: rd += alg_len + 3 * sizeof(uint16_t); break;
+	case TSIG_MACLEN_O: rd += alg_len + 4 * sizeof(uint16_t); break;
+	case TSIG_MAC_O: rd += alg_len + 5 * sizeof(uint16_t); break;
 	case TSIG_ORIGID_O:
-		rd += TSIG_NAMELEN + 4 * sizeof(uint16_t);
+		rd += alg_len + 4 * sizeof(uint16_t);
 		rd += knot_wire_read_u16(rd) + sizeof(uint16_t);
 		break;
 
 	case TSIG_ERROR_O:
-		rd += TSIG_NAMELEN + 4 * sizeof(uint16_t);
+		rd += alg_len + 4 * sizeof(uint16_t);
 		rd += knot_wire_read_u16(rd) + 2 * sizeof(uint16_t);
 		break;
 	case TSIG_OLEN_O:
-		rd += TSIG_NAMELEN + 4 * sizeof(uint16_t);
+		rd += alg_len + 4 * sizeof(uint16_t);
 		rd += knot_wire_read_u16(rd) + 3 * sizeof(uint16_t);
 		break;
 	case TSIG_OTHER_O:
-		rd += TSIG_NAMELEN + 4 * sizeof(uint16_t);
+		rd += alg_len + 4 * sizeof(uint16_t);
 		rd += knot_wire_read_u16(rd) + 4 * sizeof(uint16_t);
 		break;
 	}
@@ -121,22 +121,26 @@ static int tsig_rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error)
 	return KNOT_EOK;
 }
 
-int tsig_create_rdata(knot_rrset_t *rr, uint16_t maclen, uint16_t tsig_err)
+int tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg, uint16_t maclen, uint16_t tsig_err)
 {
-	if (!rr) {
+	if (rr == NULL || alg == NULL) {
 		return KNOT_EINVAL;
 	}
 
 	/* We already checked rr and know rdlen > 0, no need to check rets. */
-	size_t rdlen = TSIG_FIXED_RDLEN + maclen;
+	int alg_len = knot_dname_size(alg);
+	size_t rdlen = alg_len + TSIG_FIXED_RDLEN + maclen;
 	if (tsig_err != KNOT_RCODE_BADTIME) {
 		rdlen -= TSIG_OTHER_MAXLEN;
 	}
 	uint8_t *rd = knot_rrset_create_rdata(rr, rdlen);
 	memset(rd, 0, rdlen);
 
+	/* Copy alg name. */
+	knot_dname_to_wire(rd, alg, rdlen);
+
 	/* Set MAC variable length in advance. */
-	rd += TSIG_OFF_MACLEN;
+	rd += alg_len + TSIG_OFF_MACLEN;
 	knot_wire_write_u16(rd, maclen);
 
 	/* Set error. */
@@ -145,27 +149,6 @@ int tsig_create_rdata(knot_rrset_t *rr, uint16_t maclen, uint16_t tsig_err)
 	return KNOT_EOK;
 }
 
-int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name)
-{
-	uint8_t *rd = tsig_rdata_seek(tsig, TSIG_ALGNAME_O, TSIG_NAMELEN);
-	if (!rd) {
-		return KNOT_ERROR;
-	}
-
-	memcpy(rd, &alg_name, sizeof(knot_dname_t*));
-	knot_dname_retain(alg_name);
-	return KNOT_EOK;
-}
-
-int tsig_rdata_set_alg(knot_rrset_t *tsig, knot_tsig_algorithm_t alg)
-{
-	const char *s = tsig_alg_to_str(alg);
-	knot_dname_t *alg_name = knot_dname_new_from_str(s, strlen(s), NULL);
-	int ret = tsig_rdata_set_alg_name(tsig, alg_name);
-	knot_dname_release(alg_name);
-	return ret;
-}
-
 int tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time)
 {
 	uint8_t *rd = tsig_rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t));
@@ -237,11 +220,7 @@ int tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t len,
 
 const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig)
 {
-	uint8_t *rd = tsig_rdata_seek(tsig, TSIG_ALGNAME_O, TSIG_NAMELEN);
-	if (!rd) {
-		return NULL;
-	}
-	return *((knot_dname_t**)rd);
+	return knot_rrset_get_rdata(tsig, 0);
 }
 
 knot_tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
@@ -261,7 +240,7 @@ knot_tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
 	}
 
 	knot_lookup_table_t *item = knot_lookup_by_name(
-	                                      knot_tsig_alg_domain_names, name);
+	                                      knot_tsig_alg_dnames_str, name);
 	free(name);
 	if (!item) {
 		dbg_tsig("TSIG: rdata: unknown algorithm.\n");
@@ -355,7 +334,7 @@ int tsig_alg_from_name(const knot_dname_t *alg_name)
 	}
 
 	knot_lookup_table_t *found =
-		knot_lookup_by_name(knot_tsig_alg_domain_names, name);
+		knot_lookup_by_name(knot_tsig_alg_dnames_str, name);
 
 	if (!found) {
 		dbg_tsig("Unknown algorithm: %s \n", name);
@@ -412,7 +391,7 @@ const char* tsig_alg_to_str(knot_tsig_algorithm_t alg)
 {
 	knot_lookup_table_t *item;
 
-	item = knot_lookup_by_id(knot_tsig_alg_domain_names, alg);
+	item = knot_lookup_by_id(knot_tsig_alg_dnames_str, alg);
 
 	if (item != NULL) {
 		return item->name;
@@ -421,6 +400,19 @@ const char* tsig_alg_to_str(knot_tsig_algorithm_t alg)
 	}
 }
 
+const knot_dname_t* tsig_alg_to_dname(knot_tsig_algorithm_t alg)
+{
+	knot_lookup_table_t *item;
+
+	item = knot_lookup_by_id(knot_tsig_alg_dnames, alg);
+
+	if (item != NULL) {
+		return (const knot_dname_t*)item->name;
+	} else {
+		return NULL;
+	}
+}
+
 size_t tsig_wire_maxsize(const knot_tsig_key_t *key)
 {
 	size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1;
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
index 258513ef93566459818be3d082a00a4f3012277e..95cc54f146d2e15f8ec411b6de54b35c28f04f29 100644
--- a/src/libknot/tsig.h
+++ b/src/libknot/tsig.h
@@ -55,11 +55,19 @@ enum tsig_consts {
 };
 
 /*!
- * \note Uses the given domain name, do not deallocate it!
+ * \brief Create TSIG RDATA.
+ *
+ * \param rr TSIG RR to contain created data.
+ * \param alg Algorithm name.
+ * \param maclen Algorithm MAC len (may be set to 0 for empty MAC).
+ * \param tsig_err TSIG error code.
+ *
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EOK
  */
-int tsig_create_rdata(knot_rrset_t *rr,  uint16_t maclen, uint16_t tsig_err);
-int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name);
-int tsig_rdata_set_alg(knot_rrset_t *tsig, knot_tsig_algorithm_t alg);
+int tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg,
+                      uint16_t maclen, uint16_t tsig_err);
+
 int tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time);
 int tsig_rdata_store_current_time(knot_rrset_t *tsig);
 int tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge);
@@ -96,6 +104,16 @@ int tsig_alg_from_name(const knot_dname_t *name);
  */
 const char* tsig_alg_to_str(knot_tsig_algorithm_t alg);
 
+/*!
+ * \brief Convert TSIG algorithm identifier to domain name.
+ *
+ * \param alg TSIG algorithm identifier.
+ *
+ * \retval TSIG algorithm string name.
+ * \retval Empty string if undefined.
+ */
+const knot_dname_t* tsig_alg_to_dname(knot_tsig_algorithm_t alg);
+
 /*!
  * \brief Return TSIG RRSET maximum wire size for given algorithm.
  *
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
index 1f7997cf6be80875dce38a2998acf00c0af4fa7c..afd0e9c54bfee1b35a77d81e1d2d338b656f36c8 100644
--- a/src/libknot/updates/changesets.c
+++ b/src/libknot/updates/changesets.c
@@ -22,194 +22,247 @@
 #include "updates/changesets.h"
 #include "libknot/common.h"
 #include "common/descriptor.h"
+#include "common/mempattern.h"
+#include "common/mempool.h"
 #include "rrset.h"
 #include "util/debug.h"
+#include "rdata.h"
 
-static const size_t KNOT_CHANGESET_COUNT = 5;
-static const size_t KNOT_CHANGESET_STEP = 5;
-static const size_t KNOT_CHANGESET_RRSET_COUNT = 5;
-static const size_t KNOT_CHANGESET_RRSET_STEP = 5;
+static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
+                                         const knot_rrset_t *rrset2)
+{
+	return knot_rrset_equal(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
+	       && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG
+	           || knot_rdata_rrsig_type_covered(rrset1, 0)
+	              == knot_rdata_rrsig_type_covered(rrset2, 0));
+}
+
+int knot_changesets_init(knot_changesets_t **changesets, uint32_t flags)
+{
+	// Create new changesets structure
+	*changesets = xmalloc(sizeof(knot_changesets_t));
+	memset(*changesets, 0, sizeof(knot_changesets_t));
+	(*changesets)->flags = flags;
 
-/*----------------------------------------------------------------------------*/
+	// Initialize memory context for changesets (xmalloc'd)
+	struct mempool *chs_pool = mp_new(sizeof(knot_changeset_t));
+	(*changesets)->mmc_chs.ctx = chs_pool;
+	(*changesets)->mmc_chs.alloc = (mm_alloc_t)mp_alloc;
+	(*changesets)->mmc_chs.free = NULL;
+
+	// Initialize memory context for RRs in changesets (xmalloc'd)
+	struct mempool *rr_pool = mp_new(sizeof(knot_rr_ln_t));
+	(*changesets)->mmc_rr.ctx = rr_pool;
+	(*changesets)->mmc_rr.alloc = (mm_alloc_t)mp_alloc;
+	(*changesets)->mmc_rr.free = NULL;
+
+	// Init list with changesets
+	init_list(&(*changesets)->sets);
+
+	// Init changes structure
+	(*changesets)->changes = xmalloc(sizeof(knot_changes_t));
+	// Init changes' allocator (storing RRs)
+	(*changesets)->changes->mem_ctx = (*changesets)->mmc_rr;
+	// Init changes' lists
+	init_list(&(*changesets)->changes->old_nodes);
+	init_list(&(*changesets)->changes->old_nsec3);
+	init_list(&(*changesets)->changes->new_rrsets);
+	init_list(&(*changesets)->changes->old_rrsets);
 
-static int knot_changeset_check_count(knot_rrset_t ***rrsets, size_t count,
-                                        size_t *allocated)
+	return KNOT_EOK;
+}
+
+knot_changesets_t *knot_changesets_create(uint32_t flags)
 {
-	/* Check if allocated is sufficient. */
-	if (count <= *allocated) {
-		return KNOT_EOK;
+	knot_changesets_t *ch = NULL;
+	int ret = knot_changesets_init(&ch, flags);
+	if (ret != KNOT_EOK) {
+		return NULL;
+	} else {
+		return ch;
 	}
+}
 
-	/* How many steps is needed to content count? */
-	size_t extra = (count - *allocated) % KNOT_CHANGESET_RRSET_STEP;
-	extra = (extra + 1) * KNOT_CHANGESET_RRSET_STEP;
+knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch)
+{
+	if (ch == NULL) {
+		return NULL;
+	}
 
-	/* Reallocate memory block. */
-	const size_t item_len = sizeof(knot_rrset_t *);
-	const size_t new_count = *allocated + extra;
-	void *tmp = realloc(*rrsets, new_count * item_len);
-	if (tmp == NULL) {
-		return KNOT_ENOMEM;
+	// Create set changesets' memory allocator
+	knot_changeset_t *set = ch->mmc_chs.alloc(ch->mmc_chs.ctx,
+	                                          sizeof(knot_changeset_t));
+	if (set == NULL) {
+		ERR_ALLOC_FAILED;
+		return NULL;
 	}
-	*rrsets = tmp;
-	/* Init new data. */
-	memset(*rrsets + *allocated, 0, extra * item_len);
-	*allocated = new_count;
+	memset(set, 0, sizeof(knot_changeset_t));
+	set->flags = ch->flags;
 
-	return KNOT_EOK;
-}
+	// Init set's memory context (Allocator from changests structure is used)
+	set->mem_ctx = ch->mmc_rr;
 
-/*----------------------------------------------------------------------------*/
+	// Init local lists
+	init_list(&set->add);
+	init_list(&set->remove);
 
-static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
-                                         const knot_rrset_t *rrset2)
-{
-	return knot_rrset_equal(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
-	       && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG
-	           || knot_rrset_rdata_rrsig_type_covered(rrset1)
-	              == knot_rrset_rdata_rrsig_type_covered(rrset2));
-}
+	// Insert into list of sets
+	add_tail(&ch->sets, (node_t *)set);
 
-/*----------------------------------------------------------------------------*/
+	++ch->count;
 
-int knot_changeset_allocate(knot_changesets_t **changesets,
-                            uint32_t flags)
+	return set;
+}
+
+knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *chs)
 {
-	// create new changesets
-	*changesets = (knot_changesets_t *)(malloc(sizeof(knot_changesets_t)));
-	if (*changesets == NULL) {
-		return KNOT_ENOMEM;
+	if (chs == NULL || EMPTY_LIST(chs->sets)) {
+		return NULL;
 	}
 
-	memset(*changesets, 0, sizeof(knot_changesets_t));
-	(*changesets)->flags = flags;
+	return (knot_changeset_t *)(TAIL(chs->sets));
+}
 
-	if (knot_changesets_check_size(*changesets) != KNOT_EOK) {
-		free(*changesets);
-		*changesets = NULL;
-		return KNOT_ENOMEM;
+const knot_rrset_t *knot_changeset_last_rr(const knot_changeset_t *ch,
+                                           knot_changeset_part_t part)
+{
+	if (ch == NULL) {
+		return NULL;
 	}
 
-	return KNOT_EOK;
+	if (part == KNOT_CHANGESET_ADD) {
+		knot_rr_ln_t *n = TAIL(ch->add);
+		return n ? n->rr : NULL;
+	} else if (part == KNOT_CHANGESET_REMOVE) {
+		knot_rr_ln_t *n = TAIL(ch->remove);
+		return n ? n->rr : NULL;
+	}
+
+	return NULL;
 }
 
-/*----------------------------------------------------------------------------*/
+void knot_changeset_remove_last_rr(knot_changeset_t *ch,
+                                   knot_changeset_part_t part)
+{
+	if (ch == NULL) {
+		return;
+	}
 
-int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
-                              size_t *count, size_t *allocated,
-                              knot_rrset_t *rrset)
+	if (part == KNOT_CHANGESET_ADD) {
+		knot_rr_ln_t *n = TAIL(ch->add);
+		rem_node((node_t *)n);
+	} else if (part == KNOT_CHANGESET_REMOVE) {
+		knot_rr_ln_t *n = TAIL(ch->remove);
+		rem_node((node_t *)n);
+	}
+}
+
+int knot_changeset_add_rrset(knot_changeset_t *chgs, knot_rrset_t *rrset,
+                             knot_changeset_part_t part)
 {
-	int ret = knot_changeset_check_count(rrsets, *count + 1, allocated);
-	if (ret != KNOT_EOK) {
-		return ret;
+	// Create wrapper node for list
+	knot_rr_ln_t *rr_node =
+		chgs->mem_ctx.alloc(chgs->mem_ctx.ctx, sizeof(knot_rr_ln_t));
+	if (rr_node == NULL) {
+		// This will not happen with mp_alloc, but allocator can change
+		ERR_ALLOC_FAILED;
+		return KNOT_ENOMEM;
 	}
+	rr_node->rr = rrset;
 
-	(*rrsets)[*count] = rrset;
-	*count = *count + 1;
+	if (part == KNOT_CHANGESET_ADD) {
+		add_tail(&chgs->add, (node_t *)rr_node);
+	} else {
+		add_tail(&chgs->remove, (node_t *)rr_node);
+	}
 
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
-                           size_t *allocated, knot_rrset_t *rr)
+int knot_changeset_add_rr(knot_changeset_t *chgs, knot_rrset_t *rr,
+                          knot_changeset_part_t part)
 {
-	// 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
-
 	// Just check the last RRSet. If the RR belongs to it, merge it,
 	// otherwise just add the RR to the end of the list
+	list_t *l = part == KNOT_CHANGESET_ADD ? &(chgs->add) : &(chgs->remove);
+	knot_rrset_t *tail_rr =
+		EMPTY_LIST(*l) ? NULL : ((knot_rr_ln_t *)(TAIL(*l)))->rr;
 
-	if (*count > 0
-	    && knot_changeset_rrsets_match((*rrsets)[*count - 1], rr)) {
+	if (tail_rr && knot_changeset_rrsets_match(tail_rr, rr)) {
 		// Create changesets exactly as they came, with possibly
 		// duplicate records
-		if (knot_rrset_merge((*rrsets)[*count - 1],
-		                     rr) != KNOT_EOK) {
+		if (knot_rrset_merge(tail_rr, rr) != KNOT_EOK) {
 			return KNOT_ERROR;
 		}
 
-		knot_rrset_deep_free(&rr, 1, 0);
+		knot_rrset_deep_free(&rr, 1);
 		return KNOT_EOK;
 	} else {
-		return knot_changeset_add_rrset(rrsets, count, allocated, rr);
+		return knot_changeset_add_rrset(chgs, rr, part);
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changeset_add_new_rr(knot_changeset_t *changeset,
-                               knot_rrset_t *rrset,
-                               knot_changeset_part_t part)
+int knot_changes_add_rrset(knot_changes_t *ch, knot_rrset_t *rrset,
+                           knot_changes_part_t part)
 {
-	knot_rrset_t ***rrsets = NULL;
-	size_t *count = NULL;
-	size_t *allocated = NULL;
-
-	switch (part) {
-	case KNOT_CHANGESET_ADD:
-		rrsets = &changeset->add;
-		count = &changeset->add_count;
-		allocated = &changeset->add_allocated;
-		break;
-	case KNOT_CHANGESET_REMOVE:
-		rrsets = &changeset->remove;
-		count = &changeset->remove_count;
-		allocated = &changeset->remove_allocated;
-		break;
-	default:
-		assert(0);
+	if (ch == NULL || rrset == NULL) {
+		return KNOT_EINVAL;
 	}
 
-	assert(rrsets != NULL);
-	assert(count != NULL);
-	assert(allocated != NULL);
+	knot_rr_ln_t *rr_node =
+		ch->mem_ctx.alloc(ch->mem_ctx.ctx, sizeof(knot_rr_ln_t));
+	if (rr_node == NULL) {
+		// This will not happen with mp_alloc, but allocator can change
+		ERR_ALLOC_FAILED;
+		return KNOT_ENOMEM;
+	}
+	rr_node->rr = rrset;
 
-	int ret = knot_changeset_add_rr(rrsets, count, allocated, rrset);
-	if (ret != KNOT_EOK) {
-		return ret;
+	if (part == KNOT_CHANGES_NEW) {
+		add_tail(&ch->new_rrsets, (node_t *)rr_node);
+	} else {
+		assert(part == KNOT_CHANGES_OLD);
+		add_tail(&ch->old_rrsets, (node_t *)rr_node);
 	}
 
-	return ret;
+	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count,
-                                       int pos)
+int knot_changes_add_node(knot_changes_t *ch, knot_node_t *kn_node,
+                          knot_changes_part_t part)
 {
-	if (pos >= *count || *count == 0) {
-		return NULL;
+	if (ch == NULL || kn_node == NULL) {
+		return KNOT_EINVAL;
 	}
 
-	knot_rrset_t *removed = rrsets[pos];
-
-	// shift all RRSets from pos+1 one cell to the left
-	for (int i = pos; i < *count - 1; ++i) {
-		rrsets[i] = rrsets[i + 1];
+	// Using the same allocator for node and rr's, sizes are equal.
+	knot_node_ln_t *list_node =
+		ch->mem_ctx.alloc(ch->mem_ctx.ctx, sizeof(knot_node_ln_t));
+	if (list_node == NULL) {
+		// This will not happen with mp_alloc, but allocator can change
+		ERR_ALLOC_FAILED;
+		return KNOT_ENOMEM;
 	}
+	list_node->node = kn_node;
 
-	// just to be sure, set the last previously occupied position to NULL
-	rrsets[*count - 1] = NULL;
-	*count -= 1;
+	if (part == KNOT_CHANGES_NORMAL_NODE) {
+		add_tail(&ch->old_nodes, (node_t *)list_node);
+	} else {
+		assert(part == KNOT_CHANGES_NSEC3_NODE);
+		add_tail(&ch->old_nsec3, (node_t *)list_node);
+	}
 
-	return removed;
+	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-void knot_changeset_store_soa(knot_rrset_t **chg_soa,
-                               uint32_t *chg_serial, knot_rrset_t *soa)
+static void knot_changeset_store_soa(knot_rrset_t **chg_soa,
+                                     uint32_t *chg_serial, knot_rrset_t *soa)
 {
 	*chg_soa = soa;
-	*chg_serial = knot_rrset_rdata_soa_serial(soa);
+	*chg_serial = knot_rdata_soa_serial(soa);
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
                             knot_changeset_part_t part)
 {
 	switch (part) {
@@ -224,369 +277,118 @@ int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
 	default:
 		assert(0);
 	}
-
-	/*! \todo Remove return value? */
-	return KNOT_EOK;
-}
-
-/*---------------------------------------------------------------------------*/
-
-int knot_changesets_check_size(knot_changesets_t *changesets)
-{
-	/* Check if allocated is sufficient. */
-	if (changesets->count < changesets->allocated) {
-		return KNOT_EOK;
-	}
-
-	/* How many steps is needed to content count? */
-	size_t extra = (changesets->count - changesets->allocated)
-	                % KNOT_CHANGESET_STEP;
-	extra = (extra + 1) * KNOT_CHANGESET_STEP;
-
-	/* Allocate new memory block. */
-	const size_t item_len = sizeof(knot_changeset_t);
-	size_t new_count = (changesets->allocated + extra);
-	knot_changeset_t *sets = malloc(new_count * item_len);
-	if (sets == NULL) {
-		return KNOT_ENOMEM;
-	}
-
-	/* Clear new memory block and copy old data. */
-	memset(sets, 0, new_count * item_len);
-	memcpy(sets, changesets->sets, changesets->allocated * item_len);
-
-	/* Set type to all newly allocated changesets. */
-	for (int i = changesets->allocated; i < new_count; ++i) {
-		sets[i].flags = changesets->flags;
-	}
-
-	/* Replace old changesets. */
-	free(changesets->sets);
-	changesets->sets = sets;
-	changesets->allocated = new_count;
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_changeset_set_flags(knot_changeset_t *changeset,
-                             uint32_t flags)
-{
-	changeset->flags = flags;
-}
-
-/*----------------------------------------------------------------------------*/
-
-uint32_t knot_changeset_flags(knot_changeset_t *changeset)
-{
-	return changeset->flags;
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changeset_is_empty(const knot_changeset_t *changeset)
+bool knot_changeset_is_empty(const knot_changeset_t *changeset)
 {
 	if (changeset == NULL) {
-		return 0;
-	}
-
-	return (changeset->add_count == 0 && changeset->remove_count == 0);
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_free_changeset(knot_changeset_t **changeset)
-{
-	assert((*changeset)->add_allocated >= (*changeset)->add_count);
-	assert((*changeset)->remove_allocated >= (*changeset)->remove_count);
-	assert((*changeset)->allocated >= (*changeset)->size);
-
-	int j;
-	for (j = 0; j < (*changeset)->add_count; ++j) {
-		knot_rrset_deep_free(&(*changeset)->add[j], 1, 1);
+		return true;
 	}
-	free((*changeset)->add);
-
-	for (j = 0; j < (*changeset)->remove_count; ++j) {
-		knot_rrset_deep_free(&(*changeset)->remove[j], 1, 1);
-	}
-	free((*changeset)->remove);
-
-	knot_rrset_deep_free(&(*changeset)->soa_from, 1, 1);
-	knot_rrset_deep_free(&(*changeset)->soa_to, 1, 1);
-
-	free((*changeset)->data);
 
-
-	*changeset = NULL;
+	return (changeset->soa_to == NULL &&
+	        EMPTY_LIST(changeset->add) && EMPTY_LIST(changeset->remove));
 }
 
-/*----------------------------------------------------------------------------*/
-
-void knot_free_changesets(knot_changesets_t **changesets)
+size_t knot_changeset_size(const knot_changeset_t *changeset)
 {
-	if (changesets == NULL || *changesets == NULL) {
-		return;
-	}
-
-	assert((*changesets)->allocated >= (*changesets)->count);
-
-	for (int i = 0; i < (*changesets)->count; ++i) {
-		knot_changeset_t *ch = &(*changesets)->sets[i];
-		knot_free_changeset(&ch);
+	if (!changeset || knot_changeset_is_empty(changeset)) {
+		return 0;
 	}
 
-	free((*changesets)->sets);
-
-	knot_rrset_deep_free(&(*changesets)->first_soa, 1, 1);
-
-	assert((*changesets)->changes == NULL);
-
-	free(*changesets);
-	*changesets = NULL;
+	return list_size(&changeset->add) + list_size(&changeset->remove);
 }
 
-/*----------------------------------------------------------------------------*/
-/* knot_changes_t manipulation                                                */
-/*----------------------------------------------------------------------------*/
-
-int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets,
-                              int *count, int *allocated, int to_add)
+int knot_changeset_apply(knot_changeset_t *changeset,
+                         knot_changeset_part_t part,
+                         int (*func)(knot_rrset_t *, void *), void *data)
 {
-	if (rrsets == NULL || count == NULL || allocated == NULL) {
+	if (changeset == NULL || func == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	if (*count + to_add <= *allocated) {
-		return KNOT_EOK;
-	}
-
-	int new_count = (*allocated == 0) ? 2 : *allocated * 2;
-	while (new_count < *count + to_add) {
-		new_count *= 2;
-	}
-
-	/* Allocate new memory block. */
-	knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *));
-	if (rrsets_new == NULL) {
-		return KNOT_ENOMEM;
+	knot_rr_ln_t *rr_node = NULL;
+	if (part == KNOT_CHANGESET_ADD) {
+		WALK_LIST(rr_node, changeset->add) {
+			int res = func(rr_node->rr, data);
+			if (res != KNOT_EOK) {
+				return res;
+			}
+		}
+	} else if (part == KNOT_CHANGESET_REMOVE) {
+		WALK_LIST(rr_node, changeset->remove) {
+			int res = func(rr_node->rr, data);
+			if (res != KNOT_EOK) {
+				return res;
+			}
+		}
 	}
 
-	/* Initialize new memory and copy old data. */
-	memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *));
-	memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *));
-
-	/* Free old nodes and switch pointers. */
-	free(*rrsets);
-	*rrsets = rrsets_new;
-	*allocated = new_count;
-
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changes_nodes_reserve(knot_node_t ***nodes,
-                             int *count, int *allocated)
+int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2)
 {
-	if (nodes == NULL || count == NULL || allocated == NULL) {
+	if (ch1 == NULL || ch2 == NULL || ch1->data != NULL ||
+	    ch2->data != NULL) {
 		return KNOT_EINVAL;
 	}
 
-	if (*count + 2 <= *allocated) {
-		return KNOT_EOK;
-	}
-
-	int new_count = (*allocated == 0) ? 2 : *allocated * 2;
+	// Connect lists in changesets together
+	add_tail_list(&ch1->add, &ch2->add);
+	add_tail_list(&ch1->remove, &ch2->remove);
 
-	/* Allocate new memory block. */
-	const size_t node_len = sizeof(knot_node_t *);
-	knot_node_t **nodes_new = malloc(new_count * node_len);
-	if (nodes_new == NULL) {
-		return KNOT_ENOMEM;
-	}
-
-	/* Clear memory block and copy old data. */
-	memset(nodes_new, 0, new_count * node_len);
-	memcpy(nodes_new, *nodes, (*allocated) * node_len);
-
-	/* Free old nodes and switch pointers. */
-	free(*nodes);
-	*nodes = nodes_new;
-	*allocated = new_count;
+	// Use soa_to and serial from the second changeset
+	// soa_to from the first changeset is redundant, delete it
+	knot_rrset_deep_free(&ch1->soa_to, 1);
+	ch1->soa_to = ch2->soa_to;
+	ch1->serial_to = ch2->serial_to;
 
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changes_rdata_reserve(knot_rrset_t ***rdatas,
-                               int count, int *allocated, int to_add)
+static void knot_free_changeset(knot_changeset_t *changeset)
 {
-	if (rdatas == NULL || allocated == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (count + to_add <= *allocated) {
-		return KNOT_EOK;
+	if (changeset == NULL) {
+		return;
 	}
 
-	int new_count = (*allocated == 0) ? 2 : *allocated * 2;
-	while (new_count < count + to_add) {
-		new_count *= 2;
+	// Delete RRSets in lists, in case there are any left
+	knot_rr_ln_t *rr_node;
+	WALK_LIST(rr_node, changeset->add) {
+		knot_rrset_deep_free(&rr_node->rr, 1);
 	}
-
-	/* Allocate new memory block. */
-	knot_rrset_t **rdatas_new = malloc(new_count * sizeof(knot_rrset_t *));
-	if (rdatas_new == NULL) {
-		ERR_ALLOC_FAILED;
-		return KNOT_ENOMEM;
+	WALK_LIST(rr_node, changeset->remove) {
+		knot_rrset_deep_free(&rr_node->rr, 1);
 	}
 
-	/* Initialize new memory and copy old data. */
-	memset(rdatas_new, 0, new_count * sizeof(knot_rrset_t *));
-	memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rrset_t *));
-
-	/* Free old rdatas and switch pointers. */
-	free(*rdatas);
-	*rdatas = rdatas_new;
-	*allocated = new_count;
+	knot_rrset_deep_free(&changeset->soa_from, 1);
+	knot_rrset_deep_free(&changeset->soa_to, 1);
 
-	return KNOT_EOK;
+	// Delete binary data
+	free(changeset->data);
 }
 
-/*----------------------------------------------------------------------------*/
-
-/*!< \note Always adds the whole RRSet = all rdata. */
-void knot_changes_add_rdata(knot_rrset_t **rdatas, int *count,
-                            knot_rrset_t *rrset)
+void knot_changesets_free(knot_changesets_t **changesets)
 {
-	if (rdatas == NULL || count == NULL || rrset == NULL || rrset->rdata_count == 0) {
+	if (changesets == NULL || *changesets == NULL) {
 		return;
 	}
 
-	rdatas[*count] = rrset;
-	*count += 1;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count,
-                                knot_changes_t *changes, int add_rdata)
-{
-	if (rrsets == NULL || changes == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (count == 0) {
-		return KNOT_EOK;
-	}
-
-	/* Reserve twice the space, to have enough space for RRSIGs if
-	 * there are some.
-	 */
-	int ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-	                                      &changes->old_rrsets_count,
-	                                      &changes->old_rrsets_allocated,
-	                                      2 * count);
-	if (ret != KNOT_EOK) {
-//		dbg_xfrin("Failed to reserve changes rrsets.\n");
-		return ret;
-	}
-
-	/* Mark RRsets and RDATA for removal. */
-	for (unsigned i = 0; i < count; ++i) {
-		if (rrsets[i] == NULL) {
-			continue;
-		}
-
-		knot_rrset_t *rrsigs = knot_rrset_get_rrsigs(rrsets[i]);
-
-		if (add_rdata) {
-
-			/* RDATA count in the RRSet. */
-			int rdata_count = 1;
-
-			if (rrsigs != NULL) {
-				/* Increment the RDATA count by the count of
-				 * RRSIGs. */
-				rdata_count += 1;
-			}
-
-			/* Remove old RDATA. */
-			ret = knot_changes_rdata_reserve(&changes->old_rdata,
-			                          changes->old_rdata_count,
-			                          &changes->old_rdata_allocated,
-			                          rdata_count);
-			if (ret != KNOT_EOK) {
-//				dbg_xfrin("Failed to reserve changes rdata.\n");
-				return ret;
-			}
-
-			knot_changes_add_rdata(changes->old_rdata,
-			                       &changes->old_rdata_count,
-			                       rrsets[i]);
-
-			knot_changes_add_rdata(changes->old_rdata,
-			                       &changes->old_rdata_count,
-			                       rrsigs);
-		}
-
-		/* Disconnect RRsigs from rrset. */
-		knot_rrset_set_rrsigs(rrsets[i], NULL);
-		changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i];
-		if (rrsigs) {
-			changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
+	if (!EMPTY_LIST((*changesets)->sets)) {
+		knot_changeset_t *chg = NULL;
+		WALK_LIST(chg, (*changesets)->sets) {
+			knot_free_changeset(chg);
 		}
 	}
 
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
+	// Free pool with sets themselves
+	mp_delete((*changesets)->mmc_chs.ctx);
+	// Free pool with RRs in sets / changes
+	mp_delete((*changesets)->mmc_rr.ctx);
 
-int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count,
-                                knot_changes_t *changes, int add_rdata)
-{
-	if (rrsets == NULL || changes == NULL) {
-		return KNOT_EINVAL;
-	}
+	knot_rrset_deep_free(&(*changesets)->first_soa, 1);
 
-	if (count == 0) {
-		return KNOT_EOK;
-	}
-
-	int ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
-	                                      &changes->new_rrsets_count,
-	                                      &changes->new_rrsets_allocated,
-	                                      count);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	/* Mark RRsets and RDATA for removal. */
-	for (unsigned i = 0; i < count; ++i) {
-		if (rrsets[i] == NULL) {
-			continue;
-		}
-
-		if (add_rdata) {
-			ret = knot_changes_rdata_reserve(&changes->new_rdata,
-			                          changes->new_rdata_count,
-			                          &changes->new_rdata_allocated,
-			                          1);
-			if (ret != KNOT_EOK) {
-				return ret;
-			}
-
-			knot_changes_add_rdata(changes->new_rdata,
-			                       &changes->new_rdata_count,
-			                       rrsets[i]);
-		}
-
-		changes->new_rrsets[changes->new_rrsets_count++] = rrsets[i];
-	}
-
-	return KNOT_EOK;
+	free((*changesets)->changes);
+	free(*changesets);
+	*changesets = NULL;
 }
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index 4edc387d38b5da29b1c6a3055d36097461da132c..a730d265ba2a8195826f0a64b913aa436f799b49 100644
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -1,14 +1,14 @@
 /*!
  * \file changesets.h
  *
- * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>, Jan Kadlec <jan.kadlec@nic.cz>
  *
  * \brief Structure for representing IXFR/DDNS changeset and its API.
  *
  * \addtogroup xfr
  * @{
  */
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -29,96 +29,87 @@
 
 #include "rrset.h"
 #include "zone/node.h"
+#include "common/lists.h"
+#include "common/mempattern.h"
 
 /*----------------------------------------------------------------------------*/
 
 /*! \brief Changeset flags, stored as first 4 bytes in serialized changeset. */
 typedef enum {
-	KNOT_CHANGESET_TYPE_IXFR = 1 << 0,
-	KNOT_CHANGESET_TYPE_DDNS = 1 << 1
+	KNOT_CHANGESET_TYPE_IXFR   = 1 << 0,
+	KNOT_CHANGESET_TYPE_DDNS   = 1 << 1,
+	KNOT_CHANGESET_TYPE_DNSSEC = 1 << 2
 } knot_changeset_flag_t;
 
-/*! \todo Changeset must be serializable/deserializable, so
- *        all data and pointers have to be changeset-exclusive,
- *        or more advanced structure serialization scheme has to be
- *        implemented.
- *
- * \todo Preallocation of space for changeset.
- */
-typedef struct {
-	knot_rrset_t *soa_from;
-	knot_rrset_t **remove;
-	size_t remove_count;
-	size_t remove_allocated;
-
-	knot_rrset_t *soa_to;
-	knot_rrset_t **add;
-	size_t add_count;
-	size_t add_allocated;
-
-	uint8_t *data;
-	size_t size;
-	size_t allocated;
-	uint32_t serial_from;
-	uint32_t serial_to;
-
-	uint32_t flags;  /*!< DDNS / IXFR */
+
+/*! \brief One changeset received from wire, with parsed RRs. */
+typedef struct knot_changeset {
+	node_t n; /*!< List node. */
+	mm_ctx_t mem_ctx; /*!< Memory context */
+	knot_rrset_t *soa_from; /*!< Start SOA. */
+	list_t remove; /*!< List of RRs to remove. */
+	knot_rrset_t *soa_to; /*!< Destination SOA. */
+	list_t add; /*!< List of RRs to add. */
+	uint8_t *data; /*!< Serialized changeset. */
+	size_t size; /*!< Size of serialized changeset. */
+	uint32_t serial_from; /*!< SOA start serial. */
+	uint32_t serial_to; /*!< SOA destination serial. */
+	uint32_t flags; /*!< DDNS / IXFR flags. */
 } knot_changeset_t;
 
 /*----------------------------------------------------------------------------*/
 
+/*! \brief Wrapper for BIRD lists. Storing: RRSet. */
+typedef struct knot_rr_ln {
+	node_t n; /*!< List node. */
+	knot_rrset_t *rr; /*!< Actual usable data. */
+} knot_rr_ln_t;
+
+/*! \brief Wrapper for BIRD lists. Storing: Node. */
+typedef struct knot_node_ln {
+	node_t n; /*!< List node. */
+	knot_node_t *node; /*!< Actual usable data. */
+} knot_node_ln_t;
+
+/*! \brief Partial changes done to zones - used for update/transfer rollback. */
 typedef struct {
 	/*!
-	 * Deleted (without owners and RDATA) after successful update.
+	 * Memory context. Ideally a pool allocator since there is a possibility
+	 * of many changes in one transfer/update.
 	 */
-	knot_rrset_t **old_rrsets;
-	int old_rrsets_count;
-	int old_rrsets_allocated;
-
+	mm_ctx_t mem_ctx;
 	/*!
-	 * Deleted after successful update. Contains only relevant RDATA.
+	 * Deleted after successful update.
 	 */
-	knot_rrset_t **old_rdata;
-	int old_rdata_count;
-	int old_rdata_allocated;
-
+	list_t old_rrsets;
 	/*!
-	 * \brief Copied RRSets (i.e. modified by the update).
-	 *
-	 * Deleted (without owners and RDATA) after failed update.
+	 * Deleted after failed update.
 	 */
-	knot_rrset_t **new_rrsets;
-	int new_rrsets_count;
-	int new_rrsets_allocated;
-
+	list_t new_rrsets;
 	/*!
-	 * Deleted after failed update. Contains only relevant RDATA.
+	 * Deleted (without contents) after successful update.
 	 */
-	knot_rrset_t **new_rdata;
-	int new_rdata_count;
-	int new_rdata_allocated;
-
+	list_t old_nodes;
 	/*!
 	 * Deleted (without contents) after successful update.
 	 */
-	knot_node_t **old_nodes;
-	int old_nodes_count;
-	int old_nodes_allocated;
-
-	knot_node_t **old_nsec3;
-	int old_nsec3_count;
-	int old_nsec3_allocated;
+	list_t old_nsec3;
 } knot_changes_t;
 
 /*----------------------------------------------------------------------------*/
 
+/*!
+ * \brief Changeset structure (changes recieved by slave server between two
+ *        serial numbers.
+ */
 typedef struct {
-	knot_changeset_t *sets;
-	size_t count;
-	size_t allocated;
-	knot_rrset_t *first_soa;
-	uint32_t flags;
-	knot_changes_t *changes;
+	mm_ctx_t mmc_chs; /*!< Memory context for creating changesets */
+	mm_ctx_t mmc_rr; /*!< Memory context for creating RRs in changesets */
+	list_t sets; /*!< List of changesets. */
+	size_t count; /*!< Changeset count. */
+	knot_rrset_t *first_soa; /*!< First received SOA. */
+	uint32_t flags; /*!< DDNS / IXFR flags. */
+	knot_changes_t *changes; /*!< Partial changes. */
 } knot_changesets_t;
 
 /*----------------------------------------------------------------------------*/
@@ -128,64 +119,199 @@ typedef enum {
 	KNOT_CHANGESET_REMOVE
 } knot_changeset_part_t;
 
-/*----------------------------------------------------------------------------*/
-
-int knot_changeset_allocate(knot_changesets_t **changesets,
-                            uint32_t flags);
-
-int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
-                             size_t *count, size_t *allocated,
-                             knot_rrset_t *rrset);
+typedef enum {
+	KNOT_CHANGES_OLD,
+	KNOT_CHANGES_NEW,
+	KNOT_CHANGES_NORMAL_NODE,
+	KNOT_CHANGES_NSEC3_NODE
+} knot_changes_part_t;
 
-int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
-                          size_t *allocated, knot_rrset_t *rr);
+/*----------------------------------------------------------------------------*/
 
-int knot_changeset_add_new_rr(knot_changeset_t *changeset,
-                              knot_rrset_t *rrset,
-                              knot_changeset_part_t part);
+/*!
+ * \brief Inits changesets structure. The structure has to be freed
+ *        using 'knot_changesets_free()' function.
+ *
+ * \param changesets Double pointer to changesets structure.
+ * \param flags IXFR / DDNS flag.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
+ */
+int knot_changesets_init(knot_changesets_t **changesets,
+                         uint32_t flags);
 
-knot_rrset_t *knot_changeset_remove_rr(knot_rrset_t **rrsets, size_t *count,
-                                       int pos);
+/*!
+ * \brief Creates changesets structure. The created structure has to be freed
+ *        using 'knot_changesets_free()' function.
+ *
+ * \param flags IXFR / DDNS flag.
+ *
+ * \retval Created structure on success.
+ * \retval NULL on failure.
+ */
+knot_changesets_t *knot_changesets_create(uint32_t flags);
 
-void knot_changeset_store_soa(knot_rrset_t **chg_soa,
-                              uint32_t *chg_serial, knot_rrset_t *soa);
+/*!
+ * \brief Creates new changeset structure and returns it to caller.
+ *        The structure is also connected to a list of changesets.
+ *
+ * \param ch Changesets structure to create a new changeset in.
+ *
+ * \retval Created structure on success.
+ * \retval NULL on failure.
+ */
+knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch);
 
-int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
-                           knot_changeset_part_t part);
+/*!
+ * \brief Gets last changesets from from structure's list.
+ *
+ * \param ch Changesets structure to get a last changeset from.
+ *
+ * \retval Last changeset on success.
+ * \retval NULL on failure.
+ */
+knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *ch);
 
-int knot_changesets_check_size(knot_changesets_t *changesets);
+const knot_rrset_t *knot_changeset_last_rr(const knot_changeset_t *ch,
+                                           knot_changeset_part_t part);
+void knot_changeset_remove_last_rr(knot_changeset_t *ch,
+                                   knot_changeset_part_t part);
 
-void knot_changeset_set_flags(knot_changeset_t *changeset,
-                             uint32_t flags);
+/*!
+ * \brief Add RRSet to changeset. RRSet is either inserted to 'add' or to
+ *        'remove' list. Will *not* try to merge with previous RRSets.
+ *
+ * \param chgs Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ * \param part Add to 'add' or 'remove'?
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
+ */
+int knot_changeset_add_rrset(knot_changeset_t *chgs,
+                             knot_rrset_t *rrset, knot_changeset_part_t part);
 
-uint32_t knot_changeset_flags(knot_changeset_t *changeset);
+/*!
+ * \brief Add RRSet to changeset. RRSet is either inserted to 'add' or to
+ *        'remove' list. *Will* try to merge with previous RRSets.
+ *
+ * \param chgs Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ * \param part Add to 'add' or 'remove'?
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
+ */
+int knot_changeset_add_rr(knot_changeset_t *chgs,
+                          knot_rrset_t *rrset, knot_changeset_part_t part);
 
-int knot_changeset_is_empty(const knot_changeset_t *changeset);
+/*!
+ * \brief Adds a source/destination SOA RRSet to changeset.
+ *
+ * \param changeset Changeset to store SOA to.
+ * \param soa SOA RRSet to be stored to changeset.
+ * \param part To which part we store SOA (from = REMOVE, add = TO)
+ */
+void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+                            knot_changeset_part_t part);
 
-void knot_free_changeset(knot_changeset_t **changeset);
+/*!
+ * \brief Checks whether changeset is empty.
+ *
+ * \param changeset Changeset to be checked.
+ *
+ * Changeset is considered empty if it has no RRs in REMOVE and ADD sections and
+ * final SOA (soa_to) is not set.
+ *
+ * \retval true if changeset is empty.
+ * \retval false if changeset is not empty.
+ */
+bool knot_changeset_is_empty(const knot_changeset_t *changeset);
 
-void knot_free_changesets(knot_changesets_t **changesets);
+/*!
+ * \brief Get number of changes (additions and removals) in the changeset.
+ *
+ * \param changeset Changeset to be checked.
+ *
+ * \return Number of changes in the changeset.
+ */
+size_t knot_changeset_size(const knot_changeset_t *changeset);
 
-int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets,
-                                int *count, int *allocated, int to_add);
+/*!
+ * \brief Apply given function to all RRSets in one part of the changeset.
+ *
+ * \param changeset Changeset to apply the function to.
+ * \param part Part of changeset to apply the function to.
+ * \param func Function to apply to RRSets in the changeset. It is required that
+ *             the function returns KNOT_EOK on success.
+ * \param data Data to pass to the applied function.
+ *
+ * If the applied function fails, the application aborts and this function
+ * returns the return value of the applied function.
+ *
+ * \retval KNOT_EOK if OK
+ * \retval KNOT_EINVAL if \a changeset or \a func is NULL.
+ * \retval Other error code if the applied function failed.
+ */
+int knot_changeset_apply(knot_changeset_t *changeset,
+                         knot_changeset_part_t part,
+                         int (*func)(knot_rrset_t *, void *), void *data);
 
-int knot_changes_nodes_reserve(knot_node_t ***nodes,
-                               int *count, int *allocated);
+/*!
+ * \brief Frees the 'changesets' structure, including all its internal data.
+ *
+ * \param changesets Double pointer to changesets structure to be freed.
+ */
+void knot_changesets_free(knot_changesets_t **changesets);
 
-int knot_changes_rdata_reserve(knot_rrset_t ***rdatas,
-                               int count, int *allocated, int to_add);
+/*!
+ * \brief Add RRSet to changes structure.
+ *        RRSet is either inserted to 'old' or to 'new' list.
+ *
+ * \param chgs Change to add RRSet into.
+ * \param rrset RRSet to be added.
+ * \param part Add to 'old' or 'new'?
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
+ */
+int knot_changes_add_rrset(knot_changes_t *ch, knot_rrset_t *rrset,
+                           knot_changes_part_t part);
 
-void knot_changes_add_rdata(knot_rrset_t **rdatas, int *count,
-                            knot_rrset_t *rrset);
+/*!
+ * \brief Add Node to changes structure.
+          Node is either inserted to 'normal' or to 'NSEC3' list.
+ *
+ * \param chgs Change to add node into.
+ * \param node RRSet to be added.
+ * \param part Add to 'normal' or 'NSEC3'?
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
+ */
+int knot_changes_add_node(knot_changes_t *ch, knot_node_t *kn_node,
+                          knot_changes_part_t part);
 
 /*!
- * \note Also processes RRSIGs. May be switched by a parameter later, if needed.
+ * \brief Merges two changesets together, second changeset's lists are kept.
+ *
+ * \param ch1 Changeset to merge into
+ * \param ch2 Changeset to merge
+ *
+ * Beginning SOA is used from the first changeset, ending SOA from the second.
+ * Ending SOA from first changeset is deleted. SOAs in the second changeset are
+ * left untouched.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval Error code on failure.
  */
-int knot_changes_add_old_rrsets(knot_rrset_t **rrsets, int count,
-                                knot_changes_t *changes, int add_rdata);
+int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2);
 
-int knot_changes_add_new_rrsets(knot_rrset_t **rrsets, int count,
-                                knot_changes_t *changes, int add_rdata);
+/*!
+ * \param changes Double pointer of changes structure to be freed.
+ */
+void knot_changes_free(knot_changes_t **changes);
 
 #endif /* _KNOT_CHANGESETS_H_ */
 
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
index 5ade693124eb9123f16e464e5e8b04797905e1e8..bb7ad9dd6dc06f918d174443a7544db6b4798c4b 100644
--- a/src/libknot/updates/ddns.c
+++ b/src/libknot/updates/ddns.c
@@ -21,6 +21,7 @@
 
 #include "updates/ddns.h"
 #include "updates/changesets.h"
+#include "libknot/rdata.h"
 #include "util/debug.h"
 #include "packet/packet.h"
 #include "common.h"
@@ -76,7 +77,7 @@ static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset,
 	int ret;
 	for (int i = 0; i < *count; ++i) {
 		if (knot_rrset_equal(rrset, (*rrsets)[i],
-		                       KNOT_RRSET_COMPARE_HEADER) == 1) {
+		                     KNOT_RRSET_COMPARE_HEADER)) {
 			ret = knot_rrset_merge((*rrsets)[i], rrset);
 			if (ret != KNOT_EOK) {
 				return ret;
@@ -93,7 +94,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, 0);
+	ret = knot_rrset_deep_copy(rrset, &new_rrset);
 	if (ret != KNOT_EOK) {
 		return ret;
 	}
@@ -117,7 +118,7 @@ static int knot_ddns_add_prereq_dname(const knot_dname_t *dname,
 		return ret;
 	}
 
-	knot_dname_t *dname_new = knot_dname_deep_copy(dname);
+	knot_dname_t *dname_new = knot_dname_copy(dname);
 	if (dname_new == NULL) {
 		return KNOT_ENOMEM;
 	}
@@ -189,125 +190,6 @@ static int knot_ddns_add_prereq(knot_ddns_prereq_t *prereqs,
 
 /*----------------------------------------------------------------------------*/
 
-static int knot_ddns_check_remove_rr(knot_changeset_t *changeset,
-                                     const knot_rrset_t *rr)
-{
-	dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
-	for (int i = 0; i < changeset->add_count; ++i) {
-		// Removing RR(s) from this owner
-		if (knot_dname_compare(knot_rrset_owner(rr),
-		                       knot_rrset_owner(changeset->add[i])) == 0) {
-			// Removing one or all RRSets
-			if (knot_rrset_class(rr) == KNOT_CLASS_ANY) {
-				dbg_ddns_detail("Removing one or all "
-				                "RRSets\n");
-				if (knot_rrset_type(rr)
-				    == knot_rrset_type(changeset->add[i])
-				    || knot_rrset_type(rr) == KNOT_RRTYPE_ANY) {
-					knot_rrset_t *remove =
-						knot_changeset_remove_rr(
-						    changeset->add,
-						    &changeset->add_count, i);
-					dbg_ddns_detail("Removed RRSet from "
-					                "chgset:\n");
-					knot_rrset_dump(remove);
-					knot_rrset_deep_free(&remove, 1, 1);
-				}
-			} else if (knot_rrset_type(rr)
-			           == knot_rrset_type(changeset->add[i])){
-				/* All other classes are checked in
-				 * knot_ddns_check_update().
-				 */
-				assert(knot_rrset_class(rr) == KNOT_CLASS_NONE);
-
-				// Removing specific RR from a RRSet
-				int ret =
-					knot_rrset_remove_rr_using_rrset_del(changeset->add[i], rr);
-				if (ret != KNOT_EOK) {
-					dbg_ddns("ddns: Could not remove RDATA from RRSet (%s).\n",
-					         knot_strerror(ret));
-					return ret;
-				}
-
-				// if the RRSet is empty, remove from changeset
-				if (knot_rrset_rdata_rr_count(changeset->add[i])
-				    == 0) {
-					knot_rrset_t *remove =
-						knot_changeset_remove_rr(
-						    changeset->add,
-						    &changeset->add_count, i);
-					dbg_ddns_detail("RRSet empty, removing."
-					                "\n");
-					knot_rrset_deep_free(&remove, 1, 1);
-				}
-			}
-		}
-	}
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int knot_ddns_add_update(knot_changeset_t *changeset,
-                                const knot_rrset_t *rrset, uint16_t qclass,
-                                knot_rrset_t **rrset_copy)
-{
-	assert(changeset != NULL);
-	assert(rrset != NULL);
-	assert(rrset_copy != NULL);
-
-	int ret;
-
-	// create a copy of the RRSet
-	/*! \todo ref #937 If the packet was not parsed all at once, we could save this
-	 *        copy.
-	 */
-	*rrset_copy = NULL;
-	ret = knot_rrset_deep_copy(rrset, rrset_copy, 1);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	if (knot_rrset_class(rrset) == qclass) {
-		// this RRSet should be added to the zone
-		dbg_ddns_detail(" * adding RR %p\n", *rrset_copy);
-		ret = knot_changeset_add_rr(&changeset->add,
-		                            &changeset->add_count,
-		                            &changeset->add_allocated,
-		                            *rrset_copy);
-	} else {
-		// this RRSet marks removal of something from zone
-
-		/* To imitate in-order processing of UPDATE RRs, we must check
-		 * If this REMOVE RR does not affect any of the previous
-		 * ADD RRs in this update. If yes, they must be removed from
-		 * the changeset.
-		 *
-		 * See https://git.nic.cz/redmine/issues/937#note-14 and below.
-		 */
-
-		// TODO: finish, disabled for now
-
-		dbg_ddns_detail(" * removing RR %p\n", *rrset_copy);
-
-		ret = knot_ddns_check_remove_rr(changeset, *rrset_copy);
-		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(rrset_copy, 1, 1);
-			return ret;
-		}
-
-		ret = knot_changeset_add_rr(&changeset->remove,
-		                            &changeset->remove_count,
-		                            &changeset->remove_allocated,
-		                            *rrset_copy);
-	}
-
-	return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-
 static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
                                  const knot_rrset_t *rrset, knot_rcode_t *rcode)
 {
@@ -319,7 +201,7 @@ static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
 	assert(knot_rrset_ttl(rrset) == 0);
 	assert(knot_rrset_class(rrset) == KNOT_CLASS_ANY);
 
-	if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+	if (!knot_dname_is_sub(knot_rrset_owner(rrset),
 	    knot_node_owner(knot_zone_contents_apex(zone)))) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
@@ -350,7 +232,7 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
 	assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
 	assert(knot_rrset_ttl(rrset) == 0);
 
-	if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+	if (!knot_dname_is_sub(knot_rrset_owner(rrset),
 	    knot_node_owner(knot_zone_contents_apex(zone)))) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
@@ -370,7 +252,7 @@ static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
 	} else {
 		// do not have to compare the header, it is already done
 		assert(knot_rrset_type(found) == knot_rrset_type(rrset));
-		assert(knot_dname_compare(knot_rrset_owner(found),
+		assert(knot_dname_cmp(knot_rrset_owner(found),
 		                          knot_rrset_owner(rrset)) == 0);
 		if (knot_rrset_rdata_equal(found, rrset) <= 0) {
 			*rcode = KNOT_RCODE_NXRRSET;
@@ -395,7 +277,7 @@ static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
 	assert(knot_rrset_ttl(rrset) == 0);
 	assert(knot_rrset_class(rrset) == KNOT_CLASS_NONE);
 
-	if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+	if (!knot_dname_is_sub(knot_rrset_owner(rrset),
 	    knot_node_owner(knot_zone_contents_apex(zone)))) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
@@ -426,7 +308,7 @@ static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
 	assert(dname != NULL);
 	assert(rcode != NULL);
 
-	if (!knot_dname_is_subdomain(dname,
+	if (!knot_dname_is_sub(dname,
 	    knot_node_owner(knot_zone_contents_apex(zone)))) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
@@ -456,7 +338,7 @@ static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
 	assert(dname != NULL);
 	assert(rcode != NULL);
 
-	if (!knot_dname_is_subdomain(dname,
+	if (!knot_dname_is_sub(dname,
 	    knot_node_owner(knot_zone_contents_apex(zone)))) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
@@ -613,11 +495,17 @@ static int knot_ddns_check_update(const knot_rrset_t *rrset,
 	dbg_ddns("Checking UPDATE packet.\n");
 	const knot_dname_t *owner = knot_rrset_owner(rrset);
 	const knot_dname_t *qname = knot_packet_qname(query);
-	int is_sub = knot_dname_is_subdomain(owner, qname);
-	if (!is_sub && knot_dname_compare(owner, qname) != 0) {
+	int is_sub = knot_dname_is_sub(owner, qname);
+	if (!is_sub && knot_dname_cmp(owner, qname) != 0) {
 		*rcode = KNOT_RCODE_NOTZONE;
 		return KNOT_EOUTOFZONE;
 	}
+	
+	if (knot_rrtype_is_ddns_forbidden(rrset->type)) {
+		*rcode = KNOT_RCODE_REFUSED;
+		log_zone_error("Refusing to update DNSSEC-related record!\n");
+		return KNOT_EDENIED;
+	}
 
 	if (knot_rrset_class(rrset) == knot_packet_qclass(query)) {
 		if (knot_rrtype_is_metatype(knot_rrset_type(rrset))) {
@@ -647,116 +535,6 @@ static int knot_ddns_check_update(const knot_rrset_t *rrset,
 
 /*----------------------------------------------------------------------------*/
 
-int knot_ddns_process_update(const knot_zone_contents_t *zone,
-			     const knot_packet_t *query,
-                             knot_changeset_t *changeset, knot_rcode_t *rcode)
-{
-	// just put all RRSets from query's Authority section
-	// it will be distinguished when applying to the zone
-
-	if (query == NULL || changeset == NULL || rcode == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	int ret = KNOT_EOK;
-
-	/* Copy base SOA query. */
-	const knot_rrset_t *soa = knot_node_rrset(knot_zone_contents_apex(zone),
-						  KNOT_RRTYPE_SOA);
-	knot_rrset_t *soa_begin = NULL;
-	knot_rrset_t *soa_end = NULL;
-	ret = knot_rrset_deep_copy(soa, &soa_begin, 1);
-	if (ret == KNOT_EOK) {
-		knot_changeset_store_soa(&changeset->soa_from,
-		                         &changeset->serial_from, soa_begin);
-	} else {
-		*rcode = KNOT_RCODE_SERVFAIL;
-		return ret;
-	}
-
-	/* Current SERIAL */
-	int64_t sn = knot_rrset_rdata_soa_serial(soa_begin);
-	int64_t sn_new;
-	/* Incremented SERIAL
-	 * We must set it now to be able to compare SERIAL from SOAs in the
-	 * UPDATE to it. Although we do not have the new SOA yet.
-	 */
-	if (sn > -1) {
-		sn_new = (uint32_t)sn + 1;
-	} else {
-		*rcode = KNOT_RCODE_SERVFAIL;
-		return ret;
-	}
-
-	const knot_rrset_t *rrset = NULL;
-	knot_rrset_t *rrset_copy = NULL;
-
-	dbg_ddns("Processing UPDATE section.\n");
-	for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) {
-
-		rrset = knot_packet_authority_rrset(query, i);
-
-		ret = knot_ddns_check_update(rrset, query, rcode);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to check update RRSet:%s\n",
-			                knot_strerror(ret));
-			return ret;
-		}
-
-		ret = knot_ddns_add_update(changeset, rrset,
-		                          knot_packet_qclass(query),
-		                          &rrset_copy);
-
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to add update RRSet:%s\n",
-			                knot_strerror(ret));
-			*rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
-			                             : KNOT_RCODE_SERVFAIL;
-			return ret;
-		}
-
-		/* Check if the added record is SOA. If yes, check the SERIAL.
-		 * If this record should cause the SOA to be replaced in the
-		 * zone, use it as the ending SOA.
-		 *
-		 * Also handle cases where there are multiple SOAs to be added
-		 * in the same UPDATE. The one with the largest SERIAL should
-		 * be used.
-		 *
-		 * TODO: If there are more SOAs in the UPDATE one after another,
-		 *       the ddns_add_update() function will merge them into a
-		 *       RRSet. This should be handled somehow.
-		 */
-		if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA
-		    && ns_serial_compare(knot_rrset_rdata_soa_serial(rrset),
-		                         sn_new) > 0) {
-			sn_new = knot_rrset_rdata_soa_serial(rrset);
-			soa_end = (knot_rrset_t *)rrset_copy;
-		}
-	}
-
-	/* Ending SOA */
-	if (soa_end == NULL) {
-		/* If not set */
-		assert(sn_new == (uint32_t)sn + 1);
-		ret = knot_rrset_deep_copy(soa, &soa_end, 1);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("ddns: Could not copy SOA RRSet (%s).\n ",
-			         knot_strerror(ret));
-			return ret;
-		}
-		knot_rrset_rdata_soa_serial_set(soa_end, sn_new);
-	}
-
-	knot_changeset_store_soa(&changeset->soa_to,
-	                         &changeset->serial_to,
-	                         soa_end);
-
-	return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-
 void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq)
 {
 	dbg_ddns("Freeing prerequisities.\n");
@@ -764,17 +542,17 @@ void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq)
 	int i;
 
 	for (i = 0; i < (*prereq)->exist_count; ++i) {
-		knot_rrset_deep_free(&(*prereq)->exist[i], 1, 1);
+		knot_rrset_deep_free(&(*prereq)->exist[i], 1);
 	}
 	free((*prereq)->exist);
 
 	for (i = 0; i < (*prereq)->exist_full_count; ++i) {
-		knot_rrset_deep_free(&(*prereq)->exist_full[i], 1, 1);
+		knot_rrset_deep_free(&(*prereq)->exist_full[i], 1);
 	}
 	free((*prereq)->exist_full);
 
 	for (i = 0; i < (*prereq)->not_exist_count; ++i) {
-		knot_rrset_deep_free(&(*prereq)->not_exist[i], 1, 1);
+		knot_rrset_deep_free(&(*prereq)->not_exist[i], 1);
 	}
 	free((*prereq)->not_exist);
 
@@ -806,8 +584,9 @@ static int knot_ddns_check_remove_rr2(knot_changeset_t *changeset,
 	assert(removed != NULL);
 	assert(removed_count != NULL);
 
+	/*!< \todo This seems like a waste of memory to me. Also, list_size takes a long time. */
 	*removed_count = 0;
-	*removed = (knot_rrset_t **)malloc(changeset->add_count
+	*removed = (knot_rrset_t **)malloc(list_size(&changeset->add)
 	                                  * sizeof(knot_rrset_t *));
 	if (*removed == NULL) {
 		ERR_ALLOC_FAILED;
@@ -818,46 +597,44 @@ static int knot_ddns_check_remove_rr2(knot_changeset_t *changeset,
 
 	/*
 	 * We assume that each RR in the ADD section of the changeset is in its
-	 * own RRSet. It should be as this is how they are stored there by the
+	 * own RRSet. It should be, as this is how they are stored there by the
 	 * ddns_process_add() function.
 	 */
 
 	dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
-	for (int i = 0; i < changeset->add_count; ++i) {
+	knot_rr_ln_t *rr_node = NULL;
+	node_t *nxt = NULL;
+	WALK_LIST_DELSAFE(rr_node, nxt, changeset->add) {
+		knot_rrset_t *rrset = rr_node->rr;
 		// Removing RR(s) from this owner
 dbg_ddns_exec_detail(
-		char *name = knot_dname_to_str(changeset->add[i]->owner);
+		char *name = knot_dname_to_str(rrset->owner);
 		dbg_ddns_detail("ddns: remove_rr2: Removing RR of type=%u owned by %s\n",
-                                knot_rrset_type(changeset->add[i]), name);
+		                knot_rrset_type(rrset), name);
 		free(name);
 );
-		if (knot_dname_compare_non_canon(knot_rrset_owner(changeset->add[i]),
-		                                 owner) == 0) {
+		if (knot_dname_is_equal(knot_rrset_owner(rrset), owner)) {
 			// Removing one or all RRSets
 			if ((knot_rrset_rdata_rr_count(rr) == 0)
-			    && (knot_rrset_type(rr) == knot_rrset_type(changeset->add[i])
+			    && (knot_rrset_type(rr) == knot_rrset_type(rrset)
 			        || knot_rrset_type(rr) == KNOT_RRTYPE_ANY)) {
 				dbg_ddns_detail("Removing one or all RRSets\n");
-				remove = knot_changeset_remove_rr(
-				              changeset->add,
-				              &changeset->add_count, i);
+				remove = rrset;
+				rem_node((node_t *)rr_node);
+				(*removed)[(*removed_count)++] = remove;
 			} else if (knot_rrset_type(rr) ==
-			           knot_rrset_type(changeset->add[i])) {
+			           knot_rrset_type(rrset)) {
 				// Removing specific RR
 				assert(knot_rrset_rdata_rr_count(rr) != 0);
 
 				// We must check if the RDATA match
 				if (knot_rrset_rdata_equal(rr,
-				                           changeset->add[i])) {
-					remove = knot_changeset_remove_rr(
-					              changeset->add,
-					              &changeset->add_count, i);
+				                           rrset)) {
+					remove = rrset;
+					rem_node((node_t *)rr_node);
+					(*removed)[(*removed_count)++] = remove;
 				}
 			}
-
-			dbg_ddns_detail("Removed RRSet from chgset:\n");
-			knot_rrset_dump(remove);
-			(*removed)[(*removed_count)++] = remove;
 		}
 	}
 
@@ -877,17 +654,20 @@ static void knot_ddns_check_add_rr(knot_changeset_t *changeset,
 	*removed = NULL;
 
 	dbg_ddns_verb("Removing possible redundant RRs from changeset.\n");
-	for (int i = 0; i < changeset->remove_count; ++i)  {
+	knot_rr_ln_t *rr_node = NULL;
+	node_t *nxt = NULL;
+	WALK_LIST_DELSAFE(rr_node, nxt, changeset->remove) {
+		knot_rrset_t *rrset = rr_node->rr;
+		assert(rrset);
 		/* Just check exact match, the changeset contains only
 		 * whole RRs that have been removed.
 		 */
-		if (knot_rrset_equal(rr, changeset->remove[i],
+		if (knot_rrset_equal(rr, rrset,
 		                     KNOT_RRSET_COMPARE_WHOLE) == 1) {
-			*removed = knot_changeset_remove_rr(
-			                        changeset->remove,
-			                        &changeset->remove_count, i);
+			*removed = rrset;
+			rem_node((node_t *)rr_node);
 			dbg_ddns_detail("Removed RRSet from chgset:\n");
-			knot_rrset_dump(*removed);
+			knot_rrset_dump(rrset);
 			break;
 		}
 	}
@@ -949,13 +729,27 @@ static int knot_ddns_process_add_cname(knot_node_t *node,
 		 */
 
 		/* b) Store it to 'changes', together with its RRSIGs. */
-		ret = knot_changes_add_old_rrsets(&removed, 1, changes, 1);
+		ret = knot_changes_add_rrset(changes, removed, KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to add removed RRSet to "
 			         "'changes': %s\n", knot_strerror(ret));
 			return ret;
 		}
 
+		if (removed->rrsigs) {
+			ret = knot_changes_add_rrset(changes,
+			                             removed->rrsigs,
+			                             KNOT_CHANGES_OLD);
+			if (ret != KNOT_EOK) {
+				dbg_ddns("Failed to add removed RRSIGs to "
+				         "'changes': %s\n", knot_strerror(ret));
+				return ret;
+			}
+			/* Disconnect RRsigs from rrset. */
+			knot_rrset_set_rrsigs(removed, NULL);
+		}
+
+
 		/* c) And remove it from the node. */
 		UNUSED(knot_node_remove_rrset(node, KNOT_RRTYPE_CNAME));
 
@@ -980,7 +774,7 @@ static int knot_ddns_process_add_cname(knot_node_t *node,
 
 		if (from_chgset_count == 1) {
 			/* Just delete the RRSet. */
-			knot_rrset_deep_free(&(from_chgset[0]), 1, 1);
+			knot_rrset_deep_free(&(from_chgset[0]), 1);
 			/* Okay, &(from_chgset[0]) is basically equal to just
 			 * from_chgset, but it's more clear this way that we are
 			 * deleting the first RRSet in the array ;-)
@@ -990,8 +784,7 @@ static int knot_ddns_process_add_cname(knot_node_t *node,
 			 * to the REMOVE section.
 			 */
 			knot_rrset_t *removed_copy;
-			ret = knot_rrset_deep_copy(removed,
-			                           &removed_copy, 1);
+			ret = knot_rrset_deep_copy(removed, &removed_copy);
 			if (ret != KNOT_EOK) {
 				dbg_ddns("Failed to copy removed RRSet:"
 				         " %s\n", knot_strerror(ret));
@@ -1000,13 +793,10 @@ static int knot_ddns_process_add_cname(knot_node_t *node,
 			}
 
 			ret = knot_changeset_add_rrset(
-				&changeset->remove,
-				&changeset->remove_count,
-				&changeset->remove_allocated,
-				removed_copy);
+				changeset, removed_copy, KNOT_CHANGESET_REMOVE);
 			if (ret != KNOT_EOK) {
 				knot_rrset_deep_free(&removed_copy,
-				                     1, 1);
+				                     1);
 				dbg_ddns("Failed to add removed CNAME "
 				         "to changeset: %s\n",
 				         knot_strerror(ret));
@@ -1058,18 +848,30 @@ static int knot_ddns_process_add_soa(knot_node_t *node,
 		}
 
 		/* Check that the serial is indeed larger than the current one*/
-		assert(ns_serial_compare(knot_rrset_rdata_soa_serial(removed),
-		                         knot_rrset_rdata_soa_serial(rr)) < 0);
+		assert(ns_serial_compare(knot_rdata_soa_serial(removed),
+		                         knot_rdata_soa_serial(rr)) < 0);
 
 		/* 1) Store it to 'changes', together with its RRSIGs. */
-		ret = knot_changes_add_old_rrsets(
-		                        &removed, 1, changes, 1);
+		ret = knot_changes_add_rrset(changes, removed, KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to add removed RRSet to "
 			         "'changes': %s\n", knot_strerror(ret));
 			return ret;
 		}
 
+		if (removed->rrsigs) {
+			ret = knot_changes_add_rrset(changes,
+			                             removed->rrsigs,
+			                             KNOT_CHANGES_OLD);
+			if (ret != KNOT_EOK) {
+				dbg_ddns("Failed to add removed RRSIGs to "
+				         "'changes': %s\n", knot_strerror(ret));
+				return ret;
+			}
+			/* Disconnect RRsigs from rrset. */
+			knot_rrset_set_rrsigs(removed, NULL);
+		}
+
 		/* 2) And remove it from the node. */
 		UNUSED(knot_node_remove_rrset(node, KNOT_RRTYPE_SOA));
 
@@ -1095,9 +897,10 @@ static int knot_ddns_add_rr_new_normal(knot_node_t *node, knot_rrset_t *rr_copy,
 	dbg_ddns_verb("Adding normal RR.\n");
 
 	/* Add the RRSet to 'changes'. */
-	int ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1);
+
+	int ret = knot_changes_add_rrset(changes, rr_copy, KNOT_CHANGES_NEW);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&rr_copy, 1, 1);
+		knot_rrset_deep_free(&rr_copy, 1);
 		dbg_ddns("Failed to store copy of the added RR: "
 		         "%s\n", knot_strerror(ret));
 		return ret;
@@ -1115,70 +918,6 @@ static int knot_ddns_add_rr_new_normal(knot_node_t *node, knot_rrset_t *rr_copy,
 
 /*----------------------------------------------------------------------------*/
 
-static int knot_ddns_add_rr_new_rrsig(knot_node_t *node, knot_rrset_t *rr_copy,
-                                      knot_changes_t *changes,
-                                      uint16_t type_covered)
-{
-	assert(node != NULL);
-	assert(rr_copy != NULL);
-	assert(changes != NULL);
-
-	dbg_ddns_verb("Adding RRSIG RR.\n");
-
-	/* Create RRSet to be covered by the RRSIG. */
-	knot_rrset_t *covered_rrset = knot_rrset_new(
-	                        knot_rrset_get_owner(rr_copy), type_covered,
-	                        knot_rrset_class(rr_copy),
-	                        knot_rrset_ttl(rr_copy));
-	if (covered_rrset == NULL) {
-		dbg_ddns("Failed to create RRSet to be covered"
-			 " by the UPDATE RRSIG RR.\n");
-		knot_rrset_deep_free(&rr_copy, 1, 1);
-		return KNOT_ENOMEM;
-	}
-
-	/* Add the RRSet to the node. */
-	int ret = knot_node_add_rrset_no_merge(node, covered_rrset);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to add the RRSet to be covered to the node: %s"
-		         ".\n", knot_strerror(ret));
-		knot_rrset_deep_free(&rr_copy, 1, 1);
-		knot_rrset_deep_free(&covered_rrset, 1, 1);
-		return KNOT_ENOMEM;
-	}
-
-	/* Add the RRSet to 'changes'. */
-	ret = knot_changes_add_new_rrsets(&covered_rrset, 1, changes, 0);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add new RRSet (covered) to list: %s.\n",
-		          knot_strerror(ret));
-		knot_rrset_deep_free(&rr_copy, 1, 1);
-		knot_rrset_deep_free(&covered_rrset, 1, 1);
-		return ret;
-	}
-
-	/* Add the RRSIG RRSet to 'changes'. */
-	ret = knot_changes_add_new_rrsets(&rr_copy, 1, changes, 1);
-	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&rr_copy, 1, 1);
-		dbg_ddns("Failed to store copy of the added RRSIG: %s\n",
-			 knot_strerror(ret));
-		return ret;
-	}
-
-	/* Add the RRSIG RRSet to the covered RRSet. */
-	ret = knot_rrset_add_rrsigs(covered_rrset, rr_copy,
-	                            KNOT_RRSET_DUPL_SKIP);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to add RRSIG RR to the covered RRSet.\n");
-		return ret;
-	}
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
 static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy,
                                          knot_rrset_t **rr_copy)
 {
@@ -1201,9 +940,9 @@ static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy,
 	}
 
 	int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy);
-	int merged, deleted_rrs;
-	int ret = knot_rrset_merge_no_dupl(node_rrset_copy, *rr_copy, &merged,
-	                                   &deleted_rrs);
+	int merged = 0, deleted_rrs = 0;
+	int ret = knot_rrset_merge_sort(node_rrset_copy, *rr_copy, &merged,
+	                                &deleted_rrs);
 	dbg_ddns_detail("Merge returned: %d\n", ret);
 
 	if (ret != KNOT_EOK) {
@@ -1212,7 +951,7 @@ static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy,
 		return ret;
 	}
 
-	knot_rrset_deep_free(rr_copy, 1, 0);
+	knot_rrset_deep_free(rr_copy, 1);
 
 
 	if (rdata_in_copy == deleted_rrs) {
@@ -1226,91 +965,6 @@ static int knot_ddns_add_rr_merge_normal(knot_rrset_t *node_rrset_copy,
 	return KNOT_EOK;
 }
 
-/*----------------------------------------------------------------------------*/
-
-static int knot_ddns_add_rr_merge_rrsig(knot_rrset_t *node_rrset_copy,
-                                        knot_rrset_t **rr_copy,
-                                        knot_changes_t *changes)
-{
-	assert(node_rrset_copy != NULL);
-	assert(rr_copy != NULL);
-	assert(*rr_copy != NULL);
-	assert(changes != NULL);
-
-	dbg_ddns_verb("Adding RRSIG RR to existing RRSet.\n");
-
-	knot_rrset_t *rrsigs_old = knot_rrset_get_rrsigs(node_rrset_copy);
-	int ret = 0;
-
-	if (rrsigs_old != NULL) {
-		/* If there is an RRSIG RRSet already, copy it too. */
-		knot_rrset_t *rrsigs_copy = NULL;
-		ret = xfrin_copy_old_rrset(rrsigs_old, &rrsigs_copy,
-		                           changes, 1);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to copy RRSIG RRSet: "
-			         "%s\n", knot_strerror(ret));
-			return ret;
-		}
-
-		/* Replace the RRSIGs by the copy. */
-		ret = knot_rrset_set_rrsigs(node_rrset_copy, rrsigs_copy);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to replace RRSIGs in "
-			         "the RRSet: %s\n",
-			         knot_strerror(ret));
-			return ret;
-		}
-
-		/* Merge the UPDATE RR to the copied RRSIG
-		 * RRSet.
-		 */
-		dbg_ddns_detail("Merging RRSIG to the one in the RRSet.\n");
-
-		int rdata_in_copy = knot_rrset_rdata_rr_count(*rr_copy);
-		int merged, deleted_rrs;
-		ret = knot_rrset_merge_no_dupl(rrsigs_copy, *rr_copy, &merged,
-		                               &deleted_rrs);
-		if (ret < 0) {
-			dbg_xfrin("Failed to merge UPDATE RRSIG to copy: %s.\n",
-			          knot_strerror(ret));
-			return KNOT_ERROR;
-		}
-		knot_rrset_deep_free(rr_copy, 1, 0);
-
-
-		if (rdata_in_copy == deleted_rrs) {
-			/* All RDATA have been removed, because they were
-			 * duplicates or there were none (0). In general this
-			 * means, that no change was made.
-			 */
-			return 1;
-		}
-	} else {
-		/* If there is no RRSIG RRSet yet, just add the
-		 * UPDATE RR to the copied covered RRSet.
-		 */
-		/* Add the RRSet to 'changes'. */
-		ret = knot_changes_add_new_rrsets(rr_copy, 1, changes, 1);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to store copy of the added RR: %s\n",
-			         knot_strerror(ret));
-			return ret;
-		}
-
-		/* Add the RRSet to the covered RRSet. */
-		ret = knot_rrset_add_rrsigs(node_rrset_copy, *rr_copy,
-		                            KNOT_RRSET_DUPL_SKIP);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to add RRSIG RR to the"
-			         " covered RRSet.\n");
-			return ret;
-		}
-	}
-
-	return KNOT_EOK;
-}
-
 /*----------------------------------------------------------------------------*/
 /*!
  * \todo We should check, how it's possible that IXFR is not leaking due to the
@@ -1329,37 +983,26 @@ static int knot_ddns_add_rr(knot_node_t *node, const knot_rrset_t *rr,
 
 	/* Copy the RRSet from the packet. */
 	//knot_rrset_t *rr_copy;
-	int ret = knot_rrset_deep_copy(rr, rr_copy, 1);
+	int ret = knot_rrset_deep_copy(rr, rr_copy);
 	if (ret != KNOT_EOK) {
 		dbg_ddns("Failed to copy RR: %s\n", knot_strerror(ret));
 		return ret;
 	}
 
-	uint16_t type = knot_rrset_type(rr);
-	uint16_t type_covered = (type == KNOT_RRTYPE_RRSIG)
-	                ? knot_rrset_rdata_rrsig_type_covered(rr)
-	                : type;
-
 	/* If the RR belongs to a RRSet already present in the node, we must
 	 * take this RRSet from the node, copy it, and merge this RR into it.
 	 *
 	 * This code is more or less copied from xfr-in.c.
 	 */
 	knot_rrset_t *node_rrset_copy = NULL;
-	ret = xfrin_copy_rrset(node, type_covered, &node_rrset_copy, changes,
+	ret = xfrin_copy_rrset(node, rr->type, &node_rrset_copy, changes,
 	                       0);
 
 	if (node_rrset_copy == NULL) {
 		/* No such RRSet in the node. Add the whole UPDATE RRSet. */
 		dbg_ddns_detail("Adding whole UPDATE RR to the zone.\n");
-		if (type_covered != type) {
-			/* Adding RRSIG. */
-			ret = knot_ddns_add_rr_new_rrsig(node, *rr_copy,
-			                                 changes, type_covered);
-		} else {
-			ret = knot_ddns_add_rr_new_normal(node, *rr_copy,
-			                                  changes);
-		}
+		ret = knot_ddns_add_rr_new_normal(node, *rr_copy,
+		                                  changes);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to add new RR to node.\n");
 			return ret;
@@ -1374,14 +1017,7 @@ dbg_ddns_exec_detail(
 		knot_rrset_dump(*rr_copy);
 );
 
-		if (type_covered != type) {
-			/* Adding RRSIG. */
-			ret = knot_ddns_add_rr_merge_rrsig(node_rrset_copy,
-			                                   rr_copy, changes);
-		} else {
-			ret = knot_ddns_add_rr_merge_normal(node_rrset_copy,
-			                                    rr_copy);
-		}
+		ret = knot_ddns_add_rr_merge_normal(node_rrset_copy, rr_copy);
 
 dbg_ddns_exec_detail(
 		dbg_ddns_detail("After merge:\n");
@@ -1390,18 +1026,18 @@ dbg_ddns_exec_detail(
 
 		if (ret < KNOT_EOK) {
 			dbg_ddns("Failed to merge UPDATE RR to node RRSet.\n");
-			knot_rrset_deep_free(rr_copy, 1, 1);
-			knot_rrset_deep_free(&node_rrset_copy, 1, 1);
+			knot_rrset_deep_free(rr_copy, 1);
+			knot_rrset_deep_free(&node_rrset_copy, 1);
 			return ret;
 		}
 
 		// save the new RRSet together with the new RDATA to 'changes'
 		// do not overwrite 'ret', it have to be returned
-		int r = knot_changes_add_new_rrsets(&node_rrset_copy, 1,
-		                                    changes, 1);
+		int r = knot_changes_add_rrset(changes, node_rrset_copy,
+		                               KNOT_CHANGES_NEW);
 		if (r != KNOT_EOK) {
 			dbg_ddns("Failed to store RRSet copy to 'changes'\n");
-			knot_rrset_deep_free(&node_rrset_copy, 1, 1);
+			knot_rrset_deep_free(&node_rrset_copy, 1);
 			return r;
 		}
 	}
@@ -1419,16 +1055,14 @@ static int knot_ddns_final_soa_to_chgset(const knot_rrset_t *soa,
 	assert(changeset != NULL);
 
 	knot_rrset_t *soa_copy = NULL;
-	int ret = knot_rrset_deep_copy(soa, &soa_copy, 1);
+	int ret = knot_rrset_deep_copy(soa, &soa_copy);
 	if (ret != KNOT_EOK) {
 		dbg_ddns("Failed to copy SOA RR to the changeset: "
 			 "%s\n", knot_strerror(ret));
 		return ret;
 	}
 
-	knot_changeset_store_soa(&changeset->soa_to,
-	                         &changeset->serial_to,
-	                         soa_copy);
+	knot_changeset_add_soa(changeset, soa_copy, KNOT_CHANGESET_ADD);
 
 	return KNOT_EOK;
 }
@@ -1445,25 +1079,23 @@ static int knot_ddns_add_rr_to_chgset(const knot_rrset_t *rr,
 	knot_rrset_t *chgset_rr = NULL;
 	knot_ddns_check_add_rr(changeset, rr, &chgset_rr);
 	if (chgset_rr == NULL) {
-		ret = knot_rrset_deep_copy(rr, &chgset_rr, 1);
+		ret = knot_rrset_deep_copy(rr, &chgset_rr);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to copy RR to the changeset: "
 				 "%s\n", knot_strerror(ret));
 			return ret;
 		}
 		/* No such RR in the changeset, add it. */
-		ret = knot_changeset_add_rrset(&changeset->add,
-		                               &changeset->add_count,
-		                               &changeset->add_allocated,
-		                               chgset_rr);
+		ret = knot_changeset_add_rrset(changeset, chgset_rr,
+		                               KNOT_CHANGESET_ADD);
 		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&chgset_rr, 1, 1);
+			knot_rrset_deep_free(&chgset_rr, 1);
 			dbg_ddns("Failed to add RR to changeset: %s.\n",
 				 knot_strerror(ret));
 			return ret;
 		}
 	} else {
-		knot_rrset_deep_free(&chgset_rr, 1, 1);
+		knot_rrset_deep_free(&chgset_rr, 1);
 	}
 
 	return KNOT_EOK;
@@ -1623,10 +1255,8 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 	/*
 	 * 1) Copy the RRSet.
 	 */
-	uint16_t type_to_copy = (type != KNOT_RRTYPE_RRSIG) ? type
-	                : knot_rrset_rdata_rrsig_type_covered(rr);
 	knot_rrset_t *rrset_copy = NULL;
-	int ret = xfrin_copy_rrset(node, type_to_copy, &rrset_copy, changes, 1);
+	int ret = xfrin_copy_rrset(node, type, &rrset_copy, changes, 1);
 	if (ret < 0) {
 		dbg_ddns("Failed to copy RRSet for removal: %s\n",
 		         knot_strerror(ret));
@@ -1642,32 +1272,10 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 	 * Set some variables needed, according to the modified RR type.
 	 */
 
-	int rdata_count = 0;
-	knot_rrset_t *to_modify;
-	if (type == KNOT_RRTYPE_RRSIG) {
-		rdata_count = knot_rrset_rdata_rr_count(knot_rrset_rrsigs(rrset_copy));
-		to_modify = knot_rrset_get_rrsigs(rrset_copy);
-	} else {
-		rdata_count = knot_rrset_rdata_rr_count(rrset_copy);
-		to_modify = rrset_copy;
-	}
-
-	/*
-	 * 1.5) Prepare place for the removed RDATA.
-	 *      We don't know if there are some, but if this fails, at least we
-	 *      haven't removed them yet.
-	 */
-	ret = knot_changes_rdata_reserve(&changes->old_rdata,
-	                                 changes->old_rdata_count,
-	                                 &changes->old_rdata_allocated,
-	                                 rdata_count);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to reserve place for RDATA.\n");
-		return ret;
-	}
+	knot_rrset_t *to_modify = rrset_copy;
 
 	/*
-	 * 2) Remove the proper RDATA from the RRSet copy, or its RRSIGs.
+	 * 2) Remove the proper RDATA from the RRSet copy
 	 */
 	knot_rrset_t *rr_remove = NULL;
 	ret = knot_rrset_remove_rr_using_rrset(to_modify, rr, &rr_remove, 0);
@@ -1689,64 +1297,43 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 	       || knot_rrset_rdata_rr_count(rrset_copy));
 
 	/*
-	 * 3) Store the removed RDATA in 'changes'.
+	 * 3) Store the removed data in 'changes'.
 	 */
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-	                       rr_remove);
+	ret = knot_changes_add_rrset(changes, rr_remove, KNOT_CHANGES_OLD);
+	if (ret != KNOT_EOK) {
+		knot_rrset_deep_free(&rr_remove, 1);
+		dbg_ddns_detail("Failed to add data to changes.\n");
+		return ret;
+	}
 
 	/*
 	 * 4) If the RRSet is empty, remove it and store in 'changes'.
-	 *    Do this also if the RRSIGs are empty.
-	 *    And if both are empty, remove both.
-	 *    RRSIG handling first,
 	 */
-	if (type == KNOT_RRTYPE_RRSIG &&
-	    knot_rrset_rdata_rr_count(to_modify) == 0) {
-		/* Empty RRSIGs, remove the RRSIG RRSet */
-		ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-		                                 &changes->old_rrsets_count,
-		                                 &changes->old_rrsets_allocated,
-		                                 1);
-		if (ret == KNOT_EOK) {
-			knot_rrset_t *rrsig = knot_rrset_get_rrsigs(rrset_copy);
-			dbg_xfrin_detail("Removed RRSIG RRSet (%p).\n", rrsig);
-
-			assert(rrsig && rrsig == to_modify);
-
-			// add the removed RRSet to list of old RRSets
-			changes->old_rrsets[changes->old_rrsets_count++]
-			                = rrsig;
-
-			// remove it from the RRSet
-			knot_rrset_set_rrsigs(rrset_copy, NULL);
-		} else {
-			dbg_ddns("Failed to reserve space for empty RRSet.\n");
-		}
-	}
-
-	// Remove empty RRSet from node and store to changeset
-	if (type != KNOT_RRTYPE_RRSIG &&
-	    knot_rrset_rdata_rr_count(to_modify) == 0) {
+	if (knot_rrset_rdata_rr_count(to_modify) == 0) {
 		// The RRSet should not be empty if we were removing NSs from
 		// apex in case of DDNS
-		assert(!is_apex);
-
-		ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-		                                 &changes->old_rrsets_count,
-		                                 &changes->old_rrsets_allocated,
-		                                 1);
-		if (ret == KNOT_EOK) {
-			knot_rrset_t *tmp = knot_node_remove_rrset(node, type);
-			dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp);
-
-			assert(tmp == rrset_copy);
-
-			// add the removed RRSet to list of old RRSets
-			changes->old_rrsets[changes->old_rrsets_count++]
-			                = rrset_copy;
-		} else {
-			dbg_ddns("Failed to reserve space for empty RRSet.\n");
+//		assert(!is_apex);
+		// add the removed RRSet to list of old RRSets
+		ret = knot_changes_add_rrset(changes, rrset_copy,
+		                             KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			dbg_ddns("Failed to add RRSet to changes.\n");
+			return ret;
+		}
+		
+		// Do the same with its RRSIGs (automatic drop)
+		if (rrset_copy->rrsigs) {
+			ret = knot_changes_add_rrset(changes,
+			                             rrset_copy->rrsigs,
+			                             KNOT_CHANGES_OLD);
+			if (ret != KNOT_EOK) {
+				dbg_ddns("Failed to add RRSet to changes.\n");
+				return ret;
+			}
 		}
+		knot_rrset_t *tmp = knot_node_remove_rrset(node, type);
+		dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp);
+		assert(tmp == rrset_copy);
 	}
 
 	/*
@@ -1768,7 +1355,7 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 
 	if (from_chgset_count == 1) {
 		/* Just delete the RRSet. */
-		knot_rrset_deep_free(&(from_chgset[0]), 1, 1);
+		knot_rrset_deep_free(&(from_chgset[0]), 1);
 
 		/* Finish processing, no adding to changeset. */
 		free(from_chgset);
@@ -1783,7 +1370,7 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 	 *    and TTL.
 	 */
 	knot_rrset_t *to_chgset = NULL;
-	ret = knot_rrset_deep_copy(rr, &to_chgset, 1);
+	ret = knot_rrset_deep_copy(rr, &to_chgset);
 	if (ret != KNOT_EOK) {
 		dbg_ddns("Failed to copy RRSet from packet to changeset.\n");
 		return ret;
@@ -1791,53 +1378,12 @@ static int knot_ddns_process_rem_rr(const knot_rrset_t *rr,
 	knot_rrset_set_class(to_chgset, qclass);
 	knot_rrset_set_ttl(to_chgset, knot_rrset_ttl(to_modify));
 
-	ret = knot_changeset_add_rrset(&changeset->remove,
-	                               &changeset->remove_count,
-	                               &changeset->remove_allocated,
-	                               to_chgset);
+	ret = knot_changeset_add_rrset(changeset, to_chgset,
+	                               KNOT_CHANGESET_REMOVE);
 	if (ret != KNOT_EOK) {
 		dbg_ddns("Failed to store the RRSet copy to changeset: %s.\n",
 		         knot_strerror(ret));
-		knot_rrset_deep_free(&to_chgset, 1, 1);
-		return ret;
-	}
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int knot_ddns_process_rem_rrsig(knot_node_t *node,
-                                       knot_rrset_t *rrset,
-                                       knot_changes_t *changes,
-                                       knot_rrset_t **rrsig)
-{
-	assert(node != NULL);
-	assert(rrset != NULL);
-	assert(changes != NULL);
-
-	knot_rrset_t *rrset_copy = NULL;
-
-	/* Copy RRSet. */
-	int ret = xfrin_copy_old_rrset(rrset, &rrset_copy, changes, 1);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to copy RRSet from node: %s.\n",
-		         knot_strerror(ret));
-		return ret;
-	}
-
-	/* Remove RRSIGs from the copy. */
-	*rrsig = knot_rrset_get_rrsigs(rrset_copy);
-	if (*rrsig != NULL) {
-		knot_rrset_set_rrsigs(rrset_copy, NULL);
-	}
-
-	/* Put the copy to the node. */
-	ret = knot_node_add_rrset_no_merge(node, rrset_copy);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to add RRSet copy to the node: %s\n",
-		         knot_strerror(ret));
-		knot_rrset_deep_free(&rrset_copy, 1, 1);
+		knot_rrset_deep_free(&to_chgset, 1);
 		return ret;
 	}
 
@@ -1846,70 +1392,6 @@ static int knot_ddns_process_rem_rrsig(knot_node_t *node,
 
 /*----------------------------------------------------------------------------*/
 
-static int knot_ddns_process_rem_rrsigs(knot_node_t *node,
-                                        knot_changes_t *changes,
-                                        knot_rrset_t ***removed,
-                                        size_t *removed_count)
-{
-	assert(node != NULL);
-	assert(removed != NULL);
-	assert(removed_count != NULL);
-	assert(changes != NULL);
-
-	/* If removing RRSIGs, we must remove them from all RRSets in
-	 * the node. This means to copy all RRSets and then remove the
-	 * RRSIGs from them.
-	 */
-	dbg_ddns_verb("Removing all RRSIGs from node.\n");
-
-	knot_rrset_t **rrsets = knot_node_get_rrsets(node);
-	if (rrsets == NULL) {
-		// No RRSets in the node, nothing to remove
-		return KNOT_EOK;
-	}
-
-	/* Allocate space for the removed RRSIGs. There may be as many as there
-	 * are RRSets.
-	 */
-	short rrset_count = knot_node_rrset_count(node);
-
-	*removed = malloc(rrset_count * sizeof(knot_rrset_t *));
-	if (*removed == NULL) {
-		ERR_ALLOC_FAILED;
-		free(rrsets);
-		return KNOT_ENOMEM;
-	}
-	*removed_count = 0;
-
-	/* Remove all the RRSets from the node, so that we may insert the copies
-	 * right away.
-	 */
-	knot_node_remove_all_rrsets(node);
-
-	knot_rrset_t *rrsig = NULL;
-	int ret = 0;
-	for (int i = 0; i < rrset_count; ++i) {
-		ret = knot_ddns_process_rem_rrsig(node, rrsets[i], changes,
-		                                  &rrsig);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to remove RRSIG.\n");
-			free(rrsets);
-			free(*removed);
-			*removed = NULL;
-			return ret;
-		}
-		/* Store the RRSIGs to the array of removed RRSets. */
-		(*removed)[(*removed_count)++] = rrsig;
-	}
-
-	free(rrsets);
-
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
 static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
                                        knot_node_t *node,
                                        knot_changeset_t *changeset,
@@ -1952,31 +1434,20 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 	size_t removed_count = 0;
 	int ret = 0;
 
-	if (type == KNOT_RRTYPE_RRSIG) {
-		/* Remove all RRSIGs from the node. */
-		ret = knot_ddns_process_rem_rrsigs(node, changes, &removed,
-		                                   &removed_count);
-		if (ret != KNOT_EOK) {
-			dbg_ddns("Failed to remove RRSIGs. (%s)\n",
-			         knot_strerror(ret));
-			return ret;
-		}
-	} else {
-		/* Remove the RRSet from the node. */
-		removed = malloc(sizeof(knot_rrset_t *));
-		if (!removed) {
-			ERR_ALLOC_FAILED;
-			return KNOT_ENOMEM;
-		}
+	/* Remove the RRSet from the node. */
+	removed = malloc(sizeof(knot_rrset_t *));
+	if (!removed) {
+		ERR_ALLOC_FAILED;
+		return KNOT_ENOMEM;
+	}
 
-		dbg_ddns_detail("Removing RRSet of type: %d\n", type);
+	dbg_ddns_detail("Removing RRSet of type: %d\n", type);
 
-		*removed = knot_node_remove_rrset(node, type);
-		if (*removed != NULL) {
-			removed_count = 1;
-		} else {
-			removed_count = 0;
-		}
+	*removed = knot_node_remove_rrset(node, type);
+	if (*removed != NULL) {
+		removed_count = 1;
+	} else {
+		removed_count = 0;
 	}
 
 	dbg_ddns_detail("Removed: %p (first item: %p), removed count: %zu\n",
@@ -1993,12 +1464,29 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 	/* 2) Store them to 'changes' for later deallocation, together with
 	 *    their RRSIGs.
 	 */
-	ret = knot_changes_add_old_rrsets(removed, removed_count, changes, 1);
-	if (ret != KNOT_EOK) {
-		dbg_ddns("Failed to add removed RRSet to 'changes': %s.\n",
-		         knot_strerror(ret));
-		free(removed);
-		return ret;
+	for (uint i = 0; i < removed_count; ++i) {
+		ret = knot_changes_add_rrset(changes, removed[i],
+		                             KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			dbg_ddns("Failed to add removed "
+			         "RRSet to 'changes': %s.\n",
+			         knot_strerror(ret));
+			free(removed);
+			return ret;
+		}
+
+		if (removed[i]->rrsigs) {
+			ret = knot_changes_add_rrset(changes,
+			                             removed[i]->rrsigs,
+			                             KNOT_CHANGES_OLD);
+			if (ret != KNOT_EOK) {
+				dbg_ddns("Failed to add removed RRSIGs to "
+				         "'changes': %s\n", knot_strerror(ret));
+				return ret;
+			}
+			/* Disconnect RRsigs from rrset. */
+			knot_rrset_set_rrsigs(removed[i], NULL);
+		}
 	}
 
 	/* 3) Copy the RRSets, so that they can be stored to the changeset. */
@@ -2012,12 +1500,12 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 	}
 
 	for (int i = 0; i < removed_count; ++i) {
-		ret = knot_rrset_deep_copy(removed[i], &to_chgset[i], 1);
+		ret = knot_rrset_deep_copy(removed[i], &to_chgset[i]);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to copy the removed RRSet: %s.\n",
 			         knot_strerror(ret));
 			for (int j = 0; j < i; ++j) {
-				knot_rrset_deep_free(&to_chgset[j], 1, 1);
+				knot_rrset_deep_free(&to_chgset[j], 1);
 			}
 			free(to_chgset);
 			free(removed);
@@ -2036,9 +1524,12 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 	size_t from_chgset_count = 0;
 
 	/* 4 a) Remove redundant RRs from the ADD section of the changeset. */
+	knot_dname_t *owner_copy = knot_dname_copy(rrset->owner);
 	knot_rrset_t *empty_rrset =
-		knot_rrset_new(rrset->owner, type, rrset->rclass, rrset->ttl);
+		knot_rrset_new(owner_copy, type, rrset->rclass, rrset->ttl);
 	if (empty_rrset == NULL) {
+		free(to_chgset);
+		knot_dname_free(&owner_copy);
 		return KNOT_ENOMEM;
 	}
 	ret = knot_ddns_check_remove_rr2(changeset, knot_node_owner(node),
@@ -2048,7 +1539,7 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 		dbg_ddns("Failed to remove possible redundant RRs from ADD "
 		         "section: %s.\n", knot_strerror(ret));
 		for (int i = 0; i < removed_count; ++i) {
-			knot_rrset_deep_free(&to_chgset[i], 1, 1);
+			knot_rrset_deep_free(&to_chgset[i], 1);
 		}
 		free(from_chgset);
 		free(to_chgset);
@@ -2080,7 +1571,7 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 
 	/* The array is cleared, we may delete the redundant RRs. */
 	for (int i = 0; i < from_chgset_count; ++i) {
-		knot_rrset_deep_free(&from_chgset[i], 1, 1);
+		knot_rrset_deep_free(&from_chgset[i], 1);
 	}
 	free(from_chgset);
 
@@ -2088,15 +1579,13 @@ static int knot_ddns_process_rem_rrset(const knot_rrset_t *rrset,
 	 *    to some previous RRSet, there should be none.
 	 */
 	for (int i = 0; i < removed_count; ++i) {
-		ret = knot_changeset_add_rrset(&changeset->remove,
-		                               &changeset->remove_count,
-		                               &changeset->remove_allocated,
-		                               to_chgset[i]);
+		ret = knot_changeset_add_rrset(changeset, to_chgset[i],
+		                               KNOT_CHANGESET_REMOVE);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to store the RRSet copy to changeset: "
 			         "%s.\n", knot_strerror(ret));
 			for (int j = i; j < removed_count; ++j) {
-				knot_rrset_deep_free(&to_chgset[j], 1, 1);
+				knot_rrset_deep_free(&to_chgset[j], 1);
 			}
 			free(to_chgset);
 			return ret;
@@ -2225,11 +1714,11 @@ static int knot_ddns_process_rr(const knot_rrset_t *rr,
  * If anything fails, rollback must be done. The xfrin_rollback_update() may
  * be good for this.
  */
-int knot_ddns_process_update2(knot_zone_contents_t *zone,
-                              const knot_packet_t *query,
-                              knot_changeset_t *changeset,
-                              knot_changes_t *changes,
-                              knot_rcode_t *rcode)
+int knot_ddns_process_update(knot_zone_contents_t *zone,
+                             const knot_packet_t *query,
+                             knot_changeset_t *changeset,
+                             knot_changes_t *changes,
+                             knot_rcode_t *rcode)
 {
 	if (zone == NULL || query == NULL || changeset == NULL || rcode == NULL
 	    || changes == NULL) {
@@ -2243,17 +1732,17 @@ int knot_ddns_process_update2(knot_zone_contents_t *zone,
 						  KNOT_RRTYPE_SOA);
 	knot_rrset_t *soa_begin = NULL;
 	knot_rrset_t *soa_end = NULL;
-	ret = knot_rrset_deep_copy(soa, &soa_begin, 1);
+	ret = knot_rrset_deep_copy(soa, &soa_begin);
 	if (ret == KNOT_EOK) {
-		knot_changeset_store_soa(&changeset->soa_from,
-		                         &changeset->serial_from, soa_begin);
+		knot_changeset_add_soa(changeset, soa_begin,
+		                       KNOT_CHANGESET_REMOVE);
 	} else {
 		*rcode = KNOT_RCODE_SERVFAIL;
 		return ret;
 	}
 
 	/* Current SERIAL */
-	int64_t sn = knot_rrset_rdata_soa_serial(soa_begin);
+	int64_t sn = knot_rdata_soa_serial(soa_begin);
 	int64_t sn_new;
 
 	/* Incremented SERIAL
@@ -2305,7 +1794,7 @@ int knot_ddns_process_update2(knot_zone_contents_t *zone,
 		if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA
 		    && (knot_rrset_class(rr) == KNOT_CLASS_NONE
 		        || knot_rrset_class(rr) == KNOT_CLASS_ANY
-		        || ns_serial_compare(knot_rrset_rdata_soa_serial(rr),
+		        || ns_serial_compare(knot_rdata_soa_serial(rr),
 		                             sn_new) < 0)) {
 			// This ignores also SOA removals
 			dbg_ddns_verb("Ignoring SOA...\n");
@@ -2327,7 +1816,7 @@ int knot_ddns_process_update2(knot_zone_contents_t *zone,
 
 		// we need the RR copy, that's why this code is here
 		if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
-			int64_t sn_rr = knot_rrset_rdata_soa_serial(rr);
+			int64_t sn_rr = knot_rdata_soa_serial(rr);
 			dbg_ddns_verb("Replacing SOA. Old serial: %"PRId64", "
 			              "new serial: %"PRId64"\n", sn_new, sn_rr);
 			assert(ns_serial_compare(sn_rr, sn_new) >= 0);
@@ -2349,14 +1838,14 @@ int knot_ddns_process_update2(knot_zone_contents_t *zone,
 
 		/* If not set, create new SOA. */
 		assert(sn_new == (uint32_t)sn + 1);
-		ret = knot_rrset_deep_copy(soa, &soa_end, 1);
+		ret = knot_rrset_deep_copy(soa, &soa_end);
 		if (ret != KNOT_EOK) {
 			dbg_ddns("Failed to copy ending SOA: %s\n",
 			         knot_strerror(ret));
 			*rcode = KNOT_RCODE_SERVFAIL;
 			return ret;
 		}
-		knot_rrset_rdata_soa_serial_set(soa_end, sn_new);
+		knot_rdata_soa_serial_set(soa_end, sn_new);
 
 		/* And replace it in the zone. */
 		ret = xfrin_replace_rrset_in_node(
diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h
index d6164feccf7a5794fe729bdfc744437cc0f964a8..985e5d04cf87f1ed71e42b9389f8540ae7e370fb 100644
--- a/src/libknot/updates/ddns.h
+++ b/src/libknot/updates/ddns.h
@@ -66,11 +66,7 @@ int knot_ddns_process_prereqs(const knot_packet_t *query,
 int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
                             knot_ddns_prereq_t **prereqs, knot_rcode_t *rcode);
 
-int knot_ddns_process_update(const knot_zone_contents_t *zone,
-			     const knot_packet_t *query,
-                             knot_changeset_t *changeset, knot_rcode_t *rcode);
-
-int knot_ddns_process_update2(knot_zone_contents_t *zone,
+int knot_ddns_process_update(knot_zone_contents_t *zone,
                               const knot_packet_t *query,
                               knot_changeset_t *changeset,
                               knot_changes_t *changes,
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index 09143e4eff8938d18ec419cfc5e395f14dfc66a8..4391ea7187e3bedd055d6b555a77b79c57207c73 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -28,12 +28,15 @@
 #include "packet/packet.h"
 #include "dname.h"
 #include "zone/zone.h"
+#include "libknot/dnssec/zone-nsec.h"
 #include "packet/query.h"
 #include "common.h"
 #include "updates/changesets.h"
 #include "tsig.h"
 #include "tsig-op.h"
+#include "common/lists.h"
 #include "common/descriptor.h"
+#include "rdata.h"
 
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
@@ -43,7 +46,7 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
 			      uint16_t qclass, knot_ns_xfr_t *xfr, size_t *size,
 			      const knot_rrset_t *soa, int use_tsig)
 {
-	knot_packet_t *pkt = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+	knot_packet_t *pkt = knot_packet_new();
 	CHECK_ALLOC_LOG(pkt, KNOT_ENOMEM);
 
 	/*! \todo Get rid of the numeric constant. */
@@ -59,23 +62,10 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
 		return KNOT_ERROR;
 	}
 
-	knot_question_t question;
-
-	/* Retain qname until the question is freed. */
-	knot_dname_retain(qname);
-
 	/* Set random query ID. */
 	knot_packet_set_random_id(pkt);
-	knot_wire_set_id(pkt->wireformat, pkt->header.id);
-
-	// this is ugly!!
-	question.qname = (knot_dname_t *)qname;
-	question.qtype = qtype;
-	question.qclass = qclass;
-
-	rc = knot_query_set_question(pkt, &question);
+	rc = knot_query_set_question(pkt, qname, qclass, qtype);
 	if (rc != KNOT_EOK) {
-		knot_dname_release(question.qname);
 		knot_packet_free(&pkt);
 		return KNOT_ERROR;
 	}
@@ -99,7 +89,6 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
 	rc = knot_packet_to_wire(pkt, &wire, &wire_size);
 	if (rc != KNOT_EOK) {
 		dbg_xfrin("Failed to write packet to wire.\n");
-		knot_dname_release(question.qname);
 		knot_packet_free(&pkt);
 		return KNOT_ERROR;
 	}
@@ -141,9 +130,6 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
 
 	knot_packet_free(&pkt);
 
-	/* Release qname. */
-	knot_dname_release(question.qname);
-
 	return KNOT_EOK;
 }
 
@@ -193,7 +179,7 @@ int xfrin_transfer_needed(const knot_zone_contents_t *zone,
 		return KNOT_ERROR;
 	}
 
-	int64_t local_serial = knot_rrset_rdata_soa_serial(soa_rrset);
+	int64_t local_serial = knot_rdata_soa_serial(soa_rrset);
 	if (local_serial < 0) {
 dbg_xfrin_exec(
 		char *name = knot_dname_to_str(knot_rrset_owner(soa_rrset));
@@ -213,7 +199,7 @@ dbg_xfrin_exec(
 		return KNOT_EMALF;
 	}
 
-	int64_t remote_serial = knot_rrset_rdata_soa_serial(soa_rrset);
+	int64_t remote_serial = knot_rdata_soa_serial(soa_rrset);
 	if (remote_serial < 0) {
 		return KNOT_EMALF;	// maybe some other error
 	}
@@ -276,13 +262,13 @@ static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
 						    &rrset, &node,
 						    KNOT_RRSET_DUPL_MERGE);
 		if (ret > 0) {
-			knot_rrset_deep_free(&(*last)->rrsig, 1, 0);
+			knot_rrset_deep_free(&(*last)->rrsig, 1);
 		} else if (ret == KNOT_ENONODE) {
 			// Nothing to cover - print warning
 			char *name = knot_dname_to_str((*last)->rrsig->owner);
 			char type[16];
 			knot_rrtype_to_string(
-			    knot_rrset_rdata_rrsig_type_covered((*last)->rrsig),
+			    knot_rdata_rrsig_type_covered((*last)->rrsig, 0),
 			    type, 16);
 
 			log_zone_warning("No RRSet for RRSIG: "
@@ -291,7 +277,7 @@ static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
 			free(name);
 
 			// discard RRSIG
-			knot_rrset_deep_free(&(*last)->rrsig, 1, 1);
+			knot_rrset_deep_free(&(*last)->rrsig, 1);
 		} else if (ret != KNOT_EOK) {
 			dbg_xfrin("Failed to add orphan RRSIG to zone.\n");
 			return ret;
@@ -306,21 +292,6 @@ static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
 
 /*----------------------------------------------------------------------------*/
 
-static int xfrin_insert_rdata_dnames_to_table(knot_dname_t **dname, void *data)
-{
-	hattrie_t *lookup_tree = data;
-	knot_zone_contents_insert_dname_into_table(dname, lookup_tree);
-	return KNOT_EOK;
-}
-
-static int xfrin_insert_rrset_dnames_to_table(knot_rrset_t *rrset,
-                                              hattrie_t *lookup_tree)
-{
-	knot_zone_contents_insert_dname_into_table(&rrset->owner, lookup_tree);
-	rrset_dnames_apply(rrset, xfrin_insert_rdata_dnames_to_table, lookup_tree);
-	return KNOT_EOK;
-}
-
 static void xfrin_log_error(const knot_dname_t *zone_owner,
                             const knot_dname_t *rr_owner,
                             int ret)
@@ -349,6 +320,9 @@ void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
 	while (r != NULL) {
 		xfrin_orphan_rrsig_t *prev = r;
 		r = r->next;
+		if (prev->rrsig != NULL) {
+			knot_rrset_deep_free(&prev->rrsig, 1);
+		}
 		free(prev);
 	}
 
@@ -422,7 +396,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
 			if (ret != KNOT_EOK) {
 				/* No need to check TSIG error
 				 * here, propagate and check elsewhere.*/
-				knot_rrset_deep_free(&tsig, 1, 1);
+				knot_rrset_deep_free(&tsig, 1);
 				return ret;
 			}
 
@@ -432,7 +406,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
 
 			// Extract the digest from the TSIG RDATA and store it.
 			if (xfr->digest_max_size < tsig_rdata_mac_length(tsig)) {
-				knot_rrset_deep_free(&tsig, 1, 1);
+				knot_rrset_deep_free(&tsig, 1);
 				return KNOT_ESPACE;
 			}
 			memcpy(xfr->digest, tsig_rdata_mac(tsig),
@@ -448,11 +422,11 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
 		}
 	} else if (tsig != NULL) {
 		// TSIG where it should not be
-		knot_rrset_deep_free(&tsig, 1, 1);
+		knot_rrset_deep_free(&tsig, 1);
 		return KNOT_EMALF;
 	}
 
-	knot_rrset_deep_free(&tsig, 1, 1);
+	knot_rrset_deep_free(&tsig, 1);
 
 	return KNOT_EOK;
 }
@@ -479,8 +453,7 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
 
 	/*! \todo Should TC bit be checked? */
 
-	knot_packet_t *packet =
-			knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+	knot_packet_t *packet = knot_packet_new();
 	if (packet == NULL) {
 		dbg_xfrin("Could not create packet structure.\n");
 		return KNOT_ENOMEM;
@@ -506,7 +479,7 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
 		/*! \todo Cleanup. */
 		return KNOT_EMALF;
 	}
-	
+
 	if (rr == NULL) {
 		dbg_xfrin("No RRs in the packet.\n");
 		knot_packet_free(&packet);
@@ -519,7 +492,8 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
 	 */
 
 	/* RR parsed - sort out DNAME duplications. */
-	xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
+	/*! \todo Replace with RRSet duplicate checking. */
+//	xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
 
 	knot_node_t *node = NULL;
 	int in_zone = 0;
@@ -537,7 +511,7 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
 				  "Answer is not a SOA RR.\n");
 			knot_packet_free(&packet);
 			knot_node_free(&node);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			/*! \todo Cleanup. */
 			return KNOT_EMALF;
 		}
@@ -548,12 +522,12 @@ int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
 			dbg_xfrin("Invalid packet in sequence, ignoring.\n");
 			knot_packet_free(&packet);
 			knot_node_free(&node);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			return KNOT_EOK;
 		}
 
-		if (knot_dname_compare_non_canon(knot_rrset_owner(rr),
-				                 knot_packet_qname(packet)) != 0) {
+		const knot_dname_t *qname = knot_packet_qname(packet);
+		if (!knot_dname_is_equal(knot_rrset_owner(rr), qname)) {
 dbg_xfrin_exec(
 			char *rr_owner =
 				knot_dname_to_str(knot_rrset_owner(rr));
@@ -569,7 +543,7 @@ dbg_xfrin_exec(
 			/*! \todo Cleanup. */
 			knot_packet_free(&packet);
 			knot_node_free(&node);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			return KNOT_EMALF;
 		}
 
@@ -577,7 +551,7 @@ dbg_xfrin_exec(
 		if (node == NULL) {
 			dbg_xfrin("Failed to create new node.\n");
 			knot_packet_free(&packet);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			return KNOT_ENOMEM;
 		}
 
@@ -590,7 +564,7 @@ dbg_xfrin_exec(
 			dbg_xfrin("Failed to create new constr. zone.\n");
 			knot_packet_free(&packet);
 			knot_node_free(&node);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			return KNOT_ENOMEM;
 		}
 
@@ -602,31 +576,29 @@ dbg_xfrin_exec(
 			dbg_xfrin("Failed to create new zone.\n");
 			knot_packet_free(&packet);
 			knot_node_free(&node);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			/*! \todo Cleanup. */
 			return KNOT_ENOMEM;
 		}
 
 		in_zone = 1;
-		assert(node->owner == rr->owner);
 		zone = (*constr)->contents;
 		assert(zone->apex == node);
-		assert(zone->apex->owner == rr->owner);
 		// add the RRSet to the node
 		ret = knot_zone_contents_add_rrset(zone, rr, &node,
-						    KNOT_RRSET_DUPL_MERGE);
+		                                   KNOT_RRSET_DUPL_MERGE);
 		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);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 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);
+			knot_rrset_deep_free(&rr, 1);
 		}
 
 		// take next RR
@@ -640,23 +612,22 @@ dbg_xfrin_exec(
 
 	while (ret == KNOT_EOK && rr != NULL) {
 		// process the parsed RR
-		if (!knot_dname_is_subdomain(rr->owner, xfr->zone->name) &&
-		    knot_dname_compare_non_canon(rr->owner, xfr->zone->name) != 0) {
+		if (!knot_dname_is_sub(rr->owner, xfr->zone->name) &&
+		    !knot_dname_is_equal(rr->owner, xfr->zone->name)) {
 			// Out-of-zone data
 			xfrin_log_error(xfr->zone->name, rr->owner,
 			                KNOT_EOUTOFZONE);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			ret = knot_packet_parse_next_rr_answer(packet, &rr);
 			continue;
 		}
 
 		dbg_rrset_detail("\nNext RR:\n\n");
 		knot_rrset_dump(rr);
-		/* RR parsed - sort out DNAME duplications. */
-		xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
+		/*! \todo Replace with RRSet duplicate checking. */
+//		xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
 
-		if (node != NULL
-		    && knot_dname_compare_non_canon(rr->owner, node->owner) != 0) {
+		if (node != NULL && !knot_dname_is_equal(rr->owner, node->owner)) {
 dbg_xfrin_exec_detail(
 			char *name = knot_dname_to_str(node->owner);
 			dbg_xfrin_detail("Node owner: %s\n", name);
@@ -690,7 +661,7 @@ dbg_xfrin_exec_detail(
 			dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret);
 
 			knot_packet_free(&packet);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 
 			if (ret != KNOT_EOK) {
 				/*! \todo [TSIG] Handle TSIG errors. */
@@ -700,14 +671,13 @@ dbg_xfrin_exec_detail(
 			// we must now find place for all orphan RRSIGs
 			ret = xfrin_process_orphan_rrsigs(zone,
 							  (*constr)->rrsigs);
+			xfrin_free_orphan_rrsigs(&(*constr)->rrsigs);
+
 			if (ret != KNOT_EOK) {
 				dbg_xfrin("Failed to process orphan RRSIGs\n");
-				/*! \todo Cleanup?? */
 				return ret;
 			}
 
-			xfrin_free_orphan_rrsigs(&(*constr)->rrsigs);
-
 			return 1;
 		}
 
@@ -730,19 +700,19 @@ dbg_xfrin_exec_detail(
 
 				if (ret > 0) {
 					dbg_xfrin_detail("Merged RRSIGs.\n");
-					knot_rrset_deep_free(&rr, 1, 0);
+					knot_rrset_deep_free(&rr, 1);
 				} else if (ret != KNOT_EOK) {
 					dbg_xfrin("Failed to save orphan"
 						  " RRSIGs.\n");
 					knot_packet_free(&packet);
-					knot_rrset_deep_free(&rr, 1, 1);
+					knot_rrset_deep_free(&rr, 1);
 					return ret;
 				}
 			} else if (ret < 0) {
 				dbg_xfrin("Failed to add RRSIGs (%s).\n",
 					       knot_strerror(ret));
 				knot_packet_free(&packet);
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				return KNOT_ERROR;  /*! \todo Other error code. */
 			} else if (ret == 1) {
 				assert(node != NULL);
@@ -753,7 +723,7 @@ dbg_xfrin_exec_verb(
 				free(name);
 );
 				in_zone = 1;
-				knot_rrset_deep_free(&rr, 1, 0);
+				knot_rrset_deep_free(&rr, 1);
 			} else if (ret == 2) {
 				// should not happen
 				assert(0);
@@ -781,7 +751,7 @@ dbg_xfrin_exec_verb(
 			dbg_xfrin("TSIG in Answer section.\n");
 			knot_packet_free(&packet);
 			knot_node_free(&node); // ???
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			return KNOT_EMALF;
 		}
 
@@ -812,7 +782,7 @@ dbg_xfrin_exec_verb(
 			if (node == NULL) {
 				dbg_xfrin("Failed to create new node.\n");
 				knot_packet_free(&packet);
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				return KNOT_ENOMEM;
 			}
 			dbg_xfrin_detail("Created new node for the record.\n");
@@ -824,7 +794,7 @@ dbg_xfrin_exec_verb(
 					  knot_strerror(ret));
 				knot_packet_free(&packet);
 				knot_node_free(&node); // ???
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				return KNOT_ERROR;
 			} else if (ret > 0) {
 				// should not happen, this is new node
@@ -837,10 +807,11 @@ dbg_xfrin_exec_verb(
 			if (ret != KNOT_EOK) {
 				// Fatal error, free packet
 				knot_packet_free(&packet);
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				knot_node_free(&node);
 				return ret;
 			}
+
 			in_zone = 1;
 		} else {
 			assert(in_zone);
@@ -854,8 +825,9 @@ dbg_xfrin_exec_verb(
 				return KNOT_ERROR;
 			} else if (ret > 0) {
 				// merged, free the RRSet
-				knot_rrset_deep_free(&rr, 1, 0);
+				knot_rrset_deep_free(&rr, 1);
 			}
+
 		}
 
 		rr = NULL;
@@ -875,7 +847,7 @@ dbg_xfrin_exec_verb(
 			knot_node_free(&node);
 		}
 
-		knot_rrset_deep_free(&rr, 1, 1);
+		knot_rrset_deep_free(&rr, 1);
 		return KNOT_EMALF;
 	}
 
@@ -891,7 +863,7 @@ dbg_xfrin_exec_verb(
 				  knot_strerror(ret));
 				knot_packet_free(&packet);
 				knot_node_free(&node);
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				return ret;
 		}
 	}
@@ -920,7 +892,7 @@ static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt,
 	assert(packet != NULL);
 	assert(rr != NULL);
 
-	*packet = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+	*packet = knot_packet_new();
 	if (*packet == NULL) {
 		dbg_xfrin("Could not create packet structure.\n");
 		return KNOT_ENOMEM;
@@ -992,14 +964,15 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
 		return KNOT_EMALF;
 	}
 
-	xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
+	/*! \todo Replace with RRSet duplicate checking. */
+//	xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
 
 	if (*chs == NULL) {
 		dbg_xfrin_verb("Changesets empty, creating new.\n");
 
-		ret = knot_changeset_allocate(chs, KNOT_CHANGESET_TYPE_IXFR);
+		ret = knot_changesets_init(chs, KNOT_CHANGESET_TYPE_IXFR);
 		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			knot_packet_free(&packet);
 			return ret;
 		}
@@ -1007,7 +980,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
 		// the first RR must be a SOA
 		if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
 			dbg_xfrin("First RR is not a SOA RR!\n");
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			ret = KNOT_EMALF;
 			goto cleanup;
 		}
@@ -1046,7 +1019,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
 			knot_packet_free(&packet);
 			return XFRIN_RES_SOA_ONLY;
 		} else if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			knot_packet_free(&packet);
 			dbg_xfrin("Fallback to AXFR.\n");
 			ret = XFRIN_RES_FALLBACK;
@@ -1055,7 +1028,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
 	} else {
 		if ((*chs)->first_soa == NULL) {
 			dbg_xfrin("Changesets don't contain SOA first!\n");
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			ret = KNOT_EINVAL;
 			goto cleanup;
 		}
@@ -1098,12 +1071,12 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
 	 *        changesets structure, or in some place persistent between
 	 *        calls to this function.
 	 */
-	knot_changeset_t *cur = (*chs)->sets + ((*chs)->count - 1);
+	knot_changeset_t *cur = knot_changesets_get_last(*chs);
 	if (state != -1) {
 		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);
+		if (EMPTY_LIST((*chs)->sets)) {
+			knot_rrset_deep_free(&rr, 1);
 			ret = KNOT_EMALF;
 			goto cleanup;
 		}
@@ -1130,20 +1103,17 @@ dbg_xfrin_exec_verb(
 				 knot_rrset_type(rr));
 		free(name);
 );
-		if (!knot_dname_is_subdomain(rr->owner, xfr->zone->name) &&
-		    knot_dname_compare_non_canon(rr->owner, xfr->zone->name) != 0) {
+		if (!knot_dname_is_sub(rr->owner, xfr->zone->name) &&
+		    !knot_dname_is_equal(rr->owner, xfr->zone->name)) {
 			// out-of-zone domain
 			xfrin_log_error(xfr->zone->name, rr->owner,
 			                KNOT_EOUTOFZONE);
-			knot_rrset_deep_free(&rr, 1, 1);
+			knot_rrset_deep_free(&rr, 1);
 			// Skip this rr
 			ret = knot_packet_parse_next_rr_answer(packet, &rr);
 			continue;
 		}
 
-		// Handle duplications
-		xfrin_insert_rrset_dnames_to_table(rr, xfr->lookup_tree);
-
 		switch (state) {
 		case -1:
 			// a SOA is expected
@@ -1155,12 +1125,12 @@ dbg_xfrin_exec_verb(
 				dbg_xfrin_verb("RR type: %u\n",
 					       knot_rrset_type(rr));
 				ret = KNOT_EMALF;
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				goto cleanup;
 			}
 
-			if (knot_rrset_rdata_soa_serial(rr)
-			    == knot_rrset_rdata_soa_serial((*chs)->first_soa)) {
+			if (knot_rdata_soa_serial(rr)
+			    == knot_rdata_soa_serial((*chs)->first_soa)) {
 
 				/*! \note [TSIG] Check TSIG, we're at the end of
 				 *               transfer.
@@ -1168,7 +1138,7 @@ dbg_xfrin_exec_verb(
 				ret = xfrin_check_tsig(packet, xfr, 1);
 
 				// last SOA, discard and end
-				knot_rrset_deep_free(&rr, 1, 1);
+				knot_rrset_deep_free(&rr, 1);
 				knot_packet_free(&packet);
 
 				/*! \note [TSIG] If TSIG validates, consider
@@ -1180,26 +1150,21 @@ dbg_xfrin_exec_verb(
 				return ret;
 			} else {
 				// normal SOA, start new changeset
-				(*chs)->count++;
-				ret = knot_changesets_check_size(*chs);
-
 				/* Check changesets for maximum count (so they fit into journal). */
-				if ((*chs)->count > JOURNAL_NCOUNT)
+				if ((*chs)->count + 1 > JOURNAL_NCOUNT)
 					ret = KNOT_ESPACE;
 
 				if (ret != KNOT_EOK) {
-					(*chs)->count--;
-					knot_rrset_deep_free(&rr, 1, 1);
+					knot_rrset_deep_free(&rr, 1);
 					goto cleanup;
 				}
 
-				cur = (*chs)->sets + ((*chs)->count - 1);
-				ret = knot_changeset_add_soa(cur, rr,
-							     KNOT_CHANGESET_REMOVE);
-				if (ret != KNOT_EOK) {
-					knot_rrset_deep_free(&rr, 1, 1);
+				cur = knot_changesets_create_changeset(*chs);
+				if (cur == NULL) {
+					knot_rrset_deep_free(&rr, 1);
 					goto cleanup;
 				}
+				knot_changeset_add_soa(cur, rr, KNOT_CHANGESET_REMOVE);
 
 				// change state to REMOVE
 				state = KNOT_CHANGESET_REMOVE;
@@ -1212,21 +1177,16 @@ dbg_xfrin_exec_verb(
 				// we should not be here if soa_from is not set
 				assert(cur->soa_from != NULL);
 
-				ret = knot_changeset_add_soa(cur, rr,
-							     KNOT_CHANGESET_ADD);
-				if (ret != KNOT_EOK) {
-					knot_rrset_deep_free(&rr, 1, 1);
-					goto cleanup;
-				}
+				knot_changeset_add_soa(cur, rr, KNOT_CHANGESET_ADD);
 
 				state = KNOT_CHANGESET_ADD;
 			} else {
 				// just add the RR to the REMOVE part and
 				// continue
-				ret = knot_changeset_add_new_rr(cur, rr,
-								KNOT_CHANGESET_REMOVE);
+				ret = knot_changeset_add_rr(cur, rr,
+				                            KNOT_CHANGESET_REMOVE);
 				if (ret != KNOT_EOK) {
-					knot_rrset_deep_free(&rr, 1, 1);
+					knot_rrset_deep_free(&rr, 1);
 					goto cleanup;
 				}
 			}
@@ -1237,17 +1197,17 @@ dbg_xfrin_exec_verb(
 			if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
 				log_zone_info("%s Serial %u -> %u.\n",
 					      xfr->msg,
-					      knot_rrset_rdata_soa_serial(cur->soa_from),
-					      knot_rrset_rdata_soa_serial(cur->soa_to));
+					      knot_rdata_soa_serial(cur->soa_from),
+					      knot_rdata_soa_serial(cur->soa_to));
 				state = -1;
 				continue;
 			} else {
 
 				// just add the RR to the ADD part and continue
-				ret = knot_changeset_add_new_rr(cur, rr,
-								KNOT_CHANGESET_ADD);
+				ret = knot_changeset_add_rr(cur, rr,
+				                            KNOT_CHANGESET_ADD);
 				if (ret != KNOT_EOK) {
-					knot_rrset_deep_free(&rr, 1, 1);
+					knot_rrset_deep_free(&rr, 1);
 					goto cleanup;
 				}
 			}
@@ -1283,7 +1243,7 @@ cleanup:
 	assert(ret < 0);
 
 	dbg_xfrin_detail("Cleanup after processing IXFR/IN packet.\n");
-	knot_free_changesets(chs);
+	knot_changesets_free(chs);
 	knot_packet_free(&packet);
 	xfr->data = 0;
 	return ret;
@@ -1293,7 +1253,7 @@ cleanup:
 /* Applying changesets to zone                                                */
 /*----------------------------------------------------------------------------*/
 
-static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
+void xfrin_zone_contents_free(knot_zone_contents_t **contents)
 {
 	/*! \todo This should be all in some API!! */
 
@@ -1316,98 +1276,41 @@ int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
 {
 	dbg_xfrin_detail("Copying old RRSet: %p\n", old);
 	// create new RRSet by copying the old one
-	int ret = knot_rrset_deep_copy(old, copy, 1);
+	int ret = knot_rrset_deep_copy(old, copy);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to create RRSet copy.\n");
 		return KNOT_ENOMEM;
 	}
 
-	int count = 0;
-
-	// add the RRSet to the list of new RRSets
-	// create place also for RRSIGs
+	// add the RRSet to the list of new RRSets, RRSIG as well
 	if (save_new) {
-		count = 1;
-		count += (*copy)->rrsigs ? 1 : 0;
-		ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
-						  &changes->new_rrsets_count,
-						  &changes->new_rrsets_allocated,
-						  count);
-		if (ret != KNOT_EOK) {
-			dbg_xfrin("Failed to add new RRSet to list.\n");
-			knot_rrset_deep_free(copy, 1, 1);
-			return ret;
-		}
-
-
-		// add the copied RDATA to the list of new RDATA
-		ret = knot_changes_rdata_reserve(&changes->new_rdata,
-						 changes->new_rdata_count,
-						 &changes->new_rdata_allocated,
-						 count);
+		ret = knot_changes_add_rrset(changes, *copy, KNOT_CHANGES_NEW);
 		if (ret != KNOT_EOK) {
-			dbg_xfrin("Failed to add new RRSet to list.\n");
-			knot_rrset_deep_free(copy, 1, 1);
+			knot_rrset_deep_free(copy, 1);
 			return ret;
 		}
-
-		changes->new_rrsets[changes->new_rrsets_count++] = *copy;
-
-		dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list."
-				 "\n");
-		knot_changes_add_rdata(changes->new_rdata,
-					&changes->new_rdata_count,
-					*copy);
-
 		if ((*copy)->rrsigs != NULL) {
 			assert(old->rrsigs != NULL);
-			changes->new_rrsets[changes->new_rrsets_count++] =
-					(*copy)->rrsigs;
-
-			dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to "
-					 "new RDATA list.\n");
-			knot_changes_add_rdata(changes->new_rdata,
-						&changes->new_rdata_count,
-						(*copy)->rrsigs);
+			ret = knot_changes_add_rrset(changes, (*copy)->rrsigs,
+			      KNOT_CHANGES_NEW);
+			if (ret != KNOT_EOK) {
+				knot_rrset_deep_free(&((*copy)->rrsigs), 1);
+				return ret;
+			}
 		}
 	}
 
-	count = 1;
-	count += old->rrsigs ? 1 : 0;
-
-	// add the old RRSet to the list of old RRSets
-	ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-					 &changes->old_rrsets_count,
-					 &changes->old_rrsets_allocated, count);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add old RRSet to list.\n");
-		return ret;
-	}
-
-	// and old RDATA to the list of old RDATA
-	ret = knot_changes_rdata_reserve(&changes->old_rdata,
-					changes->old_rdata_count,
-					&changes->old_rdata_allocated, count);
+	ret = knot_changes_add_rrset(changes, old, KNOT_CHANGES_OLD);
 	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add old RRSet to list.\n");
 		return ret;
 	}
 
-	changes->old_rrsets[changes->old_rrsets_count++] = old;
-
-	dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n");
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       old);
-
-	if ((*copy)->rrsigs != NULL) {
-		assert(old->rrsigs != NULL);
-		changes->old_rrsets[changes->old_rrsets_count++] = old->rrsigs;
-
-		dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to "
-				 "old RDATA list.\n");
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count,
-					old->rrsigs);
+	if (old->rrsigs != NULL) {
+		ret = knot_changes_add_rrset(changes, old->rrsigs,
+		                             KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	return KNOT_EOK;
@@ -1476,18 +1379,15 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 	int copied = 0;
 
 	if (*rrset
-	    && knot_dname_compare_non_canon(knot_rrset_owner(*rrset),
-					    knot_node_owner(node)) == 0
-	    && knot_rrset_type(*rrset) == knot_rrset_rdata_rrsig_type_covered(
-				remove)) {
+	    && knot_dname_is_equal(knot_rrset_owner(*rrset), knot_node_owner(node))
+	    && knot_rrset_type(*rrset) == knot_rdata_rrsig_type_covered(remove, 0)) {
 		// 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
-		uint16_t type =
-			knot_rrset_rdata_rrsig_type_covered(remove);
+		uint16_t type = knot_rdata_rrsig_type_covered(remove, 0);
 
 		// copy the rrset
 		dbg_xfrin_detail("Copying RRSet that carries the RRSIGs.\n");
@@ -1547,22 +1447,16 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 			  knot_strerror(ret));
 		return 1;
 	}
-	/*!< \todo This RRSet will be created even when nothing was removed. */
+	/* This RRSet will be created even when nothing was removed. */
 	assert(rr_removed);
 
-	int count = 1;
 	// connect the RDATA to the list of old RDATA
-	ret = knot_changes_rdata_reserve(&changes->old_rdata,
-					 changes->old_rdata_count,
-					 &changes->old_rdata_allocated, count);
+	ret = knot_changes_add_rrset(changes, rr_removed, KNOT_CHANGES_OLD);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&rr_removed, 1, 1);
+		knot_rrset_deep_free(&rr_removed, 1);
 		return ret;
 	}
 
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       rr_removed);
-
 	// if the RRSet is empty, remove from node and add to old RRSets
 	// check if there is no RRSIGs; if there are, leave the RRSet
 	// there; it may be eventually removed when the RRSIGs are removed
@@ -1571,10 +1465,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 		knot_rrset_set_rrsigs(*rrset, NULL);
 
 		// add RRSet to the list of old RRSets
-		ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-						 &changes->old_rrsets_count,
-						 &changes->old_rrsets_allocated,
-						 1);
+		ret = knot_changes_add_rrset(changes, rrsigs, KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
 			dbg_xfrin("Failed to add empty RRSet to the "
 				  "list of old RRSets.");
@@ -1583,33 +1474,18 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 			return ret;
 		}
 
-		changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
-
-		// saving old RDATA is not necessary as there is none
-
 		// now check if the RRSet is not totally empty
 		if (knot_rrset_rdata_rr_count(*rrset) == 0) {
 			assert(knot_rrset_rrsigs(*rrset) == NULL);
-
 			// remove the whole RRSet from the node
 			knot_rrset_t *tmp = knot_node_remove_rrset(node,
 						     knot_rrset_type(*rrset));
 			assert(tmp == *rrset);
-
-			ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-						&changes->old_rrsets_count,
-						&changes->old_rrsets_allocated,
-						1);
+			int ret = knot_changes_add_rrset(changes, *rrset,
+			                                 KNOT_CHANGES_OLD);
 			if (ret != KNOT_EOK) {
-				dbg_xfrin("Failed to add empty RRSet to "
-					  "the list of old RRSets.");
-				// delete the RRSet right away
-				knot_rrset_free(rrset);
 				return ret;
 			}
-
-			changes->old_rrsets[changes->old_rrsets_count++] =
-				*rrset;
 		}
 	}
 
@@ -1648,7 +1524,7 @@ 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_dname_cmp(knot_rrset_owner(*rrset),
 				  knot_node_owner(node)) == 0
 	    && knot_rrset_type(*rrset) == knot_rrset_type(remove)) {
 		/*! \todo Does some other case even occur? */
@@ -1704,19 +1580,11 @@ dbg_xfrin_exec_detail(
 	}
 
 	if (rr_remove->rdata_count != 0) {
-		int count = 1;
-		// connect the RDATA to the list of old RDATA
-		ret = knot_changes_rdata_reserve(&changes->old_rdata,
-						changes->old_rdata_count,
-						&changes->old_rdata_allocated,
-						count);
+		ret = knot_changes_add_rrset(changes, rr_remove, KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
 			knot_rrset_free(&rr_remove);
 			return ret;
 		}
-
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count, rr_remove);
 	} else {
 		/* Discard empty RRSet. */
 		knot_rrset_free(&rr_remove);
@@ -1739,10 +1607,7 @@ dbg_xfrin_exec_detail(
 		// add the removed RRSet to list of old RRSets
 
 		assert(tmp == *rrset);
-		ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-						 &changes->old_rrsets_count,
-						 &changes->old_rrsets_allocated,
-						 1);
+		ret = knot_changes_add_rrset(changes, *rrset, KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
 			dbg_xfrin("Failed to add empty RRSet to the "
 				  "list of old RRSets.");
@@ -1750,8 +1615,6 @@ dbg_xfrin_exec_detail(
 			knot_rrset_free(rrset);
 			return ret;
 		}
-
-		changes->old_rrsets[changes->old_rrsets_count++] = *rrset;
 	}
 
 	return KNOT_EOK;
@@ -1763,7 +1626,6 @@ static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes,
                                          knot_node_t *node, uint16_t type,
                                          uint32_t chflags)
 {
-	int ret = KNOT_EOK;
 	knot_rrset_t **rrsets = NULL;
 	unsigned rrsets_count = 0;
 	int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
@@ -1839,39 +1701,19 @@ dbg_xfrin_exec_verb(
 		}
 	}
 
-	ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-					 &changes->old_rrsets_count,
-					 &changes->old_rrsets_allocated,
-					 rrsets_count);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to reserve changes rrsets.\n");
-		free(rrsets);
-		return ret;
-	}
-
 	/* Mark RRsets and RDATA for removal. */
 	for (unsigned i = 0; i < rrsets_count; ++i) {
 		if (rrsets[i] == NULL) {
 			continue;
 		}
 
-		changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i];
-
-		/* Remove old RDATA. */
-		int rdata_count = 1;//knot_rrset_rdata_rr_count(rrsets[i]);
-		ret = knot_changes_rdata_reserve(&changes->old_rdata,
-						changes->old_rdata_count,
-						&changes->old_rdata_allocated,
-						rdata_count);
+		int ret = knot_changes_add_rrset(changes, rrsets[i],
+		                                 KNOT_CHANGES_OLD);
 		if (ret != KNOT_EOK) {
-			dbg_xfrin("Failed to reserve changes rdata.\n");
 			free(rrsets);
 			return ret;
 		}
 
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count,
-					rrsets[i]);
 	}
 
 	free(rrsets);
@@ -1881,72 +1723,74 @@ dbg_xfrin_exec_verb(
 
 /*----------------------------------------------------------------------------*/
 
+static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
+                                       knot_rrset_t *rrset, int is_nsec3)
+{
+	knot_node_t *node = knot_node_new(knot_rrset_get_owner(rrset),
+					  NULL, 0);
+	if (node == NULL) {
+		dbg_xfrin("Failed to create a new node.\n");
+		return NULL;
+	}
+
+	int ret = 0;
+
+	// insert the node into zone structures and create parents if
+	// necessary
+	if (is_nsec3) {
+		ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0);
+	} else {
+		ret = knot_zone_contents_add_node(contents, node, 1, 0);
+	}
+	if (ret != KNOT_EOK) {
+		dbg_xfrin("Failed to add new node to zone contents.\n");
+		knot_node_free(&node);
+		return NULL;
+	}
+
+	/*!
+	 * \note It is not needed to set the previous node, we will do this
+	 *       in adjusting after the transfer.
+	 */
+
+	assert(contents->zone != NULL);
+	knot_node_set_zone(node, contents->zone);
+
+	return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
 int xfrin_replace_rrset_in_node(knot_node_t *node,
                                        knot_rrset_t *rrset_new,
                                        knot_changes_t *changes,
                                        knot_zone_contents_t *contents)
 {
+	if (node == NULL || rrset_new == NULL || changes == NULL
+	    || contents == NULL) {
+		return KNOT_EINVAL;
+	}
+
 	uint16_t type = knot_rrset_type(rrset_new);
 	// remove RRSet of the proper type from the node
 	dbg_xfrin_verb("Removing RRSet of type: %u.\n", type);
 	knot_rrset_t *rrset_old = knot_node_remove_rrset(node, type);
 	assert(rrset_old != NULL);
 
-	// add the old RRSet to the list of old RRSets
-	int ret = knot_changes_rrsets_reserve(&changes->old_rrsets,
-					     &changes->old_rrsets_count,
-					     &changes->old_rrsets_allocated, 1);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add old RRSet to list.\n");
-		return ret;
-	}
-
 	// save also the RDATA, because RDATA are not deleted with the RRSet
-	// The count should be 1, but just to be sure....
-	int count = 1;//knot_rrset_rdata_rr_count(rrset_old);
-	ret = knot_changes_rdata_reserve(&changes->old_rdata,
-					changes->old_rdata_count,
-					&changes->old_rdata_allocated, count);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add old RDATA to list.\n");
-		return ret;
-	}
-
 	// save the new RRSet to the new RRSet, so that it is deleted if the
 	// apply fails
-	ret = knot_changes_rrsets_reserve(&changes->new_rrsets,
-					 &changes->new_rrsets_count,
-					 &changes->new_rrsets_allocated, 1);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add new RRSet to list.\n");
-		return ret;
-	}
-
-	// The count should be 1, but just to be sure....
-	count = 1;//knot_rrset_rdata_rr_count(rrset_new);
-	// save the new RDATA
-	ret = knot_changes_rdata_reserve(&changes->new_rdata,
-					changes->new_rdata_count,
-					&changes->new_rdata_allocated, count);
+	int ret = knot_changes_add_rrset(changes, rrset_old, KNOT_CHANGES_OLD);
 	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to add new RDATA to list.\n");
 		return ret;
 	}
-
-	changes->old_rrsets[changes->old_rrsets_count++] = rrset_old;
-
-	dbg_xfrin_verb("Adding RDATA from old RRSet to the list of old RDATA."
-		       "\n");
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       rrset_old);
-
 	// store RRSIGs from the old RRSet to the new
 	knot_rrset_set_rrsigs(rrset_new, knot_rrset_get_rrsigs(rrset_old));
 
 	// insert the new RRSet to the node
 	dbg_xfrin_verb("Adding new RRSet.\n");
 	ret = knot_zone_contents_add_rrset(contents, rrset_new, &node,
-					   KNOT_RRSET_DUPL_SKIP);
+	                                   KNOT_RRSET_DUPL_SKIP);
 
 	if (ret < 0) {
 		dbg_xfrin("Failed to add RRSet to node.\n");
@@ -1954,12 +1798,10 @@ int xfrin_replace_rrset_in_node(knot_node_t *node,
 	}
 	assert(ret == 0);
 
-	changes->new_rrsets[changes->new_rrsets_count++] = rrset_new;
-
-	dbg_xfrin_verb("Adding RDATA from new RRSet to the list of new RDATA."
-		       "\n");
-	knot_changes_add_rdata(changes->new_rdata, &changes->new_rdata_count,
-			       rrset_new);
+	ret = knot_changes_add_rrset(changes, rrset_new, KNOT_CHANGES_NEW);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 
 	return KNOT_EOK;
 }
@@ -1978,18 +1820,18 @@ static int xfrin_apply_add_normal_ddns(knot_changes_t *changes,
 		 *    serial is less than the current serial, ignore.
 		 */
 		if (knot_node_rrset(node, KNOT_RRTYPE_SOA) == NULL
-		    || ns_serial_compare(knot_rrset_rdata_soa_serial(
+		    || ns_serial_compare(knot_rdata_soa_serial(
 		       knot_node_rrset(node, KNOT_RRTYPE_SOA)),
-			   knot_rrset_rdata_soa_serial(add) > 0
+			   knot_rdata_soa_serial(add) > 0
 		    )) {
 			dbg_ddns_verb("DDNS: Ignoring SOA.\n");
 			return KNOT_EOK;
 		} else {
 			dbg_ddns_verb("DDNS: replacing SOA (old serial: %u,"
 				      " new serial: %u.\n",
-				      knot_rrset_rdata_soa_serial(knot_node_rrset(node,
+				      knot_rdata_soa_serial(knot_node_rrset(node,
 							  KNOT_RRTYPE_SOA)),
-				      knot_rrset_rdata_soa_serial(add));
+				      knot_rdata_soa_serial(add));
 			/* b) Otherwise, replace the current SOA. */
 			ret = xfrin_replace_rrset_in_node(node, add,
 							      changes,
@@ -2076,7 +1918,7 @@ dbg_xfrin_exec_detail(
 	 *        in such case.
 	 */
 	if (*rrset
-	    && knot_dname_compare(knot_rrset_owner(*rrset),
+	    && knot_dname_cmp(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");
@@ -2112,15 +1954,11 @@ dbg_xfrin_exec_detail(
 		free(name);
 );
 		// add the RRSet from the changeset to the node
-		/*! \todo What about domain names?? Shouldn't we use the
-		 *        zone-contents' version of this function??
-		 */
 		/*!
 		 * \note The new zone must be adjusted nevertheless, so it
 		 *       doesn't matter whether there are some extra dnames to
 		 *       be added to the table or not.
 		 */
-//		ret = knot_node_add_rrset(node, add, 0);
 		ret = knot_zone_contents_add_rrset(contents, add, &node,
 						   KNOT_RRSET_DUPL_SKIP);
 
@@ -2154,7 +1992,7 @@ dbg_xfrin_exec_detail(
 	 */
 
 	dbg_xfrin_detail("Merging RRSets with owners: %s, %s types: %u, %u\n",
-			 (*rrset)->owner->name, add->owner->name,
+			 (*rrset)->owner, add->owner,
 			 (*rrset)->type,
 			 add->type);
 	dbg_xfrin_detail("RDATA in RRSet1: %p, RDATA in RRSet2: %p\n",
@@ -2171,7 +2009,7 @@ dbg_xfrin_exec_detail(
 	}
 
 	int merged, deleted_rrs;
-	ret = knot_rrset_merge_no_dupl(*rrset, add, &merged, &deleted_rrs);
+	ret = knot_rrset_merge_sort(*rrset, add, &merged, &deleted_rrs);
 	if (ret < 0) {
 		dbg_xfrin("Failed to merge changeset RRSet.\n");
 		return ret;
@@ -2213,7 +2051,7 @@ static int xfrin_apply_add_rrsig(knot_changes_t *changes,
 
 	int ret;
 
-	uint16_t type = knot_rrset_rdata_rrsig_type_covered(add);
+	uint16_t type = knot_rdata_rrsig_type_covered(add, 0);
 
 dbg_xfrin_exec_verb(
 	char *name = knot_dname_to_str(knot_rrset_owner(add));
@@ -2228,7 +2066,7 @@ dbg_xfrin_exec_verb(
 	 *        it's a copied one, so it is OK to modify it right away.
 	 */
 	if (*rrset
-	    && knot_dname_compare(knot_rrset_owner(*rrset),
+	    && knot_dname_cmp(knot_rrset_owner(*rrset),
 				  knot_node_owner(node)) == 0
 	    && knot_rrset_type(*rrset) == type) {
 		dbg_xfrin_verb("Using RRSet from previous iteration.\n");
@@ -2249,26 +2087,18 @@ dbg_xfrin_exec_verb(
 		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,
+		knot_dname_t *owner_copy = knot_dname_copy(knot_node_get_owner(node));
+		*rrset = knot_rrset_new(owner_copy, type,
 					knot_rrset_class(add),
 					knot_rrset_ttl(add));
 		if (*rrset == NULL) {
+			knot_dname_free(&owner_copy);
 			dbg_xfrin("Failed to create new RRSet for RRSIGs.\n");
 			return KNOT_ENOMEM;
 		}
 		dbg_xfrin_detail("Created new RRSet for RRSIG: %p.\n", *rrset);
 
 		// add the RRset to the list of new RRsets
-		ret = knot_changes_rrsets_reserve(
-			&changes->new_rrsets,
-			&changes->new_rrsets_count,
-			&changes->new_rrsets_allocated, 1);
-		if (ret != KNOT_EOK) {
-			dbg_xfrin("Failed to add old RRSet to list.\n");
-			knot_rrset_free(rrset);
-			return ret;
-		}
-
 		// add the new RRSet to the node
 		// not needed to insert it through the zone_contents() function,
 		// as the owner is already in the dname table
@@ -2279,7 +2109,11 @@ dbg_xfrin_exec_verb(
 			return KNOT_ERROR;
 		}
 
-		changes->new_rrsets[changes->new_rrsets_count++] = *rrset;
+		ret = knot_changes_add_rrset(changes, *rrset, KNOT_CHANGES_NEW);
+		if (ret != KNOT_EOK) {
+			knot_rrset_free(rrset);
+			return ret;
+		}
 	}
 
 dbg_xfrin_exec_detail(
@@ -2337,7 +2171,7 @@ dbg_xfrin_exec_detail(
 		// merge the changeset RRSet to the copy
 		dbg_xfrin_detail("Merging RRSIG to the one in the RRSet.\n");
 		int merged, deleted_rrs;
-		ret = knot_rrset_merge_no_dupl(rrsig, add, &merged, &deleted_rrs);
+		ret = knot_rrset_merge_sort(rrsig, add, &merged, &deleted_rrs);
 		if (ret != KNOT_EOK) {
 			dbg_xfrin("Failed to merge changeset RRSIG to copy: %s"
 				  ".\n", knot_strerror(ret));
@@ -2352,65 +2186,28 @@ dbg_xfrin_exec_detail(
 
 /*----------------------------------------------------------------------------*/
 
-void xfrin_cleanup_successful_update(knot_changes_t **changes)
+void xfrin_cleanup_successful_update(knot_changes_t *changes)
 {
-	for (int i = 0; i < (*changes)->old_rrsets_count; ++i) {
-		//TODO temporary fix!
-		if ((*changes)->old_rrsets[i] == NULL) {
-			log_server_warning("NULL RRSet to be freed in DDNS!\n");
-			continue;
-		}
-		if ((*changes)->old_rrsets[i]->rdata_count == 0) {
-dbg_xfrin_exec_detail(
-			char *name = knot_dname_to_str((*changes)->old_rrsets[i]->owner);
-			dbg_xfrin_detail("Deleting old RRSet: %s type %u\n",
-					 name, (*changes)->old_rrsets[i]->type);
-			free(name);
-);
-			knot_rrset_free(&(*changes)->old_rrsets[i]);
-		}
+	if (changes == NULL) {
+		return;
 	}
-
-	// delete old RDATA
-	for (int i = 0; i < (*changes)->old_rdata_count; ++i) {
-		// RDATA are stored separately so do not delete the whole chain
-		knot_rrset_deep_free_no_sig(&(*changes)->old_rdata[i], 1, 1);
+	// Free old RRSets
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, changes->old_rrsets) {
+		knot_rrset_t *rrset = rr_node->rr;
+		knot_rrset_deep_free_no_sig(&rrset, 1);
 	}
 
-	// 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 old nodes
+	knot_node_ln_t *n_node = NULL;
+	WALK_LIST(n_node, changes->old_nodes) {
+		knot_node_free(&n_node->node);
 	}
 
-	// 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 old NSEC3 nodes
+	WALK_LIST(n_node, changes->old_nsec3) {
+		knot_node_free(&n_node->node);
 	}
-
-	// free allocated arrays of nodes and rrsets
-	free((*changes)->new_rrsets);
-	free((*changes)->new_rdata);
-	free((*changes)->old_nodes);
-	free((*changes)->old_nsec3);
-	free((*changes)->old_rrsets);
-	free((*changes)->old_rdata);
-
-	free((*changes));
-	*changes = NULL;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -2470,7 +2267,6 @@ static void xfrin_cleanup_old_nodes(knot_node_t *node, void *data)
 	assert(node != NULL);
 
 	knot_node_set_new_node(node, NULL);
-	knot_dname_set_node(knot_node_get_owner(node), node);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -2489,7 +2285,6 @@ static void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents,
 
 	if (old_contents != NULL) {
 		// cleanup old zone tree - reset pointers to new node to NULL
-		// also set pointers from dnames to old nodes
 		knot_zone_contents_tree_apply_inorder(old_contents,
 						      xfrin_cleanup_old_nodes,
 						      NULL);
@@ -2504,56 +2299,17 @@ static void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents,
 
 void xfrin_rollback_update(knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents,
-                           knot_changes_t **changes)
+                           knot_changes_t *changes)
 {
-	assert(changes != NULL);
+	if (changes == NULL) {
+		return;
+	}
 
 	dbg_xfrin("Rolling back changeset application.\n");
-
-	if (*changes != NULL) {
-		// discard new RRSets
-		for (int i = 0; i < (*changes)->new_rrsets_count; ++i) {
-			//knot_rrset_deep_free(&changes->new_rrsets[i], 0, 1, 1);
-			if ((*changes)->new_rrsets[i]->rdata_count == 0) {
-				knot_rrset_free(&(*changes)->new_rrsets[i]);
-			}
-		}
-
-		for (int i = 0; i < (*changes)->new_rdata_count; ++i) {
-			dbg_xfrin_detail("Freeing %d. RDATA: %p\n", i,
-					 (*changes)->new_rdata[i]);
-
-			/*
-			 * In some case, the same RDATA may be stored in
-			 * different positions in different RDATA chains, so
-			 * some ivalid reads occur.
-			 *
-			 * More precisely, the same chain is stored multiple
-			 * times, but starting from different RDATA.
-			 *
-			 * We may check every RDATA against every one
-			 * already deleted, but that may be very time-consuming.
-			 */
-
-			/*
-			 * Every RDATA from a chain is stored separately.
-			 * We thus do not follow the RDATA chains and free only
-			 * the first RDATA in each.
-			 */
-
-			knot_rrset_deep_free_no_sig(&(*changes)->new_rdata[i], 1, 1);
-		}
-
-		// free allocated arrays of nodes and rrsets
-		free((*changes)->new_rrsets);
-		free((*changes)->new_rdata);
-		free((*changes)->old_nodes);
-		free((*changes)->old_nsec3);
-		free((*changes)->old_rrsets);
-		free((*changes)->old_rdata);
-
-		free(*changes);
-		*changes = NULL;
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, changes->new_rrsets) {
+		knot_rrset_t *rrset = rr_node->rr;
+		knot_rrset_deep_free_no_sig(&rrset, 1);
 	}
 
 	xfrin_cleanup_failed_update(old_contents, new_contents);
@@ -2571,65 +2327,78 @@ static int xfrin_apply_remove(knot_zone_contents_t *contents,
 	 * RDATA may be removed from it.
 	 */
 	int ret = 0;
-	knot_node_t *node = NULL;
-	knot_rrset_t *rrset = NULL, *rrsigs = NULL;
+	knot_node_t *last_node = NULL;
+	knot_rrset_t *rrset = NULL;
+	knot_rrset_t *rrsigs = NULL;
+	int is_nsec3 = 0;
 
-	for (int i = 0; i < chset->remove_count; ++i) {
+	knot_rr_ln_t *rr_node = NULL;
+	WALK_LIST(rr_node, chset->remove) {
+		knot_rrset_t *rr = rr_node->rr;
+		assert(rr); // No malformed changesets should get here
 dbg_xfrin_exec_verb(
 		char *name = knot_dname_to_str(
-			knot_rrset_owner(chset->remove[i]));
+			knot_rrset_owner(rr));
 		dbg_xfrin_verb("Removing RRSet: %s, type %u\n", name,
-			       knot_rrset_type(chset->remove[i]));
+			       knot_rrset_type(rr));
 		free(name);
 );
 dbg_xfrin_exec_detail(
-		knot_rrset_dump(chset->remove[i]);
+		knot_rrset_dump(rr);
 );
 
+		is_nsec3 = 0;
+
 		// check if the RRSet belongs to the NSEC3 tree
-		int is_nsec3 = knot_rrset_is_nsec3rel(chset->remove[i]);
+		if ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
+		    || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
+			&& knot_rdata_rrsig_type_covered(rr, 0)
+			    == KNOT_RRTYPE_NSEC3))
+		{
+			dbg_xfrin_verb("Removed RRSet belongs to NSEC3 tree.\n");
+			is_nsec3 = 1;
+		}
 
 		// check if the old node is not the one we should use
 		dbg_xfrin_verb("Node:%p Owner: %p Node owner: %p\n",
-			       node, knot_rrset_owner(chset->remove[i]),
-			       knot_node_owner(node));
-		if (!node || knot_rrset_owner(chset->remove[i])
-			     != knot_node_owner(node)) {
+			       last_node, knot_rrset_owner(rr),
+			       knot_node_owner(last_node));
+		if (!last_node || knot_rrset_owner(rr)
+			     != knot_node_owner(last_node)) {
 			if (is_nsec3) {
-				node = knot_zone_contents_get_nsec3_node(
+				last_node = knot_zone_contents_get_nsec3_node(
 					    contents,
-					    knot_rrset_owner(chset->remove[i]));
+					    knot_rrset_owner(rr));
 			} else {
-				node = knot_zone_contents_get_node(contents,
-					    knot_rrset_owner(chset->remove[i]));
+				last_node = knot_zone_contents_get_node(contents,
+					    knot_rrset_owner(rr));
 			}
-			if (node == NULL) {
+			if (last_node == NULL) {
 				dbg_xfrin_verb("Node not found for RR to be "
 					       "removed!\n");
 				continue;
 			}
 		}
 
-		assert(node != NULL);
+		assert(last_node != NULL);
 
 		// first check if all RRSets should be removed
 		dbg_xfrin_verb("RRSet class to be removed=%u\n",
-			       knot_rrset_class(chset->remove[i]));
-		if (knot_rrset_class(chset->remove[i]) == KNOT_CLASS_ANY) {
+			       knot_rrset_class(rr));
+		if (knot_rrset_class(rr) == KNOT_CLASS_ANY) {
 			ret = xfrin_apply_remove_all_rrsets(
-				changes, node,
-				knot_rrset_type(chset->remove[i]), chset->flags);
-		} else if (knot_rrset_type(chset->remove[i])
+				changes, last_node,
+				knot_rrset_type(rr), chset->flags);
+		} else if (knot_rrset_type(rr)
 			   == KNOT_RRTYPE_RRSIG) {
 			// this should work also for UPDATE
-			ret = xfrin_apply_remove_rrsigs(changes,
-							chset->remove[i],
-							node, &rrset, &rrsigs);
+			ret = xfrin_apply_remove_rrsigs(changes, rr,
+							last_node, &rrset, &rrsigs);
 		} else {
 			// this should work also for UPDATE
 			ret = xfrin_apply_remove_normal(changes,
-							chset->remove[i],
-							node, &rrset,
+							rr,
+							last_node, &rrset,
 							chset->flags);
 		}
 
@@ -2652,63 +2421,77 @@ static int xfrin_apply_add(knot_zone_contents_t *contents,
                            knot_changes_t *changes)
 {
 	int ret = KNOT_EOK;
-	knot_node_t *node = NULL;
+	knot_node_t *last_node = NULL;
 	knot_rrset_t *rrset = NULL;
 	knot_rrset_t *rrsigs = NULL;
+	int is_nsec3 = 0;
 
-	for (int i = 0; i < chset->add_count; ++i) {
+	knot_rr_ln_t *rr_node = NULL;
+	node_t *tmp_node;
+	WALK_LIST_DELSAFE(rr_node, tmp_node, chset->add) {
+		knot_rrset_t *rr = rr_node->rr;
+		assert(rr); // No malformed changesets should get here
 dbg_xfrin_exec_verb(
 		char *name = knot_dname_to_str(
-			knot_rrset_owner(chset->add[i]));
+			knot_rrset_owner(rr));
 		dbg_xfrin_verb("Adding RRSet: %s, type: %u\n", name,
-			       knot_rrset_type(chset->add[i]));
+			       knot_rrset_type(rr));
 		free(name);
 );
 dbg_xfrin_exec_detail(
-		knot_rrset_dump(chset->add[i]);
+		knot_rrset_dump(rr);
 );
 
+		is_nsec3 = 0;
+
 		// check if the RRSet belongs to the NSEC3 tree
-		int is_nsec3 = knot_rrset_is_nsec3rel(chset->add[i]);
+		if ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3)
+		    || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG
+			&& knot_rdata_rrsig_type_covered(rr, 0)
+			    == KNOT_RRTYPE_NSEC3))
+		{
+			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)) {
+		if (!last_node || knot_rrset_owner(rr)
+			     != knot_node_owner(last_node)) {
 			dbg_xfrin_detail("Searching for node...\n");
 			if (is_nsec3) {
-				node = knot_zone_contents_get_nsec3_node(
-					       contents,
-					       knot_rrset_owner(chset->add[i]));
+				last_node = knot_zone_contents_get_nsec3_node(
+				            contents,
+				            knot_rrset_owner(rr));
 			} else {
-				node = knot_zone_contents_get_node(contents,
-					       knot_rrset_owner(chset->add[i]));
+				last_node = knot_zone_contents_get_node(contents,
+				            knot_rrset_owner(rr));
 			}
-			if (node == NULL) {
+			if (last_node == NULL) {
 				// create new node, connect it properly to the
 				// zone nodes
 				dbg_xfrin_detail("Node not found. Creating new."
 						 "\n");
-				ret = knot_zone_contents_create_node(contents,
-				                                     chset->add[i],
-				                                     &node);
-				if (ret != KNOT_EOK) {
+				last_node = xfrin_add_new_node(contents,
+							  rr,
+							  is_nsec3);
+				if (last_node == NULL) {
 					dbg_xfrin("Failed to create new node "
 						  "in zone.\n");
-					return ret;
+					return KNOT_ERROR;
 				}
 			}
 		}
 
-		assert(node != NULL);
+		assert(last_node != NULL);
 
-		if (knot_rrset_type(chset->add[i]) == KNOT_RRTYPE_RRSIG) {
-			ret = xfrin_apply_add_rrsig(changes, chset->add[i],
-						    node, &rrset, &rrsigs,
+		if (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG) {
+			ret = xfrin_apply_add_rrsig(changes, rr, last_node,
+			                            &rrset, &rrsigs,
 						    contents);
 			assert(ret != KNOT_EOK);
 		} else {
-			ret = xfrin_apply_add_normal(changes, chset->add[i],
-						     node, &rrset, contents,
+			ret = xfrin_apply_add_normal(changes, rr,
+						     last_node, &rrset, contents,
 						     chset->flags);
 			assert(ret <= 3);
 		}
@@ -2725,52 +2508,27 @@ dbg_xfrin_exec_detail(
 				// the ADD RRSet was used, i.e. it should be
 				// removed from the changeset and saved in the
 				// list of new RRSets
-				ret = knot_changes_rrsets_reserve(
-					&changes->new_rrsets,
-					&changes->new_rrsets_count,
-					&changes->new_rrsets_allocated, 1);
+				ret = knot_changes_add_rrset(changes, rr,
+				                             KNOT_CHANGES_NEW);
 				if (ret != KNOT_EOK) {
 					dbg_xfrin("Failed to add old RRSet to "
 						  "list.\n");
 					return ret;
 				}
 
-				changes->new_rrsets[changes->new_rrsets_count++]
-					 = chset->add[i];
-
-				// the same goes for the RDATA
-				int count = 1;//knot_rrset_rdata_rr_count(chset->add[i]);
-
-				// connect the RDATA to the list of new RDATA
-				int res = knot_changes_rdata_reserve(
-					&changes->new_rdata,
-					changes->new_rdata_count,
-					&changes->new_rdata_allocated, count);
-				if (res != KNOT_EOK) {
-					return res;
-				}
-
-				knot_changes_add_rdata(changes->new_rdata,
-					    &changes->new_rdata_count,
-					    chset->add[i]);
-
-				chset->add[i] = NULL;
+				rem_node((node_t *)rr_node);
 			} else if (ret == 2) {
 				// the copy of the RRSet was used, but it was
 				// already stored in the new RRSets list
 				// just delete the add RRSet, but without RDATA
 				// DNAMES as these were merged to the copied RRSet
-				knot_rrset_deep_free(&chset->add[i], 1, 0);
-
-				// In this case, the RDATA does not have to be
-				// stored in the list of new RDATA, because
-				// it is joined to the copy of RDATA, that is
-				// already stored there
+				knot_rrset_deep_free(&rr, 1);
+				rem_node((node_t *)rr_node);
 			} else if (ret == 3) {
 				// the RRSet was used and both RRSet and RDATA
 				// were properly stored. Just clear the place
 				// in the changeset
-				chset->add[i] = NULL;
+				rem_node((node_t *)rr_node);
 			} else {
 				assert(0);
 			}
@@ -2826,7 +2584,7 @@ static int xfrin_apply_changeset(knot_zone_contents_t *contents,
 	/*! \todo Only if SOA is present? */
 	const knot_rrset_t *soa = knot_node_rrset(contents->apex,
 						  KNOT_RRTYPE_SOA);
-	if (soa == NULL || knot_rrset_rdata_soa_serial(soa)
+	if (soa == NULL || knot_rdata_soa_serial(soa)
 			   != chset->serial_from) {
 		dbg_xfrin("SOA serials do not match!!\n");
 		return KNOT_ERROR;
@@ -2847,25 +2605,22 @@ static int xfrin_apply_changeset(knot_zone_contents_t *contents,
 
 /*----------------------------------------------------------------------------*/
 
-static void xfrin_mark_empty(knot_node_t *node, void *data)
+static void xfrin_mark_empty_node(knot_node_t *node, knot_changes_t *changes,
+                                  knot_changes_part_t part)
 {
 	assert(node != NULL);
-	assert(data != NULL);
-
-	knot_changes_t *changes = (knot_changes_t *)data;
+	assert(changes != NULL);
 
 	if (knot_node_rrset_count(node) == 0
 	    && knot_node_children(node) == 0) {
-		int ret = knot_changes_nodes_reserve(&changes->old_nodes,
-						 &changes->old_nodes_count,
-						 &changes->old_nodes_allocated);
+		// Add node to changes.
+		int ret = knot_changes_add_node(changes, node, part);
 		if (ret != KNOT_EOK) {
 			/*! \todo Stop on error? */
 			return;
 		}
 
-		changes->old_nodes[changes->old_nodes_count++] = node;
-		// mark the node as empty
+		// Mark the node as empty.
 		knot_node_set_empty(node);
 
 		if (node->parent != NULL) {
@@ -2877,42 +2632,20 @@ static void xfrin_mark_empty(knot_node_t *node, void *data)
 			node->parent = NULL;
 		}
 	}
-	dbg_xfrin_detail("Space for nodes reserved, old node count = %d\n",
-			 changes->old_nodes_count);
 }
 
 /*----------------------------------------------------------------------------*/
 
 static void xfrin_mark_empty_nsec3(knot_node_t *node, void *data)
 {
-	assert(node != NULL);
-	assert(data != NULL);
-
-	knot_changes_t *changes = (knot_changes_t *)data;
-
-	if (knot_node_rrset_count(node) == 0
-	    && knot_node_children(node) == 0) {
-		int ret = knot_changes_nodes_reserve(&changes->old_nsec3,
-						 &changes->old_nsec3_count,
-						 &changes->old_nsec3_allocated);
-		if (ret != KNOT_EOK) {
-			/*! \todo Stop on error? */
-			return;
-		}
-
-		changes->old_nsec3[changes->old_nsec3_count++] = node;
-		// mark the node as empty
-		knot_node_set_empty(node);
+	xfrin_mark_empty_node(node,
+	                      (knot_changes_t *)data, KNOT_CHANGES_NSEC3_NODE);
+}
 
-		if (node->parent != NULL) {
-			assert(node->parent->children > 0);
-			--node->parent->children;
-			if (node->parent->wildcard_child == node) {
-				node->parent->wildcard_child = NULL;
-			}
-			node->parent = NULL;
-		}
-	}
+static void xfrin_mark_empty(knot_node_t *node, void *data)
+{
+	xfrin_mark_empty_node(node,
+	                      (knot_changes_t *)data, KNOT_CHANGES_NORMAL_NODE);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -2924,49 +2657,41 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents,
 
 	dbg_xfrin("Removing empty nodes from zone.\n");
 
-	dbg_xfrin_verb("OLD NODES COUNT: %d\n", changes->old_nodes_count);
-	dbg_xfrin_verb("OLD NSEC3 NODES COUNT: %d\n", changes->old_nsec3_count);
-
 	// walk through the zone and select nodes to be removed
-	/* \note This function doesn't require order, but requires to be applied
-	 * on the leaves first and then on the their parent.
-	 */
 	ret = knot_zone_contents_tree_apply_inorder_reverse(contents,
-							    xfrin_mark_empty,
-							    (void *)changes);
+	                                                    xfrin_mark_empty,
+	                                                    changes);
 	assert(ret == KNOT_EOK);
 
 	// Do the same with NSEC3 nodes.
 	ret = knot_zone_contents_nsec3_apply_inorder_reverse(contents,
-							 xfrin_mark_empty_nsec3,
-							 (void *)changes);
+	                                                     xfrin_mark_empty_nsec3,
+	                                                     changes);
 	assert(ret == KNOT_EOK);
 
-	dbg_xfrin_verb("OLD NODES COUNT: %d\n", changes->old_nodes_count);
-	dbg_xfrin_verb("OLD NSEC3 NODES COUNT: %d\n", changes->old_nsec3_count);
-
-	// remove these nodes from both hash table and the tree
+	// Remove these nodes from zone tree.
 	knot_node_t *zone_node = NULL;
 
-	for (int i = 0; i < changes->old_nodes_count; ++i) {
+	knot_node_ln_t *list_node = NULL;
+	WALK_LIST(list_node, changes->old_nodes) {
+		knot_node_t *node = list_node->node;
+		assert(node);
 		zone_node = 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);
+		char *name = knot_dname_to_str(knot_node_owner(node));
+		dbg_xfrin_detail("Old node: %p, %s\n",node, name);
 		free(name);
 );
 
 		ret = knot_zone_contents_remove_node(
-			contents, changes->old_nodes[i], &zone_node);
+			contents, node, &zone_node);
 
 		if (ret == KNOT_ENONODE) {
-			assert(knot_node_rrset_count(changes->old_nodes[i]) == 1);
-			assert(knot_node_rrset(changes->old_nodes[i],
+			assert(knot_node_rrset_count(node) == 1);
+			assert(knot_node_rrset(node,
 			                       KNOT_RRTYPE_RRSIG));
-			char *name = knot_dname_to_str(changes->old_nodes[i]->owner);
+			char *name = knot_dname_to_str(node->owner);
 			log_zone_warning("Ignoring extra RRSIG for %s!\n",
 			                 name);
 			free(name);
@@ -2974,27 +2699,27 @@ dbg_xfrin_exec_detail(
 			dbg_xfrin("Failed to remove node from zone!\n");
 			return ret;
 		}
-		assert(changes->old_nodes[i] == zone_node);
+		assert(node == zone_node);
 	}
 
 	// remove NSEC3 nodes
-	for (int i = 0; i < changes->old_nsec3_count; ++i) {
+	WALK_LIST(list_node, changes->old_nsec3) {
+		knot_node_t *node = list_node->node;
+		assert(node);
 		zone_node = NULL;
 
-		char *name = knot_dname_to_str(knot_node_owner(
-						       changes->old_nsec3[i]));
-		dbg_xfrin_detail("Old NSEC3 node #%d: %p, %s\n", i,
-				 changes->old_nsec3[i], name);
+		char *name = knot_dname_to_str(knot_node_owner(node));
+		dbg_xfrin_detail("Old NSEC3 node: %p, %s\n", node, name);
 		free(name);
 
 		ret = knot_zone_contents_remove_nsec3_node(
-			contents, changes->old_nsec3[i], &zone_node);
+			contents, node, &zone_node);
 
 		if (ret != KNOT_EOK) {
 			dbg_xfrin("Failed to remove NSEC3 node from zone!\n");
 			return KNOT_ENONODE;
 		}
-		assert(changes->old_nsec3[i] == zone_node);
+		assert(node == zone_node);
 	}
 
 	return KNOT_EOK;
@@ -3048,10 +2773,9 @@ static int xfrin_check_contents_copy(knot_zone_contents_t *old_contents)
 /*----------------------------------------------------------------------------*/
 
 int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
-                            knot_zone_contents_t **new_contents,
-                            knot_changes_t **changes)
+                            knot_zone_contents_t **new_contents)
 {
-	if (old_contents == NULL || new_contents == NULL || changes == NULL) {
+	if (old_contents == NULL || new_contents == NULL) {
 		return KNOT_EINVAL;
 	}
 
@@ -3087,27 +2811,17 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
 		return ret;
 	}
 
-	knot_changes_t *chgs = (knot_changes_t *)malloc(
-				sizeof(knot_changes_t));
-	if (chgs == NULL) {
-		dbg_xfrin("Failed to allocate structure for changes!\n");
-		xfrin_rollback_update(old_contents, &contents_copy, &chgs);
-		return KNOT_ENOMEM;
-	}
-
-	memset(chgs, 0, sizeof(knot_changes_t));
-
 	/*!
 	 * \todo Check if all nodes have their copy.
 	 */
 	ret = xfrin_check_contents_copy(old_contents);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Contents copy check failed!\n");
-		xfrin_rollback_update(old_contents, &contents_copy, &chgs);
+		xfrin_cleanup_failed_update(old_contents, &contents_copy);
 		return ret;
 	}
 
-        assert(knot_zone_contents_apex(contents_copy) != NULL);
+	assert(knot_zone_contents_apex(contents_copy) != NULL);
 
 	/*
 	 * Fix references to new nodes. Some references in new nodes may point
@@ -3118,7 +2832,6 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
 	assert(knot_zone_contents_apex(contents_copy) != NULL);
 
 	*new_contents = contents_copy;
-	*changes = chgs;
 
 	return KNOT_EOK;
 }
@@ -3159,6 +2872,7 @@ int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
 			  knot_strerror(ret));
 		return ret;
 	}
+
 	assert(knot_zone_contents_apex(contents_copy) != NULL);
 
 	return KNOT_EOK;
@@ -3170,7 +2884,7 @@ int xfrin_apply_changesets(knot_zone_t *zone,
                            knot_changesets_t *chsets,
                            knot_zone_contents_t **new_contents)
 {
-	if (zone == NULL || chsets == NULL || chsets->count == 0
+	if (zone == NULL || chsets == NULL || EMPTY_LIST(chsets->sets)
 	    || new_contents == NULL) {
 		return KNOT_EINVAL;
 	}
@@ -3185,9 +2899,7 @@ int xfrin_apply_changesets(knot_zone_t *zone,
 
 	dbg_xfrin_verb("Creating shallow copy of the zone...\n");
 	knot_zone_contents_t *contents_copy = NULL;
-	knot_changes_t *changes = NULL;
-	int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy,
-					  &changes);
+	int ret = xfrin_prepare_zone_copy(old_contents, &contents_copy);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to prepare zone copy: %s\n",
 			  knot_strerror(ret));
@@ -3200,12 +2912,12 @@ int xfrin_apply_changesets(knot_zone_t *zone,
 	dbg_xfrin("Applying changesets.\n");
 	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_changeset(contents_copy, changes,
-						  &chsets->sets[i]))
-						  != KNOT_EOK) {
+	knot_changeset_t *set = NULL;
+	WALK_LIST(set, chsets->sets) {
+		ret = xfrin_apply_changeset(contents_copy, chsets->changes, set);
+		if (ret != KNOT_EOK) {
 			xfrin_rollback_update(old_contents,
-					       &contents_copy, &changes);
+					       &contents_copy, chsets->changes);
 			dbg_xfrin("Failed to apply changesets to zone: "
 				  "%s\n", knot_strerror(ret));
 			return ret;
@@ -3218,15 +2930,14 @@ int xfrin_apply_changesets(knot_zone_t *zone,
 	 */
 
 	dbg_xfrin_verb("Finalizing updated zone...\n");
-	ret = xfrin_finalize_updated_zone(contents_copy, changes);
+	ret = xfrin_finalize_updated_zone(contents_copy, chsets->changes);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to finalize updated zone: %s\n",
 			  knot_strerror(ret));
-		xfrin_rollback_update(old_contents, &contents_copy, &changes);
+		xfrin_rollback_update(old_contents, &contents_copy, chsets->changes);
 		return ret;
 	}
 
-	chsets->changes = changes;
 	*new_contents = contents_copy;
 
 	return KNOT_EOK;
@@ -3234,56 +2945,6 @@ int xfrin_apply_changesets(knot_zone_t *zone,
 
 /*----------------------------------------------------------------------------*/
 
-static int xfrin_switch_node_in_rdata(knot_dname_t **dname, void *data)
-{
-	UNUSED(data);
-	if (dname == NULL || *dname == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if ((*dname)->node != NULL) {
-		knot_dname_update_node(*dname);
-	}
-
-	return KNOT_EOK;
-}
-
-static void xfrin_switch_node_in_rrset(knot_rrset_t *rrset)
-{
-	if (rrset == NULL) {
-		return;
-	}
-
-	if (rrset->rrsigs) {
-		xfrin_switch_node_in_rrset(rrset->rrsigs);
-	}
-
-	if (rrset->owner->node != NULL) {
-		knot_dname_update_node(rrset->owner);
-	}
-
-	rrset_dnames_apply(rrset, xfrin_switch_node_in_rdata, NULL);
-}
-
-static void xfrin_switch_node_in_node(knot_node_t **node, void *data)
-{
-	UNUSED(data);
-	if (node == NULL || *node == NULL) {
-		return;
-	}
-
-	if ((*node)->owner->node != NULL) {
-		knot_dname_update_node((*node)->owner);
-	}
-
-	knot_rrset_t **rr_array = knot_node_get_rrsets_no_copy(*node);
-	for (uint16_t i = 0; i < (*node)->rrset_count; ++i) {
-		xfrin_switch_node_in_rrset(rr_array[i]);
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
 int xfrin_switch_zone(knot_zone_t *zone,
                       knot_zone_contents_t *new_contents,
                       int transfer_type)
@@ -3303,17 +2964,6 @@ 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 || transfer_type == XFR_TYPE_UPDATE) {
-		/* Switch node references in owner DNAMEs and RDATA dnames. */
-		int ret = knot_zone_tree_apply(new_contents->nodes,
-					       xfrin_switch_node_in_node, NULL);
-		assert(ret == KNOT_EOK);
-		ret = knot_zone_tree_apply(new_contents->nsec3_nodes,
-					   xfrin_switch_node_in_node, 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);
@@ -3321,6 +2971,7 @@ int xfrin_switch_zone(knot_zone_t *zone,
 	// wait for readers to finish
 	dbg_xfrin_verb("Waiting for readers to finish...\n");
 	synchronize_rcu();
+
 	// destroy the old zone
 	dbg_xfrin_verb("Freeing old zone: %p\n", old);
 
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
index 54a22c554d6b20d2bbb473354739e68eb06c999c..7cfb91e6874f389cc7be432c09d94da6bc65c01b 100644
--- a/src/libknot/updates/xfr-in.h
+++ b/src/libknot/updates/xfr-in.h
@@ -175,8 +175,7 @@ void xfrin_free_changesets(knot_changesets_t **changesets);
  * \retval KNOT_EMALF
  * \retval KNOT_ENOMEM
  */
-int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr/*const uint8_t *pkt, size_t size,
-                              knot_changesets_t **changesets*/);
+int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr);
 
 int xfrin_apply_changesets_to_zone(knot_zone_t *zone,
                                    knot_changesets_t *chsets);
@@ -186,21 +185,20 @@ int xfrin_apply_changesets(knot_zone_t *zone,
                            knot_zone_contents_t **new_contents);
 
 int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
-                            knot_zone_contents_t **new_contents,
-                            knot_changes_t **changes);
+                            knot_zone_contents_t **new_contents);
 
 int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
                                 knot_changes_t *changes);
 
 int xfrin_switch_zone(knot_zone_t *zone,
                       knot_zone_contents_t *new_contents,
-                      int deep_free);
+                      int transfer_type);
 
-void xfrin_cleanup_successful_update(knot_changes_t **changes);
+void xfrin_cleanup_successful_update(knot_changes_t *changes);
 
 void xfrin_rollback_update(knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents,
-                           knot_changes_t **changes);
+                           knot_changes_t *changes);
 
 int xfrin_copy_rrset(knot_node_t *node, uint16_t type,
                      knot_rrset_t **rrset, knot_changes_t *changes,
@@ -214,6 +212,8 @@ int xfrin_replace_rrset_in_node(knot_node_t *node,
                                 knot_changes_t *changes,
                                 knot_zone_contents_t *contents);
 
+void xfrin_zone_contents_free(knot_zone_contents_t **contents);
+
 #endif /* _KNOTXFR_IN_H_ */
 
 /*! @} */
diff --git a/src/libknot/util/debug.c b/src/libknot/util/debug.c
index 9f8d8527fc443b5c9fd2c5b866bb45cc58280f4b..97c9e5af4c28c1d4a1b36b63dbc188ebc7ebfbfc 100644
--- a/src/libknot/util/debug.c
+++ b/src/libknot/util/debug.c
@@ -44,10 +44,7 @@ void knot_node_dump(knot_node_t *node)
 	name = knot_dname_to_str(node->owner);
 	dbg_node_detail("owner: %s\n", name);
 	free(name);
-	dbg_node_detail("labels: ");
-	hex_print(node->owner->labels, node->owner->label_count);
 	dbg_node_detail("node: %p\n", node);
-	dbg_node_detail("node (in node's owner): %p\n", node->owner->node);
 
 	if (knot_node_is_deleg_point(node)) {
 		dbg_node_detail("delegation point\n");
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
index 826260a10974674df8d720f6cc16dae2cb297549..f4a51a2925debc45298f634c0244db7f7b407076 100644
--- a/src/libknot/util/debug.h
+++ b/src/libknot/util/debug.h
@@ -873,6 +873,54 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone);
 #define dbg_rrset_exec_detail(cmds)
 #endif
 
+#ifdef KNOT_DNSSEC_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_dnssec(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dnssec_hex(data, len)  hex_log(LOG_SERVER, (data), (len))
+#define dbg_dnssec_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_dnssec(msg...)
+#define dbg_dnssec_hex(data, len)
+#define dbg_dnssec_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_dnssec_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dnssec_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#define dbg_dnssec_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_dnssec_verb(msg...)
+#define dbg_dnssec_hex_verb(data, len)
+#define dbg_dnssec_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_dnssec_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dnssec_hex_detail(data, len)  hex_log(LOG_SERVER, (data), (len))
+#define dbg_dnssec_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_dnssec_detail(msg...)
+#define dbg_dnssec_hex_detail(data, len)
+#define dbg_dnssec_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_dnssec(msg...)
+#define dbg_dnssec_hex(data, len)
+#define dbg_dnssec_exec(cmds)
+#define dbg_dnssec_verb(msg...)
+#define dbg_dnssec_hex_verb(data, len)
+#define dbg_dnssec_exec_verb(cmds)
+#define dbg_dnssec_detail(msg...)
+#define dbg_dnssec_hex_detail(data, len)
+#define dbg_dnssec_exec_detail(cmds)
+#endif
+
 /******************************************************************************/
 
 #endif /* _KNOT_DEBUG_H_ */
diff --git a/src/libknot/util/tolower.c b/src/libknot/util/tolower.c
index d8ca17d0ac47e7842b8f6b6ce928337440c91764..a12918dfb1662a7604a5d4f6693755e536e627c5 100644
--- a/src/libknot/util/tolower.c
+++ b/src/libknot/util/tolower.c
@@ -17,7 +17,7 @@
 #include <config.h>
 #include "util/tolower.h"
 
-const uint8_t char_table[CHAR_TABLE_SIZE] = {
+const uint8_t char_table[KNOT_CHAR_TABLE_SIZE] = {
 	'\x00',
 	'\x01',
 	'\x02',
diff --git a/src/libknot/util/tolower.h b/src/libknot/util/tolower.h
index 55f3eb7dac737aab0e7fbf3a065762543f8a8a24..2edab6ee35c24181c644477426ecb172c68b05d5 100644
--- a/src/libknot/util/tolower.h
+++ b/src/libknot/util/tolower.h
@@ -28,32 +28,46 @@
 #define _KNOT_TOLOWER_H_
 
 #include <stdint.h>
+#include <stdlib.h>
 
 /*! \brief Size of the character conversion table. */
-#define KNOT_CHAR_TABLE_SIZE 256
-
-enum {
-	/*! \brief Size of the character conversion table. */
-	CHAR_TABLE_SIZE = KNOT_CHAR_TABLE_SIZE
-};
+#define KNOT_CHAR_TABLE_SIZE (UINT8_MAX + 1)
 
 /*! \brief Character table mapping uppercase letters to lowercase. */
-extern const uint8_t char_table[CHAR_TABLE_SIZE];
+extern const uint8_t char_table[KNOT_CHAR_TABLE_SIZE];
 
 /*!
- * \brief Converts ASCII character to lowercase.
+ * \brief Converts binary character to lowercase.
  *
- * \param c ASCII character code.
+ * \param c  Character code.
  *
  * \return \a c converted to lowercase (or \a c if not applicable).
  */
 static inline uint8_t knot_tolower(uint8_t c) {
-#if KNOT_CHAR_TABLE_SIZE < 256
-	assert(c < CHAR_TABLE_SIZE);
-#endif
 	return char_table[c];
 }
 
+/*!
+ * \brief Convert binary data to lowercase (if lowercase equivalent exists).
+ *
+ * \param data  Binary input string.
+ * \param size  Size of the input string
+ *
+ * \return Lowercase representation of the input string.
+ */
+static inline uint8_t *knot_strtolower(const uint8_t *data, size_t size)
+{
+	uint8_t *result = (uint8_t *)malloc(size);
+	if (!result)
+		return NULL;
+
+	for (size_t i = 0; i < size; ++i) {
+		result[i] = knot_tolower(data[i]);
+	}
+
+	return result;
+}
+
 #endif /* _KNOT_TOLOWER_H_ */
 
 /*! @} */
diff --git a/src/libknot/util/wire.h b/src/libknot/util/wire.h
index e2f283de7b57bbecef837dbfeb8cb156dba15c3c..a556636d1f911aad29b00dd357abdddf1f415fff 100644
--- a/src/libknot/util/wire.h
+++ b/src/libknot/util/wire.h
@@ -148,6 +148,15 @@ static inline void knot_wire_set_qdcount(uint8_t *packet, uint16_t qdcount)
 	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, qdcount);
 }
 
+/*!
+ * \brief Adds to QDCOUNT.
+ */
+static inline void knot_wire_add_qdcount(uint8_t *packet, int16_t n)
+{
+	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT,
+	                    knot_wire_get_qdcount(packet) + n);
+}
+
 /*!
  * \brief Returns the ANCOUNT (count of Answer entries) from wire format of
  *        the packet.
@@ -173,6 +182,15 @@ static inline void knot_wire_set_ancount(uint8_t *packet, uint16_t ancount)
 	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, ancount);
 }
 
+/*!
+ * \brief Adds to ANCOUNT.
+ */
+static inline void knot_wire_add_ancount(uint8_t *packet, int16_t n)
+{
+	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT,
+	                    knot_wire_get_ancount(packet) + n);
+}
+
 /*!
  * \brief Returns the NSCOUNT (count of Authority entries) from wire format of
  *        the packet.
@@ -198,6 +216,15 @@ static inline void knot_wire_set_nscount(uint8_t *packet, uint16_t nscount)
 	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, nscount);
 }
 
+/*!
+ * \brief Adds to NSCOUNT.
+ */
+static inline void knot_wire_add_nscount(uint8_t *packet, int16_t n)
+{
+	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT,
+	                    knot_wire_get_nscount(packet) + n);
+}
+
 /*!
  * \brief Returns the ARCOUNT (count of Additional entries) from wire format of
  *        the packet.
@@ -223,6 +250,15 @@ static inline void knot_wire_set_arcount(uint8_t *packet, uint16_t arcount)
 	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, arcount);
 }
 
+/*!
+ * \brief Adds to ARCOUNT.
+ */
+static inline void knot_wire_add_arcount(uint8_t *packet, int16_t n)
+{
+	knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT,
+	                    knot_wire_get_arcount(packet) + n);
+}
+
 /*
  * Packet header flags manipulation functions.
  */
@@ -919,15 +955,21 @@ static inline uint16_t knot_wire_get_pointer(const uint8_t *pos)
 	return (knot_wire_read_u16(pos) - KNOT_WIRE_PTR_BASE);	// Return offset.
 }
 
-static inline uint8_t *knot_wire_next_label(uint8_t *lp, uint8_t *wire)
+static inline const uint8_t *knot_wire_seek_label(const uint8_t *lp, const uint8_t *wire)
 {
-	lp = lp + (lp[0] + sizeof(uint8_t));
-	if (knot_wire_is_pointer(lp)) {
+	while (knot_wire_is_pointer(lp)) {
+		if (!wire)
+			return NULL;
 		lp = wire + knot_wire_get_pointer(lp);
 	}
 	return lp;
 }
 
+static inline const uint8_t *knot_wire_next_label(const uint8_t *lp, const uint8_t *wire)
+{
+	return knot_wire_seek_label(lp + (lp[0] + sizeof(uint8_t)), wire);
+}
+
 #endif /* _KNOT_WIRE_H_ */
 
 /*! @} */
diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c
index 53ecf3d1be55c2fcc1ea6fdd6da9a7aa496c6924..3c6556d4685fb43cbea726a44e05eb2332093b2f 100644
--- a/src/libknot/zone/node.c
+++ b/src/libknot/zone/node.c
@@ -24,111 +24,89 @@
 #include "common.h"
 #include "zone/node.h"
 #include "rrset.h"
+#include "common/descriptor.h"
 #include "util/debug.h"
+#include "common/hattrie/ahtable.h"
 
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
 /*----------------------------------------------------------------------------*/
 /*!
- * \brief Returns the delegation point flag
+ * \brief Sets the given flag to node's flags.
  *
- * \param flags Flags to retrieve the flag from.
- *
- * \return A byte with only the delegation point flag set if it was set in
- *         \a flags.
+ * \param node Node to set the flag in.
+ * \param flag Flag to set.
  */
-static inline uint8_t knot_node_flags_get_deleg(uint8_t flags)
+static inline void knot_node_flags_set(knot_node_t *node, uint8_t flag)
 {
-	return flags & KNOT_NODE_FLAGS_DELEG;
+	node->flags |= flag;
 }
 
 /*----------------------------------------------------------------------------*/
 /*!
- * \brief Sets the delegation point flag.
+ * \brief Returns the given flag from node's flags.
  *
- * \param flags Flags to set the flag in.
- */
-static inline void knot_node_flags_set_deleg(uint8_t *flags)
-{
-	*flags |= KNOT_NODE_FLAGS_DELEG;
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Clears the delegation point flag.
+ * \param node Node to set the flag in.
+ * \param flag Flag to retrieve.
  *
- * \param flags Flags to clear the flag in.
+ * \return A byte with only the given flag set if it was set in \a node.
  */
-static inline void knot_node_flags_clear_deleg(uint8_t *flags)
+static inline uint8_t knot_node_flags_get(const knot_node_t *node, uint8_t flag)
 {
-	*flags &= ~KNOT_NODE_FLAGS_DELEG;
+	return node->flags & flag;
 }
 
 /*----------------------------------------------------------------------------*/
 /*!
- * \brief Returns the non-authoritative node flag
- *
- * \param flags Flags to retrieve the flag from.
+ * \brief Clears the given flag in node's flags.
  *
- * \return A byte with only the non-authoritative node flag set if it was set in
- *         \a flags.
+ * \param node Node to clear the flag in.
+ * \param flag Flag to clear.
  */
-static inline uint8_t knot_node_flags_get_nonauth(uint8_t flags)
+static inline void knot_node_flags_clear(knot_node_t *node, uint8_t flag)
 {
-	return flags & KNOT_NODE_FLAGS_NONAUTH;
+	node->flags &= ~flag;
 }
 
-/*----------------------------------------------------------------------------*/
 /*!
- * \brief Sets the non-authoritative node flag.
+ * \brief Checks whether RRSet is not already in the hash table, automatically
+ *        stores its pointer to the table if not found, but returns false in
+ *        that case.
  *
- * \param flags Flags to set the flag in.
- */
-static inline void knot_node_flags_set_nonauth(uint8_t *flags)
-{
-	*flags |= KNOT_NODE_FLAGS_NONAUTH;
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Clears the non-authoritative node flag.
+ * \param rrset  RRSet to be checked for.
+ * \param table  Hash table with already signed RRs.
  *
- * \param flags Flags to clear the flag in.
+ * \return True if RR should is signed already, false otherwise.
  */
-static inline void knot_node_flags_clear_nonauth(uint8_t *flags)
+static bool rr_already_signed(const knot_rrset_t *rrset, ahtable_t *t)
 {
-	*flags &= ~KNOT_NODE_FLAGS_NONAUTH;
-}
+	assert(rrset);
+	assert(t);
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \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;
-}
+	// Create a key = combination of owner and type mnemonic
+	int dname_size = knot_dname_size(rrset->owner);
+	assert(dname_size > 0);
+	char key[dname_size + 16];
+	memset(key, 0, sizeof(key));
+	memcpy(key, rrset->owner, dname_size);
+	int ret = knot_rrtype_to_string(rrset->type, key + dname_size, 16);
+	if (ret != KNOT_EOK) {
+		return false;
+	}
+	if (ahtable_tryget(t, key, sizeof(key))) {
+		return true;
+	}
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \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;
+	// If not in the table, insert
+	*ahtable_get(t, (char *)key, sizeof(key)) = (value_t *)rrset;
+	return false;
 }
 
 /*----------------------------------------------------------------------------*/
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
 
-knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+knot_node_t *knot_node_new(const knot_dname_t *owner, knot_node_t *parent,
                            uint8_t flags)
 {
 	knot_node_t *ret = (knot_node_t *)calloc(1, sizeof(knot_node_t));
@@ -137,9 +115,15 @@ knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
 		return NULL;
 	}
 
-	/* Store reference to owner. */
-	knot_dname_retain(owner);
-	ret->owner = owner;
+	/*! \todo This is inconsistent: knot_rrset_new() does not copy owner.
+	 *        Either copy in all _new() functions, or in none. I vote for
+	 *        the former, as it should be responsibility of the caller to
+	 *        do the copying (or not if he decides to do so).
+	 */
+	if (owner) {
+		ret->owner = knot_dname_copy(owner);
+	}
+
 	knot_node_set_parent(ret, parent);
 	ret->rrset_tree = NULL;
 	ret->flags = flags;
@@ -191,8 +175,9 @@ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset)
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
 		if (node->rrset_tree[i]->type == rrset->type) {
 			int merged, deleted_rrs;
-			int ret = knot_rrset_merge_no_dupl(node->rrset_tree[i],
-			                                   rrset, &merged, &deleted_rrs);
+			int ret = knot_rrset_merge_sort(node->rrset_tree[i],
+			                                rrset, &merged,
+			                                &deleted_rrs);
 			if (ret != KNOT_EOK) {
 				return ret;
 			} else if (merged || deleted_rrs) {
@@ -203,6 +188,7 @@ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset)
 		}
 	}
 
+	// New RRSet (with one RR)
 	return knot_node_add_rrset_no_merge(node, rrset);
 }
 
@@ -251,12 +237,6 @@ knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type)
 		}
 	}
 
-	/*!< \todo I've added this to fix a leak, but probably this wasn't the cause. Remove once tests are availabe. */
-	void *tmp = realloc(node->rrset_tree,
-	                    node->rrset_count * sizeof(knot_rrset_t *));
-	assert(tmp || node->rrset_count == 0); //Realloc to smaller memory, if it fails, something is really odd.
-	node->rrset_tree = tmp;
-
 	return ret;
 }
 
@@ -474,18 +454,6 @@ knot_dname_t *knot_node_get_owner(const knot_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
-void knot_node_set_owner(knot_node_t *node, knot_dname_t* owner)
-{
-	if (node) {
-		/* Retain new owner and release old owner. */
-		knot_dname_retain(owner);
-		knot_dname_release(node->owner);
-		node->owner = owner;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
 knot_node_t *knot_node_get_wildcard_child(const knot_node_t *node)
 {
 	if (node == NULL) {
@@ -606,7 +574,7 @@ void knot_node_set_deleg_point(knot_node_t *node)
 		return;
 	}
 
-	knot_node_flags_set_deleg(&node->flags);
+	knot_node_flags_set(node, KNOT_NODE_FLAGS_DELEG);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -617,7 +585,7 @@ int knot_node_is_deleg_point(const knot_node_t *node)
 		return KNOT_EINVAL;
 	}
 
-	return knot_node_flags_get_deleg(node->flags);
+	return knot_node_flags_get(node, KNOT_NODE_FLAGS_DELEG);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -628,7 +596,7 @@ void knot_node_set_non_auth(knot_node_t *node)
 		return;
 	}
 
-	knot_node_flags_set_nonauth(&node->flags);
+	knot_node_flags_set(node, KNOT_NODE_FLAGS_NONAUTH);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -639,7 +607,7 @@ int knot_node_is_non_auth(const knot_node_t *node)
 		return KNOT_EINVAL;
 	}
 
-	return knot_node_flags_get_nonauth(node->flags);
+	return knot_node_flags_get(node, KNOT_NODE_FLAGS_NONAUTH);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -650,8 +618,8 @@ void knot_node_set_auth(knot_node_t *node)
 		return;
 	}
 
-	knot_node_flags_clear_nonauth(&node->flags);
-	knot_node_flags_clear_deleg(&node->flags);
+	knot_node_flags_clear(node, KNOT_NODE_FLAGS_NONAUTH);
+	knot_node_flags_clear(node, KNOT_NODE_FLAGS_DELEG);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -669,14 +637,35 @@ 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);
+	return knot_node_flags_get(node, KNOT_NODE_FLAGS_EMPTY);
 }
 
 /*----------------------------------------------------------------------------*/
 
 void knot_node_set_empty(knot_node_t *node)
 {
-	knot_node_flags_set_empty(&node->flags);
+	knot_node_flags_set(node, KNOT_NODE_FLAGS_EMPTY);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_replaced_nsec(const knot_node_t *node)
+{
+	return knot_node_flags_get(node, KNOT_NODE_FLAGS_REPLACED_NSEC);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_replaced_nsec(knot_node_t *node)
+{
+	knot_node_flags_set(node, KNOT_NODE_FLAGS_REPLACED_NSEC);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_clear_replaced_nsec(knot_node_t *node)
+{
+	knot_node_flags_clear(node, KNOT_NODE_FLAGS_REPLACED_NSEC);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -689,7 +678,7 @@ void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames)
 
 	knot_rrset_t **rrs = node->rrset_tree;
 	for (uint16_t i = 0; i < node->rrset_count; ++i) {
-		knot_rrset_deep_free(&(rrs[i]), 1, free_rdata_dnames);
+		knot_rrset_deep_free(&(rrs[i]), 1);
 	}
 }
 
@@ -710,14 +699,7 @@ void knot_node_free(knot_node_t **node)
 		(*node)->rrset_count = 0;
 	}
 
-	// set owner's node pointer to NULL, but only if the 'node' does
-	// not point to the owner's node
-	if (node != &(*node)->owner->node
-	    && knot_dname_node(knot_node_owner(*node)) == *node) {
-		knot_dname_set_node((*node)->owner, NULL);
-	}
-
-	knot_dname_release((*node)->owner);
+	knot_dname_free(&(*node)->owner);
 
 	free(*node);
 	*node = NULL;
@@ -731,7 +713,7 @@ int knot_node_compare(knot_node_t *node1, knot_node_t *node2)
 {
 	assert(node1 != NULL && node2 != NULL);
 
-	return knot_dname_compare(node1->owner, node2->owner);
+	return knot_dname_cmp(node1->owner, node2->owner);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -743,15 +725,15 @@ int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
 	}
 
 	// create new node
-	*to = knot_node_new(from->owner, NULL, from->flags);
+	*to = knot_node_new(NULL, NULL, from->flags);
 	if (*to == NULL) {
 		return KNOT_ENOMEM;
 	}
 
-	// copy references
 	// do not use the API function to set parent, so that children count
 	// is not changed
 	memcpy(*to, from, sizeof(knot_node_t));
+	(*to)->owner = knot_dname_copy(from->owner);
 
 	// copy RRSets
 	size_t rrlen = sizeof(knot_rrset_t*) * from->rrset_count;
@@ -766,89 +748,47 @@ int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
 	return KNOT_EOK;
 }
 
-//const knot_node_t *knot_node_current(const knot_node_t *node)
-//{
-//	if (node == NULL || node->zone == NULL
-//	    || knot_zone_contents(node->zone) == NULL) {
-//		return node;
-//	}
-
-//	int new_gen = knot_node_zone_gen_is_new(node);
-//	int old_gen = knot_node_zone_gen_is_old(node);
-////	short ver = knot_node_zone_generation(node);
-
-//	if (old_gen && knot_node_is_new(node)) {
-//		return NULL;
-//	} else if (new_gen && knot_node_is_old(node)) {
-//		assert(node->new_node != NULL);
-//		return node->new_node;
-//	}
-//	return node;
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//knot_node_t *knot_node_get_current(knot_node_t *node)
-//{
-//	if (node == NULL || node->zone == NULL
-//	    || knot_zone_contents(node->zone) == NULL) {
-//		return node;
-//	}
-
-//	int new_gen = knot_node_zone_gen_is_new(node);
-//	int old_gen = knot_node_zone_gen_is_old(node);
-////	short ver = knot_node_zone_generation(node);
-
-//	if (old_gen && knot_node_is_new(node)) {
-//		return NULL;
-//	} else if (new_gen && knot_node_is_old(node)) {
-//		assert(node->new_node != NULL);
-//		return node->new_node;
-//	}
-
-//	assert((old_gen && knot_node_is_old(node))
-//	       || (new_gen && knot_node_is_new(node))
-//	       || (!old_gen && !new_gen));
-
-//	return node;
-//}
-
-//int knot_node_is_new(const knot_node_t *node)
-//{
-//	return knot_node_flags_get_new(node->flags);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//int knot_node_is_old(const knot_node_t *node)
-//{
-//	return knot_node_flags_get_old(node->flags);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//void knot_node_set_new(knot_node_t *node)
-//{
-//	knot_node_flags_set_new(&node->flags);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//void knot_node_set_old(knot_node_t *node)
-//{
-//	knot_node_flags_set_old(&node->flags);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//void knot_node_clear_new(knot_node_t *node)
-//{
-//	knot_node_flags_clear_new(&node->flags);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//void knot_node_clear_old(knot_node_t *node)
-//{
-//	knot_node_flags_clear_old(&node->flags);
-//}
+/*----------------------------------------------------------------------------*/
+
+bool knot_node_rr_should_be_signed(const knot_node_t *node,
+                                   const knot_rrset_t *rrset,
+                                   ahtable_t *table)
+{
+	if (node == NULL || rrset == NULL) {
+		return false;
+	}
+
+	// SOA entry is maintained separately
+	if (rrset->type == KNOT_RRTYPE_SOA) {
+		return false;
+	}
+
+	// DNSKEYs are maintained separately
+	if (rrset->type == KNOT_RRTYPE_DNSKEY) {
+		return false;
+	}
+
+	// We only want to sign NSECs and DSs when at delegation points
+	if (knot_node_is_deleg_point(node)) {
+		if (!(rrset->type == KNOT_RRTYPE_NSEC ||
+		    rrset->type == KNOT_RRTYPE_DS)) {
+			return false;
+		}
+	}
+
+	// These RRs have their signatures stored in changeset already
+	if (knot_node_is_replaced_nsec(node)
+	    && ((knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC)
+	         || (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3))) {
+		return false;
+	}
+
+	// Check for RRSet in the 'already_signed' table
+	if (table) {
+		if (rr_already_signed(rrset, table)) {
+			return false;
+		}
+	}
+
+	return true;
+}
diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h
index f42c70f9752fc8017232e41b6a5e62bd35cb4585..69fa491ca9f5b229dee189ba6521ed0e6e4bc83d 100644
--- a/src/libknot/zone/node.h
+++ b/src/libknot/zone/node.h
@@ -30,15 +30,19 @@
 
 #include "dname.h"
 #include "rrset.h"
+#include "common/hattrie/ahtable.h"
 
 struct knot_zone;
 
+/*! \brief RRSet count in node if there is only NSEC (and possibly its RRSIG).*/
+#define KNOT_NODE_RRSET_COUNT_ONLY_NSEC 1
+
 /*----------------------------------------------------------------------------*/
 /*!
  * \brief Structure representing one node in a domain name tree, i.e. one domain
  *        name in a zone.
  *
- * RRSets are ordered by type and stored in a skip-list to allow fast lookup.
+ * RRSets are stored in an array.
  */
 struct knot_node {
 	knot_dname_t *owner; /*!< Domain name being the owner of this node. */
@@ -95,12 +99,11 @@ typedef enum {
 	KNOT_NODE_FLAGS_DELEG = (uint8_t)0x01,
 	/*! \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)0x04,
-	/*! \brief Node is new and should not be used while zoen is old. */
-	KNOT_NODE_FLAGS_NEW = (uint8_t)0x08,
-	/*! \brief Node is empty and will be deleted after update. */
-	KNOT_NODE_FLAGS_EMPTY = (uint8_t)0x10
+	/*! \brief Node is empty and will be deleted after update.
+	 *  \todo Remove after dname refactoring, update description in node. */
+	KNOT_NODE_FLAGS_EMPTY = (uint8_t)0x10,
+	/*! \brief NSEC in this node needs new RRSIGs. Used for signing. */
+	KNOT_NODE_FLAGS_REPLACED_NSEC = (uint8_t)0x20
 } knot_node_flags_t;
 
 /*----------------------------------------------------------------------------*/
@@ -117,7 +120,7 @@ typedef enum {
  *
  * \return Newly created node or NULL if an error occured.
  */
-knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+knot_node_t *knot_node_new(const knot_dname_t *owner, knot_node_t *parent,
                                uint8_t flags);
 
 /*!
@@ -299,16 +302,6 @@ const knot_dname_t *knot_node_owner(const knot_node_t *node);
  */
 knot_dname_t *knot_node_get_owner(const knot_node_t *node);
 
-/*!
- * \brief Set node owner to specified dname.
- *
- * Previous owner will be replaced if exist.
- *
- * \param node Specified node.
- * \param owner New owner dname.
- */
-void knot_node_set_owner(knot_node_t *node, knot_dname_t* owner);
-
 /*!
  * \brief Returns the wildcard child of the node.
  *
@@ -385,20 +378,16 @@ void knot_node_set_auth(knot_node_t *node);
 
 int knot_node_is_auth(const knot_node_t *node);
 
-int knot_node_is_new(const knot_node_t *node);
-
-int knot_node_is_old(const knot_node_t *node);
+int knot_node_is_replaced_nsec(const knot_node_t *node);
 
-void knot_node_set_new(knot_node_t *node);
+void knot_node_set_replaced_nsec(knot_node_t *node);
 
-void knot_node_set_old(knot_node_t *node);
-
-void knot_node_clear_new(knot_node_t *node);
-
-void knot_node_clear_old(knot_node_t *node);
+void knot_node_clear_replaced_nsec(knot_node_t *node);
 
+//! \todo remove after dname refactoring
 int knot_node_is_empty(const knot_node_t *node);
 
+//! \todo remove after dname refactoring
 void knot_node_set_empty(knot_node_t *node);
 
 /*!
@@ -441,6 +430,19 @@ int knot_node_compare(knot_node_t *node1, knot_node_t *node2);
 
 int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to);
 
+/*!
+ * \brief Checks whether RRSet in a node has to be signed.
+ *
+ * \param node   Node containing the RRSet.
+ * \param rrset  RRSet we are checking for.
+ * \param table  Optional hash table with already signed RRs.
+ *
+ * \return True if RR should be signed, false otherwise.
+ */
+bool knot_node_rr_should_be_signed(const knot_node_t *node,
+                                   const knot_rrset_t *rrset,
+                                   ahtable_t *table);
+
 #endif /* _KNOT_NODE_H_ */
 
 /*! @} */
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index 584e6cb2ae1e06d9f4b97bf78de71e2d8152971b..d35d0615e32eec15a8d794e0eae29f06ad1aecb6 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -23,8 +23,11 @@
 #include "common/base32hex.h"
 #include "common/descriptor.h"
 #include "common/hattrie/hat-trie.h"
+#include "libknot/dnssec/zone-nsec.h"
 #include "libknot/zone/zone-tree.h"
+#include "libknot/util/wire.h"
 #include "consts.h"
+#include "libknot/rdata.h"
 
 /*----------------------------------------------------------------------------*/
 /* Non-API functions                                                          */
@@ -39,7 +42,6 @@ typedef struct {
 	knot_node_t *first_node;
 	knot_zone_contents_t *zone;
 	knot_node_t *previous_node;
-	hattrie_t *lookup_tree;
 	int err;
 } knot_zone_adjust_arg_t;
 
@@ -90,7 +92,7 @@ static int knot_zone_contents_check_node(
 	// assert or just check??
 	assert(contents->apex != NULL);
 
-	if (!knot_dname_is_subdomain(node->owner,
+	if (!knot_dname_is_sub(node->owner,
 				       knot_node_owner(contents->apex))) {
 dbg_zone_exec(
 		char *node_owner = knot_dname_to_str(knot_node_owner(node));
@@ -128,320 +130,46 @@ static void knot_zone_contents_destroy_node_rrsets_from_tree(
 
 /*----------------------------------------------------------------------------*/
 
-static const knot_node_t *knot_zone_contents_find_wildcard_child(
-        knot_zone_contents_t *zone, const knot_node_t *closest_encloser)
+static int knot_zone_contents_nsec3_name(const knot_zone_contents_t *zone,
+                                         const knot_dname_t *name,
+                                         knot_dname_t **nsec3_name)
 {
-	assert(zone != NULL);
-	assert(closest_encloser != NULL);
-
-	knot_dname_t *tmp = knot_dname_new_from_str("*", 1, NULL);
-	CHECK_ALLOC(tmp, NULL);
-
-	knot_dname_t *wildcard = knot_dname_cat(tmp, knot_node_owner(
-							closest_encloser));
-	if (wildcard == NULL) {
-		free(tmp);
-		return NULL;
-	}
-
-	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;
-	}
-}
-
-void knot_zone_contents_insert_dname_into_table(knot_dname_t **in_dname,
-                                                hattrie_t *lookup_tree)
-{
-	if (lookup_tree == NULL) {
-		/* = Do not check duplicates. */
-		return;
-	}
-	assert(in_dname && *in_dname);
-	/* First thing - make sure dname is not duplicated. */
-	knot_dname_t *found_dname = hattrie_get_dname(lookup_tree, *in_dname);
-	if (found_dname != NULL && found_dname != *in_dname) {
-		/* Duplicate. */
-		knot_dname_release(*in_dname);
-		knot_dname_retain(found_dname);
-		*in_dname = found_dname;
-	} else if (found_dname == NULL) {
-		/* Into the tree it goes. */
-		hattrie_insert_dname(lookup_tree, *in_dname);
-	} else {
-		assert(found_dname == *in_dname);
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Adjusts one RDATA item by replacing domain name by one present in the
- *        zone.
- *
- * This function tries to find the domain name in the zone. If the name is not
- * in the zone, it does nothing. If it is there, it destroys the domain name
- * stored in the RDATA item and replaces it by pointer to the domain name from
- * the zone.
- *
- * \warning Call this function only with RDATA items which store domain names,
- *          otherwise the behaviour is undefined.
- *
- * \param rdata RDATA where the item is located.
- * \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_dname(knot_zone_contents_t *zone,
-                                                  hattrie_t *lookup_tree,
-                                                  knot_node_t *node,
-                                                  knot_dname_t **in_dname)
-{
-//	const knot_node_t *old_dname_node = (*in_dname)->node;
-	knot_zone_contents_insert_dname_into_table(in_dname, lookup_tree);
-//	assert((*in_dname)->node == old_dname_node || old_dname_node == NULL);
-
-	knot_dname_t *dname = *in_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_EINVAL || ret == KNOT_EOUTOFZONE) {
-		// TODO: do some cleanup if needed
-		dbg_zone_detail("Failed to find the name in zone: %s\n",
-				knot_strerror(ret));
-		return;
-	}
+	assert(nsec3_name != NULL);
+	*nsec3_name = NULL;
 
-	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);
-				);
-			}
-	}
-}
+	const knot_nsec3_params_t *nsec3_params =
+		knot_zone_contents_nsec3params(zone);
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Adjusts all RDATA in the given RRSet by replacing domain names by ones
- *        present in the zone.
- *
- * This function selects the RDATA items containing a domain name (according to
- * RR type descriptor of the RRSet's type and adjusts the item using
- * knot_zone_adjust_rdata_item().
- *
- * \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,
-                                                     hattrie_t *lookup_tree,
-                                                     knot_zone_contents_t *zone,
-                                                     knot_node_t *node)
-{
-	knot_dname_t **dn = NULL;
-	while((dn = knot_rrset_get_next_dname(rrset, dn))) {
-		knot_zone_contents_adjust_rdata_dname(zone,
-						      lookup_tree,
-						      node,
-						      dn);
+	if (nsec3_params == NULL) {
+		return KNOT_ENSEC3PAR;
 	}
-}
 
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Adjusts all RRSets in the given node by replacing domain names in
- *        RDATA by ones present in the zone.
- *
- * This function just calls knot_zone_adjust_rdata_in_rrset() for all RRSets
- * in the node (including all RRSIG RRSets).
- *
- * \param node Zone node to adjust the RRSets in.
- * \param zone Zone to which the node belongs.
- */
-static int knot_zone_contents_adjust_rrsets(knot_node_t *node,
-                                             hattrie_t *lookup_tree,
-                                             knot_zone_contents_t *zone)
-{
-	knot_rrset_t **rrsets = knot_node_get_rrsets_no_copy(node);
-	short count = knot_node_rrset_count(node);
-
-	assert(count == 0 || rrsets != NULL);
-
-	for (int r = 0; r < count; ++r) {
-		assert(rrsets[r] != NULL);
-
-		/* Make sure that RRSet owner is the same as node's. */
-		if (node->owner != rrsets[r]->owner) {
-			knot_rrset_set_owner(rrsets[r], node->owner);
-		}
-
-		dbg_zone("Adjusting next RRSet.\n");
-		knot_rrset_dump(rrsets[r]);
-		knot_zone_contents_adjust_rdata_in_rrset(rrsets[r],
-							 lookup_tree, zone,
-							 node);
-		knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
-		if (rrsigs != NULL) {
-			dbg_zone("Adjusting next RRSIGs.\n");
-			knot_rrset_dump(rrsigs);
-			knot_zone_contents_adjust_rdata_in_rrset(rrsigs,
-							 lookup_tree, zone,
-								 node);
-		}
-
-		if (rrsets[r]->type == KNOT_RRTYPE_DS) {
-			int ret = knot_rrset_ds_check(rrsets[r]);
-			if (ret != KNOT_EOK) {
-				dbg_zone("DS RDATA check failed: %s\n", knot_strerror(ret));
-				return KNOT_EMALF;
-			}
-		}
+	*nsec3_name = create_nsec3_owner(name, zone->apex->owner, nsec3_params);
+	if (*nsec3_name == NULL) {
+		return KNOT_ERROR;
 	}
 
 	return KNOT_EOK;
 }
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Adjusts zone node for faster query processing.
- *
- * - Adjusts RRSets in the node (see knot_zone_adjust_rrsets()).
- * - Marks the node as delegation point or non-authoritative (below a zone cut)
- *   if applicable.
- * - Stores reference to corresponding NSEC3 node if applicable.
- *
- * \param node Zone node to adjust.
- * \param zone Zone the node belongs to.
- *
- * \todo Consider whether this function should replace RRSet owners with
- *       node owner + store this owner to the dname table. This is now done
- *       in the inserting function, though that may not be always used (e.g.
- *       old changeset processing).
- */
-static int knot_zone_contents_adjust_node(knot_node_t *node,
-                                          hattrie_t *lookup_tree,
-                                          knot_zone_contents_t *zone)
-{
-	// adjust domain names in RDATA
-	int ret = knot_zone_contents_adjust_rrsets(node, lookup_tree,
-						   zone);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-//	const knot_node_t *old_dname_node = node->owner->node;
-	knot_zone_contents_insert_dname_into_table(&node->owner, lookup_tree);
-//	assert(node->owner->node == old_dname_node || old_dname_node == NULL);
-
-	// assure that owner has proper node
-	if (knot_dname_node(knot_node_owner(node)) == NULL) {
-		knot_dname_set_node(knot_node_get_owner(node), node);
-	}
 
-	// check if this node is not a wildcard child of its parent
-	if (knot_dname_is_wildcard(knot_node_owner(node))) {
-		assert(knot_node_parent(node) != NULL);
-		knot_node_set_wildcard_child(knot_node_get_parent(node), node);
-	}
-
-	// NSEC3 node (only if NSEC3 tree is not empty)
-	/*! \todo We need only exact matches, what if node has no nsec3 node? */
-	/* This is faster, as it doesn't need ordered access. */
-	knot_node_t *nsec3 = NULL;
-	knot_dname_t *nsec3_name = NULL;
-	ret = knot_zone_contents_nsec3_name(zone, knot_node_owner(node),
-					    &nsec3_name);
-	if (ret == KNOT_EOK) {
-		assert(nsec3_name);
-		knot_zone_tree_get(zone->nsec3_nodes, nsec3_name, &nsec3);
-		knot_node_set_nsec3_node(node, nsec3);
-	} else if (ret == KNOT_ENSEC3PAR) {
-		knot_node_set_nsec3_node(node, NULL);
-	} else {
-		/* Something could be in DNAME. */
-		knot_dname_free(&nsec3_name);
-		return ret;
-	}
-	knot_dname_free(&nsec3_name);
-
-	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");
-	return KNOT_EOK;
-}
 
 /*----------------------------------------------------------------------------*/
+
 /*!
- * \brief Adjusts zone node for faster query processing.
+ * \brief Adjust normal (non NSEC3) node.
  *
- * This function is just a wrapper over knot_zone_adjust_node() to be used
- * in tree-traversing functions.
+ * Set:
+ * - reusable DNAMEs in RDATA
+ * - pointer to node stored in owner dname
+ * - pointer to wildcard childs in parent nodes if applicable
+ * - flags (delegation point, non-authoritative)
+ * - pointer to previous node
  *
- * \param node Zone node to adjust.
- * \param data Zone the node belongs to.
+ * \param tnode  Zone node to adjust.
+ * \param data   Adjusting parameters (knot_zone_adjust_arg_t *).
  */
-static void knot_zone_contents_adjust_node_in_tree(
-		knot_node_t **tnode, void *data)
+static void knot_zone_contents_adjust_normal_node(knot_node_t **tnode,
+                                                  void *data)
 {
 	assert(data != NULL);
 	assert(tnode != NULL);
@@ -450,59 +178,28 @@ static void knot_zone_contents_adjust_node_in_tree(
 	knot_node_t *node = *tnode;
 
 	if (args->err != KNOT_EOK) {
-		dbg_xfrin_detail("Error during adjusting: %s, skipping node.\n",
-				 knot_strerror(args->err));
 		return;
 	}
 
-dbg_zone_exec_verb(
-	char *name = knot_dname_to_str(node->owner);
-	dbg_zone_verb("----- Adjusting node %s -----\n", name);
-	free(name);
-);
-
-	knot_zone_contents_t *zone = args->zone;
+	// remember first node
 
-	/*
-	 *    Do other adjusting (flags, closest enclosers, wildcard children,
-	 *    etc.).
-	 */
-	args->err = knot_zone_contents_adjust_node(node, args->lookup_tree, zone);
-}
+	if (args->first_node == NULL) {
+		args->first_node = node;
+	}
 
-/*----------------------------------------------------------------------------*/
+	// check if this node is not a wildcard child of its parent
 
-static void knot_zone_contents_adjust_node_in_tree_ptr(
-		knot_node_t **tnode, void *data)
-{
-	assert(data != NULL);
-	assert(tnode != NULL);
+	if (knot_dname_is_wildcard(knot_node_owner(node))) {
+		assert(knot_node_parent(node) != NULL);
+		knot_node_set_wildcard_child(knot_node_get_parent(node), node);
+	}
 
-	knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
-	knot_node_t *node = *tnode;
+	// set flags (delegation point, non-authoritative)
 
-	dbg_zone_exec_detail(
-	if (knot_node_parent(node)) {
-		char *name = knot_dname_to_str(knot_node_owner(
-				knot_node_parent(node)));
-		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_detail("Parent is non-authoritative: %s\n",
-		       knot_node_is_non_auth(knot_node_parent(node))
-		       ? "yes" : "no");
-		free(name);
-	} else {
-		dbg_zone_detail("No parent!\n");
-	}
-);
-	/*
-	 * 1) delegation point / non-authoritative node
-	 */
 	if (knot_node_parent(node)
 	    && (knot_node_is_deleg_point(knot_node_parent(node))
-		|| knot_node_is_non_auth(knot_node_parent(node)))) {
+		|| knot_node_is_non_auth(knot_node_parent(node)))
+	) {
 		knot_node_set_non_auth(node);
 	} else if (knot_node_rrset(node, KNOT_RRTYPE_NS) != NULL
 		   && node != args->zone->apex) {
@@ -511,67 +208,49 @@ static void knot_zone_contents_adjust_node_in_tree_ptr(
 		knot_node_set_auth(node);
 	}
 
-	/*
-	 * 2) Set previous node pointer.
-	 */
+	// set pointer to previous node
+
 	knot_node_set_previous(node, args->previous_node);
 
-	if (args->first_node == NULL) {
-		args->first_node = node;
-	}
+	// update remembered previous pointer only if authoritative
 
-	/*
-	 * 3) Store previous node depending on the type of this node.
-	 */
-	if (!knot_node_is_non_auth(node)
-	    && knot_node_rrset_count(node) > 0) {
+	if (!knot_node_is_non_auth(node) && knot_node_rrset_count(node) > 0) {
 		args->previous_node = node;
 	}
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Adjusts NSEC3 node for faster query processing.
- *
- * This function is just a wrapper over knot_zone_adjust_nsec3_node() to be
- * used in tree-traversing functions.
- *
- * \param node Zone node to adjust.
- * \param data Zone the node belongs to.
- */
-static void knot_zone_contents_adjust_nsec3_node_in_tree(
-		knot_node_t **tnode, void *data)
-{
-	assert(data != NULL);
-	assert(tnode != NULL);
-	knot_node_t *node = *tnode;
-
-	knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
-
-	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) {
-		knot_dname_set_node(knot_node_get_owner(node), node);
+	// Connect to NSEC3 node (only if NSEC3 tree is not empty)
+	knot_node_t *nsec3 = NULL;
+	knot_dname_t *nsec3_name = NULL;
+	int ret = knot_zone_contents_nsec3_name(args->zone,
+	                                        knot_node_owner(node),
+	                                        &nsec3_name);
+	if (ret == KNOT_EOK) {
+		assert(nsec3_name);
+		knot_zone_tree_get(args->zone->nsec3_nodes, nsec3_name, &nsec3);
+		knot_node_set_nsec3_node(node, nsec3);
+	} else if (ret == KNOT_ENSEC3PAR) {
+		knot_node_set_nsec3_node(node, NULL);
+	} else {
+		args->err = ret;
 	}
 
-	/*
-	 * We assume, that NSEC3 nodes have none DNAMEs in their RDATA and
-	 * that node owners are all unique. \todo Harmful?
-	 */
-
-	knot_zone_contents_t *zone = args->zone;
-	assert(zone != NULL);
+	knot_dname_free(&nsec3_name);
 }
 
 /*----------------------------------------------------------------------------*/
 
-static void knot_zone_contents_adjust_nsec3_node_in_tree_ptr(
-		knot_node_t **tnode, void *data)
+/*!
+ * \brief Adjust NSEC3 node.
+ *
+ * Set:
+ * - pointer to previous node
+ * - pointer to node stored in owner dname
+ *
+ * \param tnode  Zone node to adjust.
+ * \param data   Adjusting parameters (knot_zone_adjust_arg_t *).
+ */
+static void knot_zone_contents_adjust_nsec3_node(knot_node_t **tnode,
+                                                 void *data)
 {
 	assert(data != NULL);
 	assert(tnode != NULL);
@@ -579,112 +258,16 @@ static void knot_zone_contents_adjust_nsec3_node_in_tree_ptr(
 	knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
 	knot_node_t *node = *tnode;
 
-	// 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;
+	// remember first node
 
-	if (args->first_node == NULL) {
+	if (args->first_node == NULL)
 		args->first_node = node;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
-int knot_zone_contents_nsec3_name(const knot_zone_contents_t *zone,
-                                           const knot_dname_t *name,
-                                           knot_dname_t **nsec3_name)
-{
-	assert(nsec3_name != NULL);
-
-	*nsec3_name = NULL;
-
-	const knot_nsec3_params_t *nsec3_params =
-		knot_zone_contents_nsec3params(zone);
-
-	if (nsec3_params == NULL) {
-dbg_zone_exec(
-		char *n = knot_dname_to_str(zone->apex->owner);
-		dbg_zone("No NSEC3PARAM for zone %s.\n", n);
-		free(n);
-);
-		return KNOT_ENSEC3PAR;
-	}
-
-	uint8_t *hashed_name = NULL;
-	size_t hash_size = 0;
 
-dbg_zone_exec_verb(
-	char *n = knot_dname_to_str(name);
-	dbg_zone_verb("Hashing name %s.\n", n);
-	free(n);
-);
-
-	int res = knot_nsec3_sha1(nsec3_params, knot_dname_name(name),
-				    knot_dname_size(name), &hashed_name,
-				    &hash_size);
-
-	if (res != 0) {
-		char *n = knot_dname_to_str(name);
-		dbg_zone("Error while hashing name %s.\n", n);
-		free(n);
-		return KNOT_ECRYPTO;
-	}
-
-	dbg_zone("Hash: ");
-	dbg_zone_hex((char *)hashed_name, hash_size);
-	dbg_zone("\n");
-
-	uint8_t *name_b32 = NULL;
-	size_t size = base32hex_encode_alloc(hashed_name, hash_size,
-					     &name_b32);
-
-	if (size == 0) {
-		char *n = knot_dname_to_str(name);
-		dbg_zone("Error while encoding hashed name %s to base32.\n", n);
-		free(n);
-		free(name_b32);
-		return KNOT_ECRYPTO;
-	}
-
-	assert(name_b32 != NULL);
-	free(hashed_name);
-
-dbg_zone_exec_verb(
-	/* name_b32 is not 0-terminated. */
-	char b32_string[hash_size + 1];
-	memset(b32_string, 0, hash_size + 1);
-	memcpy(b32_string, name_b32, hash_size);
-	dbg_zone_verb("Base32-encoded hash: %s\n", b32_string);
-);
-
-	/* Will be returned to caller, make sure it is released after use. */
-	*nsec3_name = knot_dname_new_from_str((char *)name_b32, size, NULL);
-
-	free(name_b32);
-
-	if (*nsec3_name == NULL) {
-		dbg_zone("Error while creating domain name for hashed name.\n");
-		return KNOT_ERROR;
-	}
-	knot_dname_to_lower(*nsec3_name);
-
-	assert(zone->apex->owner != NULL);
-	knot_dname_t *ret = knot_dname_cat(*nsec3_name, zone->apex->owner);
-
-	if (ret == NULL) {
-		dbg_zone("Error while creating NSEC3 domain name for "
-			 "hashed name.\n");
-		knot_dname_release(*nsec3_name);
-		return KNOT_ERROR;
-	}
-
-	assert(ret == *nsec3_name);
+	// set previous node
 
-	return KNOT_EOK;
+	knot_node_set_previous(node, args->previous_node);
+	args->previous_node = node;
 }
-
 /*----------------------------------------------------------------------------*/
 /*!
  * \brief Tries to find the given domain name in the zone tree.
@@ -734,19 +317,19 @@ static int knot_zc_nsec3_parameters_match(const knot_rrset_t *rrset,
 
 	dbg_zone_detail("RDATA algo: %u, iterations: %u, salt length: %u, salt:"
 			" %.*s\n",
-			knot_rrset_rdata_nsec3_algorithm(rrset, rdata_pos),
-			knot_rrset_rdata_nsec3_iterations(rrset, rdata_pos),
-			knot_rrset_rdata_nsec3_salt_length(rrset, rdata_pos),
-			knot_rrset_rdata_nsec3_salt_length(rrset, rdata_pos),
-			knot_rrset_rdata_nsec3_salt(rrset, rdata_pos));
+			knot_rdata_nsec3_algorithm(rrset, rdata_pos),
+			knot_rdata_nsec3_iterations(rrset, rdata_pos),
+			knot_rdata_nsec3_salt_length(rrset, rdata_pos),
+			knot_rdata_nsec3_salt_length(rrset, rdata_pos),
+			knot_rdata_nsec3_salt(rrset, rdata_pos));
 	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_rrset_rdata_nsec3_algorithm(rrset, rdata_pos) == params->algorithm
-		&& knot_rrset_rdata_nsec3_iterations(rrset, rdata_pos) == params->iterations
-		&& knot_rrset_rdata_nsec3_salt_length(rrset, rdata_pos) == params->salt_length
-		&& strncmp((const char *)knot_rrset_rdata_nsec3_salt(rrset, rdata_pos),
+	return (knot_rdata_nsec3_algorithm(rrset, rdata_pos) == params->algorithm
+		&& knot_rdata_nsec3_iterations(rrset, rdata_pos) == params->iterations
+		&& knot_rdata_nsec3_salt_length(rrset, rdata_pos) == params->salt_length
+		&& strncmp((const char *)knot_rdata_nsec3_salt(rrset, rdata_pos),
 			   (const char *)params->salt, params->salt_length)
 		   == 0);
 }
@@ -929,80 +512,45 @@ dbg_zone_exec_detail(
 
 	dbg_zone_detail("Creating parents of the node.\n");
 
-	knot_dname_t *chopped =
-		knot_dname_left_chop(knot_node_owner(node));
-	if(chopped == NULL) {
-		/* Root domain and root domain only. */
-		assert(node->owner && node->owner->labels &&
-		       node->owner->labels[0] == 0);
+	/* No parents for root domain. */
+	if (*node->owner == '\0')
 		return KNOT_EOK;
-	}
 
-	if (knot_dname_compare(knot_node_owner(zone->apex), chopped) == 0) {
+	knot_node_t *next_node = NULL;
+	const uint8_t *parent = knot_wire_next_label(knot_node_owner(node), NULL);
+
+	if (knot_dname_cmp(knot_node_owner(zone->apex), parent) == 0) {
 		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
-		if (knot_dname_is_wildcard(
-				knot_node_owner(node))) {
+		if (knot_dname_is_wildcard(knot_node_owner(node))) {
 			knot_node_set_wildcard_child(zone->apex, node);
 		}
 	} else {
-		knot_node_t *next_node;
-		while ((next_node
-		      = knot_zone_contents_get_node(zone, chopped)) == NULL &&
-			chopped != NULL) {
-			/* Adding new dname to zone + add to table. */
-			dbg_zone_detail("Creating new node.\n");
+		while (parent != NULL &&
+		       !(next_node = knot_zone_contents_get_node(zone, parent))) {
 
-			assert(chopped);
-			next_node = knot_node_new(chopped, NULL, flags);
+			/* Create a new node. */
+			dbg_zone_detail("Creating new node.\n");
+			next_node = knot_node_new(parent, NULL, flags);
 			if (next_node == NULL) {
-				/* Directly discard. */
-				knot_dname_free(&chopped);
 				return KNOT_ENOMEM;
 			}
-			//TODO possible leak
-//			ret = knot_zone_contents_solve_node_dnames(zone,
-//								   next_node);
-//			if (ret != KNOT_EOK) {
-//				knot_node_free(&next_node);
-//				knot_dname_release(chopped);
-//			}
-
-			if (next_node->owner != chopped) {
-				/* Node owner was in RDATA */
-				knot_dname_release(chopped);
-				knot_dname_retain(next_node->owner);
-				chopped = next_node->owner;
-			}
-
-			assert(knot_zone_contents_find_node(zone, chopped)
-			       == NULL);
-			assert(knot_node_owner(next_node) == chopped);
 
+			/* Insert node to a tree. */
 			dbg_zone_detail("Inserting new node to zone tree.\n");
-
-			ret = knot_zone_tree_insert(zone->nodes,
-						      next_node);
+			assert(knot_zone_contents_find_node(zone, parent) == NULL);
+			ret = knot_zone_tree_insert(zone->nodes, next_node);
 			if (ret != KNOT_EOK) {
-				dbg_zone("Failed to insert new node "
-					 "to zone tree.\n");
-				/*! \todo Delete the node?? */
-				/* Directly discard. */
-				knot_dname_release(chopped);
+				knot_node_free(&next_node);
 				return ret;
 			}
 
-			// set parent
+			/* Update node pointers. */
 			knot_node_set_parent(node, next_node);
-
-			// set zone
 			knot_node_set_zone(next_node, zone->zone);
-
-			// check if the node is not wildcard child of the parent
-			if (knot_dname_is_wildcard(
-					knot_node_owner(node))) {
+			if (knot_dname_is_wildcard(knot_node_owner(node))) {
 				knot_node_set_wildcard_child(next_node, node);
 			}
 
@@ -1010,15 +558,9 @@ dbg_zone_exec_detail(
 
 			dbg_zone_detail("Next parent.\n");
 			node = next_node;
-			knot_dname_t *chopped_last = chopped;
-			chopped = knot_dname_left_chop(chopped);
-
-			/* Release last chop, reference is already stored
-			 * in next_node.
-			 */
-			knot_dname_release(chopped_last);
-
+			parent = knot_wire_next_label(parent, NULL);
 		}
+
 		// set the found parent (in the zone) as the parent of the last
 		// inserted node
 		assert(knot_node_parent(node) == NULL);
@@ -1027,10 +569,6 @@ dbg_zone_exec_detail(
 		dbg_zone_detail("Created all parents.\n");
 	}
 
-	/* Directly discard. */
-	/*! \todo This may be double-release. */
-	knot_dname_release(chopped);
-
 	return KNOT_EOK;
 }
 
@@ -1085,16 +623,14 @@ dbg_zone_exec_detail(
 );
 
 	// check if the RRSet belongs to the zone
-	if (knot_dname_compare(knot_rrset_owner(rrset),
-				 zone->apex->owner) != 0
-	    && !knot_dname_is_subdomain(knot_rrset_owner(rrset),
-					  zone->apex->owner)) {
+	if (!knot_dname_is_equal(rrset->owner, zone->apex->owner)
+	    && !knot_dname_is_sub(rrset->owner, zone->apex->owner)) {
 		return KNOT_EOUTOFZONE;
 	}
 
 	if ((*node) == NULL
 	    && (*node = knot_zone_contents_get_node(zone,
-				    knot_rrset_owner(rrset))) == NULL) {
+	                                            rrset->owner)) == NULL) {
 		return KNOT_ENONODE;
 	}
 
@@ -1149,16 +685,16 @@ dbg_zone_exec(
 
 	// check if the RRSet belongs to the zone
 	if (*rrset != NULL
-	    && knot_dname_compare(knot_rrset_owner(*rrset),
+	    && knot_dname_cmp(knot_rrset_owner(*rrset),
 				    zone->apex->owner) != 0
-	    && !knot_dname_is_subdomain(knot_rrset_owner(*rrset),
+	    && !knot_dname_is_sub(knot_rrset_owner(*rrset),
 					  zone->apex->owner)) {
 		return KNOT_EOUTOFZONE;
 	}
 
 	// check if the RRSIGs belong to the RRSet
 	if (*rrset != NULL
-	    && (knot_dname_compare(knot_rrset_owner(rrsigs),
+	    && (knot_dname_cmp(knot_rrset_owner(rrsigs),
 				     knot_rrset_owner(*rrset)) != 0)) {
 		dbg_zone("RRSIGs do not belong to the given RRSet.\n");
 		return KNOT_EINVAL;
@@ -1170,7 +706,8 @@ dbg_zone_exec(
 		// find proper node
 		knot_node_t *(*get_node)(const knot_zone_contents_t *,
 					   const knot_dname_t *)
-		    = (knot_rrset_rdata_rrsig_type_covered(rrsigs) == KNOT_RRTYPE_NSEC3)
+		    = (knot_rdata_rrsig_type_covered(rrsigs, 0)
+		       == KNOT_RRTYPE_NSEC3)
 		       ? knot_zone_contents_get_nsec3_node
 		       : knot_zone_contents_get_node;
 
@@ -1186,9 +723,9 @@ dbg_zone_exec(
 		// find the RRSet in the node
 		// take only the first RDATA from the RRSIGs
 		dbg_zone_detail("Finding RRSet for type %d\n",
-				knot_rrset_rdata_rrsig_type_covered(rrsigs));
+				knot_rdata_rrsig_type_covered(rrsigs, 0));
 		*rrset = knot_node_get_rrset(
-			     *node, knot_rrset_rdata_rrsig_type_covered(rrsigs));
+			     *node, knot_rdata_rrsig_type_covered(rrsigs, 0));
 		if (*rrset == NULL) {
 			dbg_zone("Failed to find RRSet for RRSIGs.\n");
 			return KNOT_ENORRSET;
@@ -1266,9 +803,9 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
 	}
 
 	// check if the RRSet belongs to the zone
-	if (knot_dname_compare(knot_rrset_owner(rrset),
+	if (knot_dname_cmp(knot_rrset_owner(rrset),
 				 zone->apex->owner) != 0
-	    && !knot_dname_is_subdomain(knot_rrset_owner(rrset),
+	    && !knot_dname_is_sub(knot_rrset_owner(rrset),
 					  zone->apex->owner)) {
 		return KNOT_EOUTOFZONE;
 	}
@@ -1418,13 +955,13 @@ dbg_zone_exec_verb(
 	free(zone_str);
 );
 
-	if (knot_dname_compare(name, zone->apex->owner) == 0) {
+	if (knot_dname_cmp(name, zone->apex->owner) == 0) {
 		*node = zone->apex;
 		*closest_encloser = *node;
 		return KNOT_ZONE_NAME_FOUND;
 	}
 
-	if (!knot_dname_is_subdomain(name, zone->apex->owner)) {
+	if (!knot_dname_is_sub(name, zone->apex->owner)) {
 		*node = NULL;
 		*closest_encloser = NULL;
 		return KNOT_EOUTOFZONE;
@@ -1473,8 +1010,8 @@ dbg_zone_detail("Search function returned %d, node %s (%p) and prev: %s (%p)\n",
 
 		int matched_labels = knot_dname_matched_labels(
 				knot_node_owner((*closest_encloser)), name);
-		while (matched_labels < knot_dname_label_count(
-				knot_node_owner((*closest_encloser)))) {
+		while (matched_labels < knot_dname_labels(
+				knot_node_owner((*closest_encloser)), NULL)) {
 			(*closest_encloser) =
 				knot_node_parent((*closest_encloser));
 			assert(*closest_encloser);
@@ -1577,7 +1114,7 @@ int knot_zone_contents_find_nsec3_for_name(const knot_zone_contents_t *zone,
 	// check if the NSEC3 tree is not empty
 	if (knot_zone_tree_weight(zone->nsec3_nodes) == 0) {
 		dbg_zone("NSEC3 tree is empty.\n");
-		knot_dname_release(nsec3_name);
+		knot_dname_free(&nsec3_name);
 		return KNOT_ENSEC3CHAIN;
 	}
 
@@ -1594,7 +1131,7 @@ dbg_zone_exec_verb(
 		zone->nsec3_nodes, nsec3_name, &found, &prev);
 	assert(exact_match >= 0);
 
-	knot_dname_release(nsec3_name);
+	knot_dname_free(&nsec3_name);
 
 dbg_zone_exec_detail(
 	if (found) {
@@ -1715,122 +1252,80 @@ knot_node_t *knot_zone_contents_get_apex(const knot_zone_contents_t *zone)
 
 /*----------------------------------------------------------------------------*/
 
+typedef void (*adjust_callback_t)(knot_node_t **node, void *data);
+
+static int knot_zone_contents_adjust_nodes(knot_zone_tree_t *nodes,
+                                           knot_zone_adjust_arg_t *adjust_arg,
+                                           adjust_callback_t callback)
+{
+	assert(nodes);
+	assert(adjust_arg);
+	assert(callback);
+
+	adjust_arg->err = KNOT_EOK;
+	adjust_arg->first_node = NULL;
+	adjust_arg->previous_node = NULL;
+
+	hattrie_build_index(nodes);
+	int result = knot_zone_tree_apply_inorder(nodes, callback, adjust_arg);
+	assert(result == KNOT_EOK);
+
+	knot_node_set_previous(adjust_arg->first_node,
+	                       adjust_arg->previous_node);
+
+	return adjust_arg->err;
+}
+
+/*----------------------------------------------------------------------------*/
+
 int knot_zone_contents_adjust(knot_zone_contents_t *zone,
                               knot_node_t **first_nsec3_node,
                               knot_node_t **last_nsec3_node, int dupl_check)
 {
-	if (zone == NULL) {
+	if (zone == NULL)
 		return KNOT_EINVAL;
-	}
 
-	/* Heal zone indexes. */
-	hattrie_build_index(zone->nodes);
-	hattrie_build_index(zone->nsec3_nodes);
-
-	// load NSEC3PARAM (needed on adjusting function)
-	knot_zone_contents_load_nsec3param(zone);
-
-	hattrie_t *lookup_tree = NULL;
-	if (dupl_check) {
-		lookup_tree = hattrie_create();
-		if (lookup_tree == NULL) {
-			dbg_zone("Failed to create out of zone lookup structure.\n");
-			return KNOT_ERROR;
-		}
+	int result = knot_zone_contents_load_nsec3param(zone);
+	if (result != KNOT_EOK) {
+		log_zone_error("Failed to load NSEC3 params: %s\n",
+		               knot_strerror(result));
+		return result;
 	}
 
-	knot_zone_adjust_arg_t adjust_arg;
+	// adjusting parameters
+
+	knot_zone_adjust_arg_t adjust_arg = { 0 };
 	adjust_arg.zone = zone;
-	adjust_arg.first_node = NULL;
-	adjust_arg.previous_node = NULL;
-	adjust_arg.lookup_tree = lookup_tree;
-	adjust_arg.err = KNOT_EOK;
 
-	/*
-	 * First of all we must set node.prev pointers, as these are used in
-	 * the search functions.
-	 */
-	dbg_zone("Setting 'prev' pointers to NSEC3 nodes.\n");
-	int ret = knot_zone_tree_apply_inorder(zone->nsec3_nodes,
-		 knot_zone_contents_adjust_nsec3_node_in_tree_ptr, &adjust_arg);
-	assert(ret == KNOT_EOK);
+	// adjust NSEC3 nodes
 
-	if (adjust_arg.err != KNOT_EOK) {
-		dbg_zone("Failed to set 'prev' pointers to NSEC3 nodes: %s\n",
-			 knot_strerror(adjust_arg.err));
-		hattrie_free(lookup_tree);
-		return adjust_arg.err;
+	result = knot_zone_contents_adjust_nodes(zone->nsec3_nodes, &adjust_arg,
+	                                 knot_zone_contents_adjust_nsec3_node);
+	if (result != KNOT_EOK) {
+		return result;
 	}
 
-	// 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);
-	}
+	// optional output for NSEC3 nodes
+
 	if (first_nsec3_node) {
 		*first_nsec3_node = adjust_arg.first_node;
 	}
+
 	if (last_nsec3_node) {
 		*last_nsec3_node = adjust_arg.previous_node;
 	}
-	dbg_zone("Done.\n");
 
-	adjust_arg.first_node = NULL;
-	adjust_arg.previous_node = NULL;
+	// adjust normal nodes
 
-	dbg_zone("Setting 'prev' pointers to normal nodes.\n");
-	ret = knot_zone_tree_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));
-		hattrie_free(lookup_tree);
-		return adjust_arg.err;
+	result = knot_zone_contents_adjust_nodes(zone->nodes, &adjust_arg,
+	                                 knot_zone_contents_adjust_normal_node);
+	if (result != KNOT_EOK) {
+		return result;
 	}
 
-	// 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_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));
-		hattrie_free(lookup_tree);
-		return adjust_arg.err;
-	}
-
-	dbg_zone("Adjusting normal nodes.\n");
-	ret = knot_zone_tree_apply_inorder(zone->nodes,
-				knot_zone_contents_adjust_node_in_tree,
-				&adjust_arg);
-	assert(ret == KNOT_EOK);
-
-	if (adjust_arg.err != KNOT_EOK) {
-		dbg_zone("Failed to adjust normal nodes: %s\n",
-			 knot_strerror(adjust_arg.err));
-		hattrie_free(lookup_tree);
-		return adjust_arg.err;
-	}
-
-	dbg_zone("Done.\n");
-
-	hattrie_free(lookup_tree);
-
-	return ret;
+	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -2193,7 +1688,7 @@ static void knot_zc_integrity_check_flags(const knot_node_t *node,
 
 	// check the flags
 	if (check_data->deleg_point != NULL
-	    && knot_dname_is_subdomain(knot_node_owner(node),
+	    && knot_dname_is_sub(knot_node_owner(node),
 				    knot_node_owner(check_data->deleg_point))) {
 		// this is a non-authoritative node
 		if (!knot_node_is_non_auth(node)) {
@@ -2249,9 +1744,9 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node,
 	char *pname = knot_dname_to_str(parent_owner);
 
 	// if direct child
-	if (knot_dname_is_subdomain(node_owner, parent_owner)
+	if (knot_dname_is_sub(node_owner, parent_owner)
 	    && knot_dname_matched_labels(node_owner, parent_owner)
-	       == knot_dname_label_count(parent_owner)) {
+	       == knot_dname_labels(parent_owner, NULL)) {
 
 		// check the parent pointer
 		const knot_node_t *parent = knot_node_parent(node);
@@ -2306,29 +1801,6 @@ typedef struct find_dname_data {
 
 /*----------------------------------------------------------------------------*/
 
-static void knot_zc_integrity_check_owner(const knot_node_t *node,
-                                          check_data_t *check_data,
-                                          const char *name)
-{
-	// check node stored in owner
-	const knot_node_t *owner_node =
-			knot_dname_node(knot_node_owner(node));
-	if (owner_node != node) {
-		char *name2 = (owner_node != NULL)
-				? knot_dname_to_str(knot_node_owner(owner_node))
-				: "none";
-		fprintf(stderr, "Wrong owner's node: node %s, owner's node %s"
-			"\n", name, name2);
-		if (owner_node != NULL) {
-			free(name2);
-		}
-
-		++check_data->errors;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
 static void knot_zc_integrity_check_node(knot_node_t *node, void *data)
 {
 	assert(node != NULL);
@@ -2349,9 +1821,6 @@ static void knot_zc_integrity_check_node(knot_node_t *node, void *data)
 	// & wildcard child
 	knot_zc_integrity_check_parent(node, check_data, name);
 
-	// check owner
-	knot_zc_integrity_check_owner(node, check_data, name);
-
 	/*! \todo Check NSEC3 node. */
 
 	free(name);
@@ -2381,9 +1850,6 @@ static void knot_zc_integrity_check_nsec3(knot_node_t *node, void *data)
 		++check_data->errors;
 	}
 
-	// check owner
-	knot_zc_integrity_check_owner(node, check_data, name);
-
 	free(name);
 }
 
@@ -2497,8 +1963,6 @@ int knot_zc_integrity_check_child_count(check_data_t *data)
 	knot_zone_tree_apply_inorder(nodes_copy, count_children, NULL);
 
 	// add count of NSEC3 nodes to the apex' children count
-	fprintf(stderr, "Children count of new apex before NSEC3: %d\n",
-		data->contents->apex->new_node->children);
 	knot_zone_tree_apply_inorder(data->contents->nsec3_nodes,
 					     count_nsec3_nodes,
 					     (void *)apex_copy);
@@ -2576,67 +2040,10 @@ int knot_zone_contents_integrity_check(const knot_zone_contents_t *contents)
 	return data.errors;
 }
 
-struct dname_lookup_data {
-	const knot_dname_t *dname;
-	const knot_dname_t *found_dname;
-	int stopped;
-};
-
-static void find_dname_in_rdata(knot_node_t **tnode, void *data)
-{
-	struct dname_lookup_data *in_data = (struct dname_lookup_data *)data;
-	if (in_data->stopped) {
-		return;
-	}
-
-	/* For all RRSets in node. */
-	const knot_rrset_t **rrsets = knot_node_rrsets_no_copy(*tnode);
-	if (rrsets == NULL) {
-		return;
-	}
-
-	for (uint16_t i = 0; i < (*tnode)->rrset_count; i++) {
-		knot_dname_t **dname = NULL;
-		while ((dname = knot_rrset_get_next_dname(rrsets[i], dname))) {
-			if (*dname == in_data->dname) {
-				in_data->found_dname = *dname;
-				in_data->stopped = 1;
-				return;
-			} else if (knot_dname_compare(*dname,
-						      in_data->dname) == 0) {
-				in_data->found_dname = *dname;
-				in_data->stopped = 1;
-				return;
-			}
-		}
-	}
-
-	assert(in_data->stopped == 0);
-}
-
-const knot_dname_t *knot_zone_contents_find_dname_in_rdata(
-	const knot_zone_contents_t *zone,
-	const knot_dname_t *dname)
-{
-	struct dname_lookup_data data;
-	data.stopped = 0;
-	data.dname = dname;
-	data.found_dname = NULL;
-	knot_zone_tree_apply_inorder(zone->nodes,
-					     find_dname_in_rdata, &data);
-	if (data.stopped) {
-		/* Dname found. */
-		return data.found_dname;
-	} else {
-		assert(data.found_dname == NULL);
-		return NULL;
-	}
-}
-
 unsigned knot_zone_serial(const knot_zone_contents_t *zone)
 {
 	if (!zone) return 0;
 	const knot_rrset_t *soa = NULL;
 	soa = knot_node_rrset(knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
-	return knot_rrset_rdata_soa_serial(soa);
+	return knot_rdata_soa_serial(soa);
 }
diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h
index ab33d5fa06f59b0800d97e317150c802468d9a90..8ac80e00ee4637695f504c65cb1a4758ceae5a49 100644
--- a/src/libknot/zone/zone-contents.h
+++ b/src/libknot/zone/zone-contents.h
@@ -29,7 +29,7 @@
 
 #include "zone/node.h"
 #include "dname.h"
-#include "nsec3.h"
+#include "libknot/dnssec/nsec3.h"
 
 #include "zone-tree.h"
 
@@ -152,9 +152,8 @@ int knot_zone_contents_create_node(knot_zone_contents_t *contents,
  * \retval KNOT_EOUTOFZONE
  */
 int knot_zone_contents_add_rrset(knot_zone_contents_t *contents,
-                          knot_rrset_t *rrset,
-                          knot_node_t **node,
-                          knot_rrset_dupl_handling_t dupl);
+                                 knot_rrset_t *rrset, knot_node_t **node,
+                                 knot_rrset_dupl_handling_t dupl);
 
 int knot_zone_contents_add_rrsigs(knot_zone_contents_t *contents,
                            knot_rrset_t *rrsigs,
@@ -497,33 +496,6 @@ void knot_zone_contents_deep_free(knot_zone_contents_t **contents);
 
 int knot_zone_contents_integrity_check(const knot_zone_contents_t *contents);
 
-const knot_dname_t *knot_zone_contents_find_dname_in_rdata(
-	const knot_zone_contents_t *zone,
-	const knot_dname_t *dname);
-
-/*!
- * \brief Creates a NSEC3 hashed name for the given domain name.
- *
- * \note The zone's NSEC3PARAM record must be parsed prior to calling this
- *       function (see knot_zone_load_nsec3param()).
- *
- * \param zone Zone from which to take the NSEC3 parameters.
- * \param name Domain name to hash.
- * \param nsec3_name Hashed name.
- *
- * \retval KNOT_EOK
- * \retval KNOT_ENSEC3PAR
- * \retval KNOT_ECRYPTO
- * \retval KNOT_ERROR if an error occured while creating a new domain name
- *                      from the hash or concatenating it with the zone name.
- */
-int knot_zone_contents_nsec3_name(const knot_zone_contents_t *zone,
-                                           const knot_dname_t *name,
-                                           knot_dname_t **nsec3_name);
-
-void knot_zone_contents_insert_dname_into_table(knot_dname_t **in_dname,
-                                                hattrie_t *lookup_tree);
-
 /*!
  * \brief Fetch zone serial.
  *
diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c
index 009aa31c4adf297b7317efc50e0e42882530b101..860d9e26f2cc92ef34fcccf6a63d2180f9a6d326 100644
--- a/src/libknot/zone/zone-diff.c
+++ b/src/libknot/zone/zone-diff.c
@@ -24,10 +24,10 @@
 #include "zone-diff.h"
 #include "libknot/nameserver/name-server.h"
 #include "common/descriptor.h"
+#include "libknot/rdata.h"
 
 struct zone_diff_param {
-	const knot_zone_contents_t *contents;
-	char nsec3;
+	knot_zone_tree_t *nodes;
 	knot_changeset_t *changeset;
 	int ret;
 };
@@ -70,14 +70,13 @@ static int knot_zone_diff_load_soas(const knot_zone_contents_t *zone1,
 	}
 
 	int64_t soa_serial1 =
-		knot_rrset_rdata_soa_serial(soa_rrset1);
+		knot_rdata_soa_serial(soa_rrset1);
 	if (soa_serial1 == -1) {
 		dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n");
 	}
 
 	int64_t soa_serial2 =
-		knot_rrset_rdata_soa_serial(soa_rrset2);
-
+		knot_rdata_soa_serial(soa_rrset2);
 	if (soa_serial2 == -1) {
 		dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n");
 	}
@@ -110,23 +109,19 @@ static int knot_zone_diff_load_soas(const knot_zone_contents_t *zone1,
 
 	assert(changeset);
 
-	ret = knot_rrset_deep_copy(soa_rrset1, &changeset->soa_from, 1);
+	ret = knot_rrset_deep_copy_no_sig(soa_rrset1, &changeset->soa_from);
 	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);
-	assert(changeset->soa_from->rrsigs == NULL);
-
-	ret = knot_rrset_deep_copy(soa_rrset2, &changeset->soa_to, 1);
+	ret = knot_rrset_deep_copy_no_sig(soa_rrset2, &changeset->soa_to);
 	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);
+	assert(changeset->soa_from->rrsigs == NULL);
 	assert(changeset->soa_to->rrsigs == NULL);
 
 	changeset->serial_from = soa_serial1;
@@ -159,21 +154,18 @@ static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset,
 	knot_rrset_dump(rrset);
 
 	knot_rrset_t *rrset_copy = NULL;
-	int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1);
+	int ret = knot_rrset_deep_copy_no_sig(rrset, &rrset_copy);
 	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);
-	}
 	assert(knot_rrset_rrsigs(rrset_copy) == NULL);
 
-	ret = knot_changeset_add_new_rr(changeset, rrset_copy,
-	                                    KNOT_CHANGESET_ADD);
+	ret = knot_changeset_add_rrset(changeset, rrset_copy,
+	                               KNOT_CHANGESET_ADD);
 	if (ret != KNOT_EOK) {
 		/* We have to free the copy now! */
-		knot_rrset_deep_free(&rrset_copy, 1, 1);
+		knot_rrset_deep_free(&rrset_copy, 1);
 		dbg_zonediff("zone_diff: add_rrset: Could not add RRSet. "
 		             "Reason: %s.\n", knot_strerror(ret));
 		return ret;
@@ -206,21 +198,18 @@ static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset,
 	knot_rrset_dump(rrset);
 
 	knot_rrset_t *rrset_copy = NULL;
-	int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1);
+	int ret = knot_rrset_deep_copy_no_sig(rrset, &rrset_copy);
 	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);
-	}
 	assert(knot_rrset_rrsigs(rrset_copy) == NULL);
 
-	ret = knot_changeset_add_new_rr(changeset, rrset_copy,
-	                                    KNOT_CHANGESET_REMOVE);
+	ret = knot_changeset_add_rrset(changeset, rrset_copy,
+	                               KNOT_CHANGESET_REMOVE);
 	if (ret != KNOT_EOK) {
 		/* We have to free the copy now. */
-		knot_rrset_deep_free(&rrset_copy, 1, 1);
+		knot_rrset_deep_free(&rrset_copy, 1);
 		dbg_zonediff("zone_diff: remove_rrset: Could not remove RRSet. "
 		             "Reason: %s.\n", knot_strerror(ret));
 		return ret;
@@ -352,11 +341,13 @@ static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *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_dname_t *owner_copy = knot_dname_copy(knot_rrset_get_owner(rrset1));
+	*changes = knot_rrset_new(owner_copy,
 	                          knot_rrset_type(rrset1),
 	                          knot_rrset_class(rrset1),
 	                          knot_rrset_ttl(rrset1));
 	if (*changes == NULL) {
+		knot_dname_free(&owner_copy);
 		dbg_zonediff("zone_diff: diff_rdata: "
 		             "Could not create RRSet with changes.\n");
 		return KNOT_ENOMEM;
@@ -418,7 +409,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 		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);
+		int ret = knot_rrset_deep_copy(rrset1, &to_remove);
 		if (ret != KNOT_EOK) {
 			dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. "
 			             "Error: %s.\n", knot_strerror(ret));
@@ -456,7 +447,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 		 * be copied because TTLs are the same for all of them.
 		 */
 		knot_rrset_free(&to_remove);
-		int ret = knot_rrset_deep_copy(rrset1, &to_remove, 1);
+		int ret = knot_rrset_deep_copy(rrset1, &to_remove);
 		if (ret != KNOT_EOK) {
 			dbg_zonediff("zone_diff: diff_rdata: Cannot copy RRSet "
 			             "(%s).\n", knot_strerror(ret));
@@ -467,14 +458,14 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 	int ret = knot_zone_diff_changeset_remove_rrset(changeset,
 	                                            to_remove);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&to_remove, 1, 1);
+		knot_rrset_deep_free(&to_remove, 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);
+	knot_rrset_deep_free(&to_remove, 1);
 
 	/* Get RRs to add to zone. */ // TODO move to extra function, same for remove
 	knot_rrset_t *to_add = NULL;
@@ -482,7 +473,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 		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);
+		int ret = knot_rrset_deep_copy(rrset2, &to_add);
 		if (ret != KNOT_EOK) {
 			dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. "
 			             "Error: %s.\n", knot_strerror(ret));
@@ -519,7 +510,7 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 			 * be copied because TTLs are the same for all of them.
 			 */
 			knot_rrset_free(&to_add);
-			int ret = knot_rrset_deep_copy(rrset1, &to_add, 1);
+			int ret = knot_rrset_deep_copy(rrset1, &to_add);
 			if (ret != KNOT_EOK) {
 				dbg_zonediff("zone_diff: diff_rdata: Cannot copy RRSet "
 				             "(%s).\n", knot_strerror(ret));
@@ -532,14 +523,14 @@ static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
 	ret = knot_zone_diff_changeset_add_rrset(changeset,
 	                                         to_add);
 	if (ret != KNOT_EOK) {
-		knot_rrset_deep_free(&to_add, 1, 1);
+		knot_rrset_deep_free(&to_add, 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);
+	knot_rrset_deep_free(&to_add, 1);
 
 	return KNOT_EOK;
 }
@@ -579,7 +570,7 @@ static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1,
 //		return KNOT_EOK;
 //	}
 
-	assert(knot_dname_compare(knot_rrset_owner(rrset1),
+	assert(knot_dname_cmp(knot_rrset_owner(rrset1),
 	                          knot_rrset_owner(rrset2)) == 0);
 	assert(knot_rrset_type(rrset1) == knot_rrset_type(rrset2));
 
@@ -599,15 +590,17 @@ static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1,
 }
 
 /*!< \todo this could be generic function for adding / removing. */
-static void knot_zone_diff_node(knot_node_t *node, void *data)
+static void knot_zone_diff_node(knot_node_t **node_ptr, void *data)
 {
-	if (node == NULL || data == NULL) {
+	if (node_ptr == NULL || *node_ptr == NULL || data == NULL) {
 		dbg_zonediff("zone_diff: diff_node: NULL arguments.\n");
 		return;
 	}
 
+	knot_node_t *node = *node_ptr;
+
 	struct zone_diff_param *param = (struct zone_diff_param *)data;
-	if (param->changeset == NULL || param->contents == NULL) {
+	if (param->changeset == NULL || param->nodes == NULL) {
 		dbg_zonediff("zone_diff: diff_node: NULL arguments.\n");
 		param->ret = KNOT_EINVAL;
 		return;
@@ -627,16 +620,8 @@ static void knot_zone_diff_node(knot_node_t *node, void *data)
 	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);
-	}
+
+	knot_zone_tree_find(param->nodes, node_owner, &node_in_second_tree);
 
 	if (node_in_second_tree == NULL) {
 		dbg_zonediff_detail("zone_diff: diff_node: Node %s is not "
@@ -827,16 +812,17 @@ static void knot_zone_diff_node(knot_node_t *node, void *data)
 }
 
 /*!< \todo possibly not needed! */
-static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data)
+static void knot_zone_diff_add_new_nodes(knot_node_t **node_ptr, void *data)
 {
-	assert(node);
-	if (node == NULL || data == NULL) {
+	if (node_ptr == NULL || *node_ptr == NULL || data == NULL) {
 		dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n");
 		return;
 	}
 
+	knot_node_t *node = *node_ptr;
+
 	struct zone_diff_param *param = (struct zone_diff_param *)data;
-	if (param->changeset == NULL || param->contents == NULL) {
+	if (param->changeset == NULL || param->nodes == NULL) {
 		dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n");
 		param->ret = KNOT_EINVAL;
 		return;
@@ -854,8 +840,6 @@ static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data)
 	* 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);
 	/*
@@ -865,12 +849,7 @@ static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data)
 	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);
-	}
+	knot_zone_tree_get(param->nodes, node_owner, &new_node);
 
 	if (!new_node) {
 		assert(node);
@@ -886,94 +865,64 @@ static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data)
 	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)
+static int knot_zone_diff_load_trees(knot_zone_tree_t *nodes1,
+				     knot_zone_tree_t *nodes2,
+				     knot_changeset_t *changeset)
 {
-	if (zone1 == NULL || zone2 == NULL) {
-		dbg_zonediff("zone_diff: NULL argument(s).\n");
-		return KNOT_EINVAL;
-	}
+	assert(nodes1);
+	assert(nodes2);
+	assert(changeset);
 
-//	/* 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));
+	struct zone_diff_param param = { 0 };
+	param.ret = KNOT_EOK;
+	param.changeset = changeset;
 
-	/* 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;
-	}
+	// Traverse one tree, compare every node, each RRSet with its rdata.
+	param.nodes = nodes2;
+	int result = knot_zone_tree_apply(nodes1, knot_zone_diff_node, &param);
+	if (result != KNOT_EOK)
+		return result;
 
-	dbg_zonediff("zone_diff: SOAs loaded.\n");
+	// Some nodes may have been added. Add missing nodes to changeset.
+	param.nodes = nodes1;
+	result = knot_zone_tree_apply(nodes2, knot_zone_diff_add_new_nodes,
+	                              &param);
 
-	/* 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,
-	                        &param);
-	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;
-	}
+	return result;
+}
 
-	/* 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,
-	                                             &param);
-	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,
-		&param);
-	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;
+static int knot_zone_diff_load_content(const knot_zone_contents_t *zone1,
+                                       const knot_zone_contents_t *zone2,
+                                       knot_changeset_t *changeset)
+{
+	int result;
+
+	result = knot_zone_diff_load_trees(zone1->nodes, zone2->nodes, changeset);
+	if (result != KNOT_EOK)
+		return result;
+
+	result = knot_zone_diff_load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes,
+					   changeset);
+
+	return result;
+}
+
+
+static 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) {
+		return KNOT_EINVAL;
 	}
 
-	/* 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,
-		&param);
-	if (ret != KNOT_EOK) {
-		dbg_zonediff("zone_diff: Tree traversal failed "
-		             "with error: %s\n",
-		             knot_strerror(ret));
-		return ret;
+	int result = knot_zone_diff_load_soas(zone1, zone2, changeset);
+	if (result != KNOT_EOK) {
+		return result;
 	}
 
-	return KNOT_EOK;
+	return knot_zone_diff_load_content(zone1, zone2, changeset);
 }
 
 #ifdef KNOT_ZONEDIFF_DEBUG
@@ -986,48 +935,33 @@ static void knot_zone_diff_dump_changeset(knot_changeset_t *ch)
 	dbg_zonediff_detail("Changeset TO: %d\n", ch->serial_to);
 	knot_rrset_dump(ch->soa_to);
 	dbg_zonediff_detail("\n");
-	dbg_zonediff_detail("Adding %zu RRs.\n", ch->add_count);
-	dbg_zonediff_detail("Removing %zu RRs.\n", ch->remove_count);
 
 	dbg_zonediff_detail("ADD section:\n");
 	dbg_zonediff_detail("**********************************************\n");
-	for (int i = 0; i < ch->add_count; i++) {
-		knot_rrset_dump(ch->add[i]);
+	knot_rr_ln_t *rr_node;
+	WALK_LIST(rr_node, ch->add) {
+		knot_rrset_dump(rr_node->rr);
 		dbg_zonediff_detail("\n");
 	}
 	dbg_zonediff_detail("REMOVE section:\n");
 	dbg_zonediff_detail("**********************************************\n");
-	for (int i = 0; i < ch->remove_count; i++) {
-		knot_rrset_dump(ch->remove[i]);
+	WALK_LIST(rr_node, ch->remove) {
+		knot_rrset_dump(rr_node->rr);
 		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)
+int knot_zone_contents_create_diff(const knot_zone_contents_t *z1,
+                                   const knot_zone_contents_t *z2,
+                                   knot_changeset_t *changeset)
 {
 	if (z1 == NULL || z2 == NULL) {
 		dbg_zonediff("zone_diff: create_changesets: NULL arguments.\n");
 		return KNOT_EINVAL;
 	}
-	/* Create changesets. */
-	/* Setting type to IXFR - that's the default, DDNS triggers special
-	 * processing when applied. See #2110 and #2111.
-	 */
-	int ret = knot_changeset_allocate(changesets, KNOT_CHANGESET_TYPE_IXFR);
-	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);
+	int ret = knot_zone_contents_diff(z1, z2, changeset);
 	if (ret != KNOT_EOK) {
 		dbg_zonediff("zone_diff: create_changesets: "
 		             "Could not diff zones. "
@@ -1035,13 +969,20 @@ int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1,
 		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);
+	knot_zone_diff_dump_changeset(changeset);
 );
 
 	return KNOT_EOK;
 }
+
+int knot_zone_tree_add_diff(knot_zone_tree_t *t1, knot_zone_tree_t *t2,
+                            knot_changeset_t *changeset)
+{
+	if (!t1 || !t2 || !changeset)
+		return KNOT_EINVAL;
+
+	return knot_zone_diff_load_trees(t1, t2, changeset);
+}
diff --git a/src/libknot/zone/zone-diff.h b/src/libknot/zone/zone-diff.h
index 6e0eb1d5e4557ca896c565caf4cd8022e0687584..1c1824b0d5dbcbb952edb063f7af9a0a7e68e2c3 100644
--- a/src/libknot/zone/zone-diff.h
+++ b/src/libknot/zone/zone-diff.h
@@ -17,12 +17,22 @@
 #ifndef _KNOT_ZONE_DIFF_H_
 #define _KNOT_ZONE_DIFF_H_
 
+#include <stdint.h>
+
 #include "libknot/zone/zone-contents.h"
 #include "libknot/updates/changesets.h"
 
-/*! \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);
+/*!
+ * \brief Create diff between two zone trees.
+ * */
+int knot_zone_contents_create_diff(const knot_zone_contents_t *z1,
+                                   const knot_zone_contents_t *z2,
+                                   knot_changeset_t *changeset);
+
+/*!
+ * \brief Add diff between two zone trees into the changeset.
+ */
+int knot_zone_tree_add_diff(knot_zone_tree_t *t1, knot_zone_tree_t *t2,
+                            knot_changeset_t *changeset);
 
 #endif // _KNOT_ZONE_DIFF_H_
diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c
index 27cdf5f22339c64156ea93421145eb5ad8ac2eb8..c33731bc39b38a4775525d101443acdeeaaafd1f 100644
--- a/src/libknot/zone/zone-tree.c
+++ b/src/libknot/zone/zone-tree.c
@@ -27,53 +27,6 @@
 /* Non-API functions                                                          */
 /*----------------------------------------------------------------------------*/
 
-#define DNAME_LFT_MAXLEN 255 /* maximum lookup format length */
-
-/*!
- * \brief Convert domain name from wire to lookup format.
- *
- * Formats names from rightmost label to the leftmost, separated by the lowest
- * possible character (\x00). Sorting such formatted names also gives
- * correct canonical order (for NSEC/NSEC3).
- *
- * Example:
- * Name: lake.example.com. Wire: \x04lake\x07example\x03com\x00
- * Lookup format com\x00example\x00lake\x00
- *
- * Maximum length of such a domain name is DNAME_LFT_MAXLEN characters.
- *
- * \param dst Memory to store converted name into.
- * \param maxlen Maximum memory length.
- * \param src Source domain name.
- *
- * \retval KNOT_EOK if successful
- * \retval KNOT_ESPACE when not enough memory.
- * \retval KNOT_EINVAL on invalid parameters
- */
-static int dname_lf(uint8_t *dst, const knot_dname_t *src, size_t maxlen) {
-	if (src->size > maxlen)
-		return KNOT_ESPACE;
-	*dst = (uint8_t)src->size;
-	/* need to save last \x00 for root dname */
-	if (*dst > 1)
-		*dst -= 1;
-	*++dst = '\0';
-	uint8_t* l = src->name;
-	uint8_t lstack[DNAME_LFT_MAXLEN];
-	uint8_t *sp = lstack;
-	while(*l != 0) { /* build label stack */
-		*sp++ = (l - src->name);
-		l += 1 + *l;
-	}
-	while(sp != lstack) {          /* consume stack */
-		l = src->name + *--sp; /* fetch rightmost label */
-		memcpy(dst, l+1, *l);  /* write label */
-		dst += *l;
-		*dst++ = '\0';         /* label separator */
-	}
-	return KNOT_EOK;
-}
-
 static value_t knot_zone_node_copy(value_t v)
 {
 	return v;
@@ -103,13 +56,18 @@ size_t knot_zone_tree_weight(knot_zone_tree_t* tree)
 	return hattrie_weight(tree);
 }
 
+int knot_zone_tree_is_empty(knot_zone_tree_t *tree)
+{
+	return knot_zone_tree_weight(tree) == 0;
+}
+
 /*----------------------------------------------------------------------------*/
 
 int knot_zone_tree_insert(knot_zone_tree_t *tree, knot_node_t *node)
 {
 	assert(tree && node && node->owner);
-	uint8_t lf[DNAME_LFT_MAXLEN];
-	dname_lf(lf, node->owner, sizeof(lf));
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, node->owner, NULL);
 
 	*hattrie_get(tree, (char*)lf+1, *lf) = node;
 	return KNOT_EOK;
@@ -136,8 +94,8 @@ int knot_zone_tree_get(knot_zone_tree_t *tree, const knot_dname_t *owner,
 		return KNOT_EINVAL;
 	}
 
-	uint8_t lf[DNAME_LFT_MAXLEN];
-	dname_lf(lf, owner, sizeof(lf));
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, owner, NULL);
 
 	value_t *val = hattrie_tryget(tree, (char*)lf+1, *lf);
 	if (val == NULL) {
@@ -181,8 +139,8 @@ int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
 		return KNOT_EINVAL;
 	}
 
-	uint8_t lf[DNAME_LFT_MAXLEN];
-	dname_lf(lf, owner, sizeof(lf));
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, owner, NULL);
 
 	value_t *fval = NULL;
 	int ret = hattrie_find_leq(tree, (char*)lf+1, *lf, &fval);
@@ -243,8 +201,8 @@ int knot_zone_tree_remove(knot_zone_tree_t *tree,
 		return KNOT_EINVAL;
 	}
 
-	uint8_t lf[DNAME_LFT_MAXLEN];
-	dname_lf(lf, owner, sizeof(lf));
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, owner, NULL);
 
 	value_t *rval = hattrie_tryget(tree, (char*)lf+1, *lf);
 	if (rval == NULL) {
@@ -375,7 +333,9 @@ void knot_zone_tree_deep_free(knot_zone_tree_t **tree)
 
 void hattrie_insert_dname(hattrie_t *tr, knot_dname_t *dname)
 {
-	*hattrie_get(tr, (char *)dname->name, dname->size) = dname;
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, dname, NULL);
+	*hattrie_get(tr, (char*)lf+1, *lf) = dname;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -386,7 +346,10 @@ knot_dname_t *hattrie_get_dname(hattrie_t *tr, knot_dname_t *dname)
 		return NULL;
 	}
 
-	value_t *val = hattrie_tryget(tr, (char *)dname->name, dname->size);
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, dname, NULL);
+
+	value_t *val = hattrie_tryget(tr, (char*)lf+1, *lf);
 	if (val == NULL) {
 		return NULL;
 	} else {
diff --git a/src/libknot/zone/zone-tree.h b/src/libknot/zone/zone-tree.h
index d9172ce4c6e9c4c8aae705294300daad0eeae4c0..8e15d88b9c663deed20e188a7846a131fdeac288 100644
--- a/src/libknot/zone/zone-tree.h
+++ b/src/libknot/zone/zone-tree.h
@@ -51,6 +51,15 @@ knot_zone_tree_t* knot_zone_tree_create();
  */
 size_t knot_zone_tree_weight(knot_zone_tree_t* tree);
 
+/*!
+ * \brief Checks if the zone tree is empty.
+ *
+ * \param tree Zone tree to check.
+ *
+ * \return Nonzero if the zone tree is empty.
+ */
+int knot_zone_tree_is_empty(knot_zone_tree_t *tree);
+
 /*!
  * \brief Inserts the given node into the zone tree.
  *
diff --git a/src/libknot/zone/zone.c b/src/libknot/zone/zone.c
index fe597779ddfd50aae96127d0e79d626b0c37a0d5..b46b3533aeda61795184c04ebc2cfceb07f436f1 100644
--- a/src/libknot/zone/zone.c
+++ b/src/libknot/zone/zone.c
@@ -72,7 +72,7 @@ knot_zone_t *knot_zone_new_empty(knot_dname_t *name)
 knot_zone_t *knot_zone_new(knot_node_t *apex)
 {
 	knot_zone_t *zone = knot_zone_new_empty(
-			knot_dname_deep_copy(knot_node_owner(apex)));
+			knot_dname_copy(knot_node_owner(apex)));
 	if (zone == NULL) {
 		return NULL;
 	}
@@ -80,7 +80,7 @@ knot_zone_t *knot_zone_new(knot_node_t *apex)
 	dbg_zone("Creating zone contents.\n");
 	zone->contents = knot_zone_contents_new(apex, zone);
 	if (zone->contents == NULL) {
-		knot_dname_release(zone->name);
+		knot_dname_free(&zone->name);
 		free(zone);
 		return NULL;
 	}
@@ -207,7 +207,7 @@ void knot_zone_free(knot_zone_t **zone)
 		         "update.\n");
 	}
 
-	knot_dname_release((*zone)->name);
+	knot_dname_free(&(*zone)->name);
 
 	/* Call zone data destructor if exists. */
 	if ((*zone)->dtor) {
@@ -242,7 +242,7 @@ dbg_zone_exec(
 	free(name);
 );
 
-	knot_dname_release((*zone)->name);
+	knot_dname_free(&(*zone)->name);
 
 	/* Call zone data destructor if exists. */
 	if ((*zone)->dtor) {
diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h
index 5db24a5bba162f53964b2867ddc075683bc01066..834e094e23796d45b6574ce988bffc9ed9e27105 100644
--- a/src/libknot/zone/zone.h
+++ b/src/libknot/zone/zone.h
@@ -31,7 +31,7 @@
 
 #include "zone/node.h"
 #include "dname.h"
-#include "nsec3.h"
+#include "libknot/dnssec/nsec3.h"
 #include "common/ref.h"
 
 #include "zone-tree.h"
diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c
index 3650b24120018605ee937ccb58d40b5ea8848fa0..bbcaf7967738e56ac2c7bc897eec9e5f82fc5304 100644
--- a/src/libknot/zone/zonedb.c
+++ b/src/libknot/zone/zonedb.c
@@ -59,12 +59,8 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone)
 	if (db == NULL || zone == NULL) {
 		return KNOT_EINVAL;
 	}
-dbg_zonedb_exec(
-	char *name = knot_dname_to_str(zone->name);
-	dbg_zonedb("Inserting zone %s into zone db.\n", name);
-	free(name);
-);
 
+	/*! \todo Why is this check here? */
 	int ret = KNOT_EOK;
 	if (knot_zone_contents(zone)) {
 		ret = knot_zone_contents_load_nsec3param(
@@ -76,10 +72,11 @@ dbg_zonedb_exec(
 		}
 	}
 
-	/* Ordered lookup is not required, no dname conversion. */
-	const char *key = (const char*)knot_dname_name(zone->name);
-	size_t klen = knot_dname_size(zone->name);
-	*hattrie_get(db->zone_tree, key, klen) = zone;
+	/* Insert new record. */
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, zone->name, NULL);
+	*hattrie_get(db->zone_tree, (char*)lf+1, *lf) = zone;
+
 	db->zone_count++;
 
 	return ret;
@@ -91,19 +88,19 @@ knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db,
                                      const knot_dname_t *zone_name)
 {
 	/* Fetch if exists. */
-	knot_zone_t *oldzone = knot_zonedb_find_zone(db, zone_name);
-	if (oldzone == NULL) return NULL;
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, zone_name, NULL);
+
+	value_t *old_zone = hattrie_tryget(db->zone_tree, (char*)lf+1, *lf);
+	if (old_zone == NULL)
+		return NULL;
 
 	/* Remove from db. */
-	const char *key = (const char*)knot_dname_name(zone_name);
-	size_t klen = knot_dname_size(zone_name);
-	int ret = hattrie_del(db->zone_tree, key, klen);
-	if (ret < 0) {
+	if (hattrie_del(db->zone_tree, (char*)lf+1, *lf) < 0)
 		return NULL;
-	}
 
 	--db->zone_count;
-	return oldzone;
+	return (knot_zone_t *)*old_zone;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -111,11 +108,13 @@ knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db,
 knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
                                        const knot_dname_t *zone_name)
 {
-	const char *key = (const char*)knot_dname_name(zone_name);
-	size_t klen = knot_dname_size(zone_name);
-	value_t *val = hattrie_tryget(db->zone_tree, key, klen);
-	if (!val) return NULL;
-	return (knot_zone_t *)*val;
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, zone_name, NULL);
+	value_t *val = hattrie_tryget(db->zone_tree, (char*)lf+1, *lf);
+	if (val)
+		return (knot_zone_t *)*val;
+
+	return NULL;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -127,32 +126,21 @@ const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
 		return NULL;
 	}
 
-	knot_zone_t *zone = NULL;
-	const char *name = (const char*)dname->name;
-	size_t len = dname->size;
-
-	while (len > 0) {
-		value_t *found = hattrie_tryget(db->zone_tree, name, len);
-		if (found) {
-			zone = (knot_zone_t *)*found;
-			break;
-		} else {
-			/* Take label len + 1 and skip it.
-			 * ..from \x04lake\x03com\x00
-			 * ..to           \x03com\x00
-			 */
-			uint8_t to_chop = name[0] + 1;
-			len -= to_chop;
-			name += to_chop;
-		}
+	uint8_t lf[KNOT_DNAME_MAXLEN];
+	knot_dname_lf(lf, dname, NULL);
+
+	/* Attempt to find longest matching prefix.
+	 * as zones are stored as \x00com\x00lake\x00
+	 * the longest matching prefix is essentially a best match,
+	 * as it is guaranteed that inserted names end in \x00
+	 */
+	value_t *val = NULL;
+	int len = hattrie_find_lpr(db->zone_tree, (char*)lf+1, *lf, &val);
+	if (len > -1) {
+		return (const knot_zone_t *)*val;
 	}
 
-dbg_zonedb_exec(
-	char *zname = knot_dname_to_str(dname);
-	dbg_zonedb("Found zone for name %s: %p\n", zname, zone);
-	free(zname);
-);
-	return zone;
+	return NULL;
 }
 
 /*----------------------------------------------------------------------------*/
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 1199ceeb271ee0347668acfb77de6e49da26f64e..1931bdb2e9ede60ab71bef41c920394e1d65fc2f 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -46,8 +46,6 @@ unittests_SOURCES =			\
 	knot/server_tests.h		\
 	knot/rrl_tests.h		\
 	knot/rrl_tests.c		\
-	zscanner/zscanner_tests.h	\
-	zscanner/zscanner_tests.c	\
 	libknot/dname_tests.h		\
 	libknot/dname_tests.c		\
 	libknot/ztree_tests.h		\
@@ -56,8 +54,14 @@ unittests_SOURCES =			\
 	libknot/wire_tests.c		\
 	libknot/rrset_tests.c		\
 	libknot/rrset_tests.h		\
-	libknot/sign_tests.c		\
-	libknot/sign_tests.h		\
+	libknot/dnssec_keys_tests.c	\
+	libknot/dnssec_keys_tests.h	\
+	libknot/dnssec_nsec3_tests.c	\
+	libknot/dnssec_nsec3_tests.h	\
+	libknot/dnssec_sign_tests.c	\
+	libknot/dnssec_sign_tests.h	\
+	libknot/dnssec_zone_nsec_tests.c \
+	libknot/dnssec_zone_nsec_tests.h \
 	unittests_main.c
 
 unittests_LDADD = ../libknotd.la ../libknots.la @LIBOBJS@
diff --git a/src/tests/knot/conf_tests.c b/src/tests/knot/conf_tests.c
index 0904a4e2a7ea83916a437bbc7b50d62929ba4265..c7b510ffbf624256d7f11642af267ffe91e84ac9 100644
--- a/src/tests/knot/conf_tests.c
+++ b/src/tests/knot/conf_tests.c
@@ -96,7 +96,7 @@ static int conf_tests_run(int argc, char *argv[])
 	ok(!EMPTY_LIST(conf->logs), "log facilities not empty");
 
 	conf_log_t *log = (conf_log_t*)n;
-	node *nm = HEAD(log->map);
+	node_t *nm = HEAD(log->map);
 	conf_log_map_t *m = (conf_log_map_t*)nm;
 	cmp_ok(log->type, "==", LOGT_SYSLOG, "log0 is syslog");
 
@@ -127,11 +127,11 @@ static int conf_tests_run(int argc, char *argv[])
 
 	// Test 21: Load key dname
 	const char *sample_str = "key0.example.net";
-	knot_dname_t *sample = knot_dname_new_from_str(sample_str,
-	                                               strlen(sample_str), 0);
+	knot_dname_t *sample = knot_dname_from_str(sample_str,
+	                                               strlen(sample_str));
 	if (conf->key_count > 0) {
 		knot_tsig_key_t *k = &((conf_key_t *)HEAD(conf->keys))->k;
-		ok(knot_dname_compare(sample, k->name) == 0,
+		ok(knot_dname_cmp(sample, k->name) == 0,
 		   "TSIG key dname check");
 	} else {
 		ok(0, "TSIG key dname check - NO KEY FOUND");
diff --git a/src/tests/knot/rrl_tests.c b/src/tests/knot/rrl_tests.c
index e7d26dd3b8fad976f3360f80ba936db48cd2034b..8571f21d87408a19bfb749d2f648bf06e6bda2a3 100644
--- a/src/tests/knot/rrl_tests.c
+++ b/src/tests/knot/rrl_tests.c
@@ -120,24 +120,22 @@ static int rrl_tests_count(int argc, char *argv[])
 static int rrl_tests_run(int argc, char *argv[])
 {
 	/* Prepare query. */
-	knot_question_t qst;
-	qst.qclass = KNOT_CLASS_IN;
-	qst.qtype = KNOT_RRTYPE_A;
-	qst.qname = knot_dname_new_from_str("beef.", 5, NULL);
-	knot_packet_t *query = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
-	knot_query_init(query);
+	knot_packet_t *query = knot_packet_new();
 	if (knot_packet_set_max_size(query, 512) < 0) {
-		knot_dname_free(&qst.qname);
 		knot_packet_free(&query);
 		return KNOT_ERROR; /* Fatal */
 	}
-	int ret = knot_query_set_question(query, &qst);
+	knot_query_init(query);
+
+	knot_dname_t *qname = knot_dname_from_str("beef.", 5);
+	int ret = knot_query_set_question(query, qname, KNOT_CLASS_IN, KNOT_RRTYPE_A);
+	knot_dname_free(&qname);
 	if (ret != KNOT_EOK) {
-		knot_dname_free(&qst.qname);
 		knot_packet_free(&query);
 		return KNOT_ERROR; /* Fatal */
 	}
 
+
 	/* Prepare response */
 	knot_nameserver_t *ns = knot_ns_create();
 	uint8_t rbuf[65535];
@@ -148,7 +146,7 @@ static int rrl_tests_run(int argc, char *argv[])
 	rrl_req_t rq;
 	rq.w = rbuf;
 	rq.len = rlen;
-	rq.qst = &qst;
+	rq.query = query;
 	rq.flags = 0;
 
 	/* 1. create rrl table */
@@ -165,7 +163,7 @@ static int rrl_tests_run(int argc, char *argv[])
 	ok(ret == KNOT_EOK, "rrl: setlocks");
 
 	/* 4. N unlimited requests. */
-	knot_dname_t *apex = knot_dname_new_from_str("rrl.", 4, NULL);
+	knot_dname_t *apex = knot_dname_from_str("rrl.", 4);
 	knot_zone_t *zone = knot_zone_new(knot_node_new(apex, NULL, 0));
 	sockaddr_t addr;
 	sockaddr_t addr6;
@@ -220,8 +218,7 @@ static int rrl_tests_run(int argc, char *argv[])
 	ok(rd.passed, "rrl: hashtable is ~ consistent");
 #endif
 
-	knot_dname_release(qst.qname);
-	knot_dname_release(apex);
+	knot_dname_free(&apex);
 	knot_zone_deep_free(&zone);
 	knot_ns_destroy(&ns);
 	knot_packet_free(&query);
diff --git a/src/tests/libknot/dname_tests.c b/src/tests/libknot/dname_tests.c
index 73ca67de9647df7a8133d922fb8836bf36e1fbe1..515b4c2ac6b7bd68345b284b1e3b2cb80a5d928f 100644
--- a/src/tests/libknot/dname_tests.c
+++ b/src/tests/libknot/dname_tests.c
@@ -20,18 +20,8 @@
 
 /* Test dname_parse_from_wire */
 static int test_fw(size_t l, const char *w) {
-	size_t p = 0;
-	knot_dname_t *d = NULL;
-	d = knot_dname_parse_from_wire((const uint8_t*)w, &p, l, NULL, NULL);
-	int ret = (d != NULL);
-//	d = knot_dname_new_from_wire((const uint8_t*)w, l, 0);
-//	if (d) {
-//		for(unsigned i = 0; i < d->label_count; ++i) {
-//			diag("%d", knot_dname_label_size(d, i));
-//		}
-//	}
-	knot_dname_free(&d);
-	return ret;
+	const uint8_t *np = (const uint8_t *)w + l;
+	return knot_dname_wire_check((const uint8_t *)w, np, NULL) > 0;
 }
 
 static int dname_tests_count(int argc, char *argv[]);
@@ -45,12 +35,15 @@ unit_api dname_tests_api = {
 
 static int dname_tests_count(int argc, char *argv[])
 {
-	return 8;
+	return 23;
 }
 
 static int dname_tests_run(int argc, char *argv[])
 {
-	const char *w = NULL;
+	knot_dname_t *d = NULL, *d2 = NULL;
+	const char *w = NULL, *t = NULL;
+	unsigned len = 0;
+	size_t pos = 0;
 
 	/* 1. NULL wire */
 	ok(!test_fw(0, NULL), "parsing NULL dname");
@@ -68,17 +61,104 @@ static int dname_tests_run(int argc, char *argv[])
 	w = "\x40""dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
 	ok(!test_fw(65, w), "parsing label > 63b");
 
-	/* 6. label count > 127 */
+	/* 6. label count == 126 */
+	w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+	ok(test_fw(253, w), "parsing label count == 127");
+
+	/* 7. label count == 127 */
+	w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+	ok(test_fw(255, w), "parsing label count == 127");
+
+	/* 8. label count > 127 */
 	w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
 	ok(!test_fw(257, w), "parsing label count > 127");
 
-	/* 7. dname length > 255 */
+	/* 9. dname length > 255 */
 	w = "\xff""ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
 	ok(!test_fw(257, w), "parsing dname len > 255");
 
-	/* 8. special case - invalid label */
+	/* 10. special case - invalid label */
 	w = "\x20\x68\x6d\x6e\x63\x62\x67\x61\x61\x61\x61\x65\x72\x6b\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x6e\x69\x64\x68\x62\x61\x61\x61\x61\x65\x6c\x64\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x61\x63\x6f\x63\x64\x62\x61\x61\x61\x61\x65\x6b\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x69\x62\x63\x6d\x6a\x6f\x61\x61\x61\x61\x65\x72\x6a\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6f\x6c\x6e\x6c\x67\x68\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6a\x6b\x64\x66\x66\x67\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x67\x6c\x70\x70\x61\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x65\x6b\x6c\x67\x70\x66\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x0\x21\x42\x63\x84\xa5\xc6\xe7\x8\xa\xd\x11\x73\x3\x6e\x69\x63\x2\x43\x5a";
 	ok(!test_fw(277, w), "parsing invalid label (spec. case 1)");
 
+	/* 11. parse from string (correct) .*/
+	len = 10;
+	w = "\x04""abcd""\x03""efg";
+	t = "abcd.efg";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(d && knot_dname_size(d) == len && memcmp(d, w, len) == 0,
+	   "dname_fromstr: parsed correct non-FQDN name");
+	knot_dname_free(&d);
+
+	/* 12. parse FQDN from string (correct) .*/
+	t = "abcd.efg.";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(d && knot_dname_size(d) == len && memcmp(d, w, len) == 0,
+	   "dname_fromstr: parsed correct FQDN name");
+	knot_dname_free(&d);
+
+	/* 13. parse name from string (incorrect) .*/
+	t = "..";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(d == NULL, "dname_fromstr: parsed incorrect name");
+
+	/* 14. equal name is subdomain */
+	t = "ab.cd.ef";
+	d2 = knot_dname_from_str(t, strlen(t));
+	t = "ab.cd.ef";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(!knot_dname_is_sub(d, d2), "dname_subdomain: equal name");
+	knot_dname_free(&d);
+
+	/* 15. true subdomain */
+	t = "0.ab.cd.ef";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(knot_dname_is_sub(d, d2), "dname_subdomain: true subdomain");
+	knot_dname_free(&d);
+
+	/* 16. not subdomain */
+	t = "cd.ef";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(!knot_dname_is_sub(d, d2), "dname_subdomain: not subdomain");
+	knot_dname_free(&d);
+
+	/* 17. root subdomain */
+	t = ".";
+	d = knot_dname_from_str(t, strlen(t));
+	ok(knot_dname_is_sub(d2, d), "dname_subdomain: root subdomain");
+	knot_dname_free(&d);
+	knot_dname_free(&d2);
+
+	/* 18-19. dname cat (valid) */
+	w = "\x03""cat";
+	len = 5;
+	d = knot_dname_copy((const uint8_t *)w);
+	t = "*";
+	d2 = knot_dname_from_str(t, strlen(t));
+	d2 = knot_dname_cat(d2, d);
+	t = "\x01""*""\x03""cat";
+	len = 2 + 4 + 1;
+	ok (d2 && len == knot_dname_size(d2), "dname_cat: valid concatenation size");
+	ok(memcmp(d2, t, len) == 0, "dname_cat: valid concatenation");
+	knot_dname_free(&d);
+	knot_dname_free(&d2);
+
+	/* 20-21. parse from wire (valid) */
+	t = "\x04""abcd""\x03""efg";
+	len = 10;
+	pos = 0;
+	d = knot_dname_parse((const uint8_t *)t, &pos, len);
+	ok(d != NULL, "dname_parse: valid name");
+	cmp_ok(pos, "==", len, "dname_parse: valid name (parsed length)");
+	knot_dname_free(&d);
+
+	/* 22-23. parse from wire (invalid) */
+	t = "\x08""dddd";
+	len = 5;
+	pos = 0;
+	d = knot_dname_parse((const uint8_t *)t, &pos, len);
+	ok(d == NULL, "dname_parse: bad name");
+	cmp_ok(pos, "==", 0, "dname_parse: bad name (parsed length)");
+
 	return 0;
 }
diff --git a/src/tests/libknot/sign_tests.c b/src/tests/libknot/dnssec_keys_tests.c
similarity index 81%
rename from src/tests/libknot/sign_tests.c
rename to src/tests/libknot/dnssec_keys_tests.c
index 6b77218bf08bb9774a4a91d137b927d6a64d4295..300b577d464ccbeaba8e97d4f8a65fda2745f4b5 100644
--- a/src/tests/libknot/sign_tests.c
+++ b/src/tests/libknot/dnssec_keys_tests.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -15,22 +15,22 @@
  */
 
 #include <config.h>
-#include "tests/libknot/sign_tests.h"
-#include "libknot/sign/key.h"
-#include "libknot/sign/key.c" // testing static functions
+#include "tests/libknot/dnssec_keys_tests.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/key.c" // testing static functions
 
 static int sign_tests_count(int argc, char *argv[]);
 static int sign_tests_run(int argc, char *argv[]);
 
-unit_api sign_tests_api = {
-	"libknot/sign",
+unit_api dnssec_keys_tests_api = {
+	"libknot/dnssec/sign",
 	&sign_tests_count,
 	&sign_tests_run
 };
 
 static int sign_tests_count(int argc, char *argv[])
 {
-	return 25;
+	return 26;
 }
 
 static int sign_tests_run(int argc, char *argv[])
@@ -115,15 +115,18 @@ static int sign_tests_run(int argc, char *argv[])
 		free(private);
 	}
 
-	// 10. - key_param_string()
+	// 10. - key_param_base64()
 	{
-		char *output = NULL;
+		knot_binary_t output = { 0 };
 		int result;
 
-		result = key_param_string(&output, "ahoj DNS svete");
-		ok(result == KNOT_EOK && strcmp(output, "ahoj DNS svete") == 0,
-		   "key_param_string(), correct usage");
-		free(output);
+		result = key_param_base64(&output, "aGVsbG8gRE5TIHdvcmxk");
+
+		ok(result == KNOT_EOK && output.size == 15
+		   && memcmp((char *)output.data, "hello DNS world", 15) == 0,
+		   "key_param_base64(), correct usage");
+
+		knot_binary_free(&output);
 	}
 
 	// 11-16. - key_param_int()
@@ -160,7 +163,7 @@ static int sign_tests_run(int argc, char *argv[])
 		   "key_param_int(), number and text");
 	}
 
-	// 17-20. - parse_keyfile_line()
+	// 17-21. - parse_keyfile_line()
 	{
 		knot_key_params_t key = { 0 };
 		int result;
@@ -172,28 +175,35 @@ static int sign_tests_run(int argc, char *argv[])
 		   "parse_keyfile_line(), simple line with algorithm");
 		free(line);
 
-		line = strdup("Key:   secret\n");
+		line = strdup("Key:   c2VjcmV0\n");
 		result = parse_keyfile_line(&key, line, strlen(line));
-		ok(result == KNOT_EOK && strcmp(key.secret, "secret") == 0,
+		ok(result == KNOT_EOK && key.secret.size == 6
+		   && memcmp((char *)key.secret.data, "secret", 6) == 0,
 		   "parse_keyfile_line(), new line terminated line with key");
-		free(key.secret);
+		knot_binary_free(&key.secret);
 		free(line);
 
-		line = strdup("Cool: Knot DNS");
+		line = strdup("Cool: S25vdCBETlM=");
 		result = parse_keyfile_line(&key, line, strlen(line));
 		ok(result == KNOT_EOK,
 		   "parse_keyfile_line(), unknown parameter");
 		free(line);
+
+		line = strdup("Activate: 20130521144259\n");
+		result = parse_keyfile_line(&key, line, strlen(line));
+		ok(result == KNOT_EOK && key.time_activate == 1369147379,
+		   "parse_keyfile_line(), timestamp parsing");
+		free(line);
 	}
 
-	// 21. - knot_free_key_params()
+	// 22. - knot_free_key_params()
 	{
 		int result;
 		knot_key_params_t params = { 0 };
 		knot_key_params_t empty_params = { 0 };
 
 		params.algorithm = 42;
-		params.public_exponent = strdup("AQAB");
+		knot_binary_from_base64("AQAB", &params.public_exponent);
 
 		result = knot_free_key_params(&params);
 		ok(result == KNOT_EOK
@@ -201,33 +211,34 @@ static int sign_tests_run(int argc, char *argv[])
 		   "knot_free_key_params(), regular free");
 	}
 
-	// 22-25. - knot_tsig_key_from_params()
+	// 23-25. - knot_tsig_key_from_params()
 	{
 		int result;
 		knot_key_params_t params = { 0 };
-		knot_tsig_key_t tsig_key;
+		knot_tsig_key_t tsig_key = { 0 };
 		const char *owner = "shared.example.com.";
-		knot_dname_t *name = knot_dname_new_from_str(owner,
-							     strlen(owner),
-							     NULL);
+		knot_dname_t *name = knot_dname_from_str(owner,
+							     strlen(owner));
 
 		result = knot_tsig_key_from_params(&params, &tsig_key);
 		ok(result == KNOT_EINVAL,
 		   "knot_tsig_key_from_params(), empty parameters");
 
-		params.secret = "Ok6NmA==";
+		params.secret.data = (uint8_t *)"test";
+		params.secret.size = 4;
 		result = knot_tsig_key_from_params(&params, &tsig_key);
 		ok(result == KNOT_EINVAL,
 		   "knot_tsig_key_from_params(), no key name");
 
 		params.name = name;
-		params.secret = NULL;
+		params.secret.data = NULL;
+		params.secret.size = 0;
 		result = knot_tsig_key_from_params(&params, &tsig_key);
 		ok(result == KNOT_EINVAL,
 		   "knot_tsig_key_from_params(), no shared secret");
 
 		params.name = name;
-		params.secret = "Ok6NmA==";
+		knot_binary_from_base64("Ok6NmA==", &params.secret);
 		uint8_t decoded_secret[] = { 0x3a, 0x4e, 0x8d, 0x98 };
 		result = knot_tsig_key_from_params(&params, &tsig_key);
 		ok(result == KNOT_EOK
@@ -236,7 +247,6 @@ static int sign_tests_run(int argc, char *argv[])
 		             sizeof(decoded_secret)) == 0,
 		   "knot_tsig_key_from_params(), secret set properly");
 
-		knot_dname_release(name);
 		knot_tsig_key_free(&tsig_key);
 	}
 
diff --git a/src/tests/libknot/sign_tests.h b/src/tests/libknot/dnssec_keys_tests.h
similarity index 88%
rename from src/tests/libknot/sign_tests.h
rename to src/tests/libknot/dnssec_keys_tests.h
index 177f9e6fdce169c1f38e4e6094e9b44ede4367ef..e764a5c5bf0d5c439e8915d456e3d494fb2e2718 100644
--- a/src/tests/libknot/sign_tests.h
+++ b/src/tests/libknot/dnssec_keys_tests.h
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -19,6 +19,6 @@
 
 #include "common/libtap/tap_unit.h"
 
-unit_api sign_tests_api;
+unit_api dnssec_keys_tests_api;
 
 #endif
diff --git a/src/tests/libknot/dnssec_nsec3_tests.c b/src/tests/libknot/dnssec_nsec3_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..e6779b435189a0fbb332e0cabba1a5ab9c94c1cb
--- /dev/null
+++ b/src/tests/libknot/dnssec_nsec3_tests.c
@@ -0,0 +1,114 @@
+/*  Copyright (C) 2013 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 <string.h>
+
+#include "tests/libknot/dnssec_nsec3_tests.h"
+
+#include "common/descriptor.h"
+#include "common/errcode.h"
+#include "libknot/dname.h"
+#include "libknot/dnssec/algorithm.h"
+#include "libknot/dnssec/nsec3.h"
+#include "libknot/rrset.h"
+
+static int dnssec_nsec3_tests_count(int argc, char *argv[]);
+static int dnssec_nsec3_tests_run(int argc, char *argv[]);
+
+unit_api dnssec_nsec3_tests_api = {
+	"libknot/dnssec/nsec3",
+	&dnssec_nsec3_tests_count,
+	&dnssec_nsec3_tests_run
+};
+
+static int dnssec_nsec3_tests_count(int argc, char *argv[])
+{
+	return 10;
+}
+
+static int dnssec_nsec3_tests_run(int argc, char *argv[])
+{
+	int result = KNOT_EOK;
+
+	// lengths of different hashes
+
+	ok(knot_nsec3_hash_length(1) == 20,
+	   "raw hash length for SHA1");
+	ok(knot_nsec3_hash_length(42) == 0,
+	   "raw hash length for unknown algorithm");
+	ok(knot_nsec3_hash_b32_length(1) == 32,
+	   "B32 hash length for SHA1");
+	ok(knot_nsec3_hash_b32_length(42) == 0,
+	   "B32 hash length for unknown algorithm");
+
+	//  parsing NSEC3PARAMs from wire
+
+	knot_nsec3_params_t params = { 0 };
+	knot_rrset_t *rrset = NULL;
+	uint8_t rdata[] = {
+		0x01,                  // hash algorithm
+		0x00,                  // flags
+		0x00, 0x0a,            // iterations
+		0x04,                  // salt length
+		'a', 'b', 'c', 'd'     // salt
+	};
+
+	rrset = knot_rrset_new(NULL, KNOT_RRTYPE_NSEC3PARAM, KNOT_CLASS_IN, 0);
+	result = knot_rrset_add_rdata(rrset, rdata, sizeof(rdata));
+	if (result == KNOT_EOK); {
+		result = knot_nsec3_params_from_wire(&params, rrset);
+	}
+
+	ok(params.algorithm == 1, "parse algorithm from wire");
+	ok(params.flags == 0, "parse flags from wire");
+	ok(params.iterations == 10, "parse iterations from wire");
+	ok(params.salt_length == 4, "parse salt length from wire");
+	ok(memcmp(params.salt, "abcd", 4) == 0, "parse salt from wire");
+
+	knot_rrset_deep_free(&rrset, 1);
+	knot_nsec3_params_free(&params);
+
+	// hash computation
+
+	params.algorithm = 1;
+	params.flags = 0;
+	params.iterations = 7;
+	params.salt_length = 14;
+	params.salt = (uint8_t *)strdup("happywithnsec3");
+
+	const char *dname_str = "knot-dns.cz.";
+	knot_dname_t *dname = knot_dname_from_str(dname_str, strlen(dname_str));
+
+	size_t digest_size = 0;
+	uint8_t *digest = NULL;
+	result = knot_nsec3_hash(&params, dname, knot_dname_size(dname),
+	                         &digest, &digest_size);
+
+	uint8_t expected[] = {
+		0x72, 0x40, 0x55, 0x83, 0x92, 0x93, 0x95, 0x28, 0xee, 0xa2,
+		0xcc, 0xe1, 0x13, 0xbe, 0xcd, 0x41, 0xee, 0x8a, 0x71, 0xfd
+	};
+
+	ok(result == KNOT_EOK && digest_size == sizeof(expected) &&
+	   memcmp(digest, expected, sizeof(expected)) == 0, "compute hash");
+
+	free(digest);
+	free(params.salt);
+	knot_dname_free(&dname);
+
+	return 0;
+}
diff --git a/src/tests/zscanner/zscanner_tests.h b/src/tests/libknot/dnssec_nsec3_tests.h
similarity index 78%
rename from src/tests/zscanner/zscanner_tests.h
rename to src/tests/libknot/dnssec_nsec3_tests.h
index eae23caed166686358104fb569936fe63ee310eb..5ef80bab4d6f37dcf5493bc0b733cb2c60ed5ebf 100644
--- a/src/tests/zscanner/zscanner_tests.h
+++ b/src/tests/libknot/dnssec_nsec3_tests.h
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2013 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
@@ -14,12 +14,11 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef _ZSCANNER_TESTS_H_
-#define _ZSCANNER_TESTS_H_
+#ifndef _KNOTD_DNSSEC_NSEC3_TESTS_
+#define _KNOTD_DNSSEC_NSEC3_TESTS_
 
 #include "common/libtap/tap_unit.h"
 
-/* Unit API. */
-unit_api zscanner_tests_api;
+unit_api dnssec_nsec3_tests_api;
 
-#endif /* _ZSCANNER_TESTS_H_ */
+#endif
diff --git a/src/tests/libknot/dnssec_sign_tests.c b/src/tests/libknot/dnssec_sign_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..3de93ad9919653d284b707edbb9190fae67aefaa
--- /dev/null
+++ b/src/tests/libknot/dnssec_sign_tests.c
@@ -0,0 +1,152 @@
+/*  Copyright (C) 2013 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 <assert.h>
+#include <openssl/opensslconf.h>
+
+#include "tests/libknot/dnssec_sign_tests.h"
+#include "common/errcode.h"
+#include "libknot/dnssec/sign.h"
+
+static int dnssec_sign_tests_count(int argc, char *argv[]);
+static int dnssec_sign_tests_run(int argc, char *argv[]);
+
+#ifdef OPENSSL_NO_ECDSA
+static const int ecdsa_supported = 0;
+#else
+static const int ecdsa_supported = 1;
+#endif
+
+unit_api dnssec_sign_tests_api = {
+	"libknot/dnssec/sign",
+	&dnssec_sign_tests_count,
+	&dnssec_sign_tests_run
+};
+
+static void test_algorithm(const char *alg, const knot_key_params_t *kp)
+{
+	int result;
+
+	knot_dnssec_key_t key = { 0 };
+	result = knot_dnssec_key_from_params(kp, &key);
+	ok(result == KNOT_EOK, "%s: create key from params", alg);
+
+	knot_dnssec_sign_context_t *ctx;
+	ctx = knot_dnssec_sign_init(&key);
+	ok(ctx != NULL, "%s: create signing context", alg);
+
+	skip(ctx == NULL, 12, "%s: required test failed", alg);
+
+	size_t sig_size = knot_dnssec_sign_size(&key);
+	ok(sig_size > 0, "%s: get signature size", alg);
+
+	uint8_t *sig = malloc(sig_size);
+	assert(sig != NULL);
+
+	result = knot_dnssec_sign_add(ctx, (uint8_t *)"test", 4);
+	ok(result == KNOT_EOK, "%s: add data A", alg);
+
+	result = knot_dnssec_sign_new(ctx);
+	ok(result == KNOT_EOK, "%s: restart context", alg);
+
+	result = knot_dnssec_sign_add(ctx, (uint8_t *)"hello", 5);
+	ok(result == KNOT_EOK, "%s: add data B", alg);
+
+	result = knot_dnssec_sign_add(ctx, (uint8_t *)"dns", 3);
+	ok(result == KNOT_EOK, "%s: add data C", alg);
+
+	result = knot_dnssec_sign_write(ctx, sig);
+	ok(result == KNOT_EOK, "%s: write signature", alg);
+
+	result = knot_dnssec_sign_new(ctx);
+	ok(result == KNOT_EOK, "%s: restart context", alg);
+
+	result = knot_dnssec_sign_add(ctx, (uint8_t *)"wrong", 5);
+	ok(result == KNOT_EOK, "%s: add data D", alg);
+
+	result = knot_dnssec_sign_verify(ctx, sig, sig_size);
+	ok(result == KNOT_DNSSEC_EINVALID_SIGNATURE, "%s: verify invalid signature", alg);
+
+	result = knot_dnssec_sign_new(ctx);
+	ok(result == KNOT_EOK, "%s: restart context", alg);
+
+	result = knot_dnssec_sign_add(ctx, (uint8_t *)"hellodns", 8);
+	ok(result == KNOT_EOK, "%s: add data B + C", alg);
+
+	result = knot_dnssec_sign_verify(ctx, sig, sig_size);
+	ok(result == KNOT_EOK, "%s: verify valid signature", alg);
+
+	endskip;
+
+	knot_dnssec_sign_free(ctx);
+	knot_dnssec_key_free(&key);
+}
+
+static int dnssec_sign_tests_count(int argc, char *argv[])
+{
+	return 42;
+}
+
+static int dnssec_sign_tests_run(int argc, char *argv[])
+{
+	knot_key_params_t kp = { 0 };
+
+	// RSA
+
+	kp.name = knot_dname_from_str("example.com.", 12);
+	kp.algorithm = 5;
+	knot_binary_from_base64("pSxiFXG8wB1SSHdok+OdaAp6QdvqjpZ17ucNge21iYVfv+DZq52l21KdmmyEqoG9wG/87O7XG8XVLNyYPue8Mw==", &kp.modulus);
+	knot_binary_from_base64("AQAB", &kp.public_exponent);
+	knot_binary_from_base64("UuNK9Wf2SJJuUF9b45s9ypA3egVaV+O5mwHoDWO0ziWJxFXNMMsobDdusEDjCw64xnlLmrbzNJ3+ClrOnV04gQ==", &kp.private_exponent);
+	knot_binary_from_base64("0/wjqkgVZxqrFi5OMzq2qQYpxKn3HgS87Io9UG6iqis=", &kp.prime_one);
+	knot_binary_from_base64("x3gFCPpaJ4etPEM1hRd6WMAcmx5FBMjvuuzID6SWWhk=", &kp.prime_two);
+	knot_binary_from_base64("Z8qUS9NvZ0QPcJTLhRnCRY/W84ukivYW6lnlG3SQAHE=", &kp.exponent_one);
+	knot_binary_from_base64("C0kjH8rqZuoqRwqWcJ1Pcs4L0Er6JLcpuS3Ec/4f86E=", &kp.exponent_two);
+	knot_binary_from_base64("VYc62FQX0Vnd27VxkX6hsBcl7Oh00wVCeh3WTDutndg=", &kp.coefficient);
+
+	test_algorithm("RSA", &kp);
+	knot_free_key_params(&kp);
+
+	// DSA
+
+	kp.name = knot_dname_from_str("example.com.", 12);
+	kp.algorithm = 6;
+	knot_binary_from_base64("u7tr4jc7CH0+r2muVEZyjYu7hpMrQ1dHGAMv7hr5dBFYzkutfdBmDSW4C+qxaXWo14gi+jJ8XqFqQ7rQn23DdQ==", &kp.prime);
+	knot_binary_from_base64("tgZ5X6pFoCOM2NzfiAYVG1434Mk=", &kp.subprime);
+	knot_binary_from_base64("bHidtFIFYAHXp7ZxTFd6poJJG8brqO9eyJygvYSFCej/FGDqhF2TsboVvS/evW/qTaSvhkd/aiDg5eAfu1HvrQ==", &kp.base);
+	knot_binary_from_base64("FiTBDsbFDNTw7IrhPeVbzM0DMmI=", &kp.private_value);
+	knot_binary_from_base64("G1pX04Bcew8wyHsmno4Q0tNdmBLlaEdbqvQ03W5XVXUM6MPrtzxgc6jdOogqZsvGK4c+FbThBu42Z1t/ioQr8A==", &kp.public_value);
+
+	test_algorithm("DSA", &kp);
+	knot_free_key_params(&kp);
+
+	// ECDSA
+
+	skip(!ecdsa_supported, 14, "ECDSA: not supported on this system");
+
+	kp.name = knot_dname_from_str("example.com", 12);
+	kp.algorithm = 13;
+	knot_binary_from_base64("1N/PvpB8jZcvv+zr3Q987RKK1cBxDKULzEc5F/nnpSg=", &kp.private_key);
+	knot_binary_from_base64("fe3oR+S8crl9AwayWFZwJ8wXpDeg1uiXZ/X0MYBvyvj1lfuJDXawUjKuzYKLAPEVH1jt8XbM5nTTlVXUsDebVA==", &kp.rdata);
+
+	test_algorithm("ECDSA", &kp);
+	knot_free_key_params(&kp);
+
+	endskip;
+
+	return 0;
+}
diff --git a/src/tests/libknot/dnssec_sign_tests.h b/src/tests/libknot/dnssec_sign_tests.h
new file mode 100644
index 0000000000000000000000000000000000000000..a5f55342b5e4b90211e892308b0718c355b67449
--- /dev/null
+++ b/src/tests/libknot/dnssec_sign_tests.h
@@ -0,0 +1,24 @@
+/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DNSSEC_SIGN_TESTS_
+#define _KNOTD_DNSSEC_SIGN_TESTS_
+
+#include "common/libtap/tap_unit.h"
+
+unit_api dnssec_sign_tests_api;
+
+#endif
diff --git a/src/tests/libknot/dnssec_zone_nsec_tests.c b/src/tests/libknot/dnssec_zone_nsec_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..e91b82bd5b325a92be00a4f33260502a1ad4896c
--- /dev/null
+++ b/src/tests/libknot/dnssec_zone_nsec_tests.c
@@ -0,0 +1,63 @@
+/*  Copyright (C) 2013 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 "dnssec_zone_nsec_tests.h"
+#include "libknot/dname.h"
+#include "libknot/dnssec/zone-nsec.h"
+
+static int dnssec_zone_nsec_tests_count(int argc, char *argv[]);
+static int dnssec_zone_nsec_tests_run(int argc, char *argv[]);
+
+unit_api dnssec_zone_nsec_tests_api = {
+	"libknot/dnssec/zone-nsec",
+	&dnssec_zone_nsec_tests_count,
+	&dnssec_zone_nsec_tests_run
+};
+
+static int dnssec_zone_nsec_tests_count(int argc, char *argv[])
+{
+	return 1;
+}
+
+static knot_dname_t *get_dname(const char *str)
+{
+	size_t length = strlen(str);
+	return knot_dname_from_str(str, length);
+}
+
+static int dnssec_zone_nsec_tests_run(int argc, char *argv[])
+{
+	knot_dname_t *owner  = get_dname("name.example.com");
+	knot_dname_t *apex   = get_dname("example.com");
+	knot_dname_t *expect = get_dname("sv9o5lv8kgv6lm1t9dkst43b3c0aagbj.example.com");
+
+	knot_nsec3_params_t params = {
+		.algorithm = 1, .flags = 0, .iterations = 10,
+		.salt = (uint8_t *)"\xc0\x01", .salt_length = 2
+	};
+
+	knot_dname_t *result = create_nsec3_owner(owner, apex, &params);
+	ok(knot_dname_cmp(result, expect) == 0, "create_nsec3_owner()");
+
+	knot_dname_free(&result);
+	knot_dname_free(&owner);
+	knot_dname_free(&apex);
+	knot_dname_free(&expect);
+
+	return 0;
+}
diff --git a/src/tests/libknot/dnssec_zone_nsec_tests.h b/src/tests/libknot/dnssec_zone_nsec_tests.h
new file mode 100644
index 0000000000000000000000000000000000000000..7badb4f7a9d3940c306219d33f87c43d4bf28e36
--- /dev/null
+++ b/src/tests/libknot/dnssec_zone_nsec_tests.h
@@ -0,0 +1,24 @@
+/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _KNOTD_DNSSEC_ZONE_NSEC_TESTS_
+#define _KNOTD_DNSSEC_ZONE_NSEC_TESTS_
+
+#include "common/libtap/tap_unit.h"
+
+unit_api dnssec_zone_nsec_tests_api;
+
+#endif // _KNOTD_DNSSEC_ZONE_NSEC_TESTS_
diff --git a/src/tests/libknot/rrset_tests.c b/src/tests/libknot/rrset_tests.c
index d140681017bf7d023b6bbcdbfd5b732165e16353..2a952e0b1be81ba1b142363c0fe069d227913fd8 100644
--- a/src/tests/libknot/rrset_tests.c
+++ b/src/tests/libknot/rrset_tests.c
@@ -13,7 +13,7 @@
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-
+#if 0
 #include <config.h>
 #include <assert.h>
 #include <stdint.h>
@@ -169,9 +169,8 @@ static void create_test_dnames()
 {
 	for (int i = 0; i < TEST_DNAME_COUNT; i++) {
 		test_dnames[i] =
-			knot_dname_new_from_str(test_dname_strings[i],
-		                                strlen(test_dname_strings[i]),
-		                                NULL);
+			knot_dname_from_str(test_dname_strings[i],
+		                                strlen(test_dname_strings[i]));
 	}
 }
 
@@ -190,10 +189,8 @@ static void init_test_rdata_with_dname(uint8_t **rdata, uint16_t *rdata_size,
 	}
 	memcpy(*rdata + pos, &dname, sizeof(knot_dname_t *));
 	*rdata_size += sizeof(knot_dname_t *);
-	memcpy(*wire + wire_pos, dname->name, dname->size);
-	*wire_size += dname->size;
-
-	knot_dname_retain(dname);
+	memcpy(*wire + wire_pos, dname, knot_dname_size(dname));
+	*wire_size += knot_dname_size(dname);
 }
 
 static void init_test_rdata_with_binary(uint8_t **rdata, uint16_t *rdata_size,
@@ -226,7 +223,7 @@ static void create_test_rdata()
 	                           &test_rdata_array[TEST_RDATA_NS_LESS].wire,
 	                           &test_rdata_array[TEST_RDATA_NS_LESS].wire_size,
 	                           0, 0, sizeof(knot_dname_t *),
-	                           test_dnames[0]->size, test_dnames[0]);
+	                           knot_dname_size(test_dnames[0]), test_dnames[0]);
 
 	/* NS RDATA DNAME = b.dname.com. */
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_NS_GT].rdata,
@@ -234,7 +231,7 @@ static void create_test_rdata()
 	                           &test_rdata_array[TEST_RDATA_NS_GT].wire,
 	                           &test_rdata_array[TEST_RDATA_NS_GT].wire_size,
 	                           0, 0, sizeof(knot_dname_t *),
-	                           test_dnames[1]->size, test_dnames[1]);
+	                           knot_dname_size(test_dnames[1]), test_dnames[1]);
 
 	/* MX RDATA - short = 10 DNAME = a.dname.com. */
 	uint16_t id = htobe16(10);
@@ -243,14 +240,14 @@ static void create_test_rdata()
 	                            &test_rdata_array[TEST_RDATA_MX_DNAME_LESS].wire,
 	                            &test_rdata_array[TEST_RDATA_MX_DNAME_LESS].wire_size,
 	                            0, 0, sizeof(knot_dname_t *) + 2,
-	                            test_dnames[1]->size + 2, &id, 2);
+	                            knot_dname_size(test_dnames[1]) + 2, &id, 2);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MX_DNAME_LESS].rdata,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_LESS].size,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_LESS].wire,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_LESS].wire_size,
 	                           2, 2, sizeof(knot_dname_t *),
-	                           test_dnames[1]->size, test_dnames[0]);
+	                           knot_dname_size(test_dnames[1]), test_dnames[0]);
 
 	/* MX RDATA - short = 10 DNAME = b.dname.com. */
 	init_test_rdata_with_binary(&test_rdata_array[TEST_RDATA_MX_DNAME_GT].rdata,
@@ -258,17 +255,16 @@ static void create_test_rdata()
 	                            &test_rdata_array[TEST_RDATA_MX_DNAME_GT].wire,
 	                            &test_rdata_array[TEST_RDATA_MX_DNAME_GT].wire_size,
 	                            0, 0, sizeof(knot_dname_t *) + 2,
-	                            test_dnames[1]->size + 2, &id, 2);
+	                            knot_dname_size(test_dnames[1]) + 2, &id, 2);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MX_DNAME_GT].rdata,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_GT].size,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_GT].wire,
 	                           &test_rdata_array[TEST_RDATA_MX_DNAME_GT].wire_size,
 	                           2, 2, sizeof(knot_dname_t *),
-	                           test_dnames[1]->size, test_dnames[1]);
+	                           knot_dname_size(test_dnames[1]), test_dnames[1]);
 
 	test_rdata_array[TEST_RDATA_MX_BIN_LESS] = test_rdata_array[TEST_RDATA_MX_DNAME_LESS];
-	knot_dname_retain(test_dnames[1]);
 
 	/* MX RDATA - short = 20 DNAME = b.dname.com. */
 	id = htobe16(20);
@@ -277,14 +273,14 @@ static void create_test_rdata()
 	                            &test_rdata_array[TEST_RDATA_MX_BIN_GT].wire,
 	                            &test_rdata_array[TEST_RDATA_MX_BIN_GT].wire_size,
 	                            0, 0, sizeof(knot_dname_t *) + 2,
-	                            test_dnames[1]->size + 2, &id, 2);
+	                            knot_dname_size(test_dnames[1]) + 2, &id, 2);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MX_BIN_GT].rdata,
 	                           &test_rdata_array[TEST_RDATA_MX_BIN_GT].size,
 	                           &test_rdata_array[TEST_RDATA_MX_BIN_GT].wire,
 	                           &test_rdata_array[TEST_RDATA_MX_BIN_GT].wire_size,
 	                           2, 2, sizeof(knot_dname_t *),
-	                           test_dnames[1]->size, test_dnames[1]);
+	                           knot_dname_size(test_dnames[1]), test_dnames[1]);
 
 	/* MINFO RRs. */
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MINFO1].rdata,
@@ -292,32 +288,32 @@ static void create_test_rdata()
 	                           &test_rdata_array[TEST_RDATA_MINFO1].wire,
 	                           &test_rdata_array[TEST_RDATA_MINFO1].wire_size,
 	                           0, 0, sizeof(knot_dname_t *) * 2,
-	                           test_dnames[0]->size + test_dnames[1]->size,
+	                           knot_dname_size(test_dnames[0]) + knot_dname_size(test_dnames[1]),
 	                           test_dnames[0]);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MINFO1].rdata,
 	                           &test_rdata_array[TEST_RDATA_MINFO1].size,
 	                           &test_rdata_array[TEST_RDATA_MINFO1].wire,
 	                           &test_rdata_array[TEST_RDATA_MINFO1].wire_size,
-	                           sizeof(knot_dname_t *), test_dnames[0]->size,
+	                           sizeof(knot_dname_t *), knot_dname_size(test_dnames[0]),
 	                           sizeof(knot_dname_t *),
-	                           test_dnames[1]->size, test_dnames[1]);
+	                           knot_dname_size(test_dnames[1]), test_dnames[1]);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MINFO2].rdata,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].size,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].wire,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].wire_size,
 	                           0, 0, sizeof(knot_dname_t *) * 2,
-	                           test_dnames[2]->size + test_dnames[3]->size,
+	                           knot_dname_size(test_dnames[2]) + knot_dname_size(test_dnames[3]),
 	                           test_dnames[2]);
 
 	init_test_rdata_with_dname(&test_rdata_array[TEST_RDATA_MINFO2].rdata,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].size,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].wire,
 	                           &test_rdata_array[TEST_RDATA_MINFO2].wire_size,
-	                           sizeof(knot_dname_t *), test_dnames[2]->size,
+	                           sizeof(knot_dname_t *), knot_dname_size(test_dnames[2]),
 	                           sizeof(knot_dname_t *),
-	                           test_dnames[3]->size, test_dnames[3]);
+	                           knot_dname_size(test_dnames[3]), test_dnames[3]);
 }
 
 static void create_test_rrsets()
@@ -331,14 +327,14 @@ static void create_test_rrsets()
 		/* Create wire representation. */
 
 		/* Create header wire. */
-		test_rrset->header_wire_size = test_rrset->rrset.owner->size + 8;
+		test_rrset->header_wire_size = knot_dname_size(test_rrset->rrset.owner) + 8;
 		test_rrset->header_wire =
 			xmalloc(test_rrset->header_wire_size);
 		/* Copy owner wire to header wire. */
-		memcpy(test_rrset->header_wire, test_rrset->rrset.owner->name,
-		       test_rrset->rrset.owner->size);
+		memcpy(test_rrset->header_wire, test_rrset->rrset.owner,
+		       knot_dname_size(test_rrset->rrset.owner));
 		/* Copy type to wire. */
-		size_t offset = test_rrset->rrset.owner->size;
+		size_t offset = knot_dname_size(test_rrset->rrset.owner);
 		knot_wire_write_u16(test_rrset->header_wire + offset,
 		                    test_rrset->rrset.type);
 		offset += sizeof(uint16_t);
@@ -390,7 +386,7 @@ static int check_rrset_values(const knot_rrset_t *rrset,
 {
 	int errors = 0;
 
-	if (knot_dname_compare_non_canon(rrset->owner, dname)) {
+	if (!knot_dname_is_equal(rrset->owner, dname)) {
 		diag("Wrong DNAME in the created RRSet.\n");
 		++errors;
 	}
@@ -700,7 +696,7 @@ static int test_rrset_deep_copy()
 	for (int i = 0; i < TEST_RRSET_COUNT; i++) {
 		knot_rrset_t *rrset_copy = NULL;
 		knot_rrset_t *rrset = &test_rrset_array[i].rrset;
-		int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1);
+		int ret = knot_rrset_deep_copy(rrset, &rrset_copy);
 		if (ret != KNOT_EOK) {
 			diag("Could not copy RRSet.\n");
 			return 0;
@@ -830,10 +826,10 @@ static int test_rrset_merge()
 {
 	knot_rrset_t *merge_to;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE1].rrset,
-	                     &merge_to, 1);
+	                     &merge_to);
 	knot_rrset_t *merge_from;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE2].rrset,
-	                     &merge_from, 1);
+	                     &merge_from);
 	assert(merge_to);
 	assert(merge_from);
 	int ret = knot_rrset_merge(merge_to, merge_from);
@@ -890,17 +886,17 @@ static int test_rrset_merge()
 	return 1;
 }
 
-static int test_rrset_merge_no_dupl()
+static int test_rrset_merge_sort()
 {
 	/* Test that merge of two identical RRSets results in no-op. */
 	knot_rrset_t *merge_to = NULL;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE1].rrset,
-	                     &merge_to, 1);
+	                     &merge_to);
 	knot_rrset_t *merge_from = NULL;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE1].rrset,
-	                     &merge_from, 1);
+	                     &merge_from);
 	int merged, removed_rrs;
-	int ret = knot_rrset_merge_no_dupl(merge_to, merge_from, &merged, &removed_rrs);
+	int ret = knot_rrset_merge_sort(merge_to, merge_from, &merged, &removed_rrs);
 	if (ret != KNOT_EOK) {
 		diag("Merge of identical RRSets failed.\n");
 		return 0;
@@ -923,13 +919,13 @@ static int test_rrset_merge_no_dupl()
 
 	/* Merge normal, non-duplicated RRSets. */
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE1].rrset,
-	                     &merge_to, 1);
+	                     &merge_to);
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE2].rrset,
-	                     &merge_from, 1);
+	                     &merge_from);
 	assert(merge_to);
 	assert(merge_from);
 
-	ret = knot_rrset_merge_no_dupl(merge_to, merge_from, &merged,
+	ret = knot_rrset_merge_sort(merge_to, merge_from, &merged,
 	                               &removed_rrs);
 	if (ret != KNOT_EOK) {
 		diag("Merge of identical RRSets failed.\n");
@@ -955,13 +951,13 @@ static int test_rrset_merge_no_dupl()
 
 	/* Merge RRSets with both duplicated and unique RDATAs. */
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_UNIQUE1].rrset,
-	                     &merge_to, 1);
+	                     &merge_to);
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MERGE_RESULT1].rrset,
-	                     &merge_from, 1);
+	                     &merge_from);
 	assert(merge_to);
 	assert(merge_from);
 
-	ret = knot_rrset_merge_no_dupl(merge_to, merge_from, &merged,
+	ret = knot_rrset_merge_sort(merge_to, merge_from, &merged,
 	                               &removed_rrs);
 	if (ret != KNOT_EOK) {
 		diag("Merge of identical RRSets failed.\n");
@@ -1010,7 +1006,7 @@ static int test_rrset_equal()
 	/* Create equal RRSets. */
 	knot_rrset_t *rrs1 = NULL;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_A_GT].rrset,
-	                     &rrs1, 1);
+	                     &rrs1);
 	knot_rrset_t *rrs2 = &test_rrset_array[TEST_RRSET_A_GT].rrset;
 	/* Test header comparison. */
 	ret = knot_rrset_equal(rrs1, rrs2, KNOT_RRSET_COMPARE_HEADER);
@@ -1204,7 +1200,7 @@ static int test_rrset_next_dname()
 	knot_dname_t **dname = NULL;
 	int i = 0;
 	while ((dname = knot_rrset_get_next_dname(rrset, dname))) {
-		if (knot_dname_compare_non_canon(extracted_dnames[i], *dname)) {
+		if (!knot_dname_is_equal(extracted_dnames[i], *dname)) {
 			diag("Got wrong DNAME from RDATA. on index %d\n", i);
 			char *ext_name = knot_dname_to_str(extracted_dnames[i]);
 			char *act_name = knot_dname_to_str(*dname);
@@ -1227,7 +1223,7 @@ static int test_rrset_next_dname()
 	rrset = &test_rrset_array[TEST_RRSET_NS_LESS].rrset;
 	dname = NULL;
 	dname = knot_rrset_get_next_dname(rrset, dname);
-	if (dname == NULL || knot_dname_compare_non_canon(*dname, test_dnames[TEST_DNAME_GENERIC])) {
+	if (dname == NULL || !knot_dname_is_equal(*dname, test_dnames[TEST_DNAME_GENERIC])) {
 		diag("Got wrong DNAME from NS RDATA. Was %p, should be %p \n",
 		     dname ? *dname: NULL, test_dnames[TEST_DNAME_GENERIC]);
 		return 0;
@@ -1241,7 +1237,7 @@ static int test_rrset_next_dname()
 	rrset = &test_rrset_array[TEST_RRSET_MX_BIN_GT].rrset;
 	dname = NULL;
 	dname = knot_rrset_get_next_dname(rrset, dname);
-	if (dname == NULL || knot_dname_compare_non_canon(*dname, test_dnames[1])) {
+	if (dname == NULL || !knot_dname_is_equal(*dname, test_dnames[1])) {
 		diag("Got wrong DNAME from MX RDATA.\n");
 		return 0;
 	}
@@ -1254,7 +1250,7 @@ static int test_rrset_next_dname()
 	/* Try writes into DNAMEs you've gotten. */
 	rrset = NULL;
 	knot_rrset_deep_copy(&test_rrset_array[TEST_RRSET_MINFO_MULTIPLE1].rrset,
-	                     &rrset, 1);
+	                     &rrset);
 	dname = NULL;
 	i = 4;
 	while ((dname = knot_rrset_get_next_dname(rrset, dname))) {
@@ -1304,7 +1300,7 @@ static int test_rrset_find_pos()
 	                                          strlen((char *)mock_data));
 	memcpy(rdata, mock_data, strlen((char *)mock_data));
 	knot_rrset_t *rrset_find_in = NULL;
-	knot_rrset_deep_copy(rrset_source, &rrset_find_in, 1);
+	knot_rrset_deep_copy(rrset_source, &rrset_find_in);
 	rdata = knot_rrset_create_rdata(rrset_source, 10);
 	memcpy(rdata, mock_data ,10);
 	size_t rr_pos = 0;
@@ -1325,7 +1321,11 @@ static int test_rrset_find_pos()
 	/* Add second RR. */
 	knot_rrset_deep_free(&rrset_find_in, 1, 1);
 	knot_rrset_shallow_copy(rrset_source, &rrset_find_in);
-	knot_rrset_rdata_reset(rrset_find_in);
+	/* Reset RRSet. */
+	rrset_find_in->rdata = NULL;
+	rrset_find_in->rdata_indices = NULL;
+	rrset_find_in->rdata_count = 0;
+
 	rdata = knot_rrset_create_rdata(rrset_find_in, 10);
 	memcpy(rdata, mock_data ,10);
 	ret = knot_rrset_find_rr_pos(rrset_source, rrset_find_in, 0, &rr_pos);
@@ -1360,7 +1360,7 @@ static int test_rrset_remove_rr()
 	memcpy(rdata, mock_data ,10);
 	knot_rrset_t *rrset_dest = NULL;
 	/* Create copy. */
-	knot_rrset_deep_copy(rrset_source, &rrset_dest, 1);
+	knot_rrset_deep_copy(rrset_source, &rrset_dest);
 	rdata = knot_rrset_create_rdata(rrset_dest, 16);
 	memcpy(rdata, "foobarfoobarfoo", 16);
 	knot_rrset_t *returned_rr = NULL;
@@ -1448,8 +1448,8 @@ static int knot_rrset_tests_run(int argc, char *argv[])
 	ok(res, "rrset: merge");
 	res_final *= res;
 
-	res = test_rrset_merge_no_dupl();
-	ok(res, "rrset: merge no dupl");
+	res = test_rrset_merge_sort();
+	ok(res, "rrset: merge + sort");
 	res_final *= res;
 
 	res = test_rrset_next_dname();
@@ -1465,3 +1465,4 @@ static int knot_rrset_tests_run(int argc, char *argv[])
 
 	return res_final;
 }
+#endif
diff --git a/src/tests/libknot/ztree_tests.c b/src/tests/libknot/ztree_tests.c
index 53506af7b6530a3c8c85c015dda897d836f5cc60..0a9c9475e6736b31900f54c47cf42e6b3046a317 100644
--- a/src/tests/libknot/ztree_tests.c
+++ b/src/tests/libknot/ztree_tests.c
@@ -24,10 +24,10 @@ static knot_node_t NODE[NCOUNT];
 static knot_dname_t* ORDER[NCOUNT];
 static void ztree_init_data()
 {
-	NAME[0] = knot_dname_new_from_str(".", 1, NULL);
-	NAME[1] = knot_dname_new_from_str("master.ac.", 10, NULL);
-	NAME[2] = knot_dname_new_from_str("ac.", 3, NULL);
-	NAME[3] = knot_dname_new_from_str("ns.", 3, NULL);
+	NAME[0] = knot_dname_from_str(".", 1);
+	NAME[1] = knot_dname_from_str("master.ac.", 10);
+	NAME[2] = knot_dname_from_str("ac.", 3);
+	NAME[3] = knot_dname_from_str("ns.", 3);
 
 	knot_dname_t *order[NCOUNT] = {
 	        NAME[0], NAME[2], NAME[1], NAME[3]
@@ -37,7 +37,6 @@ static void ztree_init_data()
 	for (unsigned i = 0; i < NCOUNT; ++i) {
 		memset(NODE + i, 0, sizeof(knot_node_t));
 		NODE[i].owner = NAME[i];
-		NAME[i]->node = NODE + i;
 		NODE[i].prev = NODE + ((NCOUNT + i - 1) % NCOUNT);
 		NODE[i].rrset_count = 1; /* required for ordered search */
 	}
@@ -120,7 +119,7 @@ static int ztree_tests_run(int argc, char *argv[])
 	passed = 1;
 	node = NULL;
 	const knot_node_t *prev = NULL;
-	knot_dname_t *tmp_dn = knot_dname_new_from_str("z.ac.", 5, NULL);
+	knot_dname_t *tmp_dn = knot_dname_from_str("z.ac.", 5);
 	knot_zone_tree_find_less_or_equal(t, tmp_dn, &node, &prev);
 	knot_dname_free(&tmp_dn);
 	ok(prev == NODE + 1, "ztree: ordered lookup");
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
index 4b66bbc30563a292b6d095a6f580871cea669e9f..5513fc19d0874bc6068796d95ba2ef0312c12fec 100644
--- a/src/tests/unittests_main.c
+++ b/src/tests/unittests_main.c
@@ -32,11 +32,13 @@
 #include "tests/knot/server_tests.h"
 #include "tests/knot/conf_tests.h"
 #include "tests/knot/rrl_tests.h"
-#include "tests/zscanner/zscanner_tests.h"
 #include "tests/libknot/wire_tests.h"
 #include "tests/libknot/dname_tests.h"
 #include "tests/libknot/ztree_tests.h"
-#include "tests/libknot/sign_tests.h"
+#include "tests/libknot/dnssec_keys_tests.h"
+#include "tests/libknot/dnssec_nsec3_tests.h"
+#include "tests/libknot/dnssec_sign_tests.h"
+#include "tests/libknot/dnssec_zone_nsec_tests.h"
 #include "tests/libknot/rrset_tests.h"
 
 // Run all loaded units
@@ -67,15 +69,15 @@ int main(int argc, char *argv[])
 	        &server_tests_api,	//! Server unit
 	        &rrl_tests_api,		//! RRL tests
 
-	        /* Zone scanner. */
-	        &zscanner_tests_api,	//! Wrapper for external unittests
-
 	        /* Libknot library. */
 	        &wire_tests_api,
 	        &dname_tests_api,
 	        &ztree_tests_api,
-	        &sign_tests_api,	//! Key manipulation.
-	        &rrset_tests_api,
+	        &dnssec_keys_tests_api,  //! DNSSEC key manipulation.
+	        &dnssec_nsec3_tests_api, //! DNSSEC NSEC3 operations.
+	        &dnssec_sign_tests_api,  //! DNSSEC signing/verification.
+	        &dnssec_zone_nsec_tests_api, //! Zone NSEC functions.
+//	        &rrset_tests_api,
 
 	        NULL
 	};
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
index 0a9c1a9f2889575735a71b0cc734e28eb5fae070..8ed5006b653392bec08f99bf43e676a554107525 100644
--- a/src/utils/common/exec.c
+++ b/src/utils/common/exec.c
@@ -28,6 +28,7 @@
 #include "utils/common/msg.h"		// WARN
 #include "utils/common/params.h"	// params_t
 #include "utils/common/netio.h"		// send_msg
+#include "libknot/dnssec/sig0.h"
 
 static knot_lookup_table_t rtypes[] = {
 	{ KNOT_RRTYPE_A,      "has IPv4 address" },
@@ -101,8 +102,10 @@ static void print_header(const knot_packet_t *packet, const style_t *style)
 		       ";; Flags:%1s; "
 		       "ZONE: %u; PREREQ: %u; UPDATE: %u; ADDITIONAL: %u\n",
 		       opcode_str, rcode_str, knot_packet_id(packet),
-		       flags, packet->header.qdcount, packet->header.ancount,
-		       packet->header.nscount, packet->header.arcount);
+		       flags, knot_wire_get_qdcount(packet->wireformat),
+		       knot_wire_get_ancount(packet->wireformat),
+		       knot_wire_get_nscount(packet->wireformat),
+		       knot_wire_get_arcount(packet->wireformat));
 
 		break;
 	default:
@@ -110,8 +113,10 @@ static void print_header(const knot_packet_t *packet, const style_t *style)
 		       ";; Flags:%1s; "
 		       "QUERY: %u; ANSWER: %u; AUTHORITY: %u; ADDITIONAL: %u\n",
 		       opcode_str, rcode_str, knot_packet_id(packet),
-		       flags, packet->header.qdcount, packet->header.ancount,
-		       packet->header.nscount, packet->header.arcount);
+		       flags, knot_wire_get_qdcount(packet->wireformat),
+		       knot_wire_get_ancount(packet->wireformat),
+		       knot_wire_get_nscount(packet->wireformat),
+		       knot_wire_get_arcount(packet->wireformat));
 		break;
 	}
 }
@@ -197,7 +202,8 @@ static void print_section_question(const knot_dname_t *owner,
 	size_t buflen = 8192;
 	char   *buf = calloc(buflen, 1);
 
-	knot_rrset_t *question = knot_rrset_new((knot_dname_t *)owner, qtype,
+	knot_dname_t *owner_copy = knot_dname_copy(owner);
+	knot_rrset_t *question = knot_rrset_new(owner_copy, qtype,
 	                                        qclass, 0);
 
 	if (knot_rrset_txt_dump_header(question, 0, buf, buflen,
@@ -316,7 +322,7 @@ static void print_section_host(const knot_rrset_t **rrsets,
 }
 
 static void print_error_host(const uint8_t         code,
-                             const knot_question_t *question)
+                             const knot_packet_t   *packet)
 {
 	const char *rcode_str = "NULL";
 	char type[32] = "NULL";
@@ -324,12 +330,12 @@ static void print_error_host(const uint8_t         code,
 
 	knot_lookup_table_t *rcode;
 
-	owner = knot_dname_to_str(question->qname);
+	owner = knot_dname_to_str(knot_packet_qname(packet));
 	rcode = knot_lookup_by_id(knot_rcode_names, code);
 	if (rcode != NULL) {
 		rcode_str = rcode->name;
 	}
-	knot_rrtype_to_string(question->qtype, type, sizeof(type));
+	knot_rrtype_to_string(knot_packet_qtype(packet), type, sizeof(type));
 
 	if (code == KNOT_RCODE_NOERROR) {
 		printf("Host %s has no %s record\n", owner, type);
@@ -340,11 +346,10 @@ static void print_error_host(const uint8_t         code,
 	free(owner);
 }
 
-knot_packet_t* create_empty_packet(const knot_packet_prealloc_type_t type,
-                                   const size_t                      max_size)
+knot_packet_t* create_empty_packet(const size_t max_size)
 {
 	// Create packet skeleton.
-	knot_packet_t *packet = knot_packet_new(type);
+	knot_packet_t *packet = knot_packet_new();
 	if (packet == NULL) {
 		DBG_NULL;
 		return NULL;
@@ -362,7 +367,7 @@ knot_packet_t* create_empty_packet(const knot_packet_prealloc_type_t type,
 	return packet;
 }
 
-void print_header_xfr(const knot_question_t *question, const style_t  *style)
+void print_header_xfr(const knot_packet_t *packet, const style_t  *style)
 {
 	if (style == NULL) {
 		DBG_NULL;
@@ -371,7 +376,7 @@ void print_header_xfr(const knot_question_t *question, const style_t  *style)
 
 	char xfr[16] = "AXFR";
 
-	switch (question->qtype) {
+	switch (knot_packet_qtype(packet)) {
 	case KNOT_RRTYPE_AXFR:
 		break;
 	case KNOT_RRTYPE_IXFR:
@@ -382,7 +387,7 @@ void print_header_xfr(const knot_question_t *question, const style_t  *style)
 	}
 
 	if (style->show_header) {
-		char *owner = knot_dname_to_str(question->qname);
+		char *owner = knot_dname_to_str(knot_packet_qname(packet));
 		if (owner != NULL) {
 			printf("\n;; %s for %s\n", xfr, owner);
 			free(owner);
@@ -398,20 +403,21 @@ void print_data_xfr(const knot_packet_t *packet,
 		return;
 	}
 
+	uint16_t ancount = knot_wire_get_ancount(packet->wireformat);
 	switch (style->format) {
 	case FORMAT_DIG:
-		print_section_dig(packet->answer, packet->header.ancount,style);
+		print_section_dig(packet->answer, ancount,style);
 		break;
 	case FORMAT_HOST:
-		print_section_host(packet->answer, packet->header.ancount, style);
+		print_section_host(packet->answer, ancount, style);
 		break;
 	case FORMAT_FULL:
-		print_section_full(packet->answer, packet->header.ancount, style);
+		print_section_full(packet->answer, ancount, style);
 
 		// Print TSIG record if any.
 		if (style->show_additional) {
 			print_section_full(packet->additional,
-			                   packet->header.arcount,
+			                   knot_wire_get_arcount(packet->wireformat),
 			                   style);
 		}
 		break;
@@ -449,7 +455,11 @@ void print_packet(const knot_packet_t *packet,
 		return;
 	}
 
-	uint16_t additionals = packet->header.arcount;
+	uint8_t rcode = knot_wire_get_rcode(packet->wireformat);
+	uint16_t qdcount = knot_wire_get_qdcount(packet->wireformat);
+	uint16_t ancount = knot_wire_get_ancount(packet->wireformat);
+	uint16_t arcount = knot_wire_get_arcount(packet->wireformat);
+	uint16_t nscount = knot_wire_get_nscount(packet->wireformat);
 
 	// Print packet information header.
 	if (style->show_header) {
@@ -463,83 +473,82 @@ void print_packet(const knot_packet_t *packet,
 			print_opt_section(&packet->opt_rr);
 		}
 
-		additionals--;
+		arcount--;
 	}
 
 	// Print DNS sections.
 	switch (style->format) {
 	case FORMAT_DIG:
-		if (packet->header.ancount > 0) {
-			print_section_dig(packet->answer, packet->header.ancount,
+		if (ancount > 0) {
+			print_section_dig(packet->answer, ancount,
 			                  style);
 		}
 		break;
 	case FORMAT_HOST:
-		if (packet->header.ancount > 0) {
-			print_section_host(packet->answer, packet->header.ancount,
+		if (ancount > 0) {
+			print_section_host(packet->answer, ancount,
 			                   style);
 		} else {
-			uint8_t rcode = knot_wire_get_rcode(packet->wireformat);
-			print_error_host(rcode, &packet->question);
+			print_error_host(rcode, packet);
 		}
 		break;
 	case FORMAT_NSUPDATE:
-		if (style->show_question && packet->header.qdcount > 0) {
+		if (style->show_question && qdcount > 0) {
 			printf("\n;; ZONE SECTION:\n;; ");
-			print_section_question(packet->question.qname,
-			                       packet->question.qclass,
-			                       packet->question.qtype,
+			print_section_question(knot_packet_qname(packet),
+			                       knot_packet_qclass(packet),
+			                       knot_packet_qtype(packet),
 			                       style);
 		}
 
-		if (style->show_answer && packet->header.ancount > 0) {
+		if (style->show_answer && ancount > 0) {
 			printf("\n;; PREREQUISITE SECTION:\n");
 			print_section_full(packet->answer,
-			                   packet->header.ancount,
+			                   ancount,
 			                   style);
 		}
 
-		if (style->show_authority && packet->header.nscount > 0) {
+		if (style->show_authority && nscount > 0) {
 			printf("\n;; UPDATE SECTION:\n");
 			print_section_full(packet->authority,
-			                   packet->header.nscount,
+			                   nscount,
 			                   style);
 		}
 
-		if (style->show_additional && additionals > 0) {
+		if (style->show_additional && arcount > 0) {
 			printf("\n;; ADDITIONAL DATA:\n");
 			print_section_full(packet->additional,
-			                   packet->header.arcount,
+			                   arcount,
 			                   style);
 		}
 		break;
 	case FORMAT_FULL:
-		if (style->show_question && packet->header.qdcount > 0) {
+		if (style->show_question && qdcount > 0) {
 			printf("\n;; QUESTION SECTION:\n;; ");
-			print_section_question(packet->question.qname,
-			                       packet->question.qclass,
-			                       packet->question.qtype,
+			print_section_question(knot_packet_qname(packet),
+			                       knot_packet_qclass(packet),
+			                       knot_packet_qtype(packet),
 			                       style);
 		}
 
-		if (style->show_answer && packet->header.ancount > 0) {
+		if (style->show_answer && ancount > 0) {
 			printf("\n;; ANSWER SECTION:\n");
 			print_section_full(packet->answer,
-			                   packet->header.ancount,
+			                   ancount,
 			                   style);
 		}
 
-		if (style->show_authority && packet->header.nscount > 0) {
+		if (style->show_authority && nscount > 0) {
 			printf("\n;; AUTHORITY SECTION:\n");
 			print_section_full(packet->authority,
-			                   packet->header.nscount,
+			                   nscount,
 			                   style);
 		}
 
-		if (style->show_additional && additionals > 0) {
+		if (style->show_additional && arcount > 0) {
 			printf("\n;; ADDITIONAL SECTION:\n");
 				print_section_full(packet->additional,
-				                   packet->header.arcount,
+				                   arcount,
 				                   style);
 		}
 		break;
diff --git a/src/utils/common/exec.h b/src/utils/common/exec.h
index 63189e12527d0ba8b2532625b6628d486c4209ed..bee17881eecb96ee5b74fdfa42850cba7b6fcaf9 100644
--- a/src/utils/common/exec.h
+++ b/src/utils/common/exec.h
@@ -30,6 +30,8 @@
 #include "utils/common/netio.h"		// net_t
 #include "utils/common/params.h"	// style_t
 #include "libknot/libknot.h"
+#include "libknot/dnssec/key.h"
+#include "libknot/dnssec/sign.h"
 
 /*! \brief Holds data required between signing and signature verification. */
 typedef struct {
@@ -42,22 +44,20 @@ typedef struct {
 /*!
  * \brief Allocates empty packet and sets packet size and random id.
  *
- * \param type		Packet preallocation type.
  * \param max_size	Maximal packet size.
  *
  * \retval packet	if success.
  * \retval NULL		if error.
  */
-knot_packet_t* create_empty_packet(const knot_packet_prealloc_type_t type,
-                                   const size_t                      max_size);
+knot_packet_t* create_empty_packet(const size_t max_size);
 
 /*!
  * \brief Prints information header for transfer.
  *
- * \param question	Packet question section.
+ * \param packet	Parsed packet.
  * \param style		Style of the output.
  */
-void print_header_xfr(const knot_question_t *question, const style_t *style);
+void print_header_xfr(const knot_packet_t *packet, const style_t *style);
 
 /*!
  * \brief Prints answer section for 1 transfer message.
diff --git a/src/utils/common/netio.h b/src/utils/common/netio.h
index ac0e98d0a9f93b698c1848233c8c948bcc3d857a..aaf79f62b495275131a65bc0f7a5aad8cdf1c5d1 100644
--- a/src/utils/common/netio.h
+++ b/src/utils/common/netio.h
@@ -36,7 +36,7 @@
 /*! \brief Structure containing server information. */
 typedef struct {
 	/*! List node (for list container). */
-	node	n;
+	node_t	n;
 	/*! Name or address of the server. */
 	char	*name;
 	/*! Name or number of the service. */
diff --git a/src/utils/common/params.c b/src/utils/common/params.c
index a8923f1570227d927b35a23653b7b313ed7335d1..65e1e348d4047ac4acec87b1c7f2973acf6a7987 100644
--- a/src/utils/common/params.c
+++ b/src/utils/common/params.c
@@ -189,7 +189,7 @@ int params_parse_type(const char *value, uint16_t *rtype, uint32_t *xfr_serial)
 	return KNOT_EOK;
 }
 
-int params_parse_server(const char *value, list *servers, const char *def_port)
+int params_parse_server(const char *value, list_t *servers, const char *def_port)
 {
 	if (value == NULL || servers == NULL) {
 		DBG_NULL;
@@ -202,7 +202,7 @@ int params_parse_server(const char *value, list *servers, const char *def_port)
 		ERR("bad nameserver %s\n", value);
 		return KNOT_EINVAL;
 	}
-	add_tail(servers, (node *)server);
+	add_tail(servers, (node_t *)server);
 
 	return KNOT_EOK;
 }
@@ -344,9 +344,13 @@ int params_parse_tsig(const char *value, knot_key_params_t *key_params)
 	}
 
 	/* Set key name and secret. */
-	key_params->name = knot_dname_new_from_nonfqdn_str(k, strlen(k), NULL);
+	key_params->name = knot_dname_from_str(k, strlen(k));
 	knot_dname_to_lower(key_params->name);
-	key_params->secret = strdup(s);
+	int r = knot_binary_from_base64(s, &key_params->secret);
+	if (r != KNOT_EOK) {
+		free(h);
+		return r;
+	}
 
 	DBG("%s: parsed name '%s'\n", __func__, k);
 	DBG("%s: parsed secret '%s'\n", __func__, s);
diff --git a/src/utils/common/params.h b/src/utils/common/params.h
index 05539adbad2cf4048cdb32147f27349bd41888b1..b081560c34ae27997418fb49e2608c05a28a539f 100644
--- a/src/utils/common/params.h
+++ b/src/utils/common/params.h
@@ -102,7 +102,7 @@ int params_parse_class(const char *value, uint16_t *rclass);
 
 int params_parse_type(const char *value, uint16_t *rtype, uint32_t *xfr_serial);
 
-int params_parse_server(const char *value, list *servers, const char *def_port);
+int params_parse_server(const char *value, list_t *servers, const char *def_port);
 
 int params_parse_wait(const char *value, int32_t *dst);
 
diff --git a/src/utils/common/resolv.c b/src/utils/common/resolv.c
index 864a2a9838ffd0802d7be99ac96f2d706be1779d..fb4eedfc20024c89747d2ba5737012eb1ef4aea5 100644
--- a/src/utils/common/resolv.c
+++ b/src/utils/common/resolv.c
@@ -85,7 +85,7 @@ server_t* parse_nameserver(const char *nameserver, const char *def_port)
 	}
 }
 
-static int get_resolv_nameservers(list *servers, const char *def_port)
+static int get_resolv_nameservers(list_t *servers, const char *def_port)
 {
 	char	line[512];
 
@@ -139,7 +139,7 @@ static int get_resolv_nameservers(list *servers, const char *def_port)
 
 			// If value is correct, add nameserver to the list.
 			if (server != NULL) {
-				add_tail(servers, (node *)server);
+				add_tail(servers, (node_t *)server);
 			}
 		}
 
@@ -154,7 +154,7 @@ static int get_resolv_nameservers(list *servers, const char *def_port)
 	return list_size(servers);
 }
 
-int get_nameservers(list *servers, const char *def_port)
+int get_nameservers(list_t *servers, const char *def_port)
 {
 	if (servers == NULL || def_port == NULL) {
 		DBG_NULL;
@@ -178,14 +178,14 @@ int get_nameservers(list *servers, const char *def_port)
 		server = server_create(DEFAULT_IPV6_NAME, def_port);
 
 		if (server != NULL) {
-			add_tail(servers, (node *)server);
+			add_tail(servers, (node_t *)server);
 		}
 
 		// Add default ipv4 nameservers.
 		server = server_create(DEFAULT_IPV4_NAME, def_port);
 
 		if (server != NULL) {
-			add_tail(servers, (node *)server);
+			add_tail(servers, (node_t *)server);
 		}
 
 		return list_size(servers);
diff --git a/src/utils/common/resolv.h b/src/utils/common/resolv.h
index 8a240df05e07878fb87bbdb5c478f7a9465db985..adba394c28d305c0fd49d81cd09a51e1629b7a57 100644
--- a/src/utils/common/resolv.h
+++ b/src/utils/common/resolv.h
@@ -32,7 +32,7 @@
 
 server_t* parse_nameserver(const char *nameserver, const char *def_port);
 
-int get_nameservers(list *servers, const char *def_port);
+int get_nameservers(list_t *servers, const char *def_port);
 
 #endif // _UTILS__RESOLV_H_
 
diff --git a/src/utils/dig/dig_exec.c b/src/utils/dig/dig_exec.c
index 95185fc95de47b8b98b634bfe959cb3cfe978023..6b78633920448e3baa588f6fcae905d8d2ab6b81 100644
--- a/src/utils/dig/dig_exec.c
+++ b/src/utils/dig/dig_exec.c
@@ -35,7 +35,6 @@ static knot_packet_t* create_query_packet(const query_t *query,
                                           uint8_t       **data,
                                           size_t        *data_len)
 {
-	knot_question_t q;
 	knot_packet_t   *packet;
 
 	// Set packet buffer size.
@@ -52,7 +51,7 @@ static knot_packet_t* create_query_packet(const query_t *query,
 	}
 
 	// Create packet skeleton.
-	packet = create_empty_packet(KNOT_PACKET_PREALLOC_NONE, max_size);
+	packet = create_empty_packet(max_size);
 	if (packet == NULL) {
 		return NULL;
 	}
@@ -80,20 +79,19 @@ static knot_packet_t* create_query_packet(const query_t *query,
 		knot_wire_set_cd(packet->wireformat);
 	}
 
-	// Fill auxiliary question structure.
-	q.qclass = query->class_num;
-	q.qtype = query->type_num;
-	q.qname = knot_dname_new_from_str(query->owner, strlen(query->owner), 0);
-	if (q.qname == NULL) {
+	// Create QNAME from string.
+	knot_dname_t *qname = knot_dname_from_str(query->owner,
+	                                          strlen(query->owner));
+	if (qname == NULL) {
 		knot_packet_free(&packet);
 		return NULL;
 	}
 
 	// Set packet question.
-	if (knot_query_set_question(packet, &q) != KNOT_EOK) {
-		// It's necessary to release q.qname by hand as it isn't
-		// connected with the packet yet.
-		knot_dname_release(q.qname);
+	int ret = knot_query_set_question(packet, qname,
+	                                  query->class_num, query->type_num);
+	if (ret != KNOT_EOK) {
+		knot_dname_free(&qname);
 		knot_packet_free(&packet);
 		return NULL;
 	}
@@ -103,14 +101,14 @@ static knot_packet_t* create_query_packet(const query_t *query,
 		// SOA rdata in wireformat.
 		uint8_t wire[22] = { 0x0 };
 		size_t  pos = 0;
-		int     ret;
 
 		// Create rrset with SOA record.
-		knot_rrset_t *soa = knot_rrset_new(q.qname,
+		knot_rrset_t *soa = knot_rrset_new(qname,
 		                                   KNOT_RRTYPE_SOA,
 		                                   query->class_num,
 		                                   0);
 		if (soa == NULL) {
+			knot_dname_free(&qname);
 			knot_packet_free(&packet);
 			return NULL;
 		}
@@ -119,21 +117,23 @@ static knot_packet_t* create_query_packet(const query_t *query,
 		ret = knot_rrset_rdata_from_wire_one(soa, wire, &pos,
 		                                    sizeof(wire), sizeof(wire));
 		if (ret != KNOT_EOK) {
-			free(soa);
+			knot_rrset_deep_free(&soa, 1);
 			knot_packet_free(&packet);
 			return NULL;
 		}
 
 		// Set SOA serial.
-		knot_rrset_rdata_soa_serial_set(soa, query->xfr_serial);
+		knot_rdata_soa_serial_set(soa, query->xfr_serial);
 
 		// Add authority section.
 		ret = knot_query_add_rrset_authority(packet, soa);
 		if (ret != KNOT_EOK) {
-			free(soa);
+			knot_rrset_deep_free(&soa, 1);
 			knot_packet_free(&packet);
 			return NULL;
 		}
+	} else {
+		knot_dname_free(&qname);
 	}
 
 	// Create EDNS section if required.
@@ -217,16 +217,16 @@ static bool check_reply_id(const knot_packet_t *reply,
 static void check_reply_question(const knot_packet_t *reply,
                                  const knot_packet_t *query)
 {
-	if (reply->header.qdcount < 1) {
+	if (knot_wire_get_qdcount(reply->wireformat) < 1) {
 		WARN("response doesn't have question section\n");
 		return;
 	}
 
-	int name_diff = knot_dname_compare_cs(reply->question.qname,
-	                                      query->question.qname);
+	int name_diff = knot_dname_cmp(knot_packet_qname(reply),
+	                               knot_packet_qname(query));
 
-	if (reply->question.qclass != query->question.qclass ||
-	    reply->question.qtype  != query->question.qtype ||
+	if (knot_packet_qclass(reply) != knot_packet_qclass(query) ||
+	    knot_packet_qtype(reply)  != knot_packet_qtype(query) ||
 	    name_diff != 0) {
 		WARN("query/response question sections are different\n");
 		return;
@@ -235,7 +235,7 @@ static void check_reply_question(const knot_packet_t *reply,
 
 static int64_t first_serial_check(const knot_packet_t *reply)
 {
-	if (reply->header.ancount <= 0) {
+	if (knot_wire_get_ancount(reply->wireformat) <= 0) {
 		return -1;
 	}
 
@@ -244,22 +244,22 @@ static int64_t first_serial_check(const knot_packet_t *reply)
 	if (first->type != KNOT_RRTYPE_SOA) {
 		return -1;
 	} else {
-		return knot_rrset_rdata_soa_serial(first);
+		return knot_rdata_soa_serial(first);
 	}
 }
 
 static bool last_serial_check(const uint32_t serial, const knot_packet_t *reply)
 {
-	if (reply->header.ancount <= 0) {
+	if (knot_wire_get_ancount(reply->wireformat) <= 0) {
 		return false;
 	}
 
-	const knot_rrset_t *last = *(reply->answer + reply->header.ancount - 1);
+	const knot_rrset_t *last = *(reply->answer + knot_wire_get_ancount(reply->wireformat) - 1);
 
 	if (last->type != KNOT_RRTYPE_SOA) {
 		return false;
 	} else {
-		int64_t last_serial = knot_rrset_rdata_soa_serial(last);
+		int64_t last_serial = knot_rdata_soa_serial(last);
 
 		if (last_serial == serial) {
 			return true;
@@ -321,7 +321,7 @@ static int process_query_packet(const knot_packet_t     *query,
 		gettimeofday(&t_end, NULL);
 
 		// Create reply packet structure to fill up.
-		reply = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+		reply = knot_packet_new();
 		if (reply == NULL) {
 			net_close(net);
 			return -1;
@@ -398,7 +398,7 @@ static int process_query_packet(const knot_packet_t     *query,
 
 static void process_query(const query_t *query)
 {
-	node          *server = NULL;
+	node_t        *server = NULL;
 	knot_packet_t *out_packet;
 	uint8_t       *out = NULL;
 	size_t        out_len = 0;
@@ -527,7 +527,7 @@ static int process_packet_xfr(const knot_packet_t     *query,
 	}
 
 	// Print leading transfer information.
-	print_header_xfr(&query->question, style);
+	print_header_xfr(query, style);
 
 	// Loop over reply messages unless first and last SOA serials differ.
 	while (true) {
@@ -539,7 +539,7 @@ static int process_packet_xfr(const knot_packet_t     *query,
 		}
 
 		// Create reply packet structure to fill up.
-		reply = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+		reply = knot_packet_new();
 		if (reply == NULL) {
 			net_close(net);
 			return -1;
@@ -609,7 +609,7 @@ static int process_packet_xfr(const knot_packet_t     *query,
 		}
 
 		msg_count++;
-		rr_count += reply->header.ancount;
+		rr_count += knot_wire_get_ancount(reply->wireformat);
 		total_len += in_len;
 
 		// Print reply packet.
@@ -702,7 +702,7 @@ static void process_query_xfr(const query_t *query)
 
 int dig_exec(const dig_params_t *params)
 {
-	node *n = NULL;
+	node_t *n = NULL;
 
 	if (params == NULL) {
 		DBG_NULL;
diff --git a/src/utils/dig/dig_params.c b/src/utils/dig/dig_params.c
index d3b3d718000d9254435c7667417ecc3a997a8de6..6f0335c5504759fe1408ab9c858229efc34dacba 100644
--- a/src/utils/dig/dig_params.c
+++ b/src/utils/dig/dig_params.c
@@ -142,7 +142,7 @@ query_t* query_create(const char *owner, const query_t *conf)
 
 void query_free(query_t *query)
 {
-	node *n = NULL, *nxt = NULL;
+	node_t *n = NULL, *nxt = NULL;
 
 	if (query == NULL) {
 		DBG_NULL;
@@ -192,7 +192,7 @@ int dig_init(dig_params_t *params)
 
 void dig_clean(dig_params_t *params)
 {
-	node *n = NULL, *nxt = NULL;
+	node_t *n = NULL, *nxt = NULL;
 
 	if (params == NULL) {
 		DBG_NULL;
@@ -251,7 +251,7 @@ static int parse_local(const char *value, query_t *query)
 	return KNOT_EOK;
 }
 
-static int parse_name(const char *value, list *queries, const query_t *conf)
+static int parse_name(const char *value, list_t *queries, const query_t *conf)
 {
 	query_t *query = NULL;
 
@@ -268,7 +268,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 	}
 
 	// Add new query to the queries.
-	add_tail(queries, (node *)query);
+	add_tail(queries, (node_t *)query);
 
 	return KNOT_EOK;
 }
@@ -299,7 +299,7 @@ static int parse_port(const char *value, query_t *query)
 	return KNOT_EOK;
 }
 
-static int parse_reverse(const char *value, list *queries, const query_t *conf)
+static int parse_reverse(const char *value, list_t *queries, const query_t *conf)
 {
 	query_t *query = NULL;
 
@@ -323,7 +323,7 @@ static int parse_reverse(const char *value, list *queries, const query_t *conf)
 	query->type_num = KNOT_RRTYPE_PTR;
 
 	// Add new query to the queries.
-	add_tail(queries, (node *)query);
+	add_tail(queries, (node_t *)query);
 
 	return KNOT_EOK;
 }
@@ -370,7 +370,7 @@ static int parse_type(const char *value, query_t *query)
 
 static void complete_servers(query_t *query, const query_t *conf)
 {
-	node *n = NULL;
+	node_t *n = NULL;
 	char *def_port;
 
 	// Decide which default port use.
@@ -414,7 +414,7 @@ static void complete_servers(query_t *query, const query_t *conf)
 				     s->name, s->service);
 				return;
 			}
-			add_tail(&query->servers, (node *)server);
+			add_tail(&query->servers, (node_t *)server);
 		}
 	// Use system specific.
 	} else if (get_nameservers(&query->servers, def_port) <= 0) {
@@ -422,10 +422,10 @@ static void complete_servers(query_t *query, const query_t *conf)
 	}
 }
 
-void complete_queries(list *queries, const query_t *conf)
+void complete_queries(list_t *queries, const query_t *conf)
 {
 	query_t *q = NULL;
-	node    *n = NULL;
+	node_t  *n = NULL;
 
 	if (queries == NULL || conf == NULL) {
 		DBG_NULL;
@@ -441,7 +441,7 @@ void complete_queries(list *queries, const query_t *conf)
 		}
 		q->class_num = KNOT_CLASS_IN;
 		q->type_num = KNOT_RRTYPE_NS;
-		add_tail(queries, (node *)q);
+		add_tail(queries, (node_t *)q);
 	}
 
 	WALK_LIST(n, *queries) {
diff --git a/src/utils/dig/dig_params.h b/src/utils/dig/dig_params.h
index f2258ac1adeb09cf05474ad26c187363fa1cec45..a320f0808fe00c8988e7cd8823a60f8552cb6be5 100644
--- a/src/utils/dig/dig_params.h
+++ b/src/utils/dig/dig_params.h
@@ -67,11 +67,11 @@ typedef struct {
 /*! \brief Basic parameters for DNS query. */
 typedef struct {
 	/*!< List node (for list container). */
-	node		n;
+	node_t		n;
 	/*!< Name to query on. */
 	char		*owner;
 	/*!< List of nameservers to query to. */
-	list		servers;
+	list_t		servers;
 	/*!< Local interface (optional). */
 	server_t	*local;
 	/*!< Operation mode. */
@@ -115,14 +115,14 @@ typedef struct {
 	/*!< Stop processing - just pring help, version,... */
 	bool	stop;
 	/*!< List of DNS queries to process. */
-	list	queries;
+	list_t	queries;
 	/*!< Default settings for queries. */
 	query_t	*config;
 } dig_params_t;
 
 query_t* query_create(const char *owner, const query_t *config);
 void query_free(query_t *query);
-void complete_queries(list *queries, const query_t *conf);
+void complete_queries(list_t *queries, const query_t *conf);
 
 int dig_init(dig_params_t *params);
 int dig_parse(dig_params_t *params, int argc, char *argv[]);
diff --git a/src/utils/host/host_params.c b/src/utils/host/host_params.c
index 66fe67a9630e4401fa2718489125cf0dcdfd4e1f..22b9b5f3f52f7324e9aa121af9106e73d7598b50 100644
--- a/src/utils/host/host_params.c
+++ b/src/utils/host/host_params.c
@@ -84,7 +84,7 @@ void host_clean(dig_params_t *params)
 	dig_clean(params);
 }
 
-static int parse_name(const char *value, list *queries, const query_t *conf)
+static int parse_name(const char *value, list_t *queries, const query_t *conf)
 {
 	char	*reverse = get_reverse_name(value);
 	char	*fqd_name = NULL;
@@ -110,7 +110,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 			if (query == NULL) {
 				return KNOT_ENOMEM;
 			}
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 		} else {
 			free(reverse);
 
@@ -120,7 +120,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 			if (query == NULL) {
 				return KNOT_ENOMEM;
 			}
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 		}
 	// RR type is unknown, use defaults.
 	} else {
@@ -132,7 +132,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 				return KNOT_ENOMEM;
 			}
 			query->type_num = KNOT_RRTYPE_A;
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 
 			// Add query for name and type AAAA.
 			query = query_create(fqd_name, conf);
@@ -141,7 +141,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 				return KNOT_ENOMEM;
 			}
 			query->type_num = KNOT_RRTYPE_AAAA;
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 
 			// Add query for name and type MX.
 			query = query_create(fqd_name, conf);
@@ -151,7 +151,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 			}
 			free(fqd_name);
 			query->type_num = KNOT_RRTYPE_MX;
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 		} else {
 			free(fqd_name);
 
@@ -162,7 +162,7 @@ static int parse_name(const char *value, list *queries, const query_t *conf)
 				return KNOT_ENOMEM;
 			}
 			query->type_num = KNOT_RRTYPE_PTR;
-			add_tail(queries, (node *)query);
+			add_tail(queries, (node_t *)query);
 		}
 	}
 
diff --git a/src/utils/nsec3hash/nsec3hash_main.c b/src/utils/nsec3hash/nsec3hash_main.c
new file mode 100644
index 0000000000000000000000000000000000000000..e8a45caa73eb54408d2819cf4440f490c45052e5
--- /dev/null
+++ b/src/utils/nsec3hash/nsec3hash_main.c
@@ -0,0 +1,140 @@
+/*  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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "common/base32hex.h"
+#include "common/errcode.h"
+#include "common/hex.h"
+#include "common/strtonum.h"
+#include "libknot/dnssec/nsec3.h"
+
+#define PROGRAM_NAME "knsec3hash"
+
+/*!
+ * \brief Print program usage (and example).
+ */
+static void usage(void)
+{
+	fprintf(stderr, "usage:   " PROGRAM_NAME " "
+	                "<salt> <algorithm> <iterations> <domain-name>\n");
+	fprintf(stderr, "example: " PROGRAM_NAME " "
+	                "c01dcafe 1 10 knot-dns.cz\n");
+}
+
+/*!
+ * \brief Parse NSEC3 parameters and fill structure with NSEC3 parameters.
+ */
+static bool parse_nsec3_params(knot_nsec3_params_t *params, const char *salt,
+			       const char *algorithm, const char *iterations)
+{
+	int result;
+
+	result = knot_str2uint8t(algorithm, &params->algorithm);
+	if (result != KNOT_EOK) {
+		fprintf(stderr, "Could not parse algorithm number.\n");
+		return false;
+	}
+
+	result = knot_str2uint16t(iterations, &params->iterations);
+	if (result != KNOT_EOK) {
+		fprintf(stderr, "Could not parse iteration count.\n");
+		return false;
+	}
+
+	size_t salt_length;
+	result = hex_decode(salt, &params->salt, &salt_length);
+	if (result != KNOT_EOK) {
+		fprintf(stderr, "Could not parse hex encoded salt.\n");
+		return false;
+	}
+
+	if (salt_length > UINT8_MAX) {
+		fprintf(stderr, "Decoded salt is longer than %d bytes.\n",
+		        UINT8_MAX);
+		free(params->salt);
+		memset(params, '\0', sizeof(*params));
+		return false;
+	}
+
+	params->salt_length = (uint8_t)salt_length;
+
+	return true;
+}
+
+/*!
+ * \brief Entry point of 'knsec3hash'.
+ */
+int main(int argc, const char *argv[])
+{
+	int exit_code = 1;
+	knot_nsec3_params_t nsec3_params = { 0 };
+	knot_dname_t *dname = NULL;
+	uint8_t *digest = NULL;
+	size_t digest_size = 0;
+	uint8_t *b32_digest = NULL;
+	int32_t b32_length = 0;
+	int result = 0;
+
+	// knsec3hash <salt> <algorithm> <iterations> <domain>
+
+	if (argc != 5) {
+		usage();
+		goto fail;
+	}
+
+	if (!parse_nsec3_params(&nsec3_params, argv[1], argv[2], argv[3])) {
+		goto fail;
+	}
+
+	dname = knot_dname_from_str(argv[4], strlen(argv[4]));
+	if (dname == NULL) {
+		fprintf(stderr, "Cannot parse domain name.\n");
+		goto fail;
+	}
+
+	result = knot_nsec3_hash(&nsec3_params, dname, knot_dname_size(dname),
+	                         &digest, &digest_size);
+	if (result != KNOT_EOK) {
+		fprintf(stderr, "Cannot compute hash: %s\n",
+		        knot_strerror(result));
+		goto fail;
+	}
+
+	b32_length = base32hex_encode_alloc(digest, digest_size, &b32_digest);
+	if (b32_length < 0) {
+		fprintf(stderr, "Cannot encode computed hash: %s\n",
+		        knot_strerror(b32_length));
+		goto fail;
+	}
+
+	exit_code = 0;
+
+	printf("%.*s (salt=%s, hash=%d, iterations=%d)\n", b32_length,
+	       b32_digest, argv[1], nsec3_params.algorithm,
+	       nsec3_params.iterations);
+
+fail:
+
+	knot_nsec3_params_free(&nsec3_params);
+	knot_dname_free(&dname);
+	free(digest);
+	free(b32_digest);
+
+	return exit_code;
+}
diff --git a/src/utils/nsupdate/nsupdate_exec.c b/src/utils/nsupdate/nsupdate_exec.c
index 705e7c01ecdb825b4c76c19eb743f745390896f9..c3e126fb1fa25a4d8dd289f005c16de6f0c888c0 100644
--- a/src/utils/nsupdate/nsupdate_exec.c
+++ b/src/utils/nsupdate/nsupdate_exec.c
@@ -128,7 +128,7 @@ enum {
 };
 
 static int dname_isvalid(const char *lp, size_t len) {
-	knot_dname_t *dn = knot_dname_new_from_str(lp, len, NULL);
+	knot_dname_t *dn = knot_dname_from_str(lp, len);
 	if (dn == NULL) {
 		return 0;
 	}
@@ -164,26 +164,13 @@ static int parse_partial_rr(scanner_t *s, const char *lp, unsigned flags) {
 
 	/* Extract owner. */
 	size_t len = strcspn(lp, SEP_CHARS);
-	knot_dname_t *owner = knot_dname_new_from_str(lp, len, NULL);
+	knot_dname_t *owner = knot_dname_from_str(lp, len);
 	if (owner == NULL) {
 		return KNOT_EPARSEFAIL;
 	}
 
-	/* ISC nsupdate doesn't do this, but it seems right to me. */
-	if (!knot_dname_is_fqdn(owner)) {
-		knot_dname_t* suf = knot_dname_new_from_wire(s->zone_origin,
-		                                             s->zone_origin_length,
-		                                             NULL);
-		if (suf == NULL) {
-			knot_dname_free(&owner);
-			return KNOT_ENOMEM;
-		}
-		knot_dname_cat(owner, suf);
-		knot_dname_free(&suf);
-	}
-
 	s->r_owner_length = knot_dname_size(owner);
-	memcpy(s->r_owner, knot_dname_name(owner), s->r_owner_length);
+	memcpy(s->r_owner, owner, s->r_owner_length);
 	lp = tok_skipspace(lp + len);
 
 	/* Initialize */
@@ -301,33 +288,31 @@ static int pkt_append(nsupdate_params_t *p, int sect)
 {
 	/* Check packet state first. */
 	int ret = KNOT_EOK;
+	knot_dname_t * qname = NULL;
 	scanner_t *s = p->rrp;
 	if (!p->pkt) {
-		p->pkt = create_empty_packet(KNOT_PACKET_PREALLOC_RESPONSE,
-		                             MAX_PACKET_SIZE);
-		knot_question_t q;
-		q.qclass = p->class_num;
-		q.qtype = p->type_num;
-		q.qname = knot_dname_new_from_nonfqdn_str(p->zone, strlen(p->zone), NULL);
-		ret = knot_query_set_question(p->pkt, &q);
-		if (ret != KNOT_EOK) {
+		p->pkt = create_empty_packet(MAX_PACKET_SIZE);
+		qname = knot_dname_from_str(p->zone, strlen(p->zone));
+		ret = knot_query_set_question(p->pkt, qname, p->class_num, p->type_num);
+		knot_dname_free(&qname);
+		if (ret != KNOT_EOK)
 			return ret;
-		}
+
 		knot_query_set_opcode(p->pkt, KNOT_OPCODE_UPDATE);
 	}
 
 	/* Form a rrset. */
-	knot_dname_t *o = knot_dname_new_from_wire(s->r_owner, s->r_owner_length, NULL);
+	knot_dname_t *o = knot_dname_copy(s->r_owner);
 	if (!o) {
 		DBG("%s: failed to create dname - %s\n",
 		    __func__, knot_strerror(ret));
 		return KNOT_ENOMEM;
 	}
 	knot_rrset_t *rr = knot_rrset_new(o, s->r_type, s->r_class, s->r_ttl);
-	knot_dname_release(o);
 	if (!rr) {
 		DBG("%s: failed to create rrset - %s\n",
 		    __func__, knot_strerror(ret));
+		knot_dname_free(&o);
 		return KNOT_ENOMEM;
 	}
 
@@ -729,8 +714,6 @@ int cmd_send(const char* lp, nsupdate_params_t *params)
 	}
 
 	/* Clear sent packet. */
-	knot_question_t *q = knot_packet_question(params->pkt);
-	knot_dname_release(q->qname);
 	knot_packet_free_rrsets(params->pkt);
 	knot_packet_free(&params->pkt);
 
@@ -746,7 +729,7 @@ int cmd_send(const char* lp, nsupdate_params_t *params)
 	}
 
 	/* Parse response. */
-	params->resp = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+	params->resp = knot_packet_new();
 	if (!params->resp) {
 		free_sign_context(&sign_ctx);
 		return KNOT_ENOMEM;
@@ -880,26 +863,6 @@ int cmd_key(const char* lp, nsupdate_params_t *params)
 	return ret;
 }
 
-/*
- *   Not implemented.
- */
-
-int cmd_gsstsig(const char* lp, nsupdate_params_t *params)
-{
-	UNUSED(params);
-	DBG("%s: lp='%s'\n", __func__, lp);
-
-	return KNOT_ENOTSUP;
-}
-
-int cmd_oldgsstsig(const char* lp, nsupdate_params_t *params)
-{
-	UNUSED(params);
-	DBG("%s: lp='%s'\n", __func__, lp);
-
-	return KNOT_ENOTSUP;
-}
-
 int cmd_origin(const char* lp, nsupdate_params_t *params)
 {
 	DBG("%s: lp='%s'\n", __func__, lp);
@@ -920,6 +883,26 @@ int cmd_origin(const char* lp, nsupdate_params_t *params)
 	return ret;
 }
 
+/*
+ *   Not implemented.
+ */
+
+int cmd_gsstsig(const char* lp, nsupdate_params_t *params)
+{
+	UNUSED(params);
+	DBG("%s: lp='%s'\n", __func__, lp);
+
+	return KNOT_ENOTSUP;
+}
+
+int cmd_oldgsstsig(const char* lp, nsupdate_params_t *params)
+{
+	UNUSED(params);
+	DBG("%s: lp='%s'\n", __func__, lp);
+
+	return KNOT_ENOTSUP;
+}
+
 int cmd_realm(const char* lp, nsupdate_params_t *params)
 {
 	UNUSED(params);
diff --git a/src/utils/nsupdate/nsupdate_params.c b/src/utils/nsupdate/nsupdate_params.c
index 4d1f054596a877cdbbc5b5a9f079fc0020f55835..523b4ca17d5e1369401e76e6a122b7044643b563 100644
--- a/src/utils/nsupdate/nsupdate_params.c
+++ b/src/utils/nsupdate/nsupdate_params.c
@@ -46,11 +46,6 @@ static const style_t DEFAULT_STYLE_NSUPDATE = {
 	.show_footer = false,
 };
 
-static void parse_rr(const scanner_t *s)
-{
-	UNUSED(s);
-}
-
 static void parse_err(const scanner_t *s) {
 	ERR("failed to parse RR, %s\n", knot_strerror(s->error_code));
 }
@@ -69,7 +64,7 @@ static int parser_set_default(scanner_t *s, const char *fmt, ...)
 	}
 
 	/* fmt must contain newline */
-	if (scanner_process(buf, buf + n, 0, s) < 0) {
+	if (scanner_process(buf, buf + n, 1, s) < 0) {
 		return KNOT_EPARSEFAIL;
 	}
 
@@ -87,29 +82,28 @@ static int nsupdate_init(nsupdate_params_t *params)
 
 	/* Default server. */
 	params->server = server_create(DEFAULT_IPV4_NAME, DEFAULT_DNS_PORT);
-	if (!params->server) return KNOT_ENOMEM;
+	if (!params->server)
+		return KNOT_ENOMEM;
 
 	/* Default settings. */
 	params->ip = IP_ALL;
 	params->protocol = PROTO_ALL;
-	params->retries = DEFAULT_RETRIES_NSUPDATE;
-	params->wait = DEFAULT_TIMEOUT_NSUPDATE;
 	params->class_num = KNOT_CLASS_IN;
 	params->type_num = KNOT_RRTYPE_SOA;
+	params->ttl = 0;
+	params->retries = DEFAULT_RETRIES_NSUPDATE;
+	params->wait = DEFAULT_TIMEOUT_NSUPDATE;
+	params->zone = strdup(".");
+
+	/* Initialize RR parser. */
+	params->rrp = scanner_create(NULL, ".", params->class_num, 0, NULL,
+	                             parse_err, NULL);
+	if (!params->rrp)
+		return KNOT_ENOMEM;
 
 	/* Default style. */
 	params->style = DEFAULT_STYLE_NSUPDATE;
 
-	/* Initialize RR parser. */
-	params->rrp = scanner_create(".");
-	if (!params->rrp) return KNOT_ENOMEM;
-	params->rrp->process_record = parse_rr;
-	params->rrp->process_error = parse_err;
-	params->rrp->default_class = params->class_num;
-	params->zone = strdup(".");
-	nsupdate_set_ttl(params, 0);
-	nsupdate_set_origin(params, ".");
-
 	return KNOT_EOK;
 }
 
@@ -121,18 +115,17 @@ void nsupdate_clean(nsupdate_params_t *params)
 		return;
 	}
 
+	/* Free qfiles. */
+	WALK_LIST_DELSAFE(n, nxt, params->qfiles) {
+		free(n);
+	}
+
 	server_free(params->server);
 	server_free(params->srcif);
 	free(params->zone);
 	scanner_free(params->rrp);
 	knot_packet_free(&params->pkt);
 	knot_packet_free(&params->resp);
-
-	/* Free qfiles. */
-	WALK_LIST_DELSAFE(n, nxt, params->qfiles) {
-		free(n);
-	}
-
 	knot_free_key_params(&params->key_params);
 
 	/* Clean up the structure. */
diff --git a/src/utils/nsupdate/nsupdate_params.h b/src/utils/nsupdate/nsupdate_params.h
index 7fcb98a12cb78c2f11f6077dd53e9e38501d9450..91cd2d524d5080ad44e612687c31354c92d1605b 100644
--- a/src/utils/nsupdate/nsupdate_params.h
+++ b/src/utils/nsupdate/nsupdate_params.h
@@ -31,10 +31,10 @@
 
 #include "libknot/libknot.h"
 #include "common/lists.h"		// list
-#include "zscanner/scanner.h"		// scanner_t
+#include "zscanner/zscanner.h"		// scanner_t
 #include "utils/common/netio.h"		// server_t
 #include "utils/common/params.h"	// protocol_t
-#include "libknot/sign/key.h"		// knot_key_params_t
+#include "libknot/dnssec/key.h"		// knot_key_params_t
 
 #define KNSUPDATE_VERSION "knsupdate, version " PACKAGE_VERSION "\n"
 
@@ -46,12 +46,12 @@ typedef struct {
 	/*!< Stop processing - just pring help, version,... */
 	bool		stop;
 	/*!< List of files with query data. */
-	list		qfiles;
+	list_t		qfiles;
 	/*!< List of nameservers to query to. */
 	server_t	*server;
 	/*!< Local interface (optional). */
 	server_t	*srcif;
-	/*!< Operation mode. */
+	/*!< Version of ip protocol to use. */
 	ip_t		ip;
 	/*!< Type (TCP, UDP) protocol to use. */
 	protocol_t	protocol;
diff --git a/src/zscanner/Makefile.am b/src/zscanner/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..b52b1748bcea8b5f51ee247d55f3adde83cd2ebd
--- /dev/null
+++ b/src/zscanner/Makefile.am
@@ -0,0 +1,53 @@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/zscanner -I$(top_srcdir)/src -DSYSCONFDIR='"$(sysconfdir)"' -DSBINDIR='"$(sbindir)"'
+
+noinst_PROGRAMS = zscanner-tool
+noinst_LTLIBRARIES = libzscanner.la
+
+TESTS = test/run_tests.sh
+
+EXTRA_DIST =				\
+	scanner.rl			\
+	scanner_body.rl			\
+	test/run_tests.sh		\
+	test/cases
+
+BUILT_SOURCES = descriptor.h descriptor.c
+CLEANFILES = descriptor.h descriptor.c
+nodist_libzscanner_la_SOURCES = descriptor.h descriptor.c
+
+descriptor.h: $(top_srcdir)/src/common/descriptor.h
+	cp $(top_srcdir)/src/common/descriptor.h $@
+
+descriptor.c: $(top_srcdir)/src/common/descriptor.c
+	cp $(top_srcdir)/src/common/descriptor.c $@
+
+if HAVE_RAGEL
+BUILT_SOURCES += scanner.c
+CLEANFILES += scanner.c
+
+scanner.c: scanner.rl scanner_body.rl
+	$(RAGEL) $(FSM_TYPE) -s -o $@ $(srcdir)/scanner.rl
+endif
+
+zscanner_tool_SOURCES =			\
+	test/zscanner-tool.c		\
+	test/tests.h			\
+	test/tests.c			\
+	test/processing.h		\
+	test/processing.c
+
+libzscanner_la_SOURCES =		\
+	zscanner.h			\
+	error.h				\
+	error.c				\
+	file_loader.h			\
+	file_loader.c			\
+	scanner.h			\
+	scanner.c			\
+	scanner_functions.h		\
+	scanner_functions.c
+
+libzscanner_la_LIBADD = @LIBOBJS@
+zscanner_tool_LDADD = libzscanner.la @LIBOBJS@
diff --git a/src/zscanner/NOTES b/src/zscanner/NOTES
index f7c8f1b806123678d79f3d8df9ce80a2fab49763..813f7c5b73ccbb23eecc826596c360ffd3782d20 100644
--- a/src/zscanner/NOTES
+++ b/src/zscanner/NOTES
@@ -1,17 +1,17 @@
 - supported types: A, NS, CNAME, PTR, DNAME, SOA, HINFO, MINFO, MX, AFSDB, RT,
                    KX, TXT, SPF, RP, AAAA, LOC, SRV, NAPTR, CERT, DS, SSHFP,
                    IPSECKEY, RRSIG, NSEC, KEY, DNSKEY, DHCID, NSEC3, NSEC3PARAM,
-                   TLSA, APL, EUI48, EUI64, NID, L32, L64, LP, UNKNOWN
+                   TLSA, APL, EUI48, EUI64, NID, L32, L64, LP, TYPE12345
 - the class IN is supported only (CLASS12345 notation is not supported too)
 - the newline character is appended to each zone file during processing
   (for simplicity)
 - domain names can contain alphanumeric, '-', '_' and '/' characters
-- \x and \DDD notations are allowed in domain names and text strings only
+- \x and \DDD notations are allowed in domain names and in text strings
 - @ can be used instead of domain names anywhere (excluding directives)
 - directive $INCLUDE is allowed in included zone files (BEWARE of recursion)
 - relative file path is relative to parent zone file
-- blank zone file causes error (same rule is for included zone file)
-- the line numbers of multiline records are the numbers of the last lines with
+- blank zone file causes error (the same rule is for included zone file)
+- line numbers of multiline records are numbers of the last lines with
   appropriate record parts
 - items parts lengths must be multiples of 2 for HEX, 4 for base64 and 8 for
   base32hex blocks (but DHCID example from RFC is more general!)
diff --git a/src/zscanner/error.c b/src/zscanner/error.c
new file mode 100644
index 0000000000000000000000000000000000000000..4317529aa5fb7449541c071a3e12cbf0164de0a0
--- /dev/null
+++ b/src/zscanner/error.c
@@ -0,0 +1,192 @@
+/*  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 <stdlib.h>	// NULL
+
+#include "zscanner/error.h"
+
+typedef struct {
+	int        code;
+	const char *text;
+	const char *code_name;
+} err_table_t;
+
+#define ERR_ITEM(code, text) { code, text, #code }
+
+const err_table_t err_msgs[] = {
+	ERR_ITEM( ZSCANNER_OK, "OK" ),
+
+	/* Zone file loader errors. */
+	ERR_ITEM( FLOADER_EFSTAT,
+	          "Fstat error." ),
+	ERR_ITEM( FLOADER_EDIRECTORY,
+	          "Zone file is a directory." ),
+	ERR_ITEM( FLOADER_EEMPTY,
+	          "Empty zone file." ),
+	ERR_ITEM( FLOADER_EMMAP,
+	          "Mmap error." ),
+	ERR_ITEM( FLOADER_EMUNMAP,
+	          "Munmap error." ),
+	ERR_ITEM( FLOADER_ESCANNER,
+	          "Zone processing error." ),
+
+	/* Zone scanner errors. */
+	ERR_ITEM( ZSCANNER_UNCOVERED_STATE,
+	          "General scanner error." ),
+	ERR_ITEM( ZSCANNER_UNCLOSED_MULTILINE,
+	          "Unclosed last multiline block." ),
+	ERR_ITEM( ZSCANNER_ELEFT_PARENTHESIS,
+	          "Too many left parentheses." ),
+	ERR_ITEM( ZSCANNER_ERIGHT_PARENTHESIS,
+	          "Too many right parentheses." ),
+	ERR_ITEM( ZSCANNER_EUNSUPPORTED_TYPE,
+	          "Unsupported record type." ),
+	ERR_ITEM( ZSCANNER_EBAD_PREVIOUS_OWNER,
+	          "Previous owner is invalid." ),
+	ERR_ITEM( ZSCANNER_EBAD_DNAME_CHAR,
+	          "Bad domain name character." ),
+	ERR_ITEM( ZSCANNER_EBAD_OWNER,
+	          "Owner is invalid." ),
+	ERR_ITEM( ZSCANNER_ELABEL_OVERFLOW,
+	          "Maximal domain name label length has exceeded." ),
+	ERR_ITEM( ZSCANNER_EDNAME_OVERFLOW,
+	          "Maximal domain name length has exceeded." ),
+	ERR_ITEM( ZSCANNER_EBAD_NUMBER,
+	          "Bad number." ),
+	ERR_ITEM( ZSCANNER_ENUMBER64_OVERFLOW,
+	          "Number is too big." ),
+	ERR_ITEM( ZSCANNER_ENUMBER32_OVERFLOW,
+	          "Number is bigger than 32 bits." ),
+	ERR_ITEM( ZSCANNER_ENUMBER16_OVERFLOW,
+	          "Number is bigger than 16 bits." ),
+	ERR_ITEM( ZSCANNER_ENUMBER8_OVERFLOW,
+	          "Number is bigger than 8 bits." ),
+	ERR_ITEM( ZSCANNER_EFLOAT_OVERFLOW,
+	          "Float number overflow." ),
+	ERR_ITEM( ZSCANNER_ERDATA_OVERFLOW,
+	          "Maximal record data length has exceeded." ),
+	ERR_ITEM( ZSCANNER_EITEM_OVERFLOW,
+	          "Maximal item length has exceeded." ),
+	ERR_ITEM( ZSCANNER_EBAD_ADDRESS_CHAR,
+	          "Bad address character." ),
+	ERR_ITEM( ZSCANNER_EBAD_IPV4,
+	          "Bad IPv4 address." ),
+	ERR_ITEM( ZSCANNER_EBAD_IPV6,
+	          "Bad IPv6 address." ),
+	ERR_ITEM( ZSCANNER_EBAD_GATEWAY,
+	          "Bad gateway." ),
+	ERR_ITEM( ZSCANNER_EBAD_GATEWAY_KEY,
+	          "Bad gateway key." ),
+	ERR_ITEM( ZSCANNER_EBAD_APL,
+	          "Bad address prefix list." ),
+	ERR_ITEM( ZSCANNER_EBAD_RDATA,
+	          "Bad record data." ),
+	ERR_ITEM( ZSCANNER_EBAD_HEX_RDATA,
+	          "Bad record data in hex format." ),
+	ERR_ITEM( ZSCANNER_EBAD_HEX_CHAR,
+	          "Bad hexadecimal character." ),
+	ERR_ITEM( ZSCANNER_EBAD_BASE64_CHAR,
+	          "Bad Base64 character." ),
+	ERR_ITEM( ZSCANNER_EBAD_BASE32HEX_CHAR,
+	          "Bad Base32hex character." ),
+	ERR_ITEM( ZSCANNER_EBAD_REST,
+	          "Unexpected data." ),
+	ERR_ITEM( ZSCANNER_EBAD_TIMESTAMP_CHAR,
+	          "Bad timestamp character." ),
+	ERR_ITEM( ZSCANNER_EBAD_TIMESTAMP_LENGTH,
+	          "Bad timestamp length." ),
+	ERR_ITEM( ZSCANNER_EBAD_TIMESTAMP,
+	          "Bad timestamp." ),
+	ERR_ITEM( ZSCANNER_EBAD_DATE,
+	          "Bad date." ),
+	ERR_ITEM( ZSCANNER_EBAD_TIME,
+	          "Bad time." ),
+	ERR_ITEM( ZSCANNER_EBAD_TIME_UNIT,
+	          "Bad time unit." ),
+	ERR_ITEM( ZSCANNER_EBAD_BITMAP,
+	          "Bad bitmap." ),
+	ERR_ITEM( ZSCANNER_ETEXT_OVERFLOW,
+	          "Text is too long." ),
+	ERR_ITEM( ZSCANNER_EBAD_TEXT_CHAR,
+	          "Bad text character." ),
+	ERR_ITEM( ZSCANNER_EBAD_TEXT,
+	          "Bad text string." ),
+	ERR_ITEM( ZSCANNER_EBAD_DIRECTIVE,
+	          "Bad directive." ),
+	ERR_ITEM( ZSCANNER_EBAD_TTL,
+	          "Bad zone TTL." ),
+	ERR_ITEM( ZSCANNER_EBAD_ORIGIN,
+	          "Bad zone origin." ),
+	ERR_ITEM( ZSCANNER_EBAD_INCLUDE_FILENAME,
+	          "Bad filename in include directive." ),
+	ERR_ITEM( ZSCANNER_EBAD_INCLUDE_ORIGIN,
+	          "Bad origin in include directive." ),
+	ERR_ITEM( ZSCANNER_EUNPROCESSED_INCLUDE,
+	          "Include file processing error." ),
+	ERR_ITEM( ZSCANNER_EUNOPENED_INCLUDE,
+	          "Include file opening error." ),
+	ERR_ITEM( ZSCANNER_EBAD_RDATA_LENGTH,
+	          "The rdata length statement is incorrect." ),
+	ERR_ITEM( ZSCANNER_ECANNOT_TEXT_DATA,
+	          "Unable to process text form for this type." ),
+	ERR_ITEM( ZSCANNER_EBAD_LOC_DATA,
+	          "Bad zone location data." ),
+	ERR_ITEM( ZSCANNER_EUNKNOWN_BLOCK,
+	          "Unknown rdata block." ),
+	ERR_ITEM( ZSCANNER_EBAD_ALGORITHM,
+	          "Bad algorithm." ),
+	ERR_ITEM( ZSCANNER_EBAD_CERT_TYPE,
+	          "Bad certificate type." ),
+	ERR_ITEM( ZSCANNER_EBAD_EUI_LENGTH,
+	          "Bad EUI length." ),
+	ERR_ITEM( ZSCANNER_EBAD_L64_LENGTH,
+	          "Bad 64-bit locator." ),
+	ERR_ITEM( ZSCANNER_EBAD_CHAR_COLON,
+	          "Missing colon character." ),
+	ERR_ITEM( ZSCANNER_EBAD_CHAR_DASH,
+	          "Missing dash character." ),
+
+	ERR_ITEM( 0, NULL ) // Terminator
+};
+
+const char* zscanner_strerror(const int code)
+{
+	const err_table_t *err = err_msgs;
+
+	while (err->text != NULL) {
+		if (err->code == code) {
+			return err->text;
+		}
+		err++;
+	}
+
+	return NULL;
+}
+
+const char* zscanner_errorname(const int code)
+{
+	const err_table_t *err = err_msgs;
+
+	while (err->text != NULL) {
+		if (err->code == code) {
+			return err->code_name;
+		}
+		err++;
+	}
+
+	return NULL;
+}
diff --git a/src/zscanner/error.h b/src/zscanner/error.h
new file mode 100644
index 0000000000000000000000000000000000000000..6c7766b2c37cb76d49ac7ea96b72ba0a88dab11d
--- /dev/null
+++ b/src/zscanner/error.h
@@ -0,0 +1,117 @@
+/*  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 error.h
+ *
+ * \author Daniel Salzman <daniel.salzman@nic.cz>
+ *
+ * \brief Error codes and handling.
+ * @{
+ */
+
+#ifndef _ZSCANNER__ERROR_H_
+#define _ZSCANNER__ERROR_H_
+
+enum err_codes {
+	ZSCANNER_OK = 0,
+
+	FLOADER_EFSTAT = -1000,
+	FLOADER_EDIRECTORY,
+	FLOADER_EEMPTY,
+	FLOADER_EMMAP,
+	FLOADER_EMUNMAP,
+	FLOADER_ESCANNER,
+
+	ZSCANNER_UNCOVERED_STATE,
+	ZSCANNER_UNCLOSED_MULTILINE,
+	ZSCANNER_ELEFT_PARENTHESIS,
+	ZSCANNER_ERIGHT_PARENTHESIS,
+	ZSCANNER_EUNSUPPORTED_TYPE,
+	ZSCANNER_EBAD_PREVIOUS_OWNER,
+	ZSCANNER_EBAD_DNAME_CHAR,
+	ZSCANNER_EBAD_OWNER,
+	ZSCANNER_ELABEL_OVERFLOW,
+	ZSCANNER_EDNAME_OVERFLOW,
+	ZSCANNER_EBAD_NUMBER,
+	ZSCANNER_ENUMBER64_OVERFLOW,
+	ZSCANNER_ENUMBER32_OVERFLOW,
+	ZSCANNER_ENUMBER16_OVERFLOW,
+	ZSCANNER_ENUMBER8_OVERFLOW,
+	ZSCANNER_EFLOAT_OVERFLOW,
+	ZSCANNER_ERDATA_OVERFLOW,
+	ZSCANNER_EITEM_OVERFLOW,
+	ZSCANNER_EBAD_ADDRESS_CHAR,
+	ZSCANNER_EBAD_IPV4,
+	ZSCANNER_EBAD_IPV6,
+	ZSCANNER_EBAD_GATEWAY,
+	ZSCANNER_EBAD_GATEWAY_KEY,
+	ZSCANNER_EBAD_APL,
+	ZSCANNER_EBAD_RDATA,
+	ZSCANNER_EBAD_HEX_RDATA,
+	ZSCANNER_EBAD_HEX_CHAR,
+	ZSCANNER_EBAD_BASE64_CHAR,
+	ZSCANNER_EBAD_BASE32HEX_CHAR,
+	ZSCANNER_EBAD_REST,
+	ZSCANNER_EBAD_TIMESTAMP_CHAR,
+	ZSCANNER_EBAD_TIMESTAMP_LENGTH,
+	ZSCANNER_EBAD_TIMESTAMP,
+	ZSCANNER_EBAD_DATE,
+	ZSCANNER_EBAD_TIME,
+	ZSCANNER_EBAD_TIME_UNIT,
+	ZSCANNER_EBAD_BITMAP,
+	ZSCANNER_ETEXT_OVERFLOW,
+	ZSCANNER_EBAD_TEXT_CHAR,
+	ZSCANNER_EBAD_TEXT,
+	ZSCANNER_EBAD_DIRECTIVE,
+	ZSCANNER_EBAD_TTL,
+	ZSCANNER_EBAD_ORIGIN,
+	ZSCANNER_EBAD_INCLUDE_FILENAME,
+	ZSCANNER_EBAD_INCLUDE_ORIGIN,
+	ZSCANNER_EUNPROCESSED_INCLUDE,
+	ZSCANNER_EUNOPENED_INCLUDE,
+	ZSCANNER_EBAD_RDATA_LENGTH,
+	ZSCANNER_ECANNOT_TEXT_DATA,
+	ZSCANNER_EBAD_LOC_DATA,
+	ZSCANNER_EUNKNOWN_BLOCK,
+	ZSCANNER_EBAD_ALGORITHM,
+	ZSCANNER_EBAD_CERT_TYPE,
+	ZSCANNER_EBAD_EUI_LENGTH,
+	ZSCANNER_EBAD_L64_LENGTH,
+	ZSCANNER_EBAD_CHAR_COLON,
+	ZSCANNER_EBAD_CHAR_DASH
+};
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+const char* zscanner_strerror(const int code);
+
+/*!
+ * \brief Returns error code name of the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error code name.
+ */
+const char* zscanner_errorname(const int code);
+
+#endif // _ZSCANNER__ERROR_H_
+
+/*! @} */
diff --git a/src/zscanner/file_loader.c b/src/zscanner/file_loader.c
index ce44dbea380ddd9f8f30f002a729b45cf030c2de..e303bf45a73e9c3b1a5d21e31fd32a0c3b1512d5 100644
--- a/src/zscanner/file_loader.c
+++ b/src/zscanner/file_loader.c
@@ -15,8 +15,6 @@
  */
 
 #include <config.h>
-#include "zscanner/file_loader.h"
-
 #include <inttypes.h>			// PRIu64
 #include <unistd.h>			// sysconf
 #include <stdio.h>			// sprintf
@@ -27,83 +25,24 @@
 #include <sys/stat.h>			// fstat
 #include <sys/mman.h>			// mmap
 
-#include "common/errcode.h"		// error codes
-
-/*! \brief Mmap block size in bytes. */
-#define BLOCK_SIZE      30000000
+#include "zscanner/file_loader.h"
+#include "zscanner/error.h"		// error codes
 
-/*!
- * \brief Processes zone settings block.
- *
- * Before zone file processing via scanner it's necessary to process first
- * settings block using this function. Settings block contains ORIGIN and
- * TTL directive.
- *
- * \param fl		File loader structure.
- *
- * \retval  0		if success.
- * \retval -1		if error.
+/*! \brief Mmap block size in bytes. This value is then adjusted to the
+ *         multiple of memory pages which fit in.
  */
-static int load_settings(file_loader_t *fl)
-{
-	int		ret;
-	char		*settings_name;
-	scanner_t	*settings_scanner;
-
-	// Creating name for zone defaults.
-	size_t buf_len = strlen(fl->file_name) + 100;
-	settings_name = malloc(buf_len);
-	ret = snprintf(settings_name, buf_len, "ZONE DEFAULTS <%s>",
-		       fl->file_name);
-	if (ret < 0 || (size_t)ret >= buf_len) {
-		free(settings_name);
-		return -1;
-	}
-
-	// Temporary scanner for zone settings.
-	settings_scanner = scanner_create(settings_name);
-
-	// Use parent processing functions.
-	settings_scanner->process_record = fl->scanner->process_record;
-	settings_scanner->process_error  = fl->scanner->process_error;
-
-	// Scanning zone settings.
-	ret = scanner_process(fl->settings_buffer,
-			      fl->settings_buffer + fl->settings_length,
-			      true,
-			      settings_scanner);
-
-	// If no error occured, then copy scanned settings to actual context.
-	if (ret == 0) {
-		memcpy(fl->scanner->zone_origin,
-		       settings_scanner->zone_origin,
-		       settings_scanner->zone_origin_length);
-		fl->scanner->zone_origin_length =
-			settings_scanner->zone_origin_length;
-		fl->scanner->default_ttl = settings_scanner->default_ttl;
-	}
-
-	// Destroying temporary scanner.
-	scanner_free(settings_scanner);
-
-	free(settings_name);
-
-	return ret;
-}
+#define BLOCK_SIZE      30000000
 
-file_loader_t* file_loader_create(const char	 *file_name,
-				  const char	 *zone_origin,
-				  const uint16_t default_class,
-				  const uint32_t default_ttl,
-				  void (*process_record)(const scanner_t *),
-				  void (*process_error)(const scanner_t *),
-				  void *data)
+file_loader_t* file_loader_create(const char     *file_name,
+                                  const char     *origin,
+                                  const uint16_t rclass,
+                                  const uint32_t ttl,
+                                  void (*process_record)(const scanner_t *),
+                                  void (*process_error)(const scanner_t *),
+                                  void *data)
 {
-	int ret;
-
 	// Creating zeroed structure.
 	file_loader_t *fl = calloc(1, sizeof(file_loader_t));
-
 	if (fl == NULL) {
 		return NULL;
 	}
@@ -113,7 +52,6 @@ file_loader_t* file_loader_create(const char	 *file_name,
 
 	// Opening zone file.
 	fl->fd = open(fl->file_name, O_RDONLY);
-
 	if (fl->fd == -1) {
 		free(fl->file_name);
 		free(fl);
@@ -121,29 +59,15 @@ file_loader_t* file_loader_create(const char	 *file_name,
 	}
 
 	// Creating zone scanner.
-	fl->scanner = scanner_create(file_name);
-
-	// Setting processing functions and data pointer.
-	fl->scanner->process_record = process_record;
-	fl->scanner->process_error  = process_error;
-	fl->scanner->data = data;
-
-	// Default class initialization.
-	fl->scanner->default_class = default_class;
-
-	// Filling zone settings buffer.
-	ret = snprintf(fl->settings_buffer,
-		       sizeof(fl->settings_buffer),
-		       "$ORIGIN %s\n"
-		       "$TTL %u\n",
-		       zone_origin, default_ttl);
-	if (ret <= 0 || (size_t)ret >= sizeof(fl->settings_buffer)) {
-		file_loader_free(fl);
+	fl->scanner = scanner_create(fl->file_name, origin, rclass, ttl,
+	                             process_record, process_error, data);
+	if (fl->scanner == NULL) {
+		close(fl->fd);
+		free(fl->file_name);
+		free(fl);
 		return NULL;
 	}
 
-	fl->settings_length = ret;
-
 	return fl;
 }
 
@@ -157,7 +81,7 @@ void file_loader_free(file_loader_t *fl)
 
 int file_loader_process(file_loader_t *fl)
 {
-	int		ret;
+	int		ret = 0;
 	char		*data;		// Mmaped data.
 	bool		is_last_block;
 	long		page_size;
@@ -195,13 +119,6 @@ int file_loader_process(file_loader_t *fl)
 	// Number of blocks which cover the whole file (ceiling operation).
 	n_blocks = 1 + ((file_stat.st_size - 1) / default_block_size);
 
-	// Process settings using scanner (like initial ORIGIN and TTL).
-	ret = load_settings(fl);
-
-	if (ret != 0) {
-		return FLOADER_EDEFAULTS;
-	}
-
 	// Loop over zone file blocks.
 	for (block_id = 0; block_id < n_blocks; block_id++) {
 		scanner_start = block_id * default_block_size;
@@ -216,28 +133,27 @@ int file_loader_process(file_loader_t *fl)
 
 		// Zone file block mapping.
 		data = mmap(0,
-			    block_size,
-			    PROT_READ,
-			    MAP_SHARED,
-			    fl->fd,
-			    scanner_start);
-
+		            block_size,
+		            PROT_READ,
+		            MAP_SHARED,
+		            fl->fd,
+		            scanner_start);
 		if (data == MAP_FAILED) {
 			return FLOADER_EMMAP;
 		}
 
 		// Scan zone file.
 		ret = scanner_process(data,
-				      data + block_size,
-				      false,
-				      fl->scanner);
+		                      data + block_size,
+		                      false,
+		                      fl->scanner);
 
 		// Artificial last block containing newline char only.
 		if (is_last_block == true && fl->scanner->stop == 0) {
 			ret = scanner_process(zone_termination,
-					      zone_termination + 1,
-					      true,
-					      fl->scanner);
+			                      zone_termination + 1,
+			                      true,
+			                      fl->scanner);
 		}
 
 		// Zone file block unmapping.
@@ -251,5 +167,5 @@ int file_loader_process(file_loader_t *fl)
 		return FLOADER_ESCANNER;
 	}
 
-	return KNOT_EOK;
+	return ZSCANNER_OK;
 }
diff --git a/src/zscanner/file_loader.h b/src/zscanner/file_loader.h
index 6bca297b2a917cafe9fd7367a78d40a96e9a2e20..eaab9e5aeee094f85dd27775f046a5cdd8c96843 100644
--- a/src/zscanner/file_loader.h
+++ b/src/zscanner/file_loader.h
@@ -29,37 +29,25 @@
 
 #include <stdint.h>			// uint32_t
 
-#include "common/descriptor.h"		// KNOT_CLASS_IN
 #include "zscanner/scanner.h"		// scanner_t
 
-/*! \brief Settings block size in bytes. */
-#define SETTINGS_BUFFER_LENGTH		 1024
-/*! \brief Default ttl value. */
-#define DEFAULT_TTL			 3600
-/*! \brief Default class value. */
-#define DEFAULT_CLASS		KNOT_CLASS_IN
-
 /*! \brief Structure for zone file loader (each included file has one). */
 typedef struct {
 	/*!< File descriptor. */
-	int	  fd;
+	int       fd;
 	/*!< Zone file name this loader belongs to. */
-	char	  *file_name;
+	char      *file_name;
 	/*!< Zone scanner context stucture. */
 	scanner_t *scanner;
-	/*!< Zone settings buffer. */
-	char	  settings_buffer[SETTINGS_BUFFER_LENGTH];
-	/*!< Length of zone settings buffer. */
-	uint32_t  settings_length;
 } file_loader_t;
 
 /*!
  * \brief Creates file loader structure.
  *
  * \param file_name		Name of file to process.
- * \param zone_origin		Initial zone origin (used in settings block).
- * \param default_class		Default class value.
- * \param default_ttl		Default ttl value (used in settings block).
+ * \param origin		Initial zone origin.
+ * \param rclass		Zone class value.
+ * \param ttl			Initial ttl value.
  * \param process_record	Processing callback function.
  * \param process_error 	Error callback function.
  * \param data			Arbitrary data useful in callback functions.
@@ -67,13 +55,13 @@ typedef struct {
  * \retval file_loader		if success.
  * \retval 0			if error.
  */
-file_loader_t* file_loader_create(const char	 *file_name,
-				  const char	 *zone_origin,
-				  const uint16_t default_class,
-				  const uint32_t default_ttl,
-				  void (*process_record)(const scanner_t *),
-				  void (*process_error)(const scanner_t *),
-				  void *data);
+file_loader_t* file_loader_create(const char     *file_name,
+                                  const char     *origin,
+                                  const uint16_t rclass,
+                                  const uint32_t ttl,
+                                  void (*process_record)(const scanner_t *),
+                                  void (*process_error)(const scanner_t *),
+                                  void *data);
 
 /*!
  * \brief Destroys file loader structure.
@@ -90,16 +78,15 @@ void file_loader_free(file_loader_t *file_loader);
  * syntax error occures, then process_error callback function is called.
  *
  * \note Zone scanner error code and other information are stored in
- * fl.scanner context.
+ *       fl.scanner context.
  *
  * \param file_loader	File loader structure.
  *
- * \retval KNOT_EOK	if success.
- * \retval error_code   if error.
+ * \retval ZSCANNER_OK	if success.
+ * \retval error_code	if error.
  */
 int file_loader_process(file_loader_t *file_loader);
 
-
 #endif // _ZSCANNER__FILE_LOADER_H_
 
 /*! @} */
diff --git a/src/zscanner/scanner.c b/src/zscanner/scanner.c
index 2a2a97464aa7beeea1ae84c9493f5be47af44524..a2f872092872f440a923e3029819e3d0cc04a87e 100644
--- a/src/zscanner/scanner.c
+++ b/src/zscanner/scanner.c
@@ -1,5 +1,5 @@
 
-#line 1 "./zscanner/scanner.rl"
+#line 1 "./scanner.rl"
 /*  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
@@ -16,8 +16,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "zscanner/scanner.h"
-
+#include <config.h>
 #include <stdint.h>			// uint32_t
 #include <stdlib.h>			// calloc
 #include <stdio.h>			// sprintf
@@ -30,16 +29,25 @@
 #include <netinet/in.h>			// in_addr (BSD)
 #include <arpa/inet.h>			// inet_pton
 
-#include "common/errcode.h"		// error codes
-#include "common/descriptor.h"		// KNOT_RRTYPE_A
+#include "zscanner/scanner.h"
+#include "zscanner/error.h"		// error codes
 #include "zscanner/file_loader.h"	// file_loader
 #include "zscanner/scanner_functions.h"	// Base64
+#include "zscanner/descriptor.h"	// KNOT_RRTYPE_A
 
 /*! \brief Shorthand for setting warning data. */
 #define SCANNER_WARNING(code) { s->error_code = code; }
 /*! \brief Shorthand for setting error data. */
 #define SCANNER_ERROR(code)   { s->error_code = code; s->stop = true; }
 
+/*!
+ * \brief Empty function which is called if no callback function is specified.
+ */
+static inline void noop(const scanner_t *s)
+{
+	(void)s;
+}
+
 /*!
  * \brief Writes record type number to r_data.
  *
@@ -76,700 +84,679 @@ static inline void window_add_bit(const uint16_t type, scanner_t *s) {
 
 // Include scanner file (in Ragel).
 
-#line 80 "zscanner/scanner.c"
+#line 88 "scanner.c"
 static const short _zone_scanner_actions[] = {
 	0, 1, 0, 1, 1, 1, 2, 1, 
 	3, 1, 4, 1, 6, 1, 9, 1, 
 	10, 1, 11, 1, 13, 1, 14, 1, 
 	16, 1, 19, 1, 20, 1, 22, 1, 
-	23, 1, 26, 1, 27, 1, 28, 1, 
-	30, 1, 31, 1, 40, 1, 41, 1, 
-	42, 1, 44, 1, 46, 1, 47, 1, 
-	48, 1, 49, 1, 51, 1, 53, 1, 
-	54, 1, 56, 1, 58, 1, 60, 1, 
-	61, 1, 65, 1, 66, 1, 69, 1, 
-	70, 1, 72, 1, 73, 1, 76, 1, 
-	78, 1, 79, 1, 80, 1, 81, 1, 
-	82, 1, 83, 1, 84, 1, 85, 1, 
-	87, 1, 89, 1, 91, 1, 95, 1, 
-	96, 1, 100, 1, 101, 1, 105, 1, 
-	106, 1, 107, 1, 108, 1, 109, 1, 
-	110, 1, 111, 1, 112, 1, 113, 1, 
-	114, 1, 115, 1, 116, 1, 117, 1, 
-	118, 1, 120, 1, 121, 1, 122, 1, 
-	123, 1, 148, 1, 149, 1, 150, 1, 
-	151, 1, 152, 1, 153, 1, 154, 1, 
-	155, 1, 156, 1, 157, 1, 158, 1, 
-	159, 1, 160, 1, 161, 1, 162, 1, 
-	163, 1, 164, 1, 165, 1, 166, 1, 
-	167, 1, 168, 1, 169, 1, 170, 1, 
-	171, 1, 172, 1, 173, 1, 174, 1, 
-	175, 1, 176, 1, 177, 1, 178, 1, 
-	179, 1, 180, 1, 181, 1, 182, 1, 
-	183, 1, 184, 1, 185, 1, 186, 1, 
-	187, 1, 188, 1, 189, 1, 190, 1, 
-	191, 1, 192, 1, 193, 1, 194, 1, 
-	195, 1, 196, 1, 197, 1, 198, 1, 
-	199, 1, 200, 1, 201, 1, 202, 1, 
-	203, 1, 204, 1, 205, 1, 206, 1, 
-	207, 1, 208, 1, 209, 1, 210, 1, 
-	211, 1, 212, 1, 213, 1, 214, 1, 
-	215, 1, 216, 1, 217, 1, 218, 1, 
-	219, 1, 220, 1, 221, 1, 222, 1, 
-	223, 1, 224, 1, 225, 1, 227, 1, 
-	229, 1, 230, 1, 231, 1, 232, 1, 
-	239, 1, 240, 1, 245, 1, 247, 1, 
-	252, 1, 255, 1, 256, 1, 257, 1, 
-	258, 1, 260, 1, 261, 1, 262, 1, 
-	263, 1, 265, 2, 0, 44, 2, 1, 
-	0, 2, 1, 27, 2, 1, 261, 2, 
-	1, 304, 2, 2, 27, 2, 2, 263, 
-	2, 3, 27, 2, 3, 263, 2, 4, 
-	65, 2, 4, 69, 2, 5, 6, 2, 
+	23, 1, 25, 1, 26, 1, 27, 1, 
+	29, 1, 30, 1, 39, 1, 40, 1, 
+	41, 1, 43, 1, 45, 1, 46, 1, 
+	47, 1, 48, 1, 50, 1, 52, 1, 
+	53, 1, 55, 1, 57, 1, 59, 1, 
+	60, 1, 64, 1, 65, 1, 68, 1, 
+	69, 1, 71, 1, 72, 1, 75, 1, 
+	77, 1, 78, 1, 79, 1, 80, 1, 
+	81, 1, 82, 1, 83, 1, 84, 1, 
+	86, 1, 88, 1, 90, 1, 94, 1, 
+	95, 1, 99, 1, 100, 1, 104, 1, 
+	105, 1, 106, 1, 107, 1, 108, 1, 
+	109, 1, 110, 1, 111, 1, 112, 1, 
+	113, 1, 114, 1, 115, 1, 116, 1, 
+	117, 1, 119, 1, 120, 1, 121, 1, 
+	122, 1, 147, 1, 148, 1, 149, 1, 
+	150, 1, 151, 1, 152, 1, 153, 1, 
+	154, 1, 155, 1, 156, 1, 157, 1, 
+	158, 1, 159, 1, 160, 1, 161, 1, 
+	162, 1, 163, 1, 164, 1, 165, 1, 
+	166, 1, 167, 1, 168, 1, 169, 1, 
+	170, 1, 171, 1, 172, 1, 173, 1, 
+	174, 1, 175, 1, 176, 1, 177, 1, 
+	178, 1, 179, 1, 180, 1, 181, 1, 
+	182, 1, 183, 1, 184, 1, 185, 1, 
+	186, 1, 187, 1, 188, 1, 189, 1, 
+	190, 1, 191, 1, 192, 1, 193, 1, 
+	194, 1, 195, 1, 196, 1, 197, 1, 
+	198, 1, 199, 1, 200, 1, 201, 1, 
+	202, 1, 203, 1, 204, 1, 205, 1, 
+	206, 1, 207, 1, 208, 1, 209, 1, 
+	210, 1, 211, 1, 212, 1, 213, 1, 
+	214, 1, 215, 1, 216, 1, 217, 1, 
+	218, 1, 219, 1, 220, 1, 221, 1, 
+	222, 1, 223, 1, 224, 1, 226, 1, 
+	228, 1, 229, 1, 230, 1, 231, 1, 
+	238, 1, 239, 1, 244, 1, 246, 1, 
+	251, 1, 254, 1, 255, 1, 256, 1, 
+	257, 1, 259, 1, 260, 1, 261, 1, 
+	263, 2, 0, 43, 2, 1, 0, 2, 
+	1, 26, 2, 1, 260, 2, 1, 302, 
+	2, 2, 26, 2, 3, 26, 2, 4, 
+	64, 2, 4, 68, 2, 5, 6, 2, 
 	9, 10, 2, 12, 13, 2, 14, 10, 
 	2, 14, 11, 2, 15, 20, 2, 15, 
-	69, 2, 15, 76, 2, 16, 0, 2, 
-	16, 2, 2, 16, 3, 2, 16, 75, 
+	68, 2, 15, 75, 2, 16, 0, 2, 
+	16, 2, 2, 16, 3, 2, 16, 74, 
 	2, 18, 0, 2, 19, 9, 2, 22, 
-	63, 2, 22, 100, 2, 22, 111, 2, 
+	62, 2, 22, 99, 2, 22, 110, 2, 
 	23, 0, 2, 23, 1, 2, 23, 2, 
-	2, 23, 3, 2, 23, 24, 2, 23, 
-	228, 2, 25, 21, 2, 26, 1, 2, 
-	26, 2, 2, 26, 3, 2, 26, 85, 
-	2, 26, 263, 2, 27, 1, 2, 27, 
-	2, 2, 27, 3, 2, 28, 4, 2, 
-	28, 260, 2, 28, 265, 2, 29, 21, 
-	2, 30, 0, 2, 30, 1, 2, 30, 
-	2, 2, 30, 3, 2, 30, 24, 2, 
-	31, 36, 2, 32, 31, 2, 33, 65, 
-	2, 33, 245, 2, 33, 260, 2, 33, 
-	265, 2, 37, 233, 2, 37, 234, 2, 
-	37, 235, 2, 37, 236, 2, 37, 237, 
-	2, 37, 238, 2, 40, 0, 2, 40, 
-	1, 2, 40, 2, 2, 40, 3, 2, 
-	41, 0, 2, 41, 1, 2, 41, 2, 
-	2, 41, 3, 2, 41, 24, 2, 42, 
-	0, 2, 42, 1, 2, 42, 2, 2, 
-	42, 3, 2, 43, 259, 2, 44, 1, 
-	2, 44, 2, 2, 44, 3, 2, 51, 
-	2, 2, 51, 3, 2, 51, 42, 2, 
-	51, 85, 2, 52, 53, 2, 54, 1, 
-	2, 54, 2, 2, 54, 3, 2, 55, 
-	260, 2, 56, 0, 2, 57, 58, 2, 
-	59, 60, 2, 61, 0, 2, 61, 56, 
-	2, 67, 19, 2, 71, 63, 2, 72, 
-	2, 2, 72, 3, 2, 73, 4, 2, 
-	74, 19, 2, 76, 4, 2, 80, 1, 
-	2, 80, 27, 2, 82, 83, 2, 84, 
+	2, 23, 3, 2, 23, 227, 2, 24, 
+	21, 2, 25, 1, 2, 25, 2, 2, 
+	25, 3, 2, 25, 84, 2, 26, 1, 
+	2, 26, 2, 2, 26, 3, 2, 27, 
+	4, 2, 27, 259, 2, 27, 263, 2, 
+	28, 21, 2, 29, 0, 2, 29, 1, 
+	2, 29, 2, 2, 29, 3, 2, 29, 
+	227, 2, 30, 35, 2, 31, 30, 2, 
+	32, 64, 2, 32, 244, 2, 32, 259, 
+	2, 32, 263, 2, 36, 232, 2, 36, 
+	233, 2, 36, 234, 2, 36, 235, 2, 
+	36, 236, 2, 36, 237, 2, 39, 0, 
+	2, 39, 1, 2, 39, 2, 2, 39, 
+	3, 2, 40, 0, 2, 40, 1, 2, 
+	40, 2, 2, 40, 3, 2, 41, 0, 
+	2, 41, 1, 2, 41, 2, 2, 41, 
+	3, 2, 42, 258, 2, 43, 1, 2, 
+	43, 2, 2, 43, 3, 2, 50, 2, 
+	2, 50, 3, 2, 50, 41, 2, 50, 
+	84, 2, 51, 52, 2, 53, 1, 2, 
+	53, 2, 2, 53, 3, 2, 54, 259, 
+	2, 55, 0, 2, 56, 57, 2, 58, 
+	59, 2, 60, 0, 2, 60, 55, 2, 
+	66, 19, 2, 70, 62, 2, 71, 2, 
+	2, 71, 3, 2, 72, 4, 2, 73, 
+	19, 2, 75, 4, 2, 79, 1, 2, 
+	79, 26, 2, 81, 82, 2, 83, 1, 
+	2, 83, 2, 2, 83, 3, 2, 84, 
 	1, 2, 84, 2, 2, 84, 3, 2, 
-	85, 1, 2, 85, 2, 2, 85, 3, 
-	2, 85, 263, 2, 86, 87, 2, 88, 
-	260, 2, 89, 90, 2, 91, 92, 2, 
-	93, 94, 2, 93, 95, 2, 93, 96, 
-	2, 97, 98, 2, 99, 260, 2, 102, 
-	260, 2, 103, 0, 2, 119, 260, 2, 
-	121, 0, 2, 122, 0, 2, 123, 0, 
-	2, 124, 0, 2, 125, 0, 2, 126, 
-	0, 2, 127, 0, 2, 128, 0, 2, 
-	129, 0, 2, 130, 0, 2, 131, 0, 
-	2, 132, 0, 2, 133, 0, 2, 134, 
-	0, 2, 135, 0, 2, 136, 0, 2, 
-	137, 0, 2, 138, 0, 2, 139, 0, 
-	2, 140, 0, 2, 141, 0, 2, 142, 
-	0, 2, 143, 0, 2, 144, 0, 2, 
-	145, 260, 2, 146, 260, 2, 147, 260, 
-	2, 148, 1, 2, 148, 2, 2, 148, 
-	3, 2, 149, 1, 2, 149, 2, 2, 
-	149, 3, 2, 150, 1, 2, 150, 2, 
-	2, 150, 3, 2, 151, 1, 2, 151, 
-	2, 2, 151, 3, 2, 152, 1, 2, 
-	152, 2, 2, 152, 3, 2, 153, 1, 
-	2, 153, 2, 2, 153, 3, 2, 154, 
-	1, 2, 154, 2, 2, 154, 3, 2, 
-	155, 1, 2, 155, 2, 2, 155, 3, 
-	2, 156, 1, 2, 156, 2, 2, 156, 
-	3, 2, 157, 1, 2, 157, 2, 2, 
-	157, 3, 2, 158, 1, 2, 158, 2, 
-	2, 158, 3, 2, 159, 1, 2, 159, 
-	2, 2, 159, 3, 2, 160, 1, 2, 
-	160, 2, 2, 160, 3, 2, 161, 1, 
-	2, 161, 2, 2, 161, 3, 2, 162, 
-	1, 2, 162, 2, 2, 162, 3, 2, 
-	163, 1, 2, 163, 2, 2, 163, 3, 
-	2, 164, 1, 2, 164, 2, 2, 164, 
-	3, 2, 165, 1, 2, 165, 2, 2, 
-	165, 3, 2, 166, 1, 2, 166, 2, 
-	2, 166, 3, 2, 167, 1, 2, 167, 
-	2, 2, 167, 3, 2, 168, 1, 2, 
-	168, 2, 2, 168, 3, 2, 169, 1, 
-	2, 169, 2, 2, 169, 3, 2, 170, 
-	1, 2, 170, 2, 2, 170, 3, 2, 
-	171, 1, 2, 171, 2, 2, 171, 3, 
-	2, 172, 1, 2, 172, 2, 2, 172, 
-	3, 2, 173, 1, 2, 173, 2, 2, 
-	173, 3, 2, 174, 1, 2, 174, 2, 
-	2, 174, 3, 2, 175, 1, 2, 175, 
-	2, 2, 175, 3, 2, 176, 1, 2, 
-	176, 2, 2, 176, 3, 2, 177, 1, 
-	2, 177, 2, 2, 177, 3, 2, 178, 
-	1, 2, 178, 2, 2, 178, 3, 2, 
-	179, 1, 2, 179, 2, 2, 179, 3, 
-	2, 180, 1, 2, 180, 2, 2, 180, 
-	3, 2, 181, 1, 2, 181, 2, 2, 
-	181, 3, 2, 182, 1, 2, 182, 2, 
-	2, 182, 3, 2, 183, 1, 2, 183, 
-	2, 2, 183, 3, 2, 184, 1, 2, 
-	184, 2, 2, 184, 3, 2, 185, 1, 
-	2, 185, 2, 2, 185, 3, 2, 186, 
-	1, 2, 186, 2, 2, 186, 3, 2, 
-	187, 1, 2, 187, 2, 2, 187, 3, 
-	2, 188, 1, 2, 188, 2, 2, 188, 
-	3, 2, 189, 1, 2, 189, 2, 2, 
-	189, 3, 2, 190, 1, 2, 190, 2, 
-	2, 190, 3, 2, 191, 1, 2, 191, 
-	2, 2, 191, 3, 2, 192, 1, 2, 
-	192, 2, 2, 192, 3, 2, 193, 1, 
-	2, 193, 2, 2, 193, 3, 2, 194, 
-	1, 2, 194, 2, 2, 194, 3, 2, 
-	195, 1, 2, 195, 2, 2, 195, 3, 
-	2, 196, 1, 2, 196, 2, 2, 196, 
-	3, 2, 197, 1, 2, 197, 2, 2, 
-	197, 3, 2, 198, 1, 2, 198, 2, 
-	2, 198, 3, 2, 199, 1, 2, 199, 
-	2, 2, 199, 3, 2, 200, 1, 2, 
-	200, 2, 2, 200, 3, 2, 201, 1, 
-	2, 201, 2, 2, 201, 3, 2, 202, 
-	1, 2, 202, 2, 2, 202, 3, 2, 
-	203, 1, 2, 203, 2, 2, 203, 3, 
-	2, 204, 1, 2, 204, 2, 2, 204, 
-	3, 2, 205, 1, 2, 205, 2, 2, 
-	205, 3, 2, 206, 1, 2, 206, 2, 
-	2, 206, 3, 2, 207, 1, 2, 207, 
-	2, 2, 207, 3, 2, 208, 1, 2, 
-	208, 2, 2, 208, 3, 2, 209, 1, 
-	2, 209, 2, 2, 209, 3, 2, 210, 
-	1, 2, 210, 2, 2, 210, 3, 2, 
-	211, 1, 2, 211, 2, 2, 211, 3, 
-	2, 212, 1, 2, 212, 2, 2, 212, 
-	3, 2, 213, 1, 2, 213, 2, 2, 
-	213, 3, 2, 214, 1, 2, 214, 2, 
-	2, 214, 3, 2, 215, 1, 2, 215, 
-	2, 2, 215, 3, 2, 216, 1, 2, 
-	216, 2, 2, 216, 3, 2, 217, 1, 
-	2, 217, 2, 2, 217, 3, 2, 218, 
-	1, 2, 218, 2, 2, 218, 3, 2, 
-	219, 1, 2, 219, 2, 2, 219, 3, 
-	2, 220, 1, 2, 220, 2, 2, 220, 
-	3, 2, 221, 1, 2, 221, 2, 2, 
-	221, 3, 2, 222, 1, 2, 222, 2, 
-	2, 222, 3, 2, 223, 1, 2, 223, 
-	2, 2, 223, 3, 2, 224, 1, 2, 
-	224, 2, 2, 224, 3, 2, 225, 1, 
-	2, 225, 2, 2, 225, 3, 2, 226, 
-	0, 2, 229, 1, 2, 229, 2, 2, 
-	229, 3, 2, 230, 1, 2, 230, 2, 
-	2, 230, 3, 2, 231, 1, 2, 231, 
-	2, 2, 231, 3, 2, 232, 1, 2, 
-	232, 2, 2, 232, 3, 2, 239, 1, 
-	2, 239, 2, 2, 239, 3, 2, 240, 
-	1, 2, 240, 2, 2, 240, 3, 2, 
-	243, 0, 2, 244, 260, 2, 246, 100, 
-	2, 251, 100, 2, 260, 4, 2, 261, 
-	1, 2, 261, 27, 2, 263, 2, 2, 
-	263, 3, 2, 264, 261, 2, 265, 4, 
-	2, 265, 260, 2, 266, 259, 2, 267, 
-	259, 2, 268, 259, 2, 269, 259, 2, 
-	270, 259, 2, 271, 259, 2, 272, 259, 
-	2, 273, 259, 2, 274, 259, 2, 275, 
-	259, 2, 276, 259, 2, 277, 259, 2, 
-	278, 259, 2, 279, 259, 2, 280, 259, 
-	2, 281, 259, 2, 282, 259, 2, 283, 
-	259, 2, 284, 259, 2, 285, 259, 2, 
-	286, 259, 2, 287, 259, 2, 288, 259, 
-	2, 289, 259, 2, 290, 259, 2, 291, 
-	259, 2, 292, 259, 2, 293, 259, 2, 
-	294, 259, 2, 295, 259, 2, 296, 259, 
-	2, 297, 259, 2, 298, 259, 2, 299, 
-	259, 2, 300, 259, 2, 301, 259, 2, 
-	302, 259, 2, 303, 259, 2, 304, 1, 
-	3, 0, 44, 1, 3, 0, 44, 2, 
-	3, 0, 44, 3, 3, 1, 77, 0, 
-	3, 1, 103, 0, 3, 1, 226, 0, 
-	3, 1, 243, 0, 3, 1, 261, 27, 
-	3, 1, 261, 304, 3, 1, 263, 304, 
-	3, 1, 304, 27, 3, 7, 1, 8, 
-	3, 11, 17, 0, 3, 16, 75, 2, 
-	3, 16, 75, 3, 3, 19, 9, 10, 
-	3, 23, 1, 0, 3, 23, 24, 1, 
-	3, 23, 24, 2, 3, 23, 24, 3, 
-	3, 26, 1, 85, 3, 26, 2, 85, 
-	3, 26, 2, 263, 3, 26, 3, 85, 
-	3, 26, 3, 263, 3, 26, 85, 263, 
-	3, 26, 264, 261, 3, 26, 266, 259, 
-	3, 28, 81, 4, 3, 28, 260, 4, 
-	3, 28, 265, 4, 3, 28, 265, 260, 
-	3, 30, 1, 0, 3, 30, 24, 1, 
-	3, 30, 24, 2, 3, 30, 24, 3, 
-	3, 30, 24, 228, 3, 32, 31, 261, 
-	3, 33, 145, 260, 3, 33, 147, 260, 
-	3, 33, 265, 4, 3, 33, 265, 260, 
-	3, 35, 37, 233, 3, 35, 37, 234, 
-	3, 35, 37, 235, 3, 35, 37, 236, 
-	3, 35, 37, 237, 3, 35, 37, 238, 
-	3, 37, 233, 1, 3, 37, 233, 2, 
-	3, 37, 233, 3, 3, 37, 234, 1, 
-	3, 37, 234, 2, 3, 37, 234, 3, 
-	3, 37, 235, 1, 3, 37, 235, 2, 
-	3, 37, 235, 3, 3, 37, 236, 1, 
-	3, 37, 236, 2, 3, 37, 236, 3, 
-	3, 37, 237, 1, 3, 37, 237, 2, 
-	3, 37, 237, 3, 3, 37, 238, 1, 
-	3, 37, 238, 2, 3, 37, 238, 3, 
-	3, 41, 24, 1, 3, 41, 24, 2, 
-	3, 41, 24, 3, 3, 43, 259, 1, 
-	3, 43, 259, 2, 3, 43, 259, 3, 
-	3, 43, 259, 261, 3, 45, 33, 65, 
-	3, 45, 33, 260, 3, 45, 33, 265, 
-	3, 50, 32, 31, 3, 51, 42, 0, 
-	3, 51, 42, 1, 3, 51, 42, 2, 
-	3, 51, 42, 3, 3, 51, 85, 1, 
-	3, 51, 85, 2, 3, 51, 85, 3, 
-	3, 61, 56, 0, 3, 62, 57, 58, 
-	3, 64, 0, 1, 3, 67, 19, 9, 
-	3, 68, 0, 1, 3, 74, 19, 9, 
-	3, 77, 0, 1, 3, 80, 25, 21, 
-	3, 80, 27, 1, 3, 80, 27, 2, 
-	3, 80, 27, 3, 3, 82, 25, 21, 
-	3, 82, 83, 261, 3, 85, 2, 263, 
-	3, 85, 3, 263, 3, 88, 99, 260, 
-	3, 88, 145, 260, 3, 89, 90, 0, 
-	3, 89, 90, 1, 3, 89, 90, 2, 
-	3, 89, 90, 3, 3, 91, 92, 0, 
-	3, 91, 92, 1, 3, 91, 92, 2, 
-	3, 91, 92, 3, 3, 97, 98, 0, 
-	3, 97, 98, 1, 3, 97, 98, 2, 
-	3, 97, 98, 3, 3, 102, 104, 245, 
-	3, 102, 254, 260, 3, 145, 146, 260, 
-	3, 186, 226, 0, 3, 187, 226, 0, 
-	3, 188, 226, 0, 3, 189, 226, 0, 
-	3, 190, 226, 0, 3, 191, 226, 0, 
-	3, 192, 226, 0, 3, 193, 226, 0, 
-	3, 194, 226, 0, 3, 195, 226, 0, 
-	3, 196, 226, 0, 3, 197, 226, 0, 
-	3, 198, 226, 0, 3, 199, 226, 0, 
-	3, 200, 226, 0, 3, 201, 226, 0, 
-	3, 202, 226, 0, 3, 203, 226, 0, 
-	3, 204, 226, 0, 3, 205, 226, 0, 
-	3, 206, 226, 0, 3, 207, 226, 0, 
-	3, 208, 226, 0, 3, 209, 226, 0, 
-	3, 210, 226, 0, 3, 211, 226, 0, 
-	3, 212, 226, 0, 3, 213, 226, 0, 
-	3, 214, 226, 0, 3, 215, 226, 0, 
-	3, 216, 226, 0, 3, 217, 226, 0, 
-	3, 218, 226, 0, 3, 219, 226, 0, 
-	3, 220, 226, 0, 3, 221, 226, 0, 
-	3, 222, 226, 0, 3, 223, 226, 0, 
-	3, 224, 226, 0, 3, 225, 226, 0, 
-	3, 242, 32, 31, 3, 247, 248, 0, 
-	3, 247, 249, 0, 3, 250, 102, 260, 
-	3, 252, 253, 0, 3, 254, 102, 260, 
-	3, 260, 4, 81, 3, 261, 25, 21, 
-	3, 261, 304, 1, 3, 263, 304, 1, 
-	3, 265, 260, 4, 3, 266, 259, 1, 
-	3, 266, 259, 2, 3, 266, 259, 3, 
-	3, 266, 259, 261, 3, 266, 259, 263, 
-	3, 267, 259, 1, 3, 267, 259, 2, 
-	3, 267, 259, 3, 3, 267, 259, 261, 
-	3, 268, 259, 1, 3, 268, 259, 2, 
-	3, 268, 259, 3, 3, 268, 259, 261, 
-	3, 269, 259, 1, 3, 269, 259, 2, 
-	3, 269, 259, 3, 3, 269, 259, 261, 
-	3, 270, 259, 1, 3, 270, 259, 2, 
-	3, 270, 259, 3, 3, 270, 259, 261, 
-	3, 271, 259, 1, 3, 271, 259, 2, 
-	3, 271, 259, 3, 3, 271, 259, 261, 
-	3, 272, 259, 1, 3, 272, 259, 2, 
-	3, 272, 259, 3, 3, 272, 259, 261, 
-	3, 273, 259, 1, 3, 273, 259, 2, 
-	3, 273, 259, 3, 3, 273, 259, 261, 
-	3, 274, 259, 1, 3, 274, 259, 2, 
-	3, 274, 259, 3, 3, 274, 259, 261, 
-	3, 275, 259, 1, 3, 275, 259, 2, 
-	3, 275, 259, 3, 3, 275, 259, 261, 
-	3, 276, 259, 1, 3, 276, 259, 2, 
-	3, 276, 259, 3, 3, 276, 259, 261, 
-	3, 277, 259, 1, 3, 277, 259, 2, 
-	3, 277, 259, 3, 3, 277, 259, 261, 
-	3, 278, 259, 1, 3, 278, 259, 2, 
-	3, 278, 259, 3, 3, 278, 259, 261, 
-	3, 279, 259, 1, 3, 279, 259, 2, 
-	3, 279, 259, 3, 3, 279, 259, 261, 
-	3, 280, 259, 1, 3, 280, 259, 2, 
-	3, 280, 259, 3, 3, 280, 259, 261, 
-	3, 281, 259, 1, 3, 281, 259, 2, 
-	3, 281, 259, 3, 3, 281, 259, 261, 
-	3, 282, 259, 1, 3, 282, 259, 2, 
-	3, 282, 259, 3, 3, 282, 259, 261, 
-	3, 283, 259, 1, 3, 283, 259, 2, 
-	3, 283, 259, 3, 3, 283, 259, 261, 
-	3, 284, 259, 1, 3, 284, 259, 2, 
-	3, 284, 259, 3, 3, 284, 259, 261, 
-	3, 285, 259, 1, 3, 285, 259, 2, 
-	3, 285, 259, 3, 3, 285, 259, 261, 
-	3, 286, 259, 1, 3, 286, 259, 2, 
-	3, 286, 259, 3, 3, 286, 259, 261, 
-	3, 287, 259, 1, 3, 287, 259, 2, 
-	3, 287, 259, 3, 3, 287, 259, 261, 
-	3, 288, 259, 1, 3, 288, 259, 2, 
-	3, 288, 259, 3, 3, 288, 259, 261, 
-	3, 289, 259, 1, 3, 289, 259, 2, 
-	3, 289, 259, 3, 3, 289, 259, 261, 
-	3, 290, 259, 1, 3, 290, 259, 2, 
-	3, 290, 259, 3, 3, 290, 259, 261, 
-	3, 291, 259, 1, 3, 291, 259, 2, 
-	3, 291, 259, 3, 3, 291, 259, 261, 
-	3, 292, 259, 1, 3, 292, 259, 2, 
-	3, 292, 259, 3, 3, 292, 259, 261, 
-	3, 293, 259, 1, 3, 293, 259, 2, 
-	3, 293, 259, 3, 3, 293, 259, 261, 
-	3, 294, 259, 1, 3, 294, 259, 2, 
-	3, 294, 259, 3, 3, 294, 259, 261, 
-	3, 295, 259, 1, 3, 295, 259, 2, 
-	3, 295, 259, 3, 3, 295, 259, 261, 
-	3, 296, 259, 1, 3, 296, 259, 2, 
-	3, 296, 259, 3, 3, 296, 259, 261, 
-	3, 297, 259, 1, 3, 297, 259, 2, 
-	3, 297, 259, 3, 3, 297, 259, 261, 
-	3, 298, 259, 1, 3, 298, 259, 2, 
-	3, 298, 259, 3, 3, 298, 259, 261, 
-	3, 299, 259, 1, 3, 299, 259, 2, 
-	3, 299, 259, 3, 3, 299, 259, 261, 
-	3, 300, 259, 1, 3, 300, 259, 2, 
-	3, 300, 259, 3, 3, 300, 259, 261, 
-	3, 301, 259, 1, 3, 301, 259, 2, 
-	3, 301, 259, 3, 3, 301, 259, 261, 
-	3, 302, 259, 1, 3, 302, 259, 2, 
-	3, 302, 259, 3, 3, 302, 259, 261, 
-	3, 303, 259, 1, 3, 303, 259, 2, 
-	3, 303, 259, 3, 3, 303, 259, 261, 
-	3, 304, 1, 27, 4, 5, 7, 1, 
-	8, 4, 14, 11, 17, 0, 4, 16, 
-	68, 0, 1, 4, 26, 1, 263, 304, 
-	4, 26, 1, 264, 261, 4, 26, 1, 
-	266, 259, 4, 26, 2, 85, 263, 4, 
-	26, 2, 264, 261, 4, 26, 2, 266, 
-	259, 4, 26, 3, 85, 263, 4, 26, 
-	3, 264, 261, 4, 26, 3, 266, 259, 
-	4, 26, 266, 259, 261, 4, 26, 266, 
-	259, 263, 4, 28, 33, 265, 4, 4, 
-	28, 45, 33, 265, 4, 28, 265, 260, 
-	4, 4, 32, 31, 25, 21, 4, 33, 
-	265, 260, 4, 4, 35, 32, 31, 36, 
-	4, 35, 37, 233, 1, 4, 35, 37, 
-	233, 2, 4, 35, 37, 233, 3, 4, 
-	35, 37, 234, 1, 4, 35, 37, 234, 
-	2, 4, 35, 37, 234, 3, 4, 35, 
-	37, 235, 1, 4, 35, 37, 235, 2, 
-	4, 35, 37, 235, 3, 4, 35, 37, 
-	236, 1, 4, 35, 37, 236, 2, 4, 
-	35, 37, 236, 3, 4, 35, 37, 237, 
-	1, 4, 35, 37, 237, 2, 4, 35, 
-	37, 237, 3, 4, 35, 37, 238, 1, 
-	4, 35, 37, 238, 2, 4, 35, 37, 
-	238, 3, 4, 37, 235, 243, 0, 4, 
-	37, 236, 243, 0, 4, 37, 237, 243, 
-	0, 4, 37, 238, 243, 0, 4, 38, 
-	34, 32, 31, 4, 39, 34, 32, 31, 
-	4, 43, 259, 1, 261, 4, 45, 33, 
-	4, 65, 4, 51, 50, 32, 31, 4, 
-	51, 64, 0, 1, 4, 67, 19, 9, 
-	10, 4, 72, 77, 0, 1, 4, 74, 
-	19, 9, 10, 4, 82, 83, 25, 21, 
-	4, 85, 1, 263, 304, 4, 88, 145, 
-	146, 260, 4, 89, 90, 1, 0, 4, 
-	91, 92, 1, 0, 4, 97, 98, 1, 
-	0, 4, 186, 1, 226, 0, 4, 187, 
-	1, 226, 0, 4, 188, 1, 226, 0, 
-	4, 189, 1, 226, 0, 4, 190, 1, 
-	226, 0, 4, 191, 1, 226, 0, 4, 
-	192, 1, 226, 0, 4, 193, 1, 226, 
-	0, 4, 194, 1, 226, 0, 4, 195, 
-	1, 226, 0, 4, 196, 1, 226, 0, 
-	4, 197, 1, 226, 0, 4, 198, 1, 
-	226, 0, 4, 199, 1, 226, 0, 4, 
-	200, 1, 226, 0, 4, 201, 1, 226, 
-	0, 4, 202, 1, 226, 0, 4, 203, 
-	1, 226, 0, 4, 204, 1, 226, 0, 
-	4, 205, 1, 226, 0, 4, 206, 1, 
-	226, 0, 4, 207, 1, 226, 0, 4, 
-	208, 1, 226, 0, 4, 209, 1, 226, 
-	0, 4, 210, 1, 226, 0, 4, 211, 
-	1, 226, 0, 4, 212, 1, 226, 0, 
-	4, 213, 1, 226, 0, 4, 214, 1, 
-	226, 0, 4, 215, 1, 226, 0, 4, 
-	216, 1, 226, 0, 4, 217, 1, 226, 
-	0, 4, 218, 1, 226, 0, 4, 219, 
-	1, 226, 0, 4, 220, 1, 226, 0, 
-	4, 221, 1, 226, 0, 4, 222, 1, 
-	226, 0, 4, 223, 1, 226, 0, 4, 
-	224, 1, 226, 0, 4, 225, 1, 226, 
-	0, 4, 266, 259, 1, 261, 4, 266, 
-	259, 2, 263, 4, 266, 259, 3, 263, 
-	4, 266, 259, 263, 261, 4, 267, 259, 
-	1, 261, 4, 268, 259, 1, 261, 4, 
-	269, 259, 1, 261, 4, 270, 259, 1, 
-	261, 4, 271, 259, 1, 261, 4, 272, 
-	259, 1, 261, 4, 273, 259, 1, 261, 
-	4, 274, 259, 1, 261, 4, 275, 259, 
-	1, 261, 4, 276, 259, 1, 261, 4, 
-	277, 259, 1, 261, 4, 278, 259, 1, 
-	261, 4, 279, 259, 1, 261, 4, 280, 
-	259, 1, 261, 4, 281, 259, 1, 261, 
-	4, 282, 259, 1, 261, 4, 283, 259, 
-	1, 261, 4, 284, 259, 1, 261, 4, 
-	285, 259, 1, 261, 4, 286, 259, 1, 
-	261, 4, 287, 259, 1, 261, 4, 288, 
-	259, 1, 261, 4, 289, 259, 1, 261, 
-	4, 290, 259, 1, 261, 4, 291, 259, 
-	1, 261, 4, 292, 259, 1, 261, 4, 
-	293, 259, 1, 261, 4, 294, 259, 1, 
-	261, 4, 295, 259, 1, 261, 4, 296, 
-	259, 1, 261, 4, 297, 259, 1, 261, 
-	4, 298, 259, 1, 261, 4, 299, 259, 
-	1, 261, 4, 300, 259, 1, 261, 4, 
-	301, 259, 1, 261, 4, 302, 259, 1, 
-	261, 4, 303, 259, 1, 261, 5, 16, 
-	75, 77, 0, 1, 5, 26, 1, 85, 
-	263, 304, 5, 26, 1, 266, 259, 261, 
-	5, 26, 2, 266, 259, 263, 5, 26, 
-	3, 266, 259, 263, 5, 26, 266, 259, 
-	263, 261, 5, 28, 33, 265, 260, 4, 
-	5, 32, 31, 261, 25, 21, 5, 35, 
-	37, 235, 243, 0, 5, 35, 37, 236, 
-	243, 0, 5, 35, 37, 237, 243, 0, 
-	5, 35, 37, 238, 243, 0, 5, 37, 
-	235, 1, 243, 0, 5, 37, 236, 1, 
-	243, 0, 5, 37, 237, 1, 243, 0, 
-	5, 37, 238, 1, 243, 0, 5, 45, 
-	33, 265, 260, 4, 5, 82, 83, 261, 
-	25, 21, 5, 241, 38, 34, 32, 31, 
-	5, 266, 259, 1, 263, 304, 6, 26, 
-	1, 266, 259, 263, 304, 6, 28, 45, 
-	33, 265, 260, 4, 6, 35, 37, 235, 
-	1, 243, 0, 6, 35, 37, 236, 1, 
-	243, 0, 6, 35, 37, 237, 1, 243, 
-	0, 6, 35, 37, 238, 1, 243, 0, 
-	6, 266, 259, 1, 263, 261, 304, 6, 
-	266, 259, 263, 261, 304, 1, 7, 26, 
-	1, 266, 259, 263, 261, 304
+	85, 86, 2, 87, 259, 2, 88, 89, 
+	2, 90, 91, 2, 92, 93, 2, 92, 
+	94, 2, 92, 95, 2, 96, 97, 2, 
+	98, 259, 2, 101, 259, 2, 102, 0, 
+	2, 118, 259, 2, 120, 0, 2, 121, 
+	0, 2, 122, 0, 2, 123, 0, 2, 
+	124, 0, 2, 125, 0, 2, 126, 0, 
+	2, 127, 0, 2, 128, 0, 2, 129, 
+	0, 2, 130, 0, 2, 131, 0, 2, 
+	132, 0, 2, 133, 0, 2, 134, 0, 
+	2, 135, 0, 2, 136, 0, 2, 137, 
+	0, 2, 138, 0, 2, 139, 0, 2, 
+	140, 0, 2, 141, 0, 2, 142, 0, 
+	2, 143, 0, 2, 144, 259, 2, 145, 
+	259, 2, 146, 259, 2, 147, 1, 2, 
+	147, 2, 2, 147, 3, 2, 148, 1, 
+	2, 148, 2, 2, 148, 3, 2, 149, 
+	1, 2, 149, 2, 2, 149, 3, 2, 
+	150, 1, 2, 150, 2, 2, 150, 3, 
+	2, 151, 1, 2, 151, 2, 2, 151, 
+	3, 2, 152, 1, 2, 152, 2, 2, 
+	152, 3, 2, 153, 1, 2, 153, 2, 
+	2, 153, 3, 2, 154, 1, 2, 154, 
+	2, 2, 154, 3, 2, 155, 1, 2, 
+	155, 2, 2, 155, 3, 2, 156, 1, 
+	2, 156, 2, 2, 156, 3, 2, 157, 
+	1, 2, 157, 2, 2, 157, 3, 2, 
+	158, 1, 2, 158, 2, 2, 158, 3, 
+	2, 159, 1, 2, 159, 2, 2, 159, 
+	3, 2, 160, 1, 2, 160, 2, 2, 
+	160, 3, 2, 161, 1, 2, 161, 2, 
+	2, 161, 3, 2, 162, 1, 2, 162, 
+	2, 2, 162, 3, 2, 163, 1, 2, 
+	163, 2, 2, 163, 3, 2, 164, 1, 
+	2, 164, 2, 2, 164, 3, 2, 165, 
+	1, 2, 165, 2, 2, 165, 3, 2, 
+	166, 1, 2, 166, 2, 2, 166, 3, 
+	2, 167, 1, 2, 167, 2, 2, 167, 
+	3, 2, 168, 1, 2, 168, 2, 2, 
+	168, 3, 2, 169, 1, 2, 169, 2, 
+	2, 169, 3, 2, 170, 1, 2, 170, 
+	2, 2, 170, 3, 2, 171, 1, 2, 
+	171, 2, 2, 171, 3, 2, 172, 1, 
+	2, 172, 2, 2, 172, 3, 2, 173, 
+	1, 2, 173, 2, 2, 173, 3, 2, 
+	174, 1, 2, 174, 2, 2, 174, 3, 
+	2, 175, 1, 2, 175, 2, 2, 175, 
+	3, 2, 176, 1, 2, 176, 2, 2, 
+	176, 3, 2, 177, 1, 2, 177, 2, 
+	2, 177, 3, 2, 178, 1, 2, 178, 
+	2, 2, 178, 3, 2, 179, 1, 2, 
+	179, 2, 2, 179, 3, 2, 180, 1, 
+	2, 180, 2, 2, 180, 3, 2, 181, 
+	1, 2, 181, 2, 2, 181, 3, 2, 
+	182, 1, 2, 182, 2, 2, 182, 3, 
+	2, 183, 1, 2, 183, 2, 2, 183, 
+	3, 2, 184, 1, 2, 184, 2, 2, 
+	184, 3, 2, 185, 1, 2, 185, 2, 
+	2, 185, 3, 2, 186, 1, 2, 186, 
+	2, 2, 186, 3, 2, 187, 1, 2, 
+	187, 2, 2, 187, 3, 2, 188, 1, 
+	2, 188, 2, 2, 188, 3, 2, 189, 
+	1, 2, 189, 2, 2, 189, 3, 2, 
+	190, 1, 2, 190, 2, 2, 190, 3, 
+	2, 191, 1, 2, 191, 2, 2, 191, 
+	3, 2, 192, 1, 2, 192, 2, 2, 
+	192, 3, 2, 193, 1, 2, 193, 2, 
+	2, 193, 3, 2, 194, 1, 2, 194, 
+	2, 2, 194, 3, 2, 195, 1, 2, 
+	195, 2, 2, 195, 3, 2, 196, 1, 
+	2, 196, 2, 2, 196, 3, 2, 197, 
+	1, 2, 197, 2, 2, 197, 3, 2, 
+	198, 1, 2, 198, 2, 2, 198, 3, 
+	2, 199, 1, 2, 199, 2, 2, 199, 
+	3, 2, 200, 1, 2, 200, 2, 2, 
+	200, 3, 2, 201, 1, 2, 201, 2, 
+	2, 201, 3, 2, 202, 1, 2, 202, 
+	2, 2, 202, 3, 2, 203, 1, 2, 
+	203, 2, 2, 203, 3, 2, 204, 1, 
+	2, 204, 2, 2, 204, 3, 2, 205, 
+	1, 2, 205, 2, 2, 205, 3, 2, 
+	206, 1, 2, 206, 2, 2, 206, 3, 
+	2, 207, 1, 2, 207, 2, 2, 207, 
+	3, 2, 208, 1, 2, 208, 2, 2, 
+	208, 3, 2, 209, 1, 2, 209, 2, 
+	2, 209, 3, 2, 210, 1, 2, 210, 
+	2, 2, 210, 3, 2, 211, 1, 2, 
+	211, 2, 2, 211, 3, 2, 212, 1, 
+	2, 212, 2, 2, 212, 3, 2, 213, 
+	1, 2, 213, 2, 2, 213, 3, 2, 
+	214, 1, 2, 214, 2, 2, 214, 3, 
+	2, 215, 1, 2, 215, 2, 2, 215, 
+	3, 2, 216, 1, 2, 216, 2, 2, 
+	216, 3, 2, 217, 1, 2, 217, 2, 
+	2, 217, 3, 2, 218, 1, 2, 218, 
+	2, 2, 218, 3, 2, 219, 1, 2, 
+	219, 2, 2, 219, 3, 2, 220, 1, 
+	2, 220, 2, 2, 220, 3, 2, 221, 
+	1, 2, 221, 2, 2, 221, 3, 2, 
+	222, 1, 2, 222, 2, 2, 222, 3, 
+	2, 223, 1, 2, 223, 2, 2, 223, 
+	3, 2, 224, 1, 2, 224, 2, 2, 
+	224, 3, 2, 225, 0, 2, 228, 1, 
+	2, 228, 2, 2, 228, 3, 2, 229, 
+	1, 2, 229, 2, 2, 229, 3, 2, 
+	230, 1, 2, 230, 2, 2, 230, 3, 
+	2, 231, 1, 2, 231, 2, 2, 231, 
+	3, 2, 238, 1, 2, 238, 2, 2, 
+	238, 3, 2, 239, 1, 2, 239, 2, 
+	2, 239, 3, 2, 242, 0, 2, 243, 
+	259, 2, 245, 99, 2, 250, 99, 2, 
+	259, 4, 2, 260, 1, 2, 260, 26, 
+	2, 262, 260, 2, 263, 4, 2, 263, 
+	259, 2, 264, 258, 2, 265, 258, 2, 
+	266, 258, 2, 267, 258, 2, 268, 258, 
+	2, 269, 258, 2, 270, 258, 2, 271, 
+	258, 2, 272, 258, 2, 273, 258, 2, 
+	274, 258, 2, 275, 258, 2, 276, 258, 
+	2, 277, 258, 2, 278, 258, 2, 279, 
+	258, 2, 280, 258, 2, 281, 258, 2, 
+	282, 258, 2, 283, 258, 2, 284, 258, 
+	2, 285, 258, 2, 286, 258, 2, 287, 
+	258, 2, 288, 258, 2, 289, 258, 2, 
+	290, 258, 2, 291, 258, 2, 292, 258, 
+	2, 293, 258, 2, 294, 258, 2, 295, 
+	258, 2, 296, 258, 2, 297, 258, 2, 
+	298, 258, 2, 299, 258, 2, 300, 258, 
+	2, 301, 258, 2, 302, 1, 3, 0, 
+	43, 1, 3, 0, 43, 2, 3, 0, 
+	43, 3, 3, 1, 76, 0, 3, 1, 
+	102, 0, 3, 1, 225, 0, 3, 1, 
+	242, 0, 3, 1, 260, 26, 3, 1, 
+	260, 302, 3, 1, 302, 26, 3, 7, 
+	1, 8, 3, 11, 17, 0, 3, 16, 
+	74, 2, 3, 16, 74, 3, 3, 19, 
+	9, 10, 3, 23, 1, 0, 3, 25, 
+	1, 84, 3, 25, 1, 302, 3, 25, 
+	2, 84, 3, 25, 3, 84, 3, 25, 
+	262, 260, 3, 25, 264, 258, 3, 27, 
+	80, 4, 3, 27, 259, 4, 3, 27, 
+	263, 4, 3, 27, 263, 259, 3, 29, 
+	1, 0, 3, 31, 30, 260, 3, 32, 
+	144, 259, 3, 32, 146, 259, 3, 32, 
+	263, 4, 3, 32, 263, 259, 3, 34, 
+	36, 232, 3, 34, 36, 233, 3, 34, 
+	36, 234, 3, 34, 36, 235, 3, 34, 
+	36, 236, 3, 34, 36, 237, 3, 36, 
+	232, 1, 3, 36, 232, 2, 3, 36, 
+	232, 3, 3, 36, 233, 1, 3, 36, 
+	233, 2, 3, 36, 233, 3, 3, 36, 
+	234, 1, 3, 36, 234, 2, 3, 36, 
+	234, 3, 3, 36, 235, 1, 3, 36, 
+	235, 2, 3, 36, 235, 3, 3, 36, 
+	236, 1, 3, 36, 236, 2, 3, 36, 
+	236, 3, 3, 36, 237, 1, 3, 36, 
+	237, 2, 3, 36, 237, 3, 3, 42, 
+	258, 1, 3, 42, 258, 2, 3, 42, 
+	258, 3, 3, 42, 258, 260, 3, 44, 
+	32, 64, 3, 44, 32, 259, 3, 44, 
+	32, 263, 3, 49, 31, 30, 3, 50, 
+	41, 0, 3, 50, 41, 1, 3, 50, 
+	41, 2, 3, 50, 41, 3, 3, 50, 
+	84, 1, 3, 50, 84, 2, 3, 50, 
+	84, 3, 3, 60, 55, 0, 3, 61, 
+	56, 57, 3, 63, 0, 1, 3, 66, 
+	19, 9, 3, 67, 0, 1, 3, 73, 
+	19, 9, 3, 76, 0, 1, 3, 79, 
+	24, 21, 3, 79, 26, 1, 3, 79, 
+	26, 2, 3, 79, 26, 3, 3, 81, 
+	24, 21, 3, 81, 82, 260, 3, 84, 
+	1, 302, 3, 87, 98, 259, 3, 87, 
+	144, 259, 3, 88, 89, 0, 3, 88, 
+	89, 1, 3, 88, 89, 2, 3, 88, 
+	89, 3, 3, 90, 91, 0, 3, 90, 
+	91, 1, 3, 90, 91, 2, 3, 90, 
+	91, 3, 3, 96, 97, 0, 3, 96, 
+	97, 1, 3, 96, 97, 2, 3, 96, 
+	97, 3, 3, 101, 103, 244, 3, 101, 
+	253, 259, 3, 144, 145, 259, 3, 185, 
+	225, 0, 3, 186, 225, 0, 3, 187, 
+	225, 0, 3, 188, 225, 0, 3, 189, 
+	225, 0, 3, 190, 225, 0, 3, 191, 
+	225, 0, 3, 192, 225, 0, 3, 193, 
+	225, 0, 3, 194, 225, 0, 3, 195, 
+	225, 0, 3, 196, 225, 0, 3, 197, 
+	225, 0, 3, 198, 225, 0, 3, 199, 
+	225, 0, 3, 200, 225, 0, 3, 201, 
+	225, 0, 3, 202, 225, 0, 3, 203, 
+	225, 0, 3, 204, 225, 0, 3, 205, 
+	225, 0, 3, 206, 225, 0, 3, 207, 
+	225, 0, 3, 208, 225, 0, 3, 209, 
+	225, 0, 3, 210, 225, 0, 3, 211, 
+	225, 0, 3, 212, 225, 0, 3, 213, 
+	225, 0, 3, 214, 225, 0, 3, 215, 
+	225, 0, 3, 216, 225, 0, 3, 217, 
+	225, 0, 3, 218, 225, 0, 3, 219, 
+	225, 0, 3, 220, 225, 0, 3, 221, 
+	225, 0, 3, 222, 225, 0, 3, 223, 
+	225, 0, 3, 224, 225, 0, 3, 241, 
+	31, 30, 3, 246, 247, 0, 3, 246, 
+	248, 0, 3, 249, 101, 259, 3, 251, 
+	252, 0, 3, 253, 101, 259, 3, 259, 
+	4, 80, 3, 260, 24, 21, 3, 260, 
+	302, 1, 3, 263, 259, 4, 3, 264, 
+	258, 1, 3, 264, 258, 2, 3, 264, 
+	258, 3, 3, 264, 258, 260, 3, 265, 
+	258, 1, 3, 265, 258, 2, 3, 265, 
+	258, 3, 3, 265, 258, 260, 3, 266, 
+	258, 1, 3, 266, 258, 2, 3, 266, 
+	258, 3, 3, 266, 258, 260, 3, 267, 
+	258, 1, 3, 267, 258, 2, 3, 267, 
+	258, 3, 3, 267, 258, 260, 3, 268, 
+	258, 1, 3, 268, 258, 2, 3, 268, 
+	258, 3, 3, 268, 258, 260, 3, 269, 
+	258, 1, 3, 269, 258, 2, 3, 269, 
+	258, 3, 3, 269, 258, 260, 3, 270, 
+	258, 1, 3, 270, 258, 2, 3, 270, 
+	258, 3, 3, 270, 258, 260, 3, 271, 
+	258, 1, 3, 271, 258, 2, 3, 271, 
+	258, 3, 3, 271, 258, 260, 3, 272, 
+	258, 1, 3, 272, 258, 2, 3, 272, 
+	258, 3, 3, 272, 258, 260, 3, 273, 
+	258, 1, 3, 273, 258, 2, 3, 273, 
+	258, 3, 3, 273, 258, 260, 3, 274, 
+	258, 1, 3, 274, 258, 2, 3, 274, 
+	258, 3, 3, 274, 258, 260, 3, 275, 
+	258, 1, 3, 275, 258, 2, 3, 275, 
+	258, 3, 3, 275, 258, 260, 3, 276, 
+	258, 1, 3, 276, 258, 2, 3, 276, 
+	258, 3, 3, 276, 258, 260, 3, 277, 
+	258, 1, 3, 277, 258, 2, 3, 277, 
+	258, 3, 3, 277, 258, 260, 3, 278, 
+	258, 1, 3, 278, 258, 2, 3, 278, 
+	258, 3, 3, 278, 258, 260, 3, 279, 
+	258, 1, 3, 279, 258, 2, 3, 279, 
+	258, 3, 3, 279, 258, 260, 3, 280, 
+	258, 1, 3, 280, 258, 2, 3, 280, 
+	258, 3, 3, 280, 258, 260, 3, 281, 
+	258, 1, 3, 281, 258, 2, 3, 281, 
+	258, 3, 3, 281, 258, 260, 3, 282, 
+	258, 1, 3, 282, 258, 2, 3, 282, 
+	258, 3, 3, 282, 258, 260, 3, 283, 
+	258, 1, 3, 283, 258, 2, 3, 283, 
+	258, 3, 3, 283, 258, 260, 3, 284, 
+	258, 1, 3, 284, 258, 2, 3, 284, 
+	258, 3, 3, 284, 258, 260, 3, 285, 
+	258, 1, 3, 285, 258, 2, 3, 285, 
+	258, 3, 3, 285, 258, 260, 3, 286, 
+	258, 1, 3, 286, 258, 2, 3, 286, 
+	258, 3, 3, 286, 258, 260, 3, 287, 
+	258, 1, 3, 287, 258, 2, 3, 287, 
+	258, 3, 3, 287, 258, 260, 3, 288, 
+	258, 1, 3, 288, 258, 2, 3, 288, 
+	258, 3, 3, 288, 258, 260, 3, 289, 
+	258, 1, 3, 289, 258, 2, 3, 289, 
+	258, 3, 3, 289, 258, 260, 3, 290, 
+	258, 1, 3, 290, 258, 2, 3, 290, 
+	258, 3, 3, 290, 258, 260, 3, 291, 
+	258, 1, 3, 291, 258, 2, 3, 291, 
+	258, 3, 3, 291, 258, 260, 3, 292, 
+	258, 1, 3, 292, 258, 2, 3, 292, 
+	258, 3, 3, 292, 258, 260, 3, 293, 
+	258, 1, 3, 293, 258, 2, 3, 293, 
+	258, 3, 3, 293, 258, 260, 3, 294, 
+	258, 1, 3, 294, 258, 2, 3, 294, 
+	258, 3, 3, 294, 258, 260, 3, 295, 
+	258, 1, 3, 295, 258, 2, 3, 295, 
+	258, 3, 3, 295, 258, 260, 3, 296, 
+	258, 1, 3, 296, 258, 2, 3, 296, 
+	258, 3, 3, 296, 258, 260, 3, 297, 
+	258, 1, 3, 297, 258, 2, 3, 297, 
+	258, 3, 3, 297, 258, 260, 3, 298, 
+	258, 1, 3, 298, 258, 2, 3, 298, 
+	258, 3, 3, 298, 258, 260, 3, 299, 
+	258, 1, 3, 299, 258, 2, 3, 299, 
+	258, 3, 3, 299, 258, 260, 3, 300, 
+	258, 1, 3, 300, 258, 2, 3, 300, 
+	258, 3, 3, 300, 258, 260, 3, 301, 
+	258, 1, 3, 301, 258, 2, 3, 301, 
+	258, 3, 3, 301, 258, 260, 3, 302, 
+	1, 26, 4, 5, 7, 1, 8, 4, 
+	14, 11, 17, 0, 4, 16, 67, 0, 
+	1, 4, 25, 1, 84, 302, 4, 25, 
+	1, 262, 260, 4, 25, 1, 264, 258, 
+	4, 25, 2, 262, 260, 4, 25, 2, 
+	264, 258, 4, 25, 3, 262, 260, 4, 
+	25, 3, 264, 258, 4, 25, 264, 258, 
+	260, 4, 27, 32, 263, 4, 4, 27, 
+	44, 32, 263, 4, 27, 263, 259, 4, 
+	4, 31, 30, 24, 21, 4, 32, 263, 
+	259, 4, 4, 34, 31, 30, 35, 4, 
+	34, 36, 232, 1, 4, 34, 36, 232, 
+	2, 4, 34, 36, 232, 3, 4, 34, 
+	36, 233, 1, 4, 34, 36, 233, 2, 
+	4, 34, 36, 233, 3, 4, 34, 36, 
+	234, 1, 4, 34, 36, 234, 2, 4, 
+	34, 36, 234, 3, 4, 34, 36, 235, 
+	1, 4, 34, 36, 235, 2, 4, 34, 
+	36, 235, 3, 4, 34, 36, 236, 1, 
+	4, 34, 36, 236, 2, 4, 34, 36, 
+	236, 3, 4, 34, 36, 237, 1, 4, 
+	34, 36, 237, 2, 4, 34, 36, 237, 
+	3, 4, 36, 234, 242, 0, 4, 36, 
+	235, 242, 0, 4, 36, 236, 242, 0, 
+	4, 36, 237, 242, 0, 4, 37, 33, 
+	31, 30, 4, 38, 33, 31, 30, 4, 
+	42, 258, 1, 260, 4, 44, 32, 4, 
+	64, 4, 50, 49, 31, 30, 4, 50, 
+	63, 0, 1, 4, 66, 19, 9, 10, 
+	4, 71, 76, 0, 1, 4, 73, 19, 
+	9, 10, 4, 81, 82, 24, 21, 4, 
+	87, 144, 145, 259, 4, 88, 89, 1, 
+	0, 4, 90, 91, 1, 0, 4, 96, 
+	97, 1, 0, 4, 185, 1, 225, 0, 
+	4, 186, 1, 225, 0, 4, 187, 1, 
+	225, 0, 4, 188, 1, 225, 0, 4, 
+	189, 1, 225, 0, 4, 190, 1, 225, 
+	0, 4, 191, 1, 225, 0, 4, 192, 
+	1, 225, 0, 4, 193, 1, 225, 0, 
+	4, 194, 1, 225, 0, 4, 195, 1, 
+	225, 0, 4, 196, 1, 225, 0, 4, 
+	197, 1, 225, 0, 4, 198, 1, 225, 
+	0, 4, 199, 1, 225, 0, 4, 200, 
+	1, 225, 0, 4, 201, 1, 225, 0, 
+	4, 202, 1, 225, 0, 4, 203, 1, 
+	225, 0, 4, 204, 1, 225, 0, 4, 
+	205, 1, 225, 0, 4, 206, 1, 225, 
+	0, 4, 207, 1, 225, 0, 4, 208, 
+	1, 225, 0, 4, 209, 1, 225, 0, 
+	4, 210, 1, 225, 0, 4, 211, 1, 
+	225, 0, 4, 212, 1, 225, 0, 4, 
+	213, 1, 225, 0, 4, 214, 1, 225, 
+	0, 4, 215, 1, 225, 0, 4, 216, 
+	1, 225, 0, 4, 217, 1, 225, 0, 
+	4, 218, 1, 225, 0, 4, 219, 1, 
+	225, 0, 4, 220, 1, 225, 0, 4, 
+	221, 1, 225, 0, 4, 222, 1, 225, 
+	0, 4, 223, 1, 225, 0, 4, 224, 
+	1, 225, 0, 4, 264, 258, 1, 260, 
+	4, 264, 258, 1, 302, 4, 265, 258, 
+	1, 260, 4, 266, 258, 1, 260, 4, 
+	267, 258, 1, 260, 4, 268, 258, 1, 
+	260, 4, 269, 258, 1, 260, 4, 270, 
+	258, 1, 260, 4, 271, 258, 1, 260, 
+	4, 272, 258, 1, 260, 4, 273, 258, 
+	1, 260, 4, 274, 258, 1, 260, 4, 
+	275, 258, 1, 260, 4, 276, 258, 1, 
+	260, 4, 277, 258, 1, 260, 4, 278, 
+	258, 1, 260, 4, 279, 258, 1, 260, 
+	4, 280, 258, 1, 260, 4, 281, 258, 
+	1, 260, 4, 282, 258, 1, 260, 4, 
+	283, 258, 1, 260, 4, 284, 258, 1, 
+	260, 4, 285, 258, 1, 260, 4, 286, 
+	258, 1, 260, 4, 287, 258, 1, 260, 
+	4, 288, 258, 1, 260, 4, 289, 258, 
+	1, 260, 4, 290, 258, 1, 260, 4, 
+	291, 258, 1, 260, 4, 292, 258, 1, 
+	260, 4, 293, 258, 1, 260, 4, 294, 
+	258, 1, 260, 4, 295, 258, 1, 260, 
+	4, 296, 258, 1, 260, 4, 297, 258, 
+	1, 260, 4, 298, 258, 1, 260, 4, 
+	299, 258, 1, 260, 4, 300, 258, 1, 
+	260, 4, 301, 258, 1, 260, 5, 16, 
+	74, 76, 0, 1, 5, 25, 1, 264, 
+	258, 260, 5, 25, 1, 264, 258, 302, 
+	5, 27, 32, 263, 259, 4, 5, 31, 
+	30, 260, 24, 21, 5, 34, 36, 234, 
+	242, 0, 5, 34, 36, 235, 242, 0, 
+	5, 34, 36, 236, 242, 0, 5, 34, 
+	36, 237, 242, 0, 5, 36, 234, 1, 
+	242, 0, 5, 36, 235, 1, 242, 0, 
+	5, 36, 236, 1, 242, 0, 5, 36, 
+	237, 1, 242, 0, 5, 44, 32, 263, 
+	259, 4, 5, 81, 82, 260, 24, 21, 
+	5, 240, 37, 33, 31, 30, 5, 264, 
+	258, 1, 260, 302, 5, 264, 258, 260, 
+	302, 1, 6, 25, 1, 264, 258, 260, 
+	302, 6, 27, 44, 32, 263, 259, 4, 
+	6, 34, 36, 234, 1, 242, 0, 6, 
+	34, 36, 235, 1, 242, 0, 6, 34, 
+	36, 236, 1, 242, 0, 6, 34, 36, 
+	237, 1, 242, 0
 };
 
 static const short _zone_scanner_cond_offsets[] = {
 	0, 0, 2, 4, 6, 8, 10, 12, 
-	14, 14, 14, 16, 19, 21, 24, 26, 
-	28, 30, 30, 30, 30, 32, 37, 37, 
-	37, 37, 39, 39, 39, 39, 39, 41, 
-	41, 41, 41, 43, 43, 43, 43, 45, 
-	47, 47, 47, 47, 47, 49, 49, 51, 
-	51, 51, 51, 51, 53, 53, 55, 57, 
-	57, 57, 57, 57, 57, 57, 57, 59, 
-	59, 59, 61, 63, 63, 63, 65, 65, 
-	67, 67, 69, 71, 71, 71, 71, 71, 
-	73, 75, 75, 75, 75, 75, 77, 77, 
-	79, 81, 81, 83, 85, 85, 85, 85, 
-	85, 87, 87, 87, 89, 89, 91, 91, 
-	91, 91, 93, 95, 95, 95, 97, 97, 
-	99, 99, 101, 101, 101, 101, 103, 103, 
-	103, 103, 105, 105, 107, 107, 107, 107, 
-	109, 112, 114, 114, 116, 118, 120, 122, 
-	122, 124, 127, 127, 127, 127, 127, 127, 
-	127, 127, 127, 127, 129, 131, 134, 136, 
-	138, 141, 143, 143, 145, 148, 150, 152, 
-	154, 156, 158, 161, 163, 165, 167, 169, 
+	14, 14, 14, 17, 19, 22, 24, 26, 
+	28, 28, 28, 28, 30, 35, 35, 35, 
+	35, 37, 37, 37, 37, 37, 39, 39, 
+	39, 39, 41, 41, 41, 41, 43, 45, 
+	45, 45, 45, 45, 47, 47, 49, 49, 
+	49, 49, 49, 51, 51, 53, 55, 55, 
+	55, 55, 55, 55, 55, 55, 57, 57, 
+	57, 59, 61, 61, 61, 63, 63, 65, 
+	65, 67, 69, 69, 69, 69, 69, 71, 
+	73, 73, 73, 73, 73, 75, 75, 77, 
+	79, 79, 81, 83, 83, 83, 83, 83, 
+	85, 85, 85, 87, 87, 89, 89, 89, 
+	89, 91, 93, 93, 93, 95, 95, 97, 
+	97, 99, 99, 99, 99, 101, 101, 101, 
+	101, 103, 103, 105, 105, 105, 105, 107, 
+	110, 112, 112, 114, 116, 118, 120, 120, 
+	122, 125, 125, 125, 125, 125, 125, 125, 
+	125, 125, 125, 127, 129, 132, 134, 136, 
+	139, 141, 141, 143, 146, 148, 150, 152, 
+	154, 156, 159, 161, 163, 165, 167, 169, 
 	171, 173, 175, 177, 179, 181, 183, 185, 
-	187, 189, 191, 193, 196, 198, 198, 198, 
-	198, 198, 198, 198, 198, 198, 198, 200, 
-	202, 204, 207, 209, 211, 213, 215, 217, 
+	187, 189, 191, 194, 196, 196, 196, 196, 
+	196, 196, 196, 196, 196, 196, 198, 200, 
+	202, 205, 207, 209, 211, 213, 215, 217, 
 	219, 221, 223, 225, 227, 229, 231, 233, 
-	235, 237, 239, 241, 246, 251, 256, 256, 
-	256, 258, 258, 258, 258, 260, 260, 262, 
-	265, 267, 269, 274, 279, 282, 287, 289, 
+	235, 237, 239, 244, 249, 254, 254, 254, 
+	256, 256, 256, 256, 258, 258, 260, 263, 
+	265, 267, 272, 277, 280, 285, 287, 289, 
 	291, 293, 295, 297, 299, 301, 303, 305, 
-	307, 309, 311, 313, 316, 319, 321, 324, 
-	324, 324, 324, 324, 324, 324, 324, 324, 
-	324, 324, 324, 324, 324, 324, 324, 324, 
-	324, 324, 325, 325, 325, 325, 325, 326, 
-	328, 330, 332, 334, 334, 336, 336, 338, 
-	341, 343, 345, 345, 347, 349, 349, 349, 
-	349, 349, 349, 349, 351, 354, 356, 358, 
-	360, 362, 362, 364, 366, 366, 366, 366, 
-	366, 366, 366, 368, 371, 373, 376, 379, 
-	379, 379, 379, 379, 381, 384, 384, 386, 
-	388, 390, 390, 390, 392, 395, 395, 395, 
-	395, 397, 397, 397, 397, 399, 399, 399, 
-	399, 399, 401, 401, 401, 401, 403, 403, 
-	403, 403, 405, 407, 407, 407, 407, 407, 
-	409, 409, 411, 411, 411, 411, 411, 413, 
-	413, 413, 413, 413, 413, 413, 413, 415, 
-	415, 415, 417, 419, 419, 419, 421, 421, 
-	423, 423, 425, 427, 427, 427, 427, 427, 
-	429, 431, 431, 431, 431, 431, 433, 433, 
-	435, 437, 437, 439, 441, 441, 441, 441, 
-	441, 443, 443, 443, 445, 445, 447, 447, 
-	447, 447, 449, 451, 451, 451, 453, 453, 
-	455, 455, 457, 457, 457, 457, 459, 459, 
-	459, 459, 461, 461, 463, 463, 463, 463, 
-	465, 465, 465, 465, 467, 467, 469, 471, 
-	473, 475, 477, 477, 479, 482, 485, 488, 
-	490, 492, 494, 496, 496, 498, 501, 504, 
-	506, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 509, 
-	509, 509, 509, 509, 509, 509, 509, 511, 
+	307, 309, 311, 314, 317, 319, 322, 322, 
+	322, 322, 322, 322, 322, 322, 322, 322, 
+	322, 322, 322, 322, 322, 322, 322, 322, 
+	322, 323, 323, 323, 323, 323, 324, 326, 
+	328, 330, 332, 332, 334, 334, 336, 339, 
+	341, 343, 343, 345, 347, 347, 347, 347, 
+	347, 347, 347, 349, 352, 354, 356, 358, 
+	360, 360, 362, 364, 364, 364, 364, 364, 
+	364, 364, 366, 369, 371, 374, 377, 377, 
+	377, 377, 377, 379, 382, 382, 384, 386, 
+	388, 388, 388, 390, 393, 393, 393, 393, 
+	395, 395, 395, 395, 397, 397, 397, 397, 
+	397, 399, 399, 399, 399, 401, 401, 401, 
+	401, 403, 405, 405, 405, 405, 405, 407, 
+	407, 409, 409, 409, 409, 409, 411, 411, 
+	411, 411, 411, 411, 411, 411, 413, 413, 
+	413, 415, 417, 417, 417, 419, 419, 421, 
+	421, 423, 425, 425, 425, 425, 425, 427, 
+	429, 429, 429, 429, 429, 431, 431, 433, 
+	435, 435, 437, 439, 439, 439, 439, 439, 
+	441, 441, 441, 443, 443, 445, 445, 445, 
+	445, 447, 449, 449, 449, 451, 451, 453, 
+	453, 455, 455, 455, 455, 457, 457, 457, 
+	457, 459, 459, 461, 461, 461, 461, 463, 
+	463, 463, 463, 465, 465, 467, 469, 471, 
+	473, 475, 475, 477, 480, 483, 486, 488, 
+	490, 492, 494, 494, 496, 499, 502, 504, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 507, 507, 
+	507, 507, 507, 507, 507, 507, 509, 511, 
 	513, 515, 517, 519, 521, 523, 525, 527, 
-	529, 531, 533, 533, 533, 533, 533, 536, 
-	538, 538, 540, 543, 545, 545, 547, 550, 
-	552, 552, 554, 557, 560, 563, 563, 565, 
-	567, 567, 570, 570, 572, 574, 574, 577, 
-	577, 579, 581, 581, 584, 584, 586, 588, 
-	591, 591, 591, 591, 593, 595, 597, 599, 
+	529, 531, 531, 531, 531, 531, 534, 536, 
+	536, 538, 541, 543, 543, 545, 548, 550, 
+	550, 552, 555, 558, 561, 561, 563, 565, 
+	565, 568, 568, 570, 572, 572, 575, 575, 
+	577, 579, 579, 582, 582, 584, 586, 589, 
+	589, 589, 589, 591, 593, 595, 597, 599, 
 	601, 603, 605, 607, 609, 611, 613, 615, 
-	617, 619, 621, 623, 623, 625, 627, 629, 
-	631, 633, 635, 637, 639, 642, 644, 646, 
-	649, 651, 653, 655, 658, 660, 662, 664, 
-	667, 669, 671, 673, 676, 678, 681, 683, 
-	685, 688, 691, 694, 696, 699, 701, 703, 
-	706, 709, 709, 711, 713, 715, 717, 719, 
-	721, 721, 724, 727, 730, 730, 732, 734, 
-	736, 738, 740, 742, 744, 746, 748, 750, 
-	750, 753, 756, 759, 762, 765, 765, 767, 
-	769, 771, 773, 775, 777, 779, 782, 785, 
-	788, 790, 790, 790, 790, 790, 790, 792, 
-	795, 795, 795, 795, 795, 797, 799, 801, 
-	803, 805, 807, 807, 809, 812, 815, 818, 
-	821, 821, 823, 825, 827, 829, 829, 831, 
-	834, 837, 840, 840, 842, 844, 846, 848, 
-	850, 852, 858, 869, 871, 874, 880, 883, 
-	894, 897, 900, 903, 905, 907, 909, 911, 
-	917, 920, 923, 925, 927, 929, 931, 937, 
-	940, 943, 945, 947, 949, 951, 957, 960, 
-	963, 966, 966, 968, 970, 972, 974, 976, 
+	617, 619, 621, 621, 623, 625, 627, 629, 
+	631, 633, 635, 637, 640, 642, 644, 647, 
+	649, 651, 653, 656, 658, 660, 662, 665, 
+	667, 669, 671, 674, 676, 679, 681, 683, 
+	686, 689, 692, 694, 697, 699, 701, 704, 
+	707, 707, 709, 711, 713, 715, 717, 719, 
+	719, 722, 725, 728, 728, 730, 732, 734, 
+	736, 738, 740, 742, 744, 746, 748, 748, 
+	751, 754, 757, 760, 763, 763, 765, 767, 
+	769, 771, 773, 775, 777, 780, 783, 786, 
+	788, 788, 788, 788, 788, 788, 790, 793, 
+	793, 793, 793, 793, 795, 797, 799, 801, 
+	803, 805, 805, 807, 810, 813, 816, 819, 
+	819, 821, 823, 825, 827, 827, 829, 832, 
+	835, 838, 838, 840, 842, 844, 846, 848, 
+	850, 856, 867, 869, 872, 878, 881, 892, 
+	895, 898, 901, 903, 905, 907, 909, 915, 
+	918, 921, 923, 925, 927, 929, 935, 938, 
+	941, 943, 945, 947, 949, 955, 958, 961, 
+	964, 964, 966, 968, 970, 972, 974, 976, 
 	978, 980, 982, 984, 986, 988, 990, 992, 
-	994, 996, 998, 1000, 1003, 1006, 1009, 1012, 
-	1015, 1018, 1021, 1024, 1024, 1024, 1026, 1026, 
-	1026, 1026, 1028, 1028, 1030, 1030, 1030, 1030, 
-	1032, 1032, 1032, 1032, 1034, 1034, 1034, 1034, 
-	1034, 1036, 1036, 1036, 1036, 1038, 1038, 1038, 
-	1038, 1040, 1042, 1042, 1042, 1042, 1042, 1044, 
-	1044, 1046, 1046, 1046, 1046, 1046, 1048, 1048, 
-	1048, 1048, 1048, 1048, 1048, 1048, 1050, 1050, 
-	1050, 1052, 1054, 1054, 1054, 1056, 1056, 1058, 
-	1058, 1060, 1062, 1062, 1062, 1062, 1062, 1064, 
-	1066, 1066, 1066, 1066, 1066, 1068, 1068, 1070, 
-	1072, 1072, 1074, 1076, 1076, 1076, 1076, 1076, 
-	1078, 1078, 1078, 1080, 1080, 1082, 1082, 1082, 
-	1082, 1084, 1086, 1086, 1086, 1088, 1088, 1090, 
-	1090, 1092, 1092, 1092, 1092, 1094, 1094, 1094, 
-	1094, 1096, 1096, 1098, 1098, 1098, 1098, 1100, 
-	1100, 1100, 1100, 1100, 1102, 1104, 1106, 1108, 
-	1110, 1112, 1114, 1117, 1120, 1123, 1123, 1125, 
+	994, 996, 998, 1001, 1004, 1007, 1010, 1013, 
+	1016, 1019, 1022, 1022, 1022, 1024, 1024, 1024, 
+	1024, 1026, 1026, 1028, 1028, 1028, 1028, 1030, 
+	1030, 1030, 1030, 1032, 1032, 1032, 1032, 1032, 
+	1034, 1034, 1034, 1034, 1036, 1036, 1036, 1036, 
+	1038, 1040, 1040, 1040, 1040, 1040, 1042, 1042, 
+	1044, 1044, 1044, 1044, 1044, 1046, 1046, 1046, 
+	1046, 1046, 1046, 1046, 1046, 1048, 1048, 1048, 
+	1050, 1052, 1052, 1052, 1054, 1054, 1056, 1056, 
+	1058, 1060, 1060, 1060, 1060, 1060, 1062, 1064, 
+	1064, 1064, 1064, 1064, 1066, 1066, 1068, 1070, 
+	1070, 1072, 1074, 1074, 1074, 1074, 1074, 1076, 
+	1076, 1076, 1078, 1078, 1080, 1080, 1080, 1080, 
+	1082, 1084, 1084, 1084, 1086, 1086, 1088, 1088, 
+	1090, 1090, 1090, 1090, 1092, 1092, 1092, 1092, 
+	1094, 1094, 1096, 1096, 1096, 1096, 1098, 1098, 
+	1098, 1098, 1098, 1100, 1102, 1104, 1106, 1108, 
+	1110, 1112, 1115, 1118, 1121, 1121, 1123, 1123, 
 	1125, 1127, 1129, 1131, 1133, 1135, 1137, 1139, 
-	1141, 1141, 1141, 1141, 1141, 1141, 1141, 1141, 
-	1141, 1141, 1141, 1141, 1141, 1141, 1141, 1144, 
-	1144, 1146, 1149, 1152, 1155, 1155, 1157, 1159, 
-	1161, 1163, 1165, 1167, 1167, 1167, 1167, 1170, 
-	1173, 1176, 1176, 1178, 1180, 1182, 1184, 1186, 
-	1188, 1188, 1190, 1193, 1196, 1199, 1202, 1202, 
-	1204, 1206, 1206, 1209, 1209, 1211, 1213, 1213, 
-	1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 
-	1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
-	1216, 1216, 1216, 1216, 1216, 1218, 1220, 1222, 
-	1224, 1226, 1228, 1230, 1232, 1234, 1236, 1236, 
-	1236, 1236, 1236, 1236, 1236, 1236, 1238, 1238, 
-	1240, 1243, 1243, 1245, 1248, 1248, 1250, 1253, 
-	1255, 1255, 1257, 1260, 1263, 1263, 1263, 1263, 
-	1263, 1263, 1263, 1263, 1263, 1263, 1265, 1268, 
-	1268, 1268, 1270, 1273, 1275, 1278, 1280, 1283, 
-	1285, 1288, 1288, 1288, 1288, 1288, 1290, 1293, 
-	1293, 1295, 1298, 1298, 1300, 1303, 1303, 1309, 
-	1312, 1323, 1326, 1337, 1340, 1340, 1340, 1340, 
-	1340, 1340, 1340, 1340, 1342, 1345, 1345, 1345, 
-	1345
+	1139, 1139, 1139, 1139, 1139, 1139, 1139, 1139, 
+	1139, 1139, 1139, 1139, 1139, 1139, 1142, 1142, 
+	1144, 1147, 1150, 1153, 1153, 1155, 1157, 1159, 
+	1161, 1163, 1165, 1165, 1165, 1165, 1168, 1171, 
+	1174, 1174, 1176, 1178, 1180, 1182, 1184, 1186, 
+	1186, 1188, 1191, 1194, 1197, 1200, 1200, 1202, 
+	1204, 1204, 1207, 1207, 1209, 1211, 1211, 1211, 
+	1211, 1211, 1211, 1211, 1211, 1211, 1211, 1214, 
+	1214, 1214, 1214, 1214, 1214, 1214, 1214, 1214, 
+	1214, 1214, 1214, 1214, 1216, 1218, 1220, 1222, 
+	1224, 1226, 1228, 1230, 1232, 1234, 1234, 1234, 
+	1234, 1234, 1234, 1234, 1234, 1236, 1236, 1238, 
+	1241, 1241, 1243, 1246, 1246, 1248, 1251, 1253, 
+	1253, 1255, 1258, 1261, 1261, 1261, 1261, 1261, 
+	1261, 1261, 1261, 1261, 1261, 1263, 1266, 1266, 
+	1266, 1268, 1271, 1273, 1276, 1278, 1281, 1283, 
+	1286, 1286, 1286, 1286, 1286, 1288, 1291, 1291, 
+	1293, 1296, 1296, 1298, 1301, 1301, 1307, 1310, 
+	1321, 1324, 1335, 1338, 1338, 1338, 1338, 1338, 
+	1338, 1338, 1338, 1340, 1343, 1343, 1343, 1343
 };
 
 static const char _zone_scanner_cond_lengths[] = {
 	0, 2, 2, 2, 2, 2, 2, 2, 
-	0, 0, 2, 3, 2, 3, 2, 2, 
-	2, 0, 0, 0, 2, 5, 0, 0, 
-	0, 2, 0, 0, 0, 0, 2, 0, 
-	0, 0, 2, 0, 0, 0, 2, 2, 
-	0, 0, 0, 0, 2, 0, 2, 0, 
-	0, 0, 0, 2, 0, 2, 2, 0, 
-	0, 0, 0, 0, 0, 0, 2, 0, 
-	0, 2, 2, 0, 0, 2, 0, 2, 
+	0, 0, 3, 2, 3, 2, 2, 2, 
+	0, 0, 0, 2, 5, 0, 0, 0, 
+	2, 0, 0, 0, 0, 2, 0, 0, 
+	0, 2, 0, 0, 0, 2, 2, 0, 
+	0, 0, 0, 2, 0, 2, 0, 0, 
+	0, 0, 2, 0, 2, 2, 0, 0, 
+	0, 0, 0, 0, 0, 2, 0, 0, 
+	2, 2, 0, 0, 2, 0, 2, 0, 
+	2, 2, 0, 0, 0, 0, 2, 2, 
+	0, 0, 0, 0, 2, 0, 2, 2, 
 	0, 2, 2, 0, 0, 0, 0, 2, 
-	2, 0, 0, 0, 0, 2, 0, 2, 
-	2, 0, 2, 2, 0, 0, 0, 0, 
-	2, 0, 0, 2, 0, 2, 0, 0, 
-	0, 2, 2, 0, 0, 2, 0, 2, 
-	0, 2, 0, 0, 0, 2, 0, 0, 
-	0, 2, 0, 2, 0, 0, 0, 2, 
-	3, 2, 0, 2, 2, 2, 2, 0, 
-	2, 3, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 2, 2, 3, 2, 2, 
-	3, 2, 0, 2, 3, 2, 2, 2, 
-	2, 2, 3, 2, 2, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 3, 2, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 2, 2, 
+	0, 0, 2, 0, 2, 0, 0, 0, 
+	2, 2, 0, 0, 2, 0, 2, 0, 
+	2, 0, 0, 0, 2, 0, 0, 0, 
+	2, 0, 2, 0, 0, 0, 2, 3, 
+	2, 0, 2, 2, 2, 2, 0, 2, 
+	3, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 2, 2, 3, 2, 2, 3, 
+	2, 0, 2, 3, 2, 2, 2, 2, 
 	2, 3, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 5, 5, 5, 0, 0, 
-	2, 0, 0, 0, 2, 0, 2, 3, 
-	2, 2, 5, 5, 3, 5, 2, 2, 
+	2, 2, 3, 2, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 2, 2, 2, 
+	3, 2, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 3, 3, 2, 3, 0, 
+	2, 2, 5, 5, 5, 0, 0, 2, 
+	0, 0, 0, 2, 0, 2, 3, 2, 
+	2, 5, 5, 3, 5, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 3, 3, 2, 3, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 1, 0, 0, 0, 0, 1, 2, 
-	2, 2, 2, 0, 2, 0, 2, 3, 
-	2, 2, 0, 2, 2, 0, 0, 0, 
-	0, 0, 0, 2, 3, 2, 2, 2, 
+	1, 0, 0, 0, 0, 1, 2, 2, 
+	2, 2, 0, 2, 0, 2, 3, 2, 
 	2, 0, 2, 2, 0, 0, 0, 0, 
-	0, 0, 2, 3, 2, 3, 3, 0, 
-	0, 0, 0, 2, 3, 0, 2, 2, 
-	2, 0, 0, 2, 3, 0, 0, 0, 
+	0, 0, 2, 3, 2, 2, 2, 2, 
+	0, 2, 2, 0, 0, 0, 0, 0, 
+	0, 2, 3, 2, 3, 3, 0, 0, 
+	0, 0, 2, 3, 0, 2, 2, 2, 
+	0, 0, 2, 3, 0, 0, 0, 2, 
+	0, 0, 0, 2, 0, 0, 0, 0, 
 	2, 0, 0, 0, 2, 0, 0, 0, 
-	0, 2, 0, 0, 0, 2, 0, 0, 
-	0, 2, 2, 0, 0, 0, 0, 2, 
-	0, 2, 0, 0, 0, 0, 2, 0, 
-	0, 0, 0, 0, 0, 0, 2, 0, 
-	0, 2, 2, 0, 0, 2, 0, 2, 
+	2, 2, 0, 0, 0, 0, 2, 0, 
+	2, 0, 0, 0, 0, 2, 0, 0, 
+	0, 0, 0, 0, 0, 2, 0, 0, 
+	2, 2, 0, 0, 2, 0, 2, 0, 
+	2, 2, 0, 0, 0, 0, 2, 2, 
+	0, 0, 0, 0, 2, 0, 2, 2, 
 	0, 2, 2, 0, 0, 0, 0, 2, 
-	2, 0, 0, 0, 0, 2, 0, 2, 
-	2, 0, 2, 2, 0, 0, 0, 0, 
-	2, 0, 0, 2, 0, 2, 0, 0, 
-	0, 2, 2, 0, 0, 2, 0, 2, 
-	0, 2, 0, 0, 0, 2, 0, 0, 
-	0, 2, 0, 2, 0, 0, 0, 2, 
-	0, 0, 0, 2, 0, 2, 2, 2, 
-	2, 2, 0, 2, 3, 3, 3, 2, 
-	2, 2, 2, 0, 2, 3, 3, 2, 
-	3, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 2, 0, 2, 0, 0, 0, 
+	2, 2, 0, 0, 2, 0, 2, 0, 
+	2, 0, 0, 0, 2, 0, 0, 0, 
+	2, 0, 2, 0, 0, 0, 2, 0, 
+	0, 0, 2, 0, 2, 2, 2, 2, 
+	2, 0, 2, 3, 3, 3, 2, 2, 
+	2, 2, 0, 2, 3, 3, 2, 3, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -786,89 +773,88 @@ static const char _zone_scanner_cond_lengths[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 2, 2, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 0, 0, 0, 0, 3, 2, 
-	0, 2, 3, 2, 0, 2, 3, 2, 
-	0, 2, 3, 3, 3, 0, 2, 2, 
-	0, 3, 0, 2, 2, 0, 3, 0, 
-	2, 2, 0, 3, 0, 2, 2, 3, 
-	0, 0, 0, 2, 2, 2, 2, 2, 
+	2, 0, 0, 0, 0, 3, 2, 0, 
+	2, 3, 2, 0, 2, 3, 2, 0, 
+	2, 3, 3, 3, 0, 2, 2, 0, 
+	3, 0, 2, 2, 0, 3, 0, 2, 
+	2, 0, 3, 0, 2, 2, 3, 0, 
+	0, 0, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 0, 2, 2, 2, 2, 
-	2, 2, 2, 2, 3, 2, 2, 3, 
-	2, 2, 2, 3, 2, 2, 2, 3, 
-	2, 2, 2, 3, 2, 3, 2, 2, 
-	3, 3, 3, 2, 3, 2, 2, 3, 
+	2, 2, 0, 2, 2, 2, 2, 2, 
+	2, 2, 2, 3, 2, 2, 3, 2, 
+	2, 2, 3, 2, 2, 2, 3, 2, 
+	2, 2, 3, 2, 3, 2, 2, 3, 
+	3, 3, 2, 3, 2, 2, 3, 3, 
+	0, 2, 2, 2, 2, 2, 2, 0, 
+	3, 3, 3, 0, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 0, 3, 
+	3, 3, 3, 3, 0, 2, 2, 2, 
+	2, 2, 2, 2, 3, 3, 3, 2, 
+	0, 0, 0, 0, 0, 2, 3, 0, 
+	0, 0, 0, 2, 2, 2, 2, 2, 
+	2, 0, 2, 3, 3, 3, 3, 0, 
+	2, 2, 2, 2, 0, 2, 3, 3, 
 	3, 0, 2, 2, 2, 2, 2, 2, 
-	0, 3, 3, 3, 0, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 0, 
-	3, 3, 3, 3, 3, 0, 2, 2, 
-	2, 2, 2, 2, 2, 3, 3, 3, 
-	2, 0, 0, 0, 0, 0, 2, 3, 
-	0, 0, 0, 0, 2, 2, 2, 2, 
-	2, 2, 0, 2, 3, 3, 3, 3, 
-	0, 2, 2, 2, 2, 0, 2, 3, 
-	3, 3, 0, 2, 2, 2, 2, 2, 
-	2, 6, 11, 2, 3, 6, 3, 11, 
-	3, 3, 3, 2, 2, 2, 2, 6, 
+	6, 11, 2, 3, 6, 3, 11, 3, 
 	3, 3, 2, 2, 2, 2, 6, 3, 
 	3, 2, 2, 2, 2, 6, 3, 3, 
-	3, 0, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 6, 3, 3, 3, 
+	0, 2, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 3, 3, 3, 3, 3, 
-	3, 3, 3, 0, 0, 2, 0, 0, 
-	0, 2, 0, 2, 0, 0, 0, 2, 
-	0, 0, 0, 2, 0, 0, 0, 0, 
-	2, 0, 0, 0, 2, 0, 0, 0, 
-	2, 2, 0, 0, 0, 0, 2, 0, 
-	2, 0, 0, 0, 0, 2, 0, 0, 
-	0, 0, 0, 0, 0, 2, 0, 0, 
-	2, 2, 0, 0, 2, 0, 2, 0, 
-	2, 2, 0, 0, 0, 0, 2, 2, 
-	0, 0, 0, 0, 2, 0, 2, 2, 
-	0, 2, 2, 0, 0, 0, 0, 2, 
-	0, 0, 2, 0, 2, 0, 0, 0, 
-	2, 2, 0, 0, 2, 0, 2, 0, 
-	2, 0, 0, 0, 2, 0, 0, 0, 
+	2, 2, 3, 3, 3, 3, 3, 3, 
+	3, 3, 0, 0, 2, 0, 0, 0, 
 	2, 0, 2, 0, 0, 0, 2, 0, 
-	0, 0, 0, 2, 2, 2, 2, 2, 
-	2, 2, 3, 3, 3, 0, 2, 0, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
+	0, 0, 2, 0, 0, 0, 0, 2, 
+	0, 0, 0, 2, 0, 0, 0, 2, 
+	2, 0, 0, 0, 0, 2, 0, 2, 
+	0, 0, 0, 0, 2, 0, 0, 0, 
+	0, 0, 0, 0, 2, 0, 0, 2, 
+	2, 0, 0, 2, 0, 2, 0, 2, 
+	2, 0, 0, 0, 0, 2, 2, 0, 
+	0, 0, 0, 2, 0, 2, 2, 0, 
+	2, 2, 0, 0, 0, 0, 2, 0, 
+	0, 2, 0, 2, 0, 0, 0, 2, 
+	2, 0, 0, 2, 0, 2, 0, 2, 
+	0, 0, 0, 2, 0, 0, 0, 2, 
+	0, 2, 0, 0, 0, 2, 0, 0, 
+	0, 0, 2, 2, 2, 2, 2, 2, 
+	2, 3, 3, 3, 0, 2, 0, 2, 
+	2, 2, 2, 2, 2, 2, 2, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 3, 0, 2, 
+	3, 3, 3, 0, 2, 2, 2, 2, 
+	2, 2, 0, 0, 0, 3, 3, 3, 
+	0, 2, 2, 2, 2, 2, 2, 0, 
+	2, 3, 3, 3, 3, 0, 2, 2, 
+	0, 3, 0, 2, 2, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 3, 0, 
-	2, 3, 3, 3, 0, 2, 2, 2, 
-	2, 2, 2, 0, 0, 0, 3, 3, 
-	3, 0, 2, 2, 2, 2, 2, 2, 
-	0, 2, 3, 3, 3, 3, 0, 2, 
-	2, 0, 3, 0, 2, 2, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 3, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 2, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 0, 0, 
-	0, 0, 0, 0, 0, 2, 0, 2, 
-	3, 0, 2, 3, 0, 2, 3, 2, 
-	0, 2, 3, 3, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 2, 3, 0, 
-	0, 2, 3, 2, 3, 2, 3, 2, 
-	3, 0, 0, 0, 0, 2, 3, 0, 
-	2, 3, 0, 2, 3, 0, 6, 3, 
-	11, 3, 11, 3, 0, 0, 0, 0, 
-	0, 0, 0, 2, 3, 0, 0, 0, 
-	0
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 0, 0, 0, 
+	0, 0, 0, 0, 2, 0, 2, 3, 
+	0, 2, 3, 0, 2, 3, 2, 0, 
+	2, 3, 3, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 2, 3, 0, 0, 
+	2, 3, 2, 3, 2, 3, 2, 3, 
+	0, 0, 0, 0, 2, 3, 0, 2, 
+	3, 0, 2, 3, 0, 6, 3, 11, 
+	3, 11, 3, 0, 0, 0, 0, 0, 
+	0, 0, 2, 3, 0, 0, 0, 0
 };
 
 static const short _zone_scanner_cond_keys[] = {
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 58, 59, 59, 
-	60, 127, 10, 10, 59, 59, 10, 10, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 58, 59, 59, 60, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
@@ -886,20 +872,20 @@ static const short _zone_scanner_cond_keys[] = {
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
@@ -907,11 +893,11 @@ static const short _zone_scanner_cond_keys[] = {
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
@@ -919,45 +905,46 @@ static const short _zone_scanner_cond_keys[] = {
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 58, 
-	59, 59, 60, 127, -128, 9, 10, 10, 
-	11, 58, 59, 59, 60, 127, -128, 9, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 58, 59, 59, 60, 127, 
+	-128, 9, 10, 10, 11, 58, 59, 59, 
+	60, 127, -128, 9, 10, 10, 11, 58, 
+	59, 59, 60, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 58, 
-	59, 59, 60, 127, -128, 9, 10, 10, 
-	11, 58, 59, 59, 60, 127, -128, 9, 
-	10, 10, 11, 127, -128, 9, 10, 10, 
-	11, 58, 59, 59, 60, 127, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 58, 59, 59, 60, 127, 
+	-128, 9, 10, 10, 11, 58, 59, 59, 
+	60, 127, -128, 9, 10, 10, 11, 127, 
+	-128, 9, 10, 10, 11, 58, 59, 59, 
+	60, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 10, 10, 10, 10, 59, 59, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, 10, 10, 59, 59, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 10, 10, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
@@ -978,35 +965,35 @@ static const short _zone_scanner_cond_keys[] = {
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
@@ -1018,103 +1005,103 @@ static const short _zone_scanner_cond_keys[] = {
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, 10, 10, 59, 59, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, -128, 9, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, -128, 9, 
-	10, 10, 11, 127, 10, 10, 59, 59, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, -128, 9, 
 	10, 10, 11, 127, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, -128, 9, 10, 10, 
 	11, 127, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, -128, 9, 
-	10, 10, 11, 127, -128, 9, 10, 10, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, -128, 9, 10, 10, 
 	11, 127, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, 10, 10, 59, 59, 
-	9, 9, 10, 10, 32, 32, 40, 40, 
-	41, 41, 59, 59, 9, 9, 10, 10, 
-	32, 32, 40, 40, 41, 41, 43, 43, 
-	47, 47, 48, 57, 59, 59, 65, 90, 
-	97, 122, 10, 10, 59, 59, -128, 9, 
-	10, 10, 11, 127, 9, 9, 10, 10, 
-	32, 32, 40, 40, 41, 41, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 9, 9, 
-	10, 10, 32, 32, 40, 40, 41, 41, 
-	43, 43, 47, 47, 48, 57, 59, 59, 
-	65, 90, 97, 122, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 9, 9, 
-	10, 10, 32, 32, 40, 40, 41, 41, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 9, 9, 
-	10, 10, 32, 32, 40, 40, 41, 41, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 9, 9, 
-	10, 10, 32, 32, 40, 40, 41, 41, 
 	59, 59, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, 9, 9, 10, 10, 
+	32, 32, 40, 40, 41, 41, 59, 59, 
+	9, 9, 10, 10, 32, 32, 40, 40, 
+	41, 41, 43, 43, 47, 47, 48, 57, 
+	59, 59, 65, 90, 97, 122, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	9, 9, 10, 10, 32, 32, 40, 40, 
+	41, 41, 59, 59, -128, 9, 10, 10, 
+	11, 127, 9, 9, 10, 10, 32, 32, 
+	40, 40, 41, 41, 43, 43, 47, 47, 
+	48, 57, 59, 59, 65, 90, 97, 122, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 9, 9, 10, 10, 32, 32, 
+	40, 40, 41, 41, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 9, 9, 10, 10, 32, 32, 
+	40, 40, 41, 41, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 9, 9, 10, 10, 32, 32, 
+	40, 40, 41, 41, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, -128, 9, 
-	10, 10, 11, 127, -128, 9, 10, 10, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, 10, 10, 59, 59, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, -128, 9, 
 	10, 10, 11, 127, -128, 9, 10, 10, 
 	11, 127, -128, 9, 10, 10, 11, 127, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
@@ -1137,46 +1124,43 @@ static const short _zone_scanner_cond_keys[] = {
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
-	59, 59, 10, 10, 59, 59, 10, 10, 
 	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
 	10, 10, 11, 127, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
+	11, 127, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, 10, 10, 59, 59, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	-128, 9, 10, 10, 11, 127, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, -128, 9, 10, 10, 11, 127, 
 	-128, 9, 10, 10, 11, 127, -128, 9, 
-	10, 10, 11, 127, 10, 10, 59, 59, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
+	11, 127, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
 	10, 10, 59, 59, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
 	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 10, 10, 59, 59, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
+	11, 127, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
+	59, 59, 10, 10, 59, 59, -128, 9, 
+	10, 10, 11, 127, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
 	-128, 9, 10, 10, 11, 127, 10, 10, 
@@ -1184,54 +1168,56 @@ static const short _zone_scanner_cond_keys[] = {
 	10, 10, 59, 59, -128, 9, 10, 10, 
 	11, 127, 10, 10, 59, 59, -128, 9, 
 	10, 10, 11, 127, 10, 10, 59, 59, 
-	-128, 9, 10, 10, 11, 127, 9, 9, 
-	10, 10, 32, 32, 40, 40, 41, 41, 
-	59, 59, -128, 9, 10, 10, 11, 127, 
-	9, 9, 10, 10, 32, 32, 40, 40, 
-	41, 41, 43, 43, 47, 47, 48, 57, 
-	59, 59, 65, 90, 97, 122, -128, 9, 
+	-128, 9, 10, 10, 11, 127, 10, 10, 
+	59, 59, -128, 9, 10, 10, 11, 127, 
+	10, 10, 59, 59, -128, 9, 10, 10, 
+	11, 127, 9, 9, 10, 10, 32, 32, 
+	40, 40, 41, 41, 59, 59, -128, 9, 
 	10, 10, 11, 127, 9, 9, 10, 10, 
 	32, 32, 40, 40, 41, 41, 43, 43, 
 	47, 47, 48, 57, 59, 59, 65, 90, 
 	97, 122, -128, 9, 10, 10, 11, 127, 
-	10, 10, 59, 59, -128, 9, 10, 10, 
-	11, 127, 0
+	9, 9, 10, 10, 32, 32, 40, 40, 
+	41, 41, 43, 43, 47, 47, 48, 57, 
+	59, 59, 65, 90, 97, 122, -128, 9, 
+	10, 10, 11, 127, 10, 10, 59, 59, 
+	-128, 9, 10, 10, 11, 127, 0
 };
 
 static const char _zone_scanner_cond_spaces[] = {
 	0, 0, 0, 0, 0, 0, 5, 5, 
 	5, 5, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 5, 5, 0, 0, 0, 
-	5, 5, 0, 0, 0, 0, 5, 5, 
-	0, 0, 0, 0, 0, 5, 5, 5, 
+	0, 5, 5, 0, 0, 0, 5, 5, 
+	0, 0, 0, 0, 5, 5, 0, 0, 
+	0, 0, 0, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 0, 0, 0, 
-	0, 5, 5, 5, 5, 5, 5, 5, 
+	5, 5, 5, 0, 0, 0, 0, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
+	5, 5, 5, 5, 5, 5, 5, 5, 
+	5, 5, 5, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 5, 5, 5, 5, 
-	5, 5, 5, 5, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 5, 5, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 5, 5, 5, 5, 5, 5, 
+	5, 5, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 5, 5, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 5, 
-	5, 5, 5, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 5, 5, 5, 
+	5, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	5, 5, 5, 5, 5, 5, 0, 0, 
+	0, 0, 0, 0, 0, 0, 5, 5, 
+	5, 5, 5, 5, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -1239,16 +1225,16 @@ static const char _zone_scanner_cond_spaces[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 2, 2, 0, 0, 
+	0, 0, 2, 2, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 5, 5, 0, 0, 0, 
-	5, 5, 5, 5, 5, 5, 5, 5, 
-	0, 0, 0, 5, 5, 5, 5, 5, 
+	0, 5, 5, 0, 0, 0, 5, 5, 
+	5, 5, 5, 5, 5, 5, 0, 0, 
+	0, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
@@ -1257,11 +1243,10 @@ static const char _zone_scanner_cond_spaces[] = {
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 0, 0, 0, 
-	0, 0, 0, 0, 0, 5, 5, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
+	5, 5, 5, 0, 0, 0, 0, 0, 
+	0, 0, 0, 5, 5, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	5, 5, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 5, 5, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -1272,19 +1257,18 @@ static const char _zone_scanner_cond_spaces[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	5, 5, 5, 5, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 5, 5, 
+	5, 5, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 5, 
+	0, 0, 0, 0, 0, 5, 5, 5, 
 	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 5, 5, 0, 
-	0, 0, 5, 5, 5, 5, 0, 0, 
+	5, 5, 5, 5, 5, 0, 0, 0, 
+	5, 5, 5, 5, 0, 0, 0, 5, 
+	5, 5, 5, 5, 5, 0, 0, 0, 
+	5, 5, 5, 5, 5, 5, 0, 0, 
 	0, 5, 5, 5, 5, 5, 5, 0, 
-	0, 0, 5, 5, 5, 5, 5, 5, 
-	0, 0, 0, 5, 5, 5, 5, 5, 
-	5, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -1296,38 +1280,36 @@ static const char _zone_scanner_cond_spaces[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 1, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 5, 5, 5, 5, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 5, 
-	5, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 1, 
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 5, 5, 5, 5, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 5, 5, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 8, 11, 8, 8, 
-	8, 11, 3, 6, 3, 3, 3, 3, 
-	3, 3, 6, 3, 3, 1, 1, 6, 
-	6, 6, 4, 9, 4, 4, 4, 9, 
-	7, 7, 7, 8, 11, 8, 8, 8, 
-	3, 3, 3, 11, 3, 3, 10, 10, 
-	10, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 8, 
-	11, 8, 8, 8, 11, 0, 0, 0, 
+	0, 0, 0, 5, 5, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 8, 11, 8, 8, 8, 
-	11, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 8, 
-	11, 8, 8, 8, 11, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 8, 11, 8, 8, 8, 11, 
+	3, 6, 3, 3, 3, 3, 3, 3, 
+	6, 3, 3, 1, 1, 6, 6, 6, 
+	4, 9, 4, 4, 4, 9, 7, 7, 
+	7, 8, 11, 8, 8, 8, 3, 3, 
+	3, 11, 3, 3, 10, 10, 10, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 8, 11, 8, 
+	8, 8, 11, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 8, 11, 8, 8, 8, 11, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 1, 1, 
+	0, 0, 0, 0, 0, 8, 11, 8, 
+	8, 8, 11, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 1, 1, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -1338,183 +1320,185 @@ static const char _zone_scanner_cond_spaces[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	1, 1, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 1, 1, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 1, 1, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 1, 1, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 5, 5, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 5, 5, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 5, 5, 
-	5, 5, 5, 5, 0, 0, 5, 5, 
-	0, 0, 0, 5, 5, 0, 0, 0, 
-	5, 5, 0, 0, 0, 0, 0, 5, 
-	5, 0, 0, 0, 0, 0, 0, 5, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	5, 5, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 5, 5, 5, 5, 
+	5, 5, 0, 0, 5, 5, 0, 0, 
+	0, 5, 5, 0, 0, 0, 5, 5, 
+	0, 0, 0, 0, 0, 5, 5, 0, 
+	0, 0, 0, 0, 0, 5, 5, 0, 
+	0, 0, 5, 5, 0, 0, 0, 5, 
 	5, 0, 0, 0, 5, 5, 0, 0, 
 	0, 5, 5, 0, 0, 0, 5, 5, 
 	0, 0, 0, 5, 5, 0, 0, 0, 
-	5, 5, 0, 0, 0, 5, 5, 0, 
-	0, 0, 5, 5, 0, 0, 0, 4, 
-	9, 4, 4, 4, 9, 7, 7, 7, 
-	3, 6, 3, 3, 3, 3, 3, 3, 
-	6, 3, 3, 6, 6, 6, 8, 11, 
-	8, 8, 8, 3, 3, 3, 11, 3, 
-	3, 10, 10, 10, 5, 5, 0, 0, 
-	0, 0
+	5, 5, 0, 0, 0, 4, 9, 4, 
+	4, 4, 9, 7, 7, 7, 3, 6, 
+	3, 3, 3, 3, 3, 3, 6, 3, 
+	3, 6, 6, 6, 8, 11, 8, 8, 
+	8, 3, 3, 3, 11, 3, 3, 10, 
+	10, 10, 5, 5, 0, 0, 0, 0
 };
 
 static const short _zone_scanner_key_offsets[] = {
 	0, 0, 38, 56, 90, 106, 123, 131, 
-	139, 140, 141, 149, 152, 171, 177, 227, 
-	247, 283, 287, 289, 291, 301, 315, 317, 
-	319, 321, 331, 337, 339, 341, 343, 353, 
-	357, 359, 361, 371, 373, 375, 377, 387, 
-	397, 399, 401, 403, 404, 414, 415, 425, 
-	427, 429, 431, 433, 443, 447, 453, 487, 
-	489, 491, 493, 495, 497, 499, 501, 511, 
-	515, 517, 527, 537, 543, 544, 554, 555, 
-	565, 567, 577, 587, 591, 593, 595, 597, 
-	607, 617, 623, 625, 627, 629, 639, 641, 
-	651, 663, 665, 676, 688, 690, 692, 694, 
-	696, 706, 708, 710, 720, 726, 736, 738, 
-	740, 742, 752, 762, 770, 772, 782, 784, 
-	794, 796, 806, 808, 810, 812, 822, 828, 
-	830, 832, 842, 844, 854, 856, 858, 860, 
-	872, 875, 913, 917, 923, 959, 977, 985, 
-	997, 1005, 1008, 1009, 1015, 1017, 1019, 1021, 
-	1023, 1025, 1027, 1033, 1039, 1075, 1078, 1096, 
-	1132, 1135, 1143, 1155, 1163, 1166, 1182, 1230, 
-	1248, 1267, 1305, 1311, 1325, 1339, 1389, 1401, 
-	1415, 1425, 1435, 1447, 1459, 1473, 1485, 1499, 
-	1509, 1523, 1539, 1553, 1559, 1573, 1575, 1577, 
-	1579, 1581, 1583, 1589, 1591, 1593, 1599, 1607, 
-	1627, 1665, 1671, 1689, 1739, 1751, 1765, 1775, 
-	1785, 1797, 1809, 1823, 1835, 1849, 1859, 1873, 
-	1889, 1903, 1918, 1956, 1970, 1984, 1998, 2000, 
-	2002, 2012, 2014, 2016, 2018, 2028, 2030, 2040, 
-	2046, 2060, 2074, 2090, 2104, 2107, 2121, 2131, 
-	2143, 2151, 2159, 2169, 2179, 2191, 2201, 2213, 
-	2221, 2233, 2247, 2259, 2265, 2271, 2281, 2287, 
-	2288, 2289, 2300, 2307, 2323, 2338, 2340, 2342, 
-	2344, 2359, 2365, 2371, 2377, 2389, 2391, 2393, 
-	2395, 2407, 2413, 2419, 2421, 2423, 2425, 2431, 
-	2437, 2445, 2465, 2473, 2474, 2484, 2496, 2506, 
-	2509, 2515, 2531, 2532, 2550, 2558, 2559, 2568, 
-	2570, 2572, 2574, 2583, 2591, 2594, 2600, 2612, 
-	2620, 2638, 2639, 2657, 2665, 2666, 2675, 2677, 
-	2679, 2681, 2690, 2698, 2704, 2720, 2723, 2726, 
-	2733, 2740, 2748, 2756, 2773, 2776, 2777, 2787, 
-	2825, 2841, 2843, 2845, 2855, 2858, 2862, 2864, 
-	2866, 2876, 2878, 2880, 2882, 2892, 2898, 2900, 
-	2902, 2904, 2914, 2918, 2920, 2922, 2932, 2934, 
-	2936, 2938, 2948, 2958, 2960, 2962, 2964, 2965, 
-	2975, 2976, 2986, 2988, 2990, 2992, 2994, 3004, 
-	3006, 3008, 3010, 3012, 3014, 3016, 3018, 3028, 
-	3032, 3034, 3044, 3054, 3060, 3061, 3071, 3072, 
-	3082, 3084, 3094, 3104, 3108, 3110, 3112, 3114, 
-	3124, 3134, 3140, 3142, 3144, 3146, 3156, 3158, 
-	3168, 3180, 3182, 3193, 3205, 3207, 3209, 3211, 
-	3213, 3223, 3225, 3227, 3237, 3243, 3253, 3255, 
-	3257, 3259, 3269, 3279, 3287, 3289, 3299, 3301, 
-	3311, 3313, 3323, 3325, 3327, 3329, 3339, 3345, 
-	3347, 3349, 3359, 3361, 3371, 3373, 3375, 3377, 
-	3389, 3391, 3393, 3395, 3405, 3407, 3417, 3423, 
-	3431, 3439, 3451, 3457, 3473, 3476, 3479, 3482, 
-	3488, 3497, 3507, 3519, 3525, 3541, 3544, 3547, 
-	3555, 3558, 3570, 3578, 3582, 3588, 3590, 3597, 
-	3599, 3601, 3603, 3605, 3606, 3607, 3609, 3611, 
-	3613, 3614, 3620, 3622, 3626, 3627, 3629, 3631, 
-	3633, 3635, 3641, 3643, 3645, 3647, 3649, 3650, 
-	3651, 3653, 3655, 3657, 3658, 3659, 3660, 3666, 
-	3667, 3668, 3670, 3672, 3674, 3675, 3676, 3677, 
-	3683, 3685, 3687, 3689, 3691, 3693, 3695, 3697, 
-	3703, 3705, 3707, 3709, 3711, 3713, 3715, 3719, 
-	3721, 3723, 3729, 3731, 3733, 3739, 3741, 3743, 
-	3747, 3749, 3750, 3756, 3758, 3760, 3763, 3770, 
-	3772, 3774, 3776, 3778, 3779, 3780, 3782, 3784, 
-	3786, 3787, 3793, 3794, 3795, 3801, 3802, 3803, 
-	3809, 3823, 3831, 3833, 3835, 3837, 3839, 3841, 
-	3847, 3853, 3855, 3857, 3859, 3861, 3863, 3869, 
-	3873, 3875, 3881, 3883, 3885, 3891, 3893, 3895, 
-	3897, 3903, 3905, 3907, 3913, 3917, 3919, 3925, 
-	3927, 3929, 3935, 3937, 3939, 3941, 3947, 3949, 
-	3951, 3957, 3960, 3969, 3978, 3984, 3993, 3999, 
-	4014, 4020, 4028, 4036, 4044, 4062, 4070, 4088, 
-	4096, 4114, 4122, 4140, 4148, 4160, 4168, 4171, 
-	4179, 4191, 4199, 4202, 4210, 4222, 4230, 4233, 
-	4241, 4253, 4261, 4264, 4267, 4270, 4276, 4282, 
-	4294, 4300, 4303, 4312, 4318, 4333, 4339, 4342, 
-	4344, 4352, 4367, 4373, 4376, 4382, 4392, 4408, 
-	4411, 4418, 4431, 4433, 4441, 4451, 4459, 4469, 
-	4478, 4486, 4492, 4500, 4508, 4518, 4526, 4536, 
-	4545, 4553, 4559, 4568, 4570, 4584, 4596, 4610, 
-	4622, 4636, 4648, 4662, 4672, 4675, 4688, 4701, 
-	4704, 4717, 4730, 4740, 4743, 4756, 4769, 4779, 
-	4782, 4795, 4808, 4818, 4821, 4827, 4830, 4838, 
-	4846, 4849, 4852, 4855, 4861, 4864, 4872, 4880, 
-	4883, 4886, 4888, 4896, 4904, 4912, 4920, 4928, 
-	4943, 4949, 4952, 4955, 4958, 4960, 4968, 4976, 
-	4984, 4996, 5002, 5014, 5020, 5032, 5038, 5053, 
-	5059, 5062, 5065, 5068, 5071, 5074, 5080, 5086, 
-	5094, 5102, 5114, 5120, 5133, 5135, 5138, 5141, 
-	5144, 5157, 5159, 5160, 5163, 5166, 5168, 5180, 
-	5183, 5184, 5191, 5198, 5200, 5208, 5220, 5226, 
-	5234, 5242, 5254, 5260, 5276, 5279, 5282, 5285, 
-	5288, 5290, 5298, 5306, 5314, 5326, 5332, 5348, 
-	5351, 5354, 5357, 5359, 5367, 5377, 5383, 5391, 
-	5399, 5406, 5440, 5453, 5455, 5458, 5472, 5475, 
-	5516, 5525, 5528, 5531, 5537, 5545, 5553, 5562, 
-	5599, 5602, 5605, 5611, 5619, 5627, 5640, 5681, 
-	5684, 5687, 5693, 5701, 5709, 5724, 5758, 5761, 
-	5764, 5767, 5795, 5807, 5819, 5825, 5833, 5841, 
-	5849, 5857, 5865, 5873, 5881, 5889, 5897, 5905, 
-	5920, 5926, 5939, 5941, 5944, 5947, 5950, 5953, 
-	5956, 5959, 5962, 5965, 5967, 5969, 5975, 5977, 
-	5979, 5981, 5987, 5989, 5995, 5999, 6001, 6003, 
-	6009, 6011, 6013, 6015, 6021, 6027, 6029, 6031, 
-	6033, 6039, 6043, 6045, 6047, 6053, 6055, 6057, 
-	6059, 6065, 6071, 6073, 6075, 6077, 6078, 6084, 
-	6085, 6091, 6093, 6095, 6097, 6099, 6105, 6107, 
-	6109, 6111, 6113, 6115, 6117, 6119, 6125, 6129, 
-	6131, 6137, 6143, 6149, 6150, 6156, 6157, 6163, 
-	6165, 6171, 6177, 6181, 6183, 6185, 6187, 6193, 
-	6199, 6205, 6207, 6209, 6211, 6217, 6219, 6225, 
-	6233, 6235, 6242, 6250, 6252, 6254, 6256, 6258, 
-	6264, 6266, 6268, 6274, 6280, 6286, 6288, 6290, 
-	6292, 6298, 6304, 6312, 6314, 6320, 6322, 6328, 
-	6330, 6336, 6338, 6340, 6342, 6348, 6354, 6356, 
-	6358, 6364, 6366, 6372, 6374, 6376, 6378, 6386, 
-	6395, 6401, 6407, 6409, 6417, 6425, 6433, 6445, 
-	6451, 6464, 6466, 6469, 6472, 6475, 6482, 6484, 
-	6486, 6494, 6502, 6510, 6518, 6526, 6539, 6545, 
-	6557, 6563, 6570, 6576, 6583, 6590, 6596, 6603, 
-	6615, 6621, 6622, 6623, 6624, 6625, 6626, 6629, 
-	6635, 6647, 6650, 6653, 6656, 6658, 6666, 6674, 
-	6682, 6690, 6698, 6711, 6717, 6723, 6735, 6738, 
-	6741, 6744, 6746, 6754, 6762, 6770, 6778, 6786, 
-	6798, 6804, 6820, 6823, 6826, 6829, 6832, 6834, 
-	6842, 6851, 6860, 6863, 6865, 6873, 6885, 6891, 
-	6897, 6903, 6904, 6910, 6916, 6922, 6928, 6935, 
-	6938, 6944, 6950, 6951, 6957, 6963, 6970, 6976, 
-	6982, 6983, 6989, 6995, 7002, 7020, 7053, 7099, 
-	7147, 7165, 7213, 7231, 7264, 7327, 7390, 7390, 
-	7390, 7390, 7402, 7402, 7402, 7402, 7418, 7418, 
-	7435, 7438, 7438, 7476, 7479, 7479, 7495, 7498, 
-	7510, 7510, 7526, 7529, 7532, 7532, 7532, 7532, 
-	7532, 7532, 7532, 7532, 7532, 7532, 7548, 7551, 
-	7551, 7551, 7561, 7564, 7576, 7579, 7591, 7594, 
-	7606, 7609, 7609, 7609, 7609, 7609, 7622, 7625, 
-	7625, 7641, 7644, 7644, 7660, 7663, 7663, 7677, 
-	7680, 7693, 7696, 7737, 7746, 7746, 7746, 7746, 
-	7746, 7746, 7746, 7746, 7762, 7765, 7765, 7765, 
-	7765
+	139, 140, 141, 144, 163, 169, 219, 239, 
+	275, 279, 281, 283, 293, 307, 309, 311, 
+	313, 323, 329, 331, 333, 335, 345, 349, 
+	351, 353, 363, 365, 367, 369, 379, 389, 
+	391, 393, 395, 396, 406, 407, 417, 419, 
+	421, 423, 425, 435, 439, 445, 479, 481, 
+	483, 485, 487, 489, 491, 493, 503, 507, 
+	509, 519, 529, 535, 536, 546, 547, 557, 
+	559, 569, 579, 583, 585, 587, 589, 599, 
+	609, 615, 617, 619, 621, 631, 633, 643, 
+	655, 657, 668, 680, 682, 684, 686, 688, 
+	698, 700, 702, 712, 718, 728, 730, 732, 
+	734, 744, 754, 762, 764, 774, 776, 786, 
+	788, 798, 800, 802, 804, 814, 820, 822, 
+	824, 834, 836, 846, 848, 850, 852, 864, 
+	867, 905, 909, 915, 951, 969, 977, 989, 
+	997, 1000, 1001, 1007, 1009, 1011, 1013, 1015, 
+	1017, 1019, 1025, 1031, 1067, 1070, 1088, 1124, 
+	1127, 1135, 1147, 1155, 1158, 1174, 1222, 1240, 
+	1259, 1297, 1303, 1317, 1331, 1381, 1393, 1407, 
+	1417, 1427, 1439, 1451, 1465, 1477, 1491, 1501, 
+	1515, 1531, 1545, 1551, 1565, 1567, 1569, 1571, 
+	1573, 1575, 1581, 1583, 1585, 1591, 1599, 1619, 
+	1657, 1663, 1681, 1731, 1743, 1757, 1767, 1777, 
+	1789, 1801, 1815, 1827, 1841, 1851, 1865, 1881, 
+	1895, 1910, 1948, 1962, 1976, 1990, 1992, 1994, 
+	2004, 2006, 2008, 2010, 2020, 2022, 2032, 2038, 
+	2052, 2066, 2082, 2096, 2099, 2113, 2123, 2135, 
+	2143, 2151, 2161, 2171, 2183, 2193, 2205, 2213, 
+	2225, 2239, 2251, 2257, 2263, 2273, 2279, 2280, 
+	2281, 2292, 2299, 2315, 2330, 2332, 2334, 2336, 
+	2351, 2357, 2363, 2369, 2381, 2383, 2385, 2387, 
+	2399, 2405, 2411, 2413, 2415, 2417, 2423, 2429, 
+	2437, 2457, 2465, 2466, 2476, 2488, 2498, 2501, 
+	2507, 2523, 2524, 2542, 2550, 2551, 2560, 2562, 
+	2564, 2566, 2575, 2583, 2586, 2592, 2604, 2612, 
+	2630, 2631, 2649, 2657, 2658, 2667, 2669, 2671, 
+	2673, 2682, 2690, 2696, 2712, 2715, 2718, 2725, 
+	2732, 2740, 2748, 2765, 2768, 2769, 2779, 2817, 
+	2833, 2835, 2837, 2847, 2850, 2854, 2856, 2858, 
+	2868, 2870, 2872, 2874, 2884, 2890, 2892, 2894, 
+	2896, 2906, 2910, 2912, 2914, 2924, 2926, 2928, 
+	2930, 2940, 2950, 2952, 2954, 2956, 2957, 2967, 
+	2968, 2978, 2980, 2982, 2984, 2986, 2996, 2998, 
+	3000, 3002, 3004, 3006, 3008, 3010, 3020, 3024, 
+	3026, 3036, 3046, 3052, 3053, 3063, 3064, 3074, 
+	3076, 3086, 3096, 3100, 3102, 3104, 3106, 3116, 
+	3126, 3132, 3134, 3136, 3138, 3148, 3150, 3160, 
+	3172, 3174, 3185, 3197, 3199, 3201, 3203, 3205, 
+	3215, 3217, 3219, 3229, 3235, 3245, 3247, 3249, 
+	3251, 3261, 3271, 3279, 3281, 3291, 3293, 3303, 
+	3305, 3315, 3317, 3319, 3321, 3331, 3337, 3339, 
+	3341, 3351, 3353, 3363, 3365, 3367, 3369, 3381, 
+	3383, 3385, 3387, 3397, 3399, 3409, 3415, 3423, 
+	3431, 3443, 3449, 3465, 3468, 3471, 3474, 3480, 
+	3489, 3499, 3511, 3517, 3533, 3536, 3539, 3547, 
+	3550, 3562, 3570, 3574, 3580, 3582, 3589, 3591, 
+	3593, 3595, 3597, 3598, 3599, 3601, 3603, 3605, 
+	3606, 3612, 3614, 3618, 3619, 3621, 3623, 3625, 
+	3627, 3633, 3635, 3637, 3639, 3641, 3642, 3643, 
+	3645, 3647, 3649, 3650, 3651, 3652, 3658, 3659, 
+	3660, 3662, 3664, 3666, 3667, 3668, 3669, 3675, 
+	3677, 3679, 3681, 3683, 3685, 3687, 3689, 3695, 
+	3697, 3699, 3701, 3703, 3705, 3707, 3711, 3713, 
+	3715, 3721, 3723, 3725, 3731, 3733, 3735, 3739, 
+	3741, 3742, 3748, 3750, 3752, 3755, 3762, 3764, 
+	3766, 3768, 3770, 3771, 3772, 3774, 3776, 3778, 
+	3779, 3785, 3786, 3787, 3793, 3794, 3795, 3801, 
+	3815, 3823, 3825, 3827, 3829, 3831, 3833, 3839, 
+	3845, 3847, 3849, 3851, 3853, 3855, 3861, 3865, 
+	3867, 3873, 3875, 3877, 3883, 3885, 3887, 3889, 
+	3895, 3897, 3899, 3905, 3909, 3911, 3917, 3919, 
+	3921, 3927, 3929, 3931, 3933, 3939, 3941, 3943, 
+	3949, 3952, 3961, 3970, 3976, 3985, 3991, 4006, 
+	4012, 4020, 4028, 4036, 4054, 4062, 4080, 4088, 
+	4106, 4114, 4132, 4140, 4152, 4160, 4163, 4171, 
+	4183, 4191, 4194, 4202, 4214, 4222, 4225, 4233, 
+	4245, 4253, 4256, 4259, 4262, 4268, 4274, 4286, 
+	4292, 4295, 4304, 4310, 4325, 4331, 4334, 4336, 
+	4344, 4359, 4365, 4368, 4374, 4384, 4400, 4403, 
+	4410, 4423, 4425, 4433, 4443, 4451, 4461, 4470, 
+	4478, 4484, 4492, 4500, 4510, 4518, 4528, 4537, 
+	4545, 4551, 4560, 4562, 4576, 4588, 4602, 4614, 
+	4628, 4640, 4654, 4664, 4667, 4680, 4693, 4696, 
+	4709, 4722, 4732, 4735, 4748, 4761, 4771, 4774, 
+	4787, 4800, 4810, 4813, 4819, 4822, 4830, 4838, 
+	4841, 4844, 4847, 4853, 4856, 4864, 4872, 4875, 
+	4878, 4880, 4888, 4896, 4904, 4912, 4920, 4935, 
+	4941, 4944, 4947, 4950, 4952, 4960, 4968, 4976, 
+	4988, 4994, 5006, 5012, 5024, 5030, 5045, 5051, 
+	5054, 5057, 5060, 5063, 5066, 5072, 5078, 5086, 
+	5094, 5106, 5112, 5125, 5127, 5130, 5133, 5136, 
+	5149, 5151, 5152, 5155, 5158, 5160, 5172, 5175, 
+	5176, 5183, 5190, 5192, 5200, 5212, 5218, 5226, 
+	5234, 5246, 5252, 5268, 5271, 5274, 5277, 5280, 
+	5282, 5290, 5298, 5306, 5318, 5324, 5340, 5343, 
+	5346, 5349, 5351, 5359, 5369, 5375, 5383, 5391, 
+	5398, 5432, 5445, 5447, 5450, 5464, 5467, 5508, 
+	5517, 5520, 5523, 5529, 5537, 5545, 5554, 5591, 
+	5594, 5597, 5603, 5611, 5619, 5632, 5673, 5676, 
+	5679, 5685, 5693, 5701, 5716, 5750, 5753, 5756, 
+	5759, 5787, 5799, 5811, 5817, 5825, 5833, 5841, 
+	5849, 5857, 5865, 5873, 5881, 5889, 5897, 5912, 
+	5918, 5931, 5933, 5936, 5939, 5942, 5945, 5948, 
+	5951, 5954, 5957, 5959, 5961, 5967, 5969, 5971, 
+	5973, 5979, 5981, 5987, 5991, 5993, 5995, 6001, 
+	6003, 6005, 6007, 6013, 6019, 6021, 6023, 6025, 
+	6031, 6035, 6037, 6039, 6045, 6047, 6049, 6051, 
+	6057, 6063, 6065, 6067, 6069, 6070, 6076, 6077, 
+	6083, 6085, 6087, 6089, 6091, 6097, 6099, 6101, 
+	6103, 6105, 6107, 6109, 6111, 6117, 6121, 6123, 
+	6129, 6135, 6141, 6142, 6148, 6149, 6155, 6157, 
+	6163, 6169, 6173, 6175, 6177, 6179, 6185, 6191, 
+	6197, 6199, 6201, 6203, 6209, 6211, 6217, 6225, 
+	6227, 6234, 6242, 6244, 6246, 6248, 6250, 6256, 
+	6258, 6260, 6266, 6272, 6278, 6280, 6282, 6284, 
+	6290, 6296, 6304, 6306, 6312, 6314, 6320, 6322, 
+	6328, 6330, 6332, 6334, 6340, 6346, 6348, 6350, 
+	6356, 6358, 6364, 6366, 6368, 6370, 6378, 6387, 
+	6393, 6399, 6401, 6409, 6417, 6425, 6437, 6443, 
+	6456, 6458, 6461, 6464, 6467, 6474, 6476, 6478, 
+	6486, 6494, 6502, 6510, 6518, 6531, 6537, 6549, 
+	6555, 6562, 6568, 6575, 6582, 6588, 6595, 6607, 
+	6613, 6614, 6615, 6616, 6617, 6618, 6621, 6627, 
+	6639, 6642, 6645, 6648, 6650, 6658, 6666, 6674, 
+	6682, 6690, 6703, 6709, 6715, 6727, 6730, 6733, 
+	6736, 6738, 6746, 6754, 6762, 6770, 6778, 6790, 
+	6796, 6812, 6815, 6818, 6821, 6824, 6826, 6834, 
+	6843, 6852, 6855, 6857, 6865, 6877, 6883, 6889, 
+	6895, 6896, 6902, 6908, 6914, 6920, 6927, 6930, 
+	6936, 6942, 6943, 6949, 6955, 6962, 6968, 6974, 
+	6975, 6981, 6987, 6994, 7012, 7045, 7091, 7139, 
+	7157, 7205, 7223, 7256, 7319, 7382, 7382, 7382, 
+	7382, 7394, 7394, 7394, 7394, 7410, 7410, 7427, 
+	7430, 7430, 7468, 7471, 7471, 7487, 7490, 7502, 
+	7502, 7518, 7521, 7524, 7524, 7524, 7524, 7524, 
+	7524, 7524, 7524, 7524, 7524, 7540, 7543, 7543, 
+	7543, 7553, 7556, 7568, 7571, 7583, 7586, 7598, 
+	7601, 7601, 7601, 7601, 7601, 7614, 7617, 7617, 
+	7633, 7636, 7636, 7652, 7655, 7655, 7669, 7672, 
+	7685, 7688, 7729, 7738, 7738, 7738, 7738, 7738, 
+	7738, 7738, 7738, 7754, 7757, 7757, 7757, 7757
 };
 
 static const short _zone_scanner_trans_keys[] = {
@@ -1535,8 +1519,7 @@ static const short _zone_scanner_trans_keys[] = {
 	2107, 2314, 2363, 2570, 2619, -128, 8, 11, 
 	58, 60, 127, 9, 32, 40, 41, 778, 
 	827, 1034, 1083, 9, 32, 40, 41, 778, 
-	827, 1034, 1083, 10, 35, 9, 32, 40, 
-	41, 778, 827, 1034, 1083, 1034, 896, 1151, 
+	827, 1034, 1083, 10, 35, 1034, 896, 1151, 
 	9, 32, 40, 41, 92, 1802, 1851, 2058, 
 	2107, 2314, 2363, 2570, 2619, -128, 8, 11, 
 	58, 60, 127, 778, 1034, 640, 895, 896, 
@@ -2493,155 +2476,154 @@ static const short _zone_scanner_trans_keys[] = {
 
 static const char _zone_scanner_single_lengths[] = {
 	0, 36, 16, 34, 16, 11, 8, 8, 
-	1, 1, 8, 1, 13, 2, 42, 18, 
-	36, 4, 2, 2, 10, 12, 2, 2, 
-	2, 10, 6, 2, 2, 2, 10, 4, 
-	2, 2, 10, 2, 2, 2, 10, 10, 
-	2, 2, 2, 1, 10, 1, 10, 2, 
-	2, 2, 2, 10, 4, 6, 34, 2, 
-	2, 2, 2, 2, 2, 2, 10, 4, 
-	2, 10, 10, 6, 1, 10, 1, 10, 
-	2, 10, 10, 4, 2, 2, 2, 10, 
-	10, 6, 2, 2, 2, 10, 2, 10, 
-	12, 2, 11, 12, 2, 2, 2, 2, 
-	10, 2, 2, 10, 6, 10, 2, 2, 
-	2, 10, 10, 8, 2, 10, 2, 10, 
-	2, 10, 2, 2, 2, 10, 6, 2, 
-	2, 10, 2, 10, 2, 2, 0, 10, 
-	1, 36, 4, 6, 34, 16, 6, 10, 
-	6, 1, 1, 6, 2, 2, 2, 2, 
-	2, 2, 2, 6, 34, 1, 16, 34, 
-	1, 6, 10, 6, 1, 16, 40, 18, 
-	13, 36, 2, 8, 8, 42, 12, 14, 
-	10, 10, 12, 12, 14, 12, 14, 10, 
-	14, 16, 14, 2, 14, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 8, 18, 
-	36, 2, 18, 42, 12, 14, 10, 10, 
-	12, 12, 14, 12, 14, 10, 14, 16, 
-	14, 9, 36, 12, 12, 10, 2, 2, 
-	10, 2, 2, 2, 10, 2, 10, 2, 
-	8, 8, 12, 10, 1, 12, 10, 12, 
-	8, 8, 10, 10, 12, 10, 12, 8, 
-	12, 14, 12, 2, 2, 10, 2, 1, 
-	1, 5, 3, 6, 5, 0, 0, 0, 
-	5, 2, 2, 2, 4, 0, 0, 0, 
-	4, 4, 2, 0, 0, 0, 4, 6, 
-	6, 18, 8, 1, 8, 10, 8, 1, 
-	6, 10, 1, 12, 8, 1, 3, 0, 
-	0, 0, 3, 8, 1, 6, 6, 8, 
-	12, 1, 12, 8, 1, 3, 0, 0, 
-	0, 3, 8, 2, 10, 1, 1, 1, 
-	1, 2, 2, 11, 1, 1, 10, 38, 
-	16, 2, 2, 10, 1, 4, 2, 2, 
+	1, 1, 1, 13, 2, 42, 18, 36, 
+	4, 2, 2, 10, 12, 2, 2, 2, 
+	10, 6, 2, 2, 2, 10, 4, 2, 
+	2, 10, 2, 2, 2, 10, 10, 2, 
+	2, 2, 1, 10, 1, 10, 2, 2, 
+	2, 2, 10, 4, 6, 34, 2, 2, 
+	2, 2, 2, 2, 2, 10, 4, 2, 
+	10, 10, 6, 1, 10, 1, 10, 2, 
+	10, 10, 4, 2, 2, 2, 10, 10, 
+	6, 2, 2, 2, 10, 2, 10, 12, 
+	2, 11, 12, 2, 2, 2, 2, 10, 
+	2, 2, 10, 6, 10, 2, 2, 2, 
+	10, 10, 8, 2, 10, 2, 10, 2, 
 	10, 2, 2, 2, 10, 6, 2, 2, 
-	2, 10, 4, 2, 2, 10, 2, 2, 
-	2, 10, 10, 2, 2, 2, 1, 10, 
-	1, 10, 2, 2, 2, 2, 10, 2, 
-	2, 2, 2, 2, 2, 2, 10, 4, 
-	2, 10, 10, 6, 1, 10, 1, 10, 
-	2, 10, 10, 4, 2, 2, 2, 10, 
-	10, 6, 2, 2, 2, 10, 2, 10, 
-	12, 2, 11, 12, 2, 2, 2, 2, 
-	10, 2, 2, 10, 6, 10, 2, 2, 
-	2, 10, 10, 8, 2, 10, 2, 10, 
-	2, 10, 2, 2, 2, 10, 6, 2, 
-	2, 10, 2, 10, 2, 2, 0, 10, 
-	2, 2, 2, 10, 2, 10, 6, 6, 
-	6, 6, 0, 10, 1, 1, 1, 6, 
-	7, 8, 6, 0, 10, 1, 1, 6, 
-	1, 10, 2, 4, 2, 2, 3, 2, 
-	2, 2, 2, 1, 1, 2, 2, 2, 
-	1, 2, 2, 4, 1, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 1, 1, 
-	2, 2, 2, 1, 1, 1, 2, 1, 
-	1, 2, 2, 2, 1, 1, 1, 2, 
+	10, 2, 10, 2, 2, 0, 10, 1, 
+	36, 4, 6, 34, 16, 6, 10, 6, 
+	1, 1, 6, 2, 2, 2, 2, 2, 
+	2, 2, 6, 34, 1, 16, 34, 1, 
+	6, 10, 6, 1, 16, 40, 18, 13, 
+	36, 2, 8, 8, 42, 12, 14, 10, 
+	10, 12, 12, 14, 12, 14, 10, 14, 
+	16, 14, 2, 14, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 8, 18, 36, 
+	2, 18, 42, 12, 14, 10, 10, 12, 
+	12, 14, 12, 14, 10, 14, 16, 14, 
+	9, 36, 12, 12, 10, 2, 2, 10, 
+	2, 2, 2, 10, 2, 10, 2, 8, 
+	8, 12, 10, 1, 12, 10, 12, 8, 
+	8, 10, 10, 12, 10, 12, 8, 12, 
+	14, 12, 2, 2, 10, 2, 1, 1, 
+	5, 3, 6, 5, 0, 0, 0, 5, 
+	2, 2, 2, 4, 0, 0, 0, 4, 
+	4, 2, 0, 0, 0, 4, 6, 6, 
+	18, 8, 1, 8, 10, 8, 1, 6, 
+	10, 1, 12, 8, 1, 3, 0, 0, 
+	0, 3, 8, 1, 6, 6, 8, 12, 
+	1, 12, 8, 1, 3, 0, 0, 0, 
+	3, 8, 2, 10, 1, 1, 1, 1, 
+	2, 2, 11, 1, 1, 10, 38, 16, 
+	2, 2, 10, 1, 4, 2, 2, 10, 
+	2, 2, 2, 10, 6, 2, 2, 2, 
+	10, 4, 2, 2, 10, 2, 2, 2, 
+	10, 10, 2, 2, 2, 1, 10, 1, 
+	10, 2, 2, 2, 2, 10, 2, 2, 
+	2, 2, 2, 2, 2, 10, 4, 2, 
+	10, 10, 6, 1, 10, 1, 10, 2, 
+	10, 10, 4, 2, 2, 2, 10, 10, 
+	6, 2, 2, 2, 10, 2, 10, 12, 
+	2, 11, 12, 2, 2, 2, 2, 10, 
+	2, 2, 10, 6, 10, 2, 2, 2, 
+	10, 10, 8, 2, 10, 2, 10, 2, 
+	10, 2, 2, 2, 10, 6, 2, 2, 
+	10, 2, 10, 2, 2, 0, 10, 2, 
+	2, 2, 10, 2, 10, 6, 6, 6, 
+	6, 0, 10, 1, 1, 1, 6, 7, 
+	8, 6, 0, 10, 1, 1, 6, 1, 
+	10, 2, 4, 2, 2, 3, 2, 2, 
+	2, 2, 1, 1, 2, 2, 2, 1, 
+	2, 2, 4, 1, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 1, 1, 2, 
+	2, 2, 1, 1, 1, 2, 1, 1, 
+	2, 2, 2, 1, 1, 1, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 4, 2, 2, 
+	2, 2, 2, 2, 2, 2, 4, 2, 
+	1, 2, 2, 2, 3, 3, 2, 2, 
+	2, 2, 1, 1, 2, 2, 2, 1, 
+	2, 1, 1, 2, 1, 1, 2, 12, 
+	2, 2, 2, 2, 2, 2, 2, 6, 
 	2, 2, 2, 2, 2, 2, 4, 2, 
-	2, 2, 2, 2, 2, 2, 2, 4, 
-	2, 1, 2, 2, 2, 3, 3, 2, 
-	2, 2, 2, 1, 1, 2, 2, 2, 
-	1, 2, 1, 1, 2, 1, 1, 2, 
-	12, 2, 2, 2, 2, 2, 2, 2, 
-	6, 2, 2, 2, 2, 2, 2, 4, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 2, 4, 2, 2, 2, 
+	2, 2, 2, 4, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 1, 3, 3, 2, 3, 6, 9, 
-	6, 6, 6, 6, 16, 6, 16, 6, 
-	16, 6, 12, 2, 10, 2, 1, 6, 
-	10, 6, 1, 6, 10, 6, 1, 6, 
-	10, 6, 1, 1, 1, 2, 6, 6, 
-	2, 1, 3, 6, 9, 2, 1, 0, 
-	6, 9, 2, 1, 2, 10, 10, 1, 
-	1, 3, 0, 6, 8, 6, 8, 7, 
-	8, 6, 6, 6, 8, 6, 8, 7, 
-	8, 6, 7, 0, 12, 10, 12, 10, 
-	12, 10, 12, 10, 1, 11, 11, 1, 
-	11, 11, 10, 1, 11, 11, 10, 1, 
-	11, 11, 10, 1, 6, 1, 6, 6, 
-	1, 1, 1, 6, 1, 6, 6, 1, 
-	1, 0, 6, 6, 6, 6, 6, 9, 
-	2, 1, 1, 1, 0, 6, 6, 6, 
-	6, 6, 6, 6, 6, 6, 9, 2, 
-	1, 1, 1, 1, 1, 0, 6, 6, 
-	6, 6, 6, 7, 2, 1, 1, 1, 
-	13, 2, 1, 1, 1, 0, 10, 1, 
+	1, 3, 3, 2, 3, 6, 9, 6, 
+	6, 6, 6, 16, 6, 16, 6, 16, 
+	6, 12, 2, 10, 2, 1, 6, 10, 
+	6, 1, 6, 10, 6, 1, 6, 10, 
+	6, 1, 1, 1, 2, 6, 6, 2, 
+	1, 3, 6, 9, 2, 1, 0, 6, 
+	9, 2, 1, 2, 10, 10, 1, 1, 
+	3, 0, 6, 8, 6, 8, 7, 8, 
+	6, 6, 6, 8, 6, 8, 7, 8, 
+	6, 7, 0, 12, 10, 12, 10, 12, 
+	10, 12, 10, 1, 11, 11, 1, 11, 
+	11, 10, 1, 11, 11, 10, 1, 11, 
+	11, 10, 1, 6, 1, 6, 6, 1, 
+	1, 1, 6, 1, 6, 6, 1, 1, 
+	0, 6, 6, 6, 6, 6, 9, 2, 
 	1, 1, 1, 0, 6, 6, 6, 6, 
-	6, 6, 0, 10, 1, 1, 1, 1, 
-	0, 6, 6, 6, 6, 0, 10, 1, 
-	1, 1, 0, 6, 10, 6, 6, 6, 
-	7, 34, 7, 2, 1, 14, 1, 35, 
-	3, 1, 1, 6, 6, 6, 7, 35, 
+	6, 6, 6, 6, 6, 9, 2, 1, 
+	1, 1, 1, 1, 0, 6, 6, 6, 
+	6, 6, 7, 2, 1, 1, 1, 13, 
+	2, 1, 1, 1, 0, 10, 1, 1, 
+	1, 1, 0, 6, 6, 6, 6, 6, 
+	6, 0, 10, 1, 1, 1, 1, 0, 
+	6, 6, 6, 6, 0, 10, 1, 1, 
+	1, 0, 6, 10, 6, 6, 6, 7, 
+	34, 7, 2, 1, 14, 1, 35, 3, 
 	1, 1, 6, 6, 6, 7, 35, 1, 
-	1, 6, 6, 6, 9, 34, 1, 1, 
-	1, 28, 12, 6, 6, 6, 6, 6, 
-	6, 6, 6, 6, 6, 6, 6, 9, 
-	6, 7, 2, 1, 1, 1, 1, 1, 
-	1, 1, 1, 2, 2, 6, 2, 2, 
-	2, 6, 2, 6, 4, 2, 2, 6, 
-	2, 2, 2, 6, 6, 2, 2, 2, 
-	6, 4, 2, 2, 6, 2, 2, 2, 
-	6, 6, 2, 2, 2, 1, 6, 1, 
-	6, 2, 2, 2, 2, 6, 2, 2, 
-	2, 2, 2, 2, 2, 6, 4, 2, 
-	6, 6, 6, 1, 6, 1, 6, 2, 
-	6, 6, 4, 2, 2, 2, 6, 6, 
-	6, 2, 2, 2, 6, 2, 6, 8, 
-	2, 7, 8, 2, 2, 2, 2, 6, 
-	2, 2, 6, 6, 6, 2, 2, 2, 
-	6, 6, 8, 2, 6, 2, 6, 2, 
-	6, 2, 2, 2, 6, 6, 2, 2, 
-	6, 2, 6, 2, 2, 0, 6, 3, 
-	2, 2, 0, 6, 6, 6, 6, 6, 
-	7, 2, 1, 1, 1, 1, 2, 0, 
-	6, 6, 6, 6, 6, 7, 6, 6, 
-	0, 1, 0, 1, 1, 0, 1, 2, 
-	2, 1, 1, 1, 1, 1, 1, 0, 
-	6, 1, 1, 1, 0, 6, 6, 6, 
-	6, 6, 7, 2, 0, 2, 1, 1, 
-	1, 0, 6, 6, 6, 6, 6, 6, 
-	0, 10, 1, 1, 1, 1, 0, 6, 
-	7, 3, 1, 0, 6, 6, 0, 0, 
-	0, 1, 0, 0, 0, 0, 3, 1, 
-	0, 0, 1, 0, 0, 3, 0, 0, 
-	1, 0, 0, 3, 12, 17, 40, 40, 
-	12, 40, 12, 17, 45, 45, 0, 0, 
-	0, 4, 0, 0, 0, 10, 0, 11, 
-	1, 0, 38, 1, 0, 10, 1, 6, 
-	0, 10, 1, 1, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 10, 1, 0, 
-	0, 10, 1, 10, 1, 10, 1, 10, 
-	1, 0, 0, 0, 0, 13, 1, 0, 
-	10, 1, 0, 10, 1, 0, 14, 1, 
-	7, 1, 35, 3, 0, 0, 0, 0, 
-	0, 0, 0, 10, 1, 0, 0, 0, 
-	0
+	1, 6, 6, 6, 7, 35, 1, 1, 
+	6, 6, 6, 9, 34, 1, 1, 1, 
+	28, 12, 6, 6, 6, 6, 6, 6, 
+	6, 6, 6, 6, 6, 6, 9, 6, 
+	7, 2, 1, 1, 1, 1, 1, 1, 
+	1, 1, 2, 2, 6, 2, 2, 2, 
+	6, 2, 6, 4, 2, 2, 6, 2, 
+	2, 2, 6, 6, 2, 2, 2, 6, 
+	4, 2, 2, 6, 2, 2, 2, 6, 
+	6, 2, 2, 2, 1, 6, 1, 6, 
+	2, 2, 2, 2, 6, 2, 2, 2, 
+	2, 2, 2, 2, 6, 4, 2, 6, 
+	6, 6, 1, 6, 1, 6, 2, 6, 
+	6, 4, 2, 2, 2, 6, 6, 6, 
+	2, 2, 2, 6, 2, 6, 8, 2, 
+	7, 8, 2, 2, 2, 2, 6, 2, 
+	2, 6, 6, 6, 2, 2, 2, 6, 
+	6, 8, 2, 6, 2, 6, 2, 6, 
+	2, 2, 2, 6, 6, 2, 2, 6, 
+	2, 6, 2, 2, 0, 6, 3, 2, 
+	2, 0, 6, 6, 6, 6, 6, 7, 
+	2, 1, 1, 1, 1, 2, 0, 6, 
+	6, 6, 6, 6, 7, 6, 6, 0, 
+	1, 0, 1, 1, 0, 1, 2, 2, 
+	1, 1, 1, 1, 1, 1, 0, 6, 
+	1, 1, 1, 0, 6, 6, 6, 6, 
+	6, 7, 2, 0, 2, 1, 1, 1, 
+	0, 6, 6, 6, 6, 6, 6, 0, 
+	10, 1, 1, 1, 1, 0, 6, 7, 
+	3, 1, 0, 6, 6, 0, 0, 0, 
+	1, 0, 0, 0, 0, 3, 1, 0, 
+	0, 1, 0, 0, 3, 0, 0, 1, 
+	0, 0, 3, 12, 17, 40, 40, 12, 
+	40, 12, 17, 45, 45, 0, 0, 0, 
+	4, 0, 0, 0, 10, 0, 11, 1, 
+	0, 38, 1, 0, 10, 1, 6, 0, 
+	10, 1, 1, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 10, 1, 0, 0, 
+	10, 1, 10, 1, 10, 1, 10, 1, 
+	0, 0, 0, 0, 13, 1, 0, 10, 
+	1, 0, 10, 1, 0, 14, 1, 7, 
+	1, 35, 3, 0, 0, 0, 0, 0, 
+	0, 0, 10, 1, 0, 0, 0, 0
 };
 
 static const char _zone_scanner_range_lengths[] = {
 	0, 1, 1, 0, 0, 3, 0, 0, 
-	0, 0, 0, 1, 3, 2, 4, 1, 
-	0, 0, 0, 0, 0, 1, 0, 0, 
+	0, 0, 1, 3, 2, 4, 1, 0, 
+	0, 0, 0, 0, 1, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -2654,32 +2636,32 @@ static const char _zone_scanner_range_lengths[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 0, 0, 1, 1, 1, 1, 
-	1, 1, 0, 0, 0, 0, 0, 0, 
-	0, 0, 2, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 0, 4, 0, 
-	3, 1, 2, 3, 3, 4, 0, 0, 
+	0, 0, 0, 0, 0, 1, 1, 1, 
+	1, 0, 0, 1, 1, 1, 1, 1, 
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 2, 0, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 0, 4, 0, 3, 
+	1, 2, 3, 3, 4, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 2, 0, 0, 0, 0, 
-	0, 0, 2, 0, 0, 2, 0, 1, 
-	1, 2, 0, 4, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 3, 1, 1, 1, 2, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	3, 3, 2, 2, 1, 1, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 2, 2, 0, 2, 0, 
-	0, 3, 2, 5, 5, 1, 1, 1, 
-	5, 2, 2, 2, 4, 1, 1, 1, 
-	4, 1, 2, 1, 1, 1, 1, 0, 
-	1, 1, 0, 0, 1, 1, 1, 1, 
-	0, 3, 0, 3, 0, 0, 3, 1, 
-	1, 1, 3, 0, 1, 0, 3, 0, 
+	0, 0, 2, 0, 0, 0, 0, 0, 
+	0, 2, 0, 0, 2, 0, 1, 1, 
+	2, 0, 4, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	3, 1, 1, 1, 2, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 2, 3, 
+	3, 2, 2, 1, 1, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 2, 2, 0, 2, 0, 0, 
+	3, 2, 5, 5, 1, 1, 1, 5, 
+	2, 2, 2, 4, 1, 1, 1, 4, 
+	1, 2, 1, 1, 1, 1, 0, 1, 
+	1, 0, 0, 1, 1, 1, 1, 0, 
 	3, 0, 3, 0, 0, 3, 1, 1, 
-	1, 3, 0, 2, 3, 1, 1, 3, 
-	3, 3, 3, 3, 1, 0, 0, 0, 
-	0, 0, 0, 0, 1, 0, 0, 0, 
+	1, 3, 0, 1, 0, 3, 0, 3, 
+	0, 3, 0, 0, 3, 1, 1, 1, 
+	3, 0, 2, 3, 1, 1, 3, 3, 
+	3, 3, 3, 1, 0, 0, 0, 0, 
+	0, 0, 0, 1, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -2692,59 +2674,59 @@ static const char _zone_scanner_range_lengths[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 1, 1, 0, 
 	0, 0, 0, 0, 0, 0, 1, 1, 
-	0, 0, 0, 0, 0, 0, 0, 1, 
-	1, 3, 3, 3, 1, 1, 1, 0, 
-	1, 1, 3, 3, 3, 1, 1, 1, 
-	1, 1, 3, 0, 2, 0, 2, 0, 
+	3, 3, 3, 1, 1, 1, 0, 1, 
+	1, 3, 3, 3, 1, 1, 1, 1, 
+	1, 3, 0, 2, 0, 2, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 2, 0, 0, 0, 0, 0, 0, 
-	0, 2, 0, 0, 0, 0, 0, 0, 
+	2, 0, 0, 0, 0, 0, 0, 0, 
+	2, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 2, 0, 0, 
 	0, 0, 0, 0, 0, 0, 2, 0, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 2, 0, 0, 2, 0, 0, 0, 
-	0, 0, 2, 0, 0, 0, 2, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 2, 0, 0, 2, 0, 0, 2, 
-	1, 3, 0, 0, 0, 0, 0, 2, 
 	0, 0, 0, 0, 0, 0, 2, 0, 
-	0, 2, 0, 0, 2, 0, 0, 0, 
-	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	2, 0, 0, 2, 0, 0, 0, 0, 
 	0, 2, 0, 0, 0, 2, 0, 0, 
-	2, 1, 3, 3, 2, 3, 0, 3, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 3, 3, 1, 3, 1, 1, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 1, 
+	3, 0, 0, 0, 0, 0, 2, 0, 
+	0, 0, 0, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 0, 0, 2, 
+	0, 0, 2, 0, 0, 2, 0, 0, 
+	2, 0, 0, 0, 2, 0, 0, 2, 
+	1, 3, 3, 2, 3, 0, 3, 0, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 2, 0, 3, 
-	2, 1, 3, 0, 3, 2, 1, 1, 
-	1, 3, 2, 1, 2, 0, 3, 1, 
-	3, 5, 1, 1, 1, 1, 1, 1, 
-	0, 0, 1, 1, 1, 1, 1, 1, 
-	0, 0, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 0, 1, 1, 1, 1, 
-	1, 1, 0, 1, 1, 1, 0, 1, 
-	1, 1, 0, 1, 0, 1, 1, 1, 
-	1, 1, 1, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 3, 
-	2, 1, 1, 1, 1, 1, 1, 1, 
-	3, 0, 3, 0, 3, 0, 3, 2, 
-	1, 1, 1, 1, 1, 3, 0, 1, 
-	1, 3, 0, 3, 0, 1, 1, 1, 
-	0, 0, 0, 1, 1, 1, 1, 1, 
-	0, 3, 3, 1, 1, 3, 0, 1, 
-	1, 3, 3, 3, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 3, 3, 1, 
-	1, 1, 1, 1, 0, 0, 1, 1, 
-	0, 0, 3, 0, 1, 0, 1, 3, 
-	3, 1, 1, 0, 1, 1, 1, 1, 
-	1, 1, 0, 1, 1, 3, 3, 1, 
-	1, 0, 1, 1, 3, 0, 1, 1, 
-	1, 0, 0, 3, 0, 1, 1, 1, 
+	1, 3, 3, 1, 3, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 2, 0, 3, 2, 
+	1, 3, 0, 3, 2, 1, 1, 1, 
+	3, 2, 1, 2, 0, 3, 1, 3, 
+	5, 1, 1, 1, 1, 1, 1, 0, 
+	0, 1, 1, 1, 1, 1, 1, 0, 
+	0, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 0, 1, 1, 1, 1, 1, 
+	1, 0, 1, 1, 1, 0, 1, 1, 
+	1, 0, 1, 0, 1, 1, 1, 1, 
+	1, 1, 0, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 2, 
 	1, 1, 1, 1, 1, 1, 1, 3, 
-	0, 3, 0, 1, 1, 1, 1, 1, 
-	1, 1, 1, 0, 0, 0, 0, 0, 
+	0, 3, 0, 3, 0, 3, 2, 1, 
+	1, 1, 1, 1, 3, 0, 1, 1, 
+	3, 0, 3, 0, 1, 1, 1, 0, 
+	0, 0, 1, 1, 1, 1, 1, 0, 
+	3, 3, 1, 1, 3, 0, 1, 1, 
+	3, 3, 3, 1, 1, 1, 1, 1, 
+	1, 1, 1, 3, 3, 3, 1, 1, 
+	1, 1, 1, 0, 0, 1, 1, 0, 
+	0, 3, 0, 1, 0, 1, 3, 3, 
+	1, 1, 0, 1, 1, 1, 1, 1, 
+	1, 0, 1, 1, 3, 3, 1, 1, 
+	0, 1, 1, 3, 0, 1, 1, 1, 
+	0, 0, 3, 0, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 0, 
+	3, 0, 1, 1, 1, 1, 1, 1, 
+	1, 1, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -2758,178 +2740,176 @@ static const char _zone_scanner_range_lengths[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 1, 1, 3, 
-	2, 2, 1, 1, 1, 1, 3, 0, 
-	3, 0, 1, 1, 1, 3, 0, 1, 
-	1, 1, 1, 1, 1, 3, 0, 3, 
-	3, 3, 3, 3, 3, 3, 3, 5, 
-	2, 0, 0, 0, 0, 0, 1, 3, 
+	0, 0, 0, 0, 1, 1, 3, 2, 
+	2, 1, 1, 1, 1, 3, 0, 3, 
+	0, 1, 1, 1, 3, 0, 1, 1, 
+	1, 1, 1, 1, 3, 0, 3, 3, 
+	3, 3, 3, 3, 3, 3, 5, 2, 
+	0, 0, 0, 0, 0, 1, 3, 3, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 3, 2, 3, 5, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 3, 
 	3, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 3, 2, 3, 5, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
-	1, 3, 1, 1, 1, 3, 3, 3, 
-	3, 0, 3, 3, 3, 3, 2, 1, 
-	3, 3, 0, 3, 3, 2, 3, 3, 
-	0, 3, 3, 2, 3, 8, 3, 4, 
-	3, 4, 3, 8, 9, 9, 0, 0, 
-	0, 4, 0, 0, 0, 3, 0, 3, 
-	1, 0, 0, 1, 0, 3, 1, 3, 
-	0, 3, 1, 1, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 3, 1, 0, 
-	0, 0, 1, 1, 1, 1, 1, 1, 
-	1, 0, 0, 0, 0, 0, 1, 0, 
-	3, 1, 0, 3, 1, 0, 0, 1, 
-	3, 1, 3, 3, 0, 0, 0, 0, 
-	0, 0, 0, 3, 1, 0, 0, 0, 
-	0
+	3, 1, 1, 1, 3, 3, 3, 3, 
+	0, 3, 3, 3, 3, 2, 1, 3, 
+	3, 0, 3, 3, 2, 3, 3, 0, 
+	3, 3, 2, 3, 8, 3, 4, 3, 
+	4, 3, 8, 9, 9, 0, 0, 0, 
+	4, 0, 0, 0, 3, 0, 3, 1, 
+	0, 0, 1, 0, 3, 1, 3, 0, 
+	3, 1, 1, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 3, 1, 0, 0, 
+	0, 1, 1, 1, 1, 1, 1, 1, 
+	0, 0, 0, 0, 0, 1, 0, 3, 
+	1, 0, 3, 1, 0, 0, 1, 3, 
+	1, 3, 3, 0, 0, 0, 0, 0, 
+	0, 0, 3, 1, 0, 0, 0, 0
 };
 
 static const short _zone_scanner_index_offsets[] = {
 	0, 0, 38, 56, 91, 108, 123, 132, 
-	141, 143, 145, 154, 157, 174, 179, 226, 
-	246, 283, 288, 291, 294, 305, 319, 322, 
-	325, 328, 339, 346, 349, 352, 355, 366, 
-	371, 374, 377, 388, 391, 394, 397, 408, 
-	419, 422, 425, 428, 430, 441, 443, 454, 
-	457, 460, 463, 466, 477, 482, 489, 524, 
-	527, 530, 533, 536, 539, 542, 545, 556, 
-	561, 564, 575, 586, 593, 595, 606, 608, 
-	619, 622, 633, 644, 649, 652, 655, 658, 
-	669, 680, 687, 690, 693, 696, 707, 710, 
-	721, 734, 737, 749, 762, 765, 768, 771, 
-	774, 785, 788, 791, 802, 809, 820, 823, 
-	826, 829, 840, 851, 860, 863, 874, 877, 
-	888, 891, 902, 905, 908, 911, 922, 929, 
-	932, 935, 946, 949, 960, 963, 966, 968, 
-	980, 983, 1021, 1026, 1033, 1069, 1087, 1095, 
-	1107, 1115, 1118, 1120, 1127, 1130, 1133, 1136, 
-	1139, 1142, 1145, 1150, 1157, 1193, 1196, 1214, 
-	1250, 1253, 1261, 1273, 1281, 1284, 1301, 1346, 
-	1365, 1382, 1420, 1425, 1437, 1449, 1496, 1509, 
-	1524, 1535, 1546, 1559, 1572, 1587, 1600, 1615, 
-	1626, 1641, 1658, 1673, 1678, 1693, 1696, 1699, 
-	1702, 1705, 1708, 1713, 1716, 1719, 1724, 1733, 
-	1753, 1791, 1796, 1815, 1862, 1875, 1890, 1901, 
-	1912, 1925, 1938, 1953, 1966, 1981, 1992, 2007, 
-	2024, 2039, 2052, 2090, 2104, 2118, 2131, 2134, 
-	2137, 2148, 2151, 2154, 2157, 2168, 2171, 2182, 
-	2187, 2199, 2211, 2226, 2239, 2242, 2256, 2267, 
-	2280, 2289, 2298, 2309, 2320, 2333, 2344, 2357, 
-	2366, 2379, 2394, 2407, 2412, 2417, 2428, 2433, 
-	2435, 2437, 2446, 2452, 2464, 2475, 2477, 2479, 
-	2481, 2492, 2497, 2502, 2507, 2516, 2518, 2520, 
-	2522, 2531, 2537, 2542, 2544, 2546, 2548, 2554, 
-	2561, 2569, 2589, 2598, 2600, 2610, 2622, 2632, 
-	2635, 2642, 2656, 2658, 2674, 2683, 2685, 2692, 
-	2694, 2696, 2698, 2705, 2714, 2717, 2724, 2734, 
-	2743, 2759, 2761, 2777, 2786, 2788, 2795, 2797, 
-	2799, 2801, 2808, 2817, 2822, 2836, 2839, 2842, 
-	2847, 2852, 2858, 2864, 2879, 2882, 2884, 2895, 
-	2934, 2951, 2954, 2957, 2968, 2971, 2976, 2979, 
-	2982, 2993, 2996, 2999, 3002, 3013, 3020, 3023, 
-	3026, 3029, 3040, 3045, 3048, 3051, 3062, 3065, 
-	3068, 3071, 3082, 3093, 3096, 3099, 3102, 3104, 
-	3115, 3117, 3128, 3131, 3134, 3137, 3140, 3151, 
-	3154, 3157, 3160, 3163, 3166, 3169, 3172, 3183, 
-	3188, 3191, 3202, 3213, 3220, 3222, 3233, 3235, 
-	3246, 3249, 3260, 3271, 3276, 3279, 3282, 3285, 
-	3296, 3307, 3314, 3317, 3320, 3323, 3334, 3337, 
-	3348, 3361, 3364, 3376, 3389, 3392, 3395, 3398, 
-	3401, 3412, 3415, 3418, 3429, 3436, 3447, 3450, 
-	3453, 3456, 3467, 3478, 3487, 3490, 3501, 3504, 
-	3515, 3518, 3529, 3532, 3535, 3538, 3549, 3556, 
-	3559, 3562, 3573, 3576, 3587, 3590, 3593, 3595, 
-	3607, 3610, 3613, 3616, 3627, 3630, 3641, 3648, 
-	3656, 3664, 3674, 3678, 3692, 3695, 3698, 3701, 
-	3708, 3717, 3727, 3737, 3741, 3755, 3758, 3761, 
-	3769, 3772, 3784, 3790, 3795, 3800, 3803, 3809, 
-	3812, 3815, 3818, 3821, 3823, 3825, 3828, 3831, 
-	3834, 3836, 3841, 3844, 3849, 3851, 3854, 3857, 
-	3860, 3863, 3868, 3871, 3874, 3877, 3880, 3882, 
-	3884, 3887, 3890, 3893, 3895, 3897, 3899, 3904, 
-	3906, 3908, 3911, 3914, 3917, 3919, 3921, 3923, 
-	3928, 3931, 3934, 3937, 3940, 3943, 3946, 3949, 
-	3954, 3957, 3960, 3963, 3966, 3969, 3972, 3977, 
-	3980, 3983, 3988, 3991, 3994, 3999, 4002, 4005, 
-	4010, 4013, 4015, 4020, 4023, 4026, 4030, 4036, 
-	4039, 4042, 4045, 4048, 4050, 4052, 4055, 4058, 
-	4061, 4063, 4068, 4070, 4072, 4077, 4079, 4081, 
-	4086, 4100, 4106, 4109, 4112, 4115, 4118, 4121, 
-	4126, 4133, 4136, 4139, 4142, 4145, 4148, 4153, 
-	4158, 4161, 4166, 4169, 4172, 4177, 4180, 4183, 
-	4186, 4191, 4194, 4197, 4202, 4207, 4210, 4215, 
-	4218, 4221, 4226, 4229, 4232, 4235, 4240, 4243, 
-	4246, 4251, 4254, 4261, 4268, 4273, 4280, 4287, 
-	4300, 4307, 4315, 4323, 4331, 4349, 4357, 4375, 
-	4383, 4401, 4409, 4425, 4431, 4443, 4449, 4452, 
-	4460, 4472, 4480, 4483, 4491, 4503, 4511, 4514, 
-	4522, 4534, 4542, 4545, 4548, 4551, 4556, 4563, 
-	4573, 4578, 4581, 4588, 4595, 4608, 4613, 4616, 
-	4618, 4626, 4639, 4644, 4647, 4652, 4663, 4677, 
-	4680, 4685, 4694, 4696, 4704, 4714, 4722, 4732, 
-	4741, 4750, 4757, 4765, 4773, 4783, 4791, 4801, 
-	4810, 4819, 4826, 4835, 4837, 4851, 4863, 4877, 
-	4889, 4903, 4915, 4929, 4940, 4943, 4956, 4969, 
-	4972, 4985, 4998, 5009, 5012, 5025, 5038, 5049, 
-	5052, 5065, 5078, 5089, 5092, 5099, 5102, 5110, 
-	5118, 5121, 5124, 5127, 5134, 5137, 5145, 5153, 
-	5156, 5159, 5161, 5169, 5177, 5185, 5193, 5201, 
-	5214, 5219, 5222, 5225, 5228, 5230, 5238, 5246, 
-	5254, 5264, 5271, 5281, 5288, 5298, 5305, 5318, 
-	5323, 5326, 5329, 5332, 5335, 5338, 5342, 5349, 
-	5357, 5365, 5375, 5382, 5393, 5396, 5399, 5402, 
-	5405, 5419, 5422, 5424, 5427, 5430, 5432, 5444, 
-	5447, 5449, 5454, 5459, 5461, 5469, 5479, 5486, 
-	5494, 5502, 5512, 5516, 5530, 5533, 5536, 5539, 
-	5542, 5544, 5552, 5560, 5568, 5578, 5582, 5596, 
-	5599, 5602, 5605, 5607, 5615, 5626, 5633, 5641, 
-	5649, 5657, 5692, 5703, 5706, 5709, 5724, 5727, 
-	5766, 5773, 5776, 5779, 5786, 5794, 5802, 5811, 
-	5848, 5851, 5854, 5861, 5869, 5877, 5888, 5927, 
-	5930, 5933, 5940, 5948, 5956, 5969, 6004, 6007, 
-	6010, 6013, 6042, 6055, 6065, 6072, 6080, 6088, 
-	6096, 6104, 6112, 6120, 6128, 6136, 6144, 6152, 
-	6165, 6172, 6183, 6186, 6189, 6192, 6195, 6198, 
-	6201, 6204, 6207, 6210, 6213, 6216, 6223, 6226, 
-	6229, 6232, 6239, 6242, 6249, 6254, 6257, 6260, 
-	6267, 6270, 6273, 6276, 6283, 6290, 6293, 6296, 
-	6299, 6306, 6311, 6314, 6317, 6324, 6327, 6330, 
-	6333, 6340, 6347, 6350, 6353, 6356, 6358, 6365, 
-	6367, 6374, 6377, 6380, 6383, 6386, 6393, 6396, 
-	6399, 6402, 6405, 6408, 6411, 6414, 6421, 6426, 
-	6429, 6436, 6443, 6450, 6452, 6459, 6461, 6468, 
-	6471, 6478, 6485, 6490, 6493, 6496, 6499, 6506, 
-	6513, 6520, 6523, 6526, 6529, 6536, 6539, 6546, 
-	6555, 6558, 6566, 6575, 6578, 6581, 6584, 6587, 
-	6594, 6597, 6600, 6607, 6614, 6621, 6624, 6627, 
-	6630, 6637, 6644, 6653, 6656, 6663, 6666, 6673, 
-	6676, 6683, 6686, 6689, 6692, 6699, 6706, 6709, 
-	6712, 6719, 6722, 6729, 6732, 6735, 6737, 6745, 
-	6752, 6757, 6762, 6764, 6772, 6780, 6788, 6798, 
-	6805, 6816, 6819, 6822, 6825, 6828, 6833, 6836, 
-	6838, 6846, 6854, 6862, 6870, 6878, 6889, 6896, 
-	6906, 6910, 6915, 6919, 6924, 6929, 6933, 6938, 
-	6946, 6951, 6953, 6955, 6957, 6959, 6961, 6964, 
-	6968, 6978, 6981, 6984, 6987, 6989, 6997, 7005, 
-	7013, 7021, 7029, 7040, 7045, 7049, 7057, 7060, 
-	7063, 7066, 7068, 7076, 7084, 7092, 7100, 7108, 
-	7118, 7122, 7136, 7139, 7142, 7145, 7148, 7150, 
-	7158, 7167, 7174, 7177, 7179, 7187, 7197, 7201, 
-	7205, 7209, 7211, 7215, 7219, 7223, 7227, 7233, 
-	7236, 7240, 7244, 7246, 7250, 7254, 7260, 7264, 
-	7268, 7270, 7274, 7278, 7284, 7300, 7326, 7370, 
-	7415, 7431, 7476, 7492, 7518, 7573, 7628, 7629, 
-	7630, 7631, 7640, 7641, 7642, 7643, 7657, 7658, 
-	7673, 7676, 7677, 7716, 7719, 7720, 7734, 7737, 
-	7747, 7748, 7762, 7765, 7768, 7769, 7770, 7771, 
-	7772, 7773, 7774, 7775, 7776, 7777, 7791, 7794, 
-	7795, 7796, 7807, 7810, 7822, 7825, 7837, 7840, 
-	7852, 7855, 7856, 7857, 7858, 7859, 7873, 7876, 
-	7877, 7891, 7894, 7895, 7909, 7912, 7913, 7928, 
-	7931, 7942, 7945, 7984, 7991, 7992, 7993, 7994, 
-	7995, 7996, 7997, 7998, 8012, 8015, 8016, 8017, 
-	8018
+	141, 143, 145, 148, 165, 170, 217, 237, 
+	274, 279, 282, 285, 296, 310, 313, 316, 
+	319, 330, 337, 340, 343, 346, 357, 362, 
+	365, 368, 379, 382, 385, 388, 399, 410, 
+	413, 416, 419, 421, 432, 434, 445, 448, 
+	451, 454, 457, 468, 473, 480, 515, 518, 
+	521, 524, 527, 530, 533, 536, 547, 552, 
+	555, 566, 577, 584, 586, 597, 599, 610, 
+	613, 624, 635, 640, 643, 646, 649, 660, 
+	671, 678, 681, 684, 687, 698, 701, 712, 
+	725, 728, 740, 753, 756, 759, 762, 765, 
+	776, 779, 782, 793, 800, 811, 814, 817, 
+	820, 831, 842, 851, 854, 865, 868, 879, 
+	882, 893, 896, 899, 902, 913, 920, 923, 
+	926, 937, 940, 951, 954, 957, 959, 971, 
+	974, 1012, 1017, 1024, 1060, 1078, 1086, 1098, 
+	1106, 1109, 1111, 1118, 1121, 1124, 1127, 1130, 
+	1133, 1136, 1141, 1148, 1184, 1187, 1205, 1241, 
+	1244, 1252, 1264, 1272, 1275, 1292, 1337, 1356, 
+	1373, 1411, 1416, 1428, 1440, 1487, 1500, 1515, 
+	1526, 1537, 1550, 1563, 1578, 1591, 1606, 1617, 
+	1632, 1649, 1664, 1669, 1684, 1687, 1690, 1693, 
+	1696, 1699, 1704, 1707, 1710, 1715, 1724, 1744, 
+	1782, 1787, 1806, 1853, 1866, 1881, 1892, 1903, 
+	1916, 1929, 1944, 1957, 1972, 1983, 1998, 2015, 
+	2030, 2043, 2081, 2095, 2109, 2122, 2125, 2128, 
+	2139, 2142, 2145, 2148, 2159, 2162, 2173, 2178, 
+	2190, 2202, 2217, 2230, 2233, 2247, 2258, 2271, 
+	2280, 2289, 2300, 2311, 2324, 2335, 2348, 2357, 
+	2370, 2385, 2398, 2403, 2408, 2419, 2424, 2426, 
+	2428, 2437, 2443, 2455, 2466, 2468, 2470, 2472, 
+	2483, 2488, 2493, 2498, 2507, 2509, 2511, 2513, 
+	2522, 2528, 2533, 2535, 2537, 2539, 2545, 2552, 
+	2560, 2580, 2589, 2591, 2601, 2613, 2623, 2626, 
+	2633, 2647, 2649, 2665, 2674, 2676, 2683, 2685, 
+	2687, 2689, 2696, 2705, 2708, 2715, 2725, 2734, 
+	2750, 2752, 2768, 2777, 2779, 2786, 2788, 2790, 
+	2792, 2799, 2808, 2813, 2827, 2830, 2833, 2838, 
+	2843, 2849, 2855, 2870, 2873, 2875, 2886, 2925, 
+	2942, 2945, 2948, 2959, 2962, 2967, 2970, 2973, 
+	2984, 2987, 2990, 2993, 3004, 3011, 3014, 3017, 
+	3020, 3031, 3036, 3039, 3042, 3053, 3056, 3059, 
+	3062, 3073, 3084, 3087, 3090, 3093, 3095, 3106, 
+	3108, 3119, 3122, 3125, 3128, 3131, 3142, 3145, 
+	3148, 3151, 3154, 3157, 3160, 3163, 3174, 3179, 
+	3182, 3193, 3204, 3211, 3213, 3224, 3226, 3237, 
+	3240, 3251, 3262, 3267, 3270, 3273, 3276, 3287, 
+	3298, 3305, 3308, 3311, 3314, 3325, 3328, 3339, 
+	3352, 3355, 3367, 3380, 3383, 3386, 3389, 3392, 
+	3403, 3406, 3409, 3420, 3427, 3438, 3441, 3444, 
+	3447, 3458, 3469, 3478, 3481, 3492, 3495, 3506, 
+	3509, 3520, 3523, 3526, 3529, 3540, 3547, 3550, 
+	3553, 3564, 3567, 3578, 3581, 3584, 3586, 3598, 
+	3601, 3604, 3607, 3618, 3621, 3632, 3639, 3647, 
+	3655, 3665, 3669, 3683, 3686, 3689, 3692, 3699, 
+	3708, 3718, 3728, 3732, 3746, 3749, 3752, 3760, 
+	3763, 3775, 3781, 3786, 3791, 3794, 3800, 3803, 
+	3806, 3809, 3812, 3814, 3816, 3819, 3822, 3825, 
+	3827, 3832, 3835, 3840, 3842, 3845, 3848, 3851, 
+	3854, 3859, 3862, 3865, 3868, 3871, 3873, 3875, 
+	3878, 3881, 3884, 3886, 3888, 3890, 3895, 3897, 
+	3899, 3902, 3905, 3908, 3910, 3912, 3914, 3919, 
+	3922, 3925, 3928, 3931, 3934, 3937, 3940, 3945, 
+	3948, 3951, 3954, 3957, 3960, 3963, 3968, 3971, 
+	3974, 3979, 3982, 3985, 3990, 3993, 3996, 4001, 
+	4004, 4006, 4011, 4014, 4017, 4021, 4027, 4030, 
+	4033, 4036, 4039, 4041, 4043, 4046, 4049, 4052, 
+	4054, 4059, 4061, 4063, 4068, 4070, 4072, 4077, 
+	4091, 4097, 4100, 4103, 4106, 4109, 4112, 4117, 
+	4124, 4127, 4130, 4133, 4136, 4139, 4144, 4149, 
+	4152, 4157, 4160, 4163, 4168, 4171, 4174, 4177, 
+	4182, 4185, 4188, 4193, 4198, 4201, 4206, 4209, 
+	4212, 4217, 4220, 4223, 4226, 4231, 4234, 4237, 
+	4242, 4245, 4252, 4259, 4264, 4271, 4278, 4291, 
+	4298, 4306, 4314, 4322, 4340, 4348, 4366, 4374, 
+	4392, 4400, 4416, 4422, 4434, 4440, 4443, 4451, 
+	4463, 4471, 4474, 4482, 4494, 4502, 4505, 4513, 
+	4525, 4533, 4536, 4539, 4542, 4547, 4554, 4564, 
+	4569, 4572, 4579, 4586, 4599, 4604, 4607, 4609, 
+	4617, 4630, 4635, 4638, 4643, 4654, 4668, 4671, 
+	4676, 4685, 4687, 4695, 4705, 4713, 4723, 4732, 
+	4741, 4748, 4756, 4764, 4774, 4782, 4792, 4801, 
+	4810, 4817, 4826, 4828, 4842, 4854, 4868, 4880, 
+	4894, 4906, 4920, 4931, 4934, 4947, 4960, 4963, 
+	4976, 4989, 5000, 5003, 5016, 5029, 5040, 5043, 
+	5056, 5069, 5080, 5083, 5090, 5093, 5101, 5109, 
+	5112, 5115, 5118, 5125, 5128, 5136, 5144, 5147, 
+	5150, 5152, 5160, 5168, 5176, 5184, 5192, 5205, 
+	5210, 5213, 5216, 5219, 5221, 5229, 5237, 5245, 
+	5255, 5262, 5272, 5279, 5289, 5296, 5309, 5314, 
+	5317, 5320, 5323, 5326, 5329, 5333, 5340, 5348, 
+	5356, 5366, 5373, 5384, 5387, 5390, 5393, 5396, 
+	5410, 5413, 5415, 5418, 5421, 5423, 5435, 5438, 
+	5440, 5445, 5450, 5452, 5460, 5470, 5477, 5485, 
+	5493, 5503, 5507, 5521, 5524, 5527, 5530, 5533, 
+	5535, 5543, 5551, 5559, 5569, 5573, 5587, 5590, 
+	5593, 5596, 5598, 5606, 5617, 5624, 5632, 5640, 
+	5648, 5683, 5694, 5697, 5700, 5715, 5718, 5757, 
+	5764, 5767, 5770, 5777, 5785, 5793, 5802, 5839, 
+	5842, 5845, 5852, 5860, 5868, 5879, 5918, 5921, 
+	5924, 5931, 5939, 5947, 5960, 5995, 5998, 6001, 
+	6004, 6033, 6046, 6056, 6063, 6071, 6079, 6087, 
+	6095, 6103, 6111, 6119, 6127, 6135, 6143, 6156, 
+	6163, 6174, 6177, 6180, 6183, 6186, 6189, 6192, 
+	6195, 6198, 6201, 6204, 6207, 6214, 6217, 6220, 
+	6223, 6230, 6233, 6240, 6245, 6248, 6251, 6258, 
+	6261, 6264, 6267, 6274, 6281, 6284, 6287, 6290, 
+	6297, 6302, 6305, 6308, 6315, 6318, 6321, 6324, 
+	6331, 6338, 6341, 6344, 6347, 6349, 6356, 6358, 
+	6365, 6368, 6371, 6374, 6377, 6384, 6387, 6390, 
+	6393, 6396, 6399, 6402, 6405, 6412, 6417, 6420, 
+	6427, 6434, 6441, 6443, 6450, 6452, 6459, 6462, 
+	6469, 6476, 6481, 6484, 6487, 6490, 6497, 6504, 
+	6511, 6514, 6517, 6520, 6527, 6530, 6537, 6546, 
+	6549, 6557, 6566, 6569, 6572, 6575, 6578, 6585, 
+	6588, 6591, 6598, 6605, 6612, 6615, 6618, 6621, 
+	6628, 6635, 6644, 6647, 6654, 6657, 6664, 6667, 
+	6674, 6677, 6680, 6683, 6690, 6697, 6700, 6703, 
+	6710, 6713, 6720, 6723, 6726, 6728, 6736, 6743, 
+	6748, 6753, 6755, 6763, 6771, 6779, 6789, 6796, 
+	6807, 6810, 6813, 6816, 6819, 6824, 6827, 6829, 
+	6837, 6845, 6853, 6861, 6869, 6880, 6887, 6897, 
+	6901, 6906, 6910, 6915, 6920, 6924, 6929, 6937, 
+	6942, 6944, 6946, 6948, 6950, 6952, 6955, 6959, 
+	6969, 6972, 6975, 6978, 6980, 6988, 6996, 7004, 
+	7012, 7020, 7031, 7036, 7040, 7048, 7051, 7054, 
+	7057, 7059, 7067, 7075, 7083, 7091, 7099, 7109, 
+	7113, 7127, 7130, 7133, 7136, 7139, 7141, 7149, 
+	7158, 7165, 7168, 7170, 7178, 7188, 7192, 7196, 
+	7200, 7202, 7206, 7210, 7214, 7218, 7224, 7227, 
+	7231, 7235, 7237, 7241, 7245, 7251, 7255, 7259, 
+	7261, 7265, 7269, 7275, 7291, 7317, 7361, 7406, 
+	7422, 7467, 7483, 7509, 7564, 7619, 7620, 7621, 
+	7622, 7631, 7632, 7633, 7634, 7648, 7649, 7664, 
+	7667, 7668, 7707, 7710, 7711, 7725, 7728, 7738, 
+	7739, 7753, 7756, 7759, 7760, 7761, 7762, 7763, 
+	7764, 7765, 7766, 7767, 7768, 7782, 7785, 7786, 
+	7787, 7798, 7801, 7813, 7816, 7828, 7831, 7843, 
+	7846, 7847, 7848, 7849, 7850, 7864, 7867, 7868, 
+	7882, 7885, 7886, 7900, 7903, 7904, 7919, 7922, 
+	7933, 7936, 7975, 7982, 7983, 7984, 7985, 7986, 
+	7987, 7988, 7989, 8003, 8006, 8007, 8008, 8009
 };
 
 static const short _zone_scanner_indicies[] = {
@@ -2947,1856 +2927,1853 @@ static const short _zone_scanner_indicies[] = {
 	53, 54, 35, 56, 56, 57, 58, 59, 
 	60, 61, 59, 60, 61, 62, 63, 64, 
 	64, 65, 66, 55, 68, 68, 70, 71, 
-	72, 73, 74, 75, 75, 76, 77, 67, 
-	67, 67, 69, 79, 79, 80, 81, 82, 
-	83, 82, 83, 78, 85, 85, 86, 87, 
-	88, 89, 88, 89, 84, 88, 89, 91, 
-	90, 85, 85, 86, 87, 88, 89, 88, 
-	89, 78, 73, 74, 69, 92, 92, 93, 
-	94, 72, 88, 89, 88, 95, 96, 97, 
-	88, 98, 67, 67, 67, 78, 88, 99, 
-	89, 95, 78, 100, 100, 102, 103, 67, 
-	105, 106, 107, 108, 109, 110, 111, 112, 
-	113, 114, 115, 116, 117, 118, 72, 105, 
-	106, 107, 108, 109, 110, 111, 112, 113, 
-	114, 115, 116, 117, 118, 19, 20, 119, 
-	120, 121, 122, 123, 124, 67, 67, 104, 
-	67, 101, 126, 126, 127, 128, 28, 29, 
-	30, 31, 32, 28, 29, 30, 31, 32, 
-	82, 83, 129, 130, 27, 125, 132, 132, 
-	133, 134, 39, 40, 41, 42, 43, 44, 
-	45, 46, 47, 48, 49, 50, 51, 52, 
-	39, 40, 41, 42, 43, 44, 45, 46, 
-	47, 48, 49, 50, 51, 52, 88, 89, 
-	135, 136, 131, 137, 138, 137, 138, 35, 
-	139, 139, 35, 140, 140, 35, 141, 141, 
-	142, 143, 144, 145, 146, 146, 147, 148, 
-	55, 85, 88, 85, 86, 87, 89, 149, 
-	88, 149, 150, 151, 95, 74, 78, 152, 
-	152, 35, 153, 153, 35, 154, 154, 35, 
-	155, 155, 156, 157, 158, 159, 160, 160, 
-	161, 162, 55, 163, 164, 165, 163, 164, 
-	165, 35, 166, 166, 35, 167, 167, 35, 
-	168, 168, 35, 169, 169, 170, 171, 172, 
-	173, 174, 174, 175, 176, 55, 177, 178, 
-	177, 178, 35, 179, 179, 35, 180, 180, 
-	35, 181, 181, 182, 183, 184, 185, 186, 
-	186, 187, 188, 55, 189, 189, 35, 190, 
-	190, 35, 191, 191, 35, 192, 192, 193, 
-	194, 195, 196, 197, 197, 198, 199, 55, 
-	200, 200, 201, 202, 203, 204, 205, 205, 
-	206, 207, 55, 208, 208, 35, 209, 209, 
-	35, 210, 211, 35, 212, 35, 213, 213, 
-	214, 215, 216, 217, 218, 218, 219, 220, 
-	55, 221, 35, 222, 222, 223, 224, 225, 
-	226, 227, 227, 228, 229, 55, 230, 230, 
-	35, 231, 231, 35, 232, 232, 35, 233, 
-	233, 35, 234, 234, 235, 236, 237, 238, 
-	239, 239, 240, 241, 55, 242, 243, 242, 
-	243, 35, 244, 244, 245, 246, 247, 248, 
-	35, 249, 249, 250, 251, 252, 253, 254, 
-	255, 256, 257, 258, 259, 260, 261, 262, 
-	263, 264, 265, 252, 253, 254, 255, 256, 
-	257, 258, 259, 260, 261, 262, 263, 264, 
-	265, 266, 267, 35, 243, 243, 35, 268, 
-	268, 35, 269, 269, 35, 270, 270, 35, 
-	271, 271, 35, 272, 272, 35, 273, 273, 
-	35, 274, 274, 275, 276, 277, 278, 279, 
-	279, 280, 281, 55, 282, 283, 282, 283, 
-	35, 284, 284, 35, 285, 285, 286, 287, 
-	288, 289, 290, 290, 291, 292, 55, 293, 
-	293, 294, 295, 296, 297, 298, 298, 299, 
-	300, 55, 301, 302, 303, 304, 303, 304, 
-	35, 305, 35, 306, 306, 307, 308, 309, 
-	310, 311, 311, 312, 313, 55, 314, 35, 
-	315, 315, 316, 317, 318, 319, 320, 320, 
-	321, 322, 55, 323, 323, 35, 324, 324, 
-	325, 326, 327, 328, 329, 329, 330, 331, 
-	55, 332, 332, 333, 334, 335, 336, 337, 
-	337, 338, 339, 55, 340, 341, 340, 341, 
-	35, 342, 342, 35, 343, 343, 35, 344, 
-	344, 35, 345, 345, 346, 347, 348, 349, 
-	350, 350, 351, 352, 55, 353, 353, 354, 
-	355, 356, 357, 358, 358, 359, 360, 55, 
-	361, 362, 363, 361, 362, 363, 35, 364, 
-	364, 35, 365, 365, 35, 366, 366, 35, 
-	367, 367, 368, 369, 370, 371, 372, 372, 
-	373, 374, 55, 375, 375, 35, 376, 376, 
-	377, 378, 379, 380, 381, 381, 382, 383, 
-	55, 384, 384, 385, 386, 387, 387, 388, 
-	389, 390, 390, 391, 392, 55, 393, 393, 
-	35, 394, 394, 395, 396, 397, 398, 399, 
-	400, 400, 401, 402, 55, 403, 403, 404, 
-	405, 406, 406, 407, 408, 409, 409, 410, 
-	411, 55, 412, 412, 35, 413, 413, 35, 
-	414, 414, 35, 415, 415, 35, 416, 416, 
-	417, 418, 419, 420, 421, 421, 422, 423, 
-	55, 424, 424, 35, 425, 425, 35, 426, 
-	426, 427, 428, 429, 430, 431, 431, 432, 
-	433, 55, 434, 435, 436, 434, 435, 436, 
-	35, 437, 437, 438, 439, 440, 441, 442, 
-	442, 443, 444, 55, 445, 445, 35, 446, 
-	446, 35, 447, 447, 35, 448, 448, 449, 
-	450, 451, 452, 453, 453, 454, 455, 55, 
-	456, 456, 457, 458, 459, 460, 461, 461, 
-	462, 463, 55, 464, 465, 466, 467, 464, 
-	465, 466, 467, 35, 468, 468, 35, 469, 
-	469, 470, 471, 472, 473, 474, 474, 475, 
-	476, 55, 477, 477, 35, 478, 478, 479, 
-	480, 481, 482, 483, 483, 484, 485, 55, 
-	486, 486, 35, 487, 487, 488, 489, 490, 
-	491, 492, 492, 493, 494, 55, 495, 495, 
-	35, 496, 496, 35, 497, 497, 35, 498, 
-	498, 499, 500, 501, 502, 503, 503, 504, 
-	505, 55, 506, 507, 508, 506, 507, 508, 
-	35, 509, 509, 35, 510, 510, 35, 511, 
-	511, 512, 513, 514, 515, 516, 516, 517, 
-	518, 55, 519, 519, 35, 520, 520, 521, 
-	522, 523, 524, 525, 525, 526, 527, 55, 
-	528, 528, 35, 529, 529, 35, 531, 530, 
-	533, 533, 534, 535, 537, 538, 539, 539, 
-	540, 541, 536, 532, 266, 267, 35, 542, 
-	542, 543, 544, 5, 6, 7, 8, 9, 
-	10, 11, 12, 13, 14, 15, 16, 17, 
-	18, 5, 6, 7, 8, 9, 10, 11, 
-	12, 13, 14, 15, 16, 17, 18, 19, 
-	20, 545, 546, 4, 0, 547, 243, 547, 
-	243, 35, 548, 548, 549, 550, 551, 552, 
-	35, 553, 553, 554, 555, 557, 558, 559, 
-	560, 561, 562, 563, 564, 565, 566, 567, 
-	568, 569, 570, 557, 558, 559, 560, 561, 
-	562, 563, 564, 565, 566, 567, 568, 569, 
-	570, 571, 572, 556, 530, 573, 573, 574, 
-	575, 577, 578, 579, 580, 581, 577, 578, 
-	579, 580, 581, 582, 583, 576, 23, 573, 
-	573, 574, 575, 582, 583, 584, 23, 586, 
-	587, 588, 589, 590, 586, 587, 588, 589, 
-	590, 585, 23, 591, 591, 592, 593, 595, 
-	596, 594, 23, 571, 572, 35, 19, 20, 
-	598, 599, 600, 598, 599, 600, 597, 601, 
-	601, 597, 602, 602, 597, 603, 603, 597, 
-	604, 604, 597, 605, 605, 597, 606, 606, 
-	597, 607, 607, 607, 607, 597, 609, 609, 
-	610, 611, 612, 613, 608, 614, 614, 615, 
-	616, 5, 6, 7, 8, 9, 10, 11, 
-	12, 13, 14, 15, 16, 17, 18, 5, 
-	6, 7, 8, 9, 10, 11, 12, 13, 
-	14, 15, 16, 17, 18, 617, 618, 4, 
-	530, 617, 618, 619, 621, 621, 622, 623, 
-	28, 29, 30, 31, 32, 28, 29, 30, 
-	31, 32, 624, 625, 27, 620, 626, 626, 
-	627, 628, 5, 6, 7, 8, 9, 10, 
+	72, 73, 74, 67, 67, 75, 76, 67, 
+	67, 67, 69, 78, 78, 79, 80, 81, 
+	82, 81, 82, 77, 78, 78, 79, 80, 
+	81, 82, 81, 82, 83, 81, 82, 85, 
+	84, 73, 74, 69, 86, 86, 87, 88, 
+	72, 81, 82, 81, 89, 90, 91, 81, 
+	92, 67, 67, 67, 77, 81, 93, 82, 
+	89, 77, 94, 94, 96, 97, 67, 99, 
+	100, 101, 102, 103, 104, 105, 106, 107, 
+	108, 109, 110, 111, 112, 72, 99, 100, 
+	101, 102, 103, 104, 105, 106, 107, 108, 
+	109, 110, 111, 112, 19, 20, 113, 114, 
+	115, 116, 117, 118, 67, 67, 98, 67, 
+	95, 120, 120, 121, 122, 28, 29, 30, 
+	31, 32, 28, 29, 30, 31, 32, 81, 
+	82, 123, 124, 27, 119, 126, 126, 127, 
+	128, 39, 40, 41, 42, 43, 44, 45, 
+	46, 47, 48, 49, 50, 51, 52, 39, 
+	40, 41, 42, 43, 44, 45, 46, 47, 
+	48, 49, 50, 51, 52, 81, 82, 129, 
+	130, 125, 131, 132, 131, 132, 35, 133, 
+	133, 35, 134, 134, 35, 135, 135, 136, 
+	137, 138, 139, 140, 140, 141, 142, 55, 
+	78, 81, 78, 79, 80, 82, 143, 81, 
+	143, 144, 145, 89, 74, 77, 146, 146, 
+	35, 147, 147, 35, 148, 148, 35, 149, 
+	149, 150, 151, 152, 153, 154, 154, 155, 
+	156, 55, 157, 158, 159, 157, 158, 159, 
+	35, 160, 160, 35, 161, 161, 35, 162, 
+	162, 35, 163, 163, 164, 165, 166, 167, 
+	168, 168, 169, 170, 55, 171, 172, 171, 
+	172, 35, 173, 173, 35, 174, 174, 35, 
+	175, 175, 176, 177, 178, 179, 180, 180, 
+	181, 182, 55, 183, 183, 35, 184, 184, 
+	35, 185, 185, 35, 186, 186, 187, 188, 
+	189, 190, 191, 191, 192, 193, 55, 194, 
+	194, 195, 196, 197, 198, 199, 199, 200, 
+	201, 55, 202, 202, 35, 203, 203, 35, 
+	204, 205, 35, 206, 35, 207, 207, 208, 
+	209, 210, 211, 212, 212, 213, 214, 55, 
+	215, 35, 216, 216, 217, 218, 219, 220, 
+	221, 221, 222, 223, 55, 224, 224, 35, 
+	225, 225, 35, 226, 226, 35, 227, 227, 
+	35, 228, 228, 229, 230, 231, 232, 233, 
+	233, 234, 235, 55, 236, 237, 236, 237, 
+	35, 238, 238, 239, 240, 241, 242, 35, 
+	243, 243, 244, 245, 246, 247, 248, 249, 
+	250, 251, 252, 253, 254, 255, 256, 257, 
+	258, 259, 246, 247, 248, 249, 250, 251, 
+	252, 253, 254, 255, 256, 257, 258, 259, 
+	260, 261, 35, 237, 237, 35, 262, 262, 
+	35, 263, 263, 35, 264, 264, 35, 265, 
+	265, 35, 266, 266, 35, 267, 267, 35, 
+	268, 268, 269, 270, 271, 272, 273, 273, 
+	274, 275, 55, 276, 277, 276, 277, 35, 
+	278, 278, 35, 279, 279, 280, 281, 282, 
+	283, 284, 284, 285, 286, 55, 287, 287, 
+	288, 289, 290, 291, 292, 292, 293, 294, 
+	55, 295, 296, 297, 298, 297, 298, 35, 
+	299, 35, 300, 300, 301, 302, 303, 304, 
+	305, 305, 306, 307, 55, 308, 35, 309, 
+	309, 310, 311, 312, 313, 314, 314, 315, 
+	316, 55, 317, 317, 35, 318, 318, 319, 
+	320, 321, 322, 323, 323, 324, 325, 55, 
+	326, 326, 327, 328, 329, 330, 331, 331, 
+	332, 333, 55, 334, 335, 334, 335, 35, 
+	336, 336, 35, 337, 337, 35, 338, 338, 
+	35, 339, 339, 340, 341, 342, 343, 344, 
+	344, 345, 346, 55, 347, 347, 348, 349, 
+	350, 351, 352, 352, 353, 354, 55, 355, 
+	356, 357, 355, 356, 357, 35, 358, 358, 
+	35, 359, 359, 35, 360, 360, 35, 361, 
+	361, 362, 363, 364, 365, 366, 366, 367, 
+	368, 55, 369, 369, 35, 370, 370, 371, 
+	372, 373, 374, 375, 375, 376, 377, 55, 
+	378, 378, 379, 380, 381, 381, 382, 383, 
+	384, 384, 385, 386, 55, 387, 387, 35, 
+	388, 388, 389, 390, 391, 392, 393, 394, 
+	394, 395, 396, 55, 397, 397, 398, 399, 
+	400, 400, 401, 402, 403, 403, 404, 405, 
+	55, 406, 406, 35, 407, 407, 35, 408, 
+	408, 35, 409, 409, 35, 410, 410, 411, 
+	412, 413, 414, 415, 415, 416, 417, 55, 
+	418, 418, 35, 419, 419, 35, 420, 420, 
+	421, 422, 423, 424, 425, 425, 426, 427, 
+	55, 428, 429, 430, 428, 429, 430, 35, 
+	431, 431, 432, 433, 434, 435, 436, 436, 
+	437, 438, 55, 439, 439, 35, 440, 440, 
+	35, 441, 441, 35, 442, 442, 443, 444, 
+	445, 446, 447, 447, 448, 449, 55, 450, 
+	450, 451, 452, 453, 454, 455, 455, 456, 
+	457, 55, 458, 459, 460, 461, 458, 459, 
+	460, 461, 35, 462, 462, 35, 463, 463, 
+	464, 465, 466, 467, 468, 468, 469, 470, 
+	55, 471, 471, 35, 472, 472, 473, 474, 
+	475, 476, 477, 477, 478, 479, 55, 480, 
+	480, 35, 481, 481, 482, 483, 484, 485, 
+	486, 486, 487, 488, 55, 489, 489, 35, 
+	490, 490, 35, 491, 491, 35, 492, 492, 
+	493, 494, 495, 496, 497, 497, 498, 499, 
+	55, 500, 501, 502, 500, 501, 502, 35, 
+	503, 503, 35, 504, 504, 35, 505, 505, 
+	506, 507, 508, 509, 510, 510, 511, 512, 
+	55, 513, 513, 35, 514, 514, 515, 516, 
+	517, 518, 519, 519, 520, 521, 55, 522, 
+	522, 35, 523, 523, 35, 525, 524, 527, 
+	527, 528, 529, 531, 532, 533, 533, 534, 
+	535, 530, 526, 260, 261, 35, 536, 536, 
+	537, 538, 5, 6, 7, 8, 9, 10, 
 	11, 12, 13, 14, 15, 16, 17, 18, 
 	5, 6, 7, 8, 9, 10, 11, 12, 
-	13, 14, 15, 16, 17, 18, 629, 630, 
-	4, 530, 629, 630, 35, 24, 24, 25, 
-	26, 33, 34, 631, 23, 633, 634, 635, 
-	636, 637, 633, 634, 635, 636, 637, 632, 
-	23, 638, 638, 639, 640, 642, 643, 641, 
-	23, 53, 54, 35, 645, 645, 646, 647, 
-	59, 60, 61, 59, 60, 61, 648, 649, 
-	64, 64, 650, 651, 644, 652, 652, 653, 
-	654, 67, 105, 106, 107, 108, 109, 110, 
-	111, 112, 113, 114, 115, 116, 117, 118, 
-	72, 105, 106, 107, 108, 109, 110, 111, 
-	112, 113, 114, 115, 116, 117, 118, 655, 
-	656, 75, 75, 657, 658, 67, 67, 104, 
-	67, 532, 660, 660, 661, 662, 59, 60, 
-	61, 59, 60, 61, 82, 83, 663, 664, 
-	665, 666, 667, 668, 659, 669, 669, 670, 
-	671, 72, 88, 89, 99, 95, 96, 97, 
-	672, 98, 67, 67, 67, 78, 673, 673, 
-	674, 675, 5, 6, 7, 8, 9, 10, 
-	11, 12, 13, 14, 15, 16, 17, 18, 
-	5, 6, 7, 8, 9, 10, 11, 12, 
-	13, 14, 15, 16, 17, 18, 88, 89, 
-	676, 677, 4, 0, 88, 676, 89, 677, 
-	84, 678, 678, 679, 680, 88, 89, 88, 
-	89, 89, 89, 89, 78, 678, 678, 679, 
-	680, 88, 89, 88, 89, 89, 89, 89, 
-	84, 681, 681, 682, 683, 67, 105, 106, 
-	107, 108, 109, 110, 111, 112, 113, 114, 
-	115, 116, 117, 118, 72, 105, 106, 107, 
-	108, 109, 110, 111, 112, 113, 114, 115, 
-	116, 117, 118, 88, 89, 676, 684, 96, 
-	97, 676, 685, 67, 67, 104, 67, 101, 
-	79, 79, 80, 81, 137, 138, 137, 138, 
-	82, 83, 82, 83, 659, 79, 79, 80, 
-	81, 163, 164, 165, 163, 164, 165, 82, 
-	83, 82, 83, 659, 79, 79, 80, 81, 
-	208, 208, 82, 83, 82, 83, 659, 79, 
-	79, 80, 81, 230, 230, 82, 83, 82, 
-	83, 659, 79, 79, 80, 81, 547, 243, 
-	547, 243, 82, 83, 82, 83, 659, 79, 
-	79, 80, 81, 282, 283, 282, 283, 82, 
-	83, 82, 83, 659, 79, 79, 80, 81, 
-	301, 302, 303, 304, 303, 304, 82, 83, 
-	82, 83, 659, 79, 79, 80, 81, 340, 
-	341, 340, 341, 82, 83, 82, 83, 659, 
-	79, 79, 80, 81, 361, 362, 363, 361, 
-	362, 363, 82, 83, 82, 83, 659, 79, 
-	79, 80, 81, 424, 424, 82, 83, 82, 
-	83, 659, 79, 79, 80, 81, 434, 435, 
-	436, 434, 435, 436, 82, 83, 82, 83, 
-	659, 79, 79, 80, 81, 464, 465, 466, 
-	467, 464, 465, 466, 467, 82, 83, 82, 
-	83, 659, 79, 79, 80, 81, 506, 507, 
-	508, 506, 507, 508, 82, 83, 82, 83, 
-	659, 88, 686, 89, 684, 78, 79, 79, 
-	80, 81, 598, 599, 600, 598, 599, 600, 
-	82, 83, 82, 83, 687, 688, 688, 597, 
-	689, 689, 597, 690, 690, 597, 691, 691, 
-	597, 692, 692, 597, 693, 693, 693, 693, 
-	597, 694, 694, 597, 695, 695, 597, 696, 
-	696, 696, 696, 597, 698, 698, 699, 700, 
-	82, 83, 701, 702, 697, 704, 704, 705, 
-	706, 28, 29, 30, 31, 32, 28, 29, 
-	30, 31, 32, 82, 83, 707, 708, 27, 
-	703, 709, 709, 710, 711, 5, 6, 7, 
-	8, 9, 10, 11, 12, 13, 14, 15, 
-	16, 17, 18, 5, 6, 7, 8, 9, 
-	10, 11, 12, 13, 14, 15, 16, 17, 
-	18, 88, 89, 712, 713, 4, 0, 88, 
-	712, 89, 713, 131, 715, 715, 716, 717, 
-	59, 60, 61, 59, 60, 61, 82, 83, 
-	718, 719, 665, 666, 720, 721, 714, 722, 
-	722, 723, 724, 67, 105, 106, 107, 108, 
-	109, 110, 111, 112, 113, 114, 115, 116, 
-	117, 118, 72, 105, 106, 107, 108, 109, 
-	110, 111, 112, 113, 114, 115, 116, 117, 
-	118, 88, 89, 686, 684, 96, 97, 725, 
-	685, 67, 67, 104, 67, 101, 698, 698, 
-	699, 700, 137, 138, 137, 138, 82, 83, 
-	701, 702, 714, 698, 698, 699, 700, 163, 
-	164, 165, 163, 164, 165, 82, 83, 701, 
-	702, 714, 698, 698, 699, 700, 208, 208, 
-	82, 83, 701, 702, 714, 698, 698, 699, 
-	700, 230, 230, 82, 83, 701, 702, 714, 
-	698, 698, 699, 700, 547, 243, 547, 243, 
-	82, 83, 701, 702, 714, 698, 698, 699, 
-	700, 282, 283, 282, 283, 82, 83, 701, 
-	702, 714, 698, 698, 699, 700, 301, 302, 
-	303, 304, 303, 304, 82, 83, 701, 702, 
-	714, 698, 698, 699, 700, 340, 341, 340, 
-	341, 82, 83, 701, 702, 714, 698, 698, 
-	699, 700, 361, 362, 363, 361, 362, 363, 
-	82, 83, 701, 702, 714, 698, 698, 699, 
-	700, 424, 424, 82, 83, 701, 702, 714, 
-	698, 698, 699, 700, 434, 435, 436, 434, 
-	435, 436, 82, 83, 701, 702, 714, 698, 
-	698, 699, 700, 464, 465, 466, 467, 464, 
-	465, 466, 467, 82, 83, 701, 702, 714, 
-	698, 698, 699, 700, 506, 507, 508, 506, 
-	507, 508, 82, 83, 701, 702, 714, 726, 
-	726, 91, 728, 729, 90, 90, 730, 731, 
-	90, 90, 90, 727, 732, 732, 733, 734, 
+	13, 14, 15, 16, 17, 18, 19, 20, 
+	539, 540, 4, 0, 541, 237, 541, 237, 
+	35, 542, 542, 543, 544, 545, 546, 35, 
+	547, 547, 548, 549, 551, 552, 553, 554, 
+	555, 556, 557, 558, 559, 560, 561, 562, 
+	563, 564, 551, 552, 553, 554, 555, 556, 
+	557, 558, 559, 560, 561, 562, 563, 564, 
+	565, 566, 550, 524, 567, 567, 568, 569, 
+	571, 572, 573, 574, 575, 571, 572, 573, 
+	574, 575, 576, 577, 570, 23, 567, 567, 
+	568, 569, 576, 577, 578, 23, 580, 581, 
+	582, 583, 584, 580, 581, 582, 583, 584, 
+	579, 23, 585, 585, 586, 587, 589, 590, 
+	588, 23, 565, 566, 35, 19, 20, 592, 
+	593, 594, 592, 593, 594, 591, 595, 595, 
+	591, 596, 596, 591, 597, 597, 591, 598, 
+	598, 591, 599, 599, 591, 600, 600, 591, 
+	601, 601, 601, 601, 591, 603, 603, 604, 
+	605, 606, 607, 602, 608, 608, 609, 610, 
 	5, 6, 7, 8, 9, 10, 11, 12, 
 	13, 14, 15, 16, 17, 18, 5, 6, 
 	7, 8, 9, 10, 11, 12, 13, 14, 
-	15, 16, 17, 18, 82, 83, 735, 736, 
-	4, 101, 79, 82, 79, 80, 81, 83, 
-	737, 735, 737, 738, 739, 736, 618, 78, 
-	85, 88, 85, 86, 87, 89, 740, 676, 
-	740, 741, 742, 677, 618, 84, 678, 88, 
-	678, 679, 680, 743, 676, 743, 744, 745, 
-	89, 684, 78, 746, 746, 35, 747, 747, 
-	35, 748, 748, 749, 750, 751, 752, 753, 
-	753, 754, 755, 55, 756, 756, 35, 757, 
-	757, 35, 758, 758, 35, 759, 759, 760, 
-	761, 762, 763, 764, 764, 765, 766, 55, 
-	767, 767, 35, 768, 768, 769, 770, 771, 
-	772, 773, 773, 774, 775, 55, 19, 119, 
-	20, 120, 78, 776, 776, 777, 778, 88, 
-	89, 88, 89, 20, 20, 20, 78, 776, 
-	776, 777, 778, 88, 89, 88, 89, 20, 
-	20, 20, 84, 776, 88, 776, 777, 778, 
-	89, 779, 676, 779, 780, 781, 684, 20, 
-	120, 78, 678, 88, 678, 679, 680, 782, 
-	88, 782, 783, 784, 89, 95, 78, 655, 
-	656, 69, 85, 88, 85, 86, 87, 89, 
-	785, 676, 785, 786, 787, 684, 656, 78, 
-	609, 609, 610, 611, 137, 138, 137, 138, 
-	612, 613, 788, 609, 609, 610, 611, 163, 
-	164, 165, 163, 164, 165, 612, 613, 788, 
-	609, 609, 610, 611, 208, 208, 612, 613, 
-	788, 609, 609, 610, 611, 230, 230, 612, 
-	613, 788, 609, 609, 610, 611, 547, 243, 
-	547, 243, 612, 613, 788, 609, 609, 610, 
-	611, 282, 283, 282, 283, 612, 613, 788, 
-	609, 609, 610, 611, 301, 302, 303, 304, 
-	303, 304, 612, 613, 788, 609, 609, 610, 
-	611, 340, 341, 340, 341, 612, 613, 788, 
-	609, 609, 610, 611, 361, 362, 363, 361, 
-	362, 363, 612, 613, 788, 609, 609, 610, 
-	611, 424, 424, 612, 613, 788, 609, 609, 
-	610, 611, 434, 435, 436, 434, 435, 436, 
-	612, 613, 788, 609, 609, 610, 611, 464, 
-	465, 466, 467, 464, 465, 466, 467, 612, 
-	613, 788, 609, 609, 610, 611, 506, 507, 
-	508, 506, 507, 508, 612, 613, 788, 19, 
-	21, 20, 22, 84, 19, 545, 20, 546, 
-	131, 609, 609, 610, 611, 242, 243, 242, 
-	243, 612, 613, 788, 88, 135, 89, 136, 
-	131, 790, 789, 792, 791, 794, 796, 797, 
-	798, 795, 795, 795, 795, 793, 799, 800, 
-	799, 799, 799, 793, 801, 802, 803, 801, 
-	804, 803, 801, 801, 803, 803, 803, 793, 
-	799, 800, 799, 806, 805, 799, 799, 805, 
-	805, 805, 793, 807, 805, 809, 808, 810, 
-	808, 811, 813, 811, 814, 812, 811, 811, 
-	812, 812, 812, 808, 801, 801, 801, 801, 
-	793, 815, 815, 815, 815, 793, 818, 819, 
-	817, 817, 816, 820, 817, 820, 819, 820, 
-	817, 821, 817, 816, 822, 817, 824, 823, 
-	825, 823, 826, 827, 826, 829, 826, 827, 
-	828, 827, 823, 830, 831, 832, 830, 830, 
-	816, 820, 820, 820, 820, 833, 834, 830, 
-	835, 823, 836, 823, 837, 838, 839, 837, 
-	837, 823, 841, 841, 842, 843, 844, 845, 
-	840, 841, 841, 842, 843, 844, 845, 847, 
-	846, 849, 849, 850, 851, 853, 854, 855, 
-	856, 857, 853, 854, 855, 856, 857, 858, 
-	859, 858, 859, 852, 848, 849, 849, 850, 
-	851, 858, 859, 858, 859, 860, 858, 859, 
-	849, 849, 850, 851, 858, 859, 858, 859, 
-	861, 848, 864, 865, 866, 867, 868, 864, 
-	865, 866, 867, 868, 863, 862, 869, 869, 
-	870, 871, 873, 874, 873, 874, 872, 848, 
-	844, 845, 840, 876, 876, 877, 878, 879, 
-	880, 875, 876, 876, 877, 878, 881, 883, 
-	884, 882, 879, 880, 882, 882, 882, 875, 
-	885, 875, 887, 887, 888, 889, 890, 891, 
-	892, 891, 893, 894, 893, 894, 891, 891, 
-	891, 886, 895, 895, 896, 897, 898, 899, 
-	898, 899, 886, 898, 899, 885, 901, 900, 
-	900, 900, 900, 875, 902, 900, 904, 903, 
-	905, 903, 907, 908, 906, 906, 906, 906, 
-	903, 887, 887, 888, 889, 893, 894, 893, 
-	894, 886, 879, 880, 875, 909, 909, 910, 
-	911, 912, 913, 619, 909, 909, 910, 911, 
-	912, 913, 914, 914, 914, 915, 917, 917, 
-	918, 919, 920, 921, 920, 922, 916, 924, 
-	924, 925, 926, 927, 929, 930, 928, 931, 
-	932, 931, 933, 928, 928, 928, 923, 935, 
-	934, 936, 936, 937, 938, 939, 940, 941, 
-	940, 942, 943, 942, 943, 940, 940, 940, 
-	923, 944, 944, 945, 946, 931, 932, 931, 
-	932, 84, 931, 932, 935, 948, 947, 947, 
-	947, 947, 934, 949, 947, 951, 950, 952, 
-	950, 954, 955, 953, 953, 953, 953, 950, 
-	936, 936, 937, 938, 942, 943, 942, 943, 
-	923, 931, 956, 932, 933, 84, 957, 957, 
-	958, 959, 927, 929, 930, 928, 960, 961, 
-	928, 928, 928, 934, 960, 961, 619, 912, 
-	913, 619, 963, 963, 963, 963, 962, 964, 
-	964, 964, 964, 962, 965, 966, 965, 965, 
-	965, 962, 967, 968, 967, 967, 967, 962, 
-	968, 968, 969, 970, 963, 971, 972, 973, 
-	973, 974, 975, 963, 963, 963, 962, 971, 
-	972, 962, 968, 962, 977, 977, 978, 979, 
-	980, 981, 982, 982, 983, 984, 976, 985, 
-	985, 986, 987, 988, 989, 990, 991, 992, 
-	993, 994, 995, 996, 997, 998, 999, 1000, 
-	1001, 988, 989, 990, 991, 992, 993, 994, 
-	995, 996, 997, 998, 999, 1000, 1001, 1002, 
-	1003, 1004, 1004, 1005, 1006, 976, 1007, 1007, 
-	1008, 1009, 1010, 1011, 1012, 1010, 1011, 1012, 
-	1013, 1014, 1015, 1015, 1016, 1017, 976, 1018, 
-	1018, 976, 1019, 1019, 976, 1020, 1020, 1021, 
-	1022, 1023, 1024, 1025, 1025, 1026, 1027, 976, 
-	1002, 1003, 976, 1028, 1029, 1028, 1029, 976, 
-	1030, 1030, 976, 1031, 1031, 976, 1032, 1032, 
-	1033, 1034, 1035, 1036, 1037, 1037, 1038, 1039, 
-	976, 1040, 1040, 976, 1041, 1041, 976, 1042, 
-	1042, 976, 1043, 1043, 1044, 1045, 1046, 1047, 
-	1048, 1048, 1049, 1050, 976, 1051, 1052, 1053, 
-	1051, 1052, 1053, 976, 1054, 1054, 976, 1055, 
-	1055, 976, 1056, 1056, 976, 1057, 1057, 1058, 
-	1059, 1060, 1061, 1062, 1062, 1063, 1064, 976, 
-	1065, 1066, 1065, 1066, 976, 1067, 1067, 976, 
-	1068, 1068, 976, 1069, 1069, 1070, 1071, 1072, 
-	1073, 1074, 1074, 1075, 1076, 976, 1077, 1077, 
-	976, 1078, 1078, 976, 1079, 1079, 976, 1080, 
-	1080, 1081, 1082, 1083, 1084, 1085, 1085, 1086, 
-	1087, 976, 1088, 1088, 1089, 1090, 1091, 1092, 
-	1093, 1093, 1094, 1095, 976, 1096, 1096, 976, 
-	1097, 1097, 976, 1098, 1099, 976, 1100, 976, 
-	1101, 1101, 1102, 1103, 1104, 1105, 1106, 1106, 
-	1107, 1108, 976, 1109, 976, 1110, 1110, 1111, 
-	1112, 1113, 1114, 1115, 1115, 1116, 1117, 976, 
-	1118, 1118, 976, 1119, 1119, 976, 1120, 1120, 
-	976, 1121, 1121, 976, 1122, 1122, 1123, 1124, 
-	1125, 1126, 1127, 1127, 1128, 1129, 976, 1130, 
-	1130, 976, 1131, 1131, 976, 1132, 1132, 976, 
-	1133, 1133, 976, 1134, 1134, 976, 1135, 1135, 
-	976, 1136, 1136, 976, 1137, 1137, 1138, 1139, 
-	1140, 1141, 1142, 1142, 1143, 1144, 976, 1145, 
-	1146, 1145, 1146, 976, 1147, 1147, 976, 1148, 
-	1148, 1149, 1150, 1151, 1152, 1153, 1153, 1154, 
-	1155, 976, 1156, 1156, 1157, 1158, 1159, 1160, 
-	1161, 1161, 1162, 1163, 976, 1164, 1165, 1166, 
-	1167, 1166, 1167, 976, 1168, 976, 1169, 1169, 
-	1170, 1171, 1172, 1173, 1174, 1174, 1175, 1176, 
-	976, 1177, 976, 1178, 1178, 1179, 1180, 1181, 
-	1182, 1183, 1183, 1184, 1185, 976, 1186, 1186, 
-	976, 1187, 1187, 1188, 1189, 1190, 1191, 1192, 
-	1192, 1193, 1194, 976, 1195, 1195, 1196, 1197, 
-	1198, 1199, 1200, 1200, 1201, 1202, 976, 1203, 
-	1204, 1203, 1204, 976, 1205, 1205, 976, 1206, 
-	1206, 976, 1207, 1207, 976, 1208, 1208, 1209, 
-	1210, 1211, 1212, 1213, 1213, 1214, 1215, 976, 
-	1216, 1216, 1217, 1218, 1219, 1220, 1221, 1221, 
-	1222, 1223, 976, 1224, 1225, 1226, 1224, 1225, 
-	1226, 976, 1227, 1227, 976, 1228, 1228, 976, 
-	1229, 1229, 976, 1230, 1230, 1231, 1232, 1233, 
-	1234, 1235, 1235, 1236, 1237, 976, 1238, 1238, 
-	976, 1239, 1239, 1240, 1241, 1242, 1243, 1244, 
-	1244, 1245, 1246, 976, 1247, 1247, 1248, 1249, 
-	1250, 1250, 1251, 1252, 1253, 1253, 1254, 1255, 
-	976, 1256, 1256, 976, 1257, 1257, 1258, 1259, 
-	1260, 1261, 1262, 1263, 1263, 1264, 1265, 976, 
-	1266, 1266, 1267, 1268, 1269, 1269, 1270, 1271, 
-	1272, 1272, 1273, 1274, 976, 1275, 1275, 976, 
-	1276, 1276, 976, 1277, 1277, 976, 1278, 1278, 
-	976, 1279, 1279, 1280, 1281, 1282, 1283, 1284, 
-	1284, 1285, 1286, 976, 1287, 1287, 976, 1288, 
-	1288, 976, 1289, 1289, 1290, 1291, 1292, 1293, 
-	1294, 1294, 1295, 1296, 976, 1297, 1298, 1299, 
-	1297, 1298, 1299, 976, 1300, 1300, 1301, 1302, 
-	1303, 1304, 1305, 1305, 1306, 1307, 976, 1308, 
-	1308, 976, 1309, 1309, 976, 1310, 1310, 976, 
-	1311, 1311, 1312, 1313, 1314, 1315, 1316, 1316, 
-	1317, 1318, 976, 1319, 1319, 1320, 1321, 1322, 
-	1323, 1324, 1324, 1325, 1326, 976, 1327, 1328, 
-	1329, 1330, 1327, 1328, 1329, 1330, 976, 1331, 
-	1331, 976, 1332, 1332, 1333, 1334, 1335, 1336, 
-	1337, 1337, 1338, 1339, 976, 1340, 1340, 976, 
-	1341, 1341, 1342, 1343, 1344, 1345, 1346, 1346, 
-	1347, 1348, 976, 1349, 1349, 976, 1350, 1350, 
-	1351, 1352, 1353, 1354, 1355, 1355, 1356, 1357, 
-	976, 1358, 1358, 976, 1359, 1359, 976, 1360, 
-	1360, 976, 1361, 1361, 1362, 1363, 1364, 1365, 
-	1366, 1366, 1367, 1368, 976, 1369, 1370, 1371, 
-	1369, 1370, 1371, 976, 1372, 1372, 976, 1373, 
-	1373, 976, 1374, 1374, 1375, 1376, 1377, 1378, 
-	1379, 1379, 1380, 1381, 976, 1382, 1382, 976, 
-	1383, 1383, 1384, 1385, 1386, 1387, 1388, 1388, 
-	1389, 1390, 976, 1391, 1391, 976, 1392, 1392, 
-	976, 1393, 976, 1394, 1394, 1395, 1396, 1398, 
-	1399, 1400, 1400, 1401, 1402, 1397, 976, 1403, 
-	1403, 976, 1404, 1404, 976, 1405, 1405, 976, 
-	1406, 1406, 1407, 1408, 1409, 1410, 1411, 1411, 
-	1412, 1413, 976, 1414, 1414, 976, 1415, 1415, 
-	1416, 1417, 1418, 1419, 1420, 1420, 1421, 1422, 
-	976, 1424, 1424, 1425, 1426, 1427, 1428, 1423, 
-	1424, 1424, 1425, 1426, 1427, 1428, 1430, 1429, 
-	1431, 1431, 1432, 1433, 1435, 1436, 1434, 1429, 
-	1438, 1438, 1439, 1440, 1442, 1443, 1441, 1441, 
-	1441, 1437, 1444, 1444, 1444, 1437, 1445, 1445, 
-	1446, 1447, 1448, 1449, 1450, 1450, 1451, 1452, 
-	1441, 1441, 1441, 1437, 1448, 1449, 1437, 1442, 
-	1443, 1423, 1427, 1428, 1423, 1453, 1453, 1454, 
-	1455, 1456, 1457, 1423, 1453, 1453, 1454, 1455, 
-	1458, 1456, 1457, 1459, 1429, 1460, 1460, 1461, 
-	1462, 1464, 1464, 1465, 1466, 1463, 1429, 1467, 
-	1467, 1468, 1469, 1471, 1472, 1470, 1470, 1470, 
-	1437, 1473, 1473, 1473, 1437, 1474, 1474, 1475, 
-	1476, 1477, 1478, 1479, 1479, 1480, 1481, 1470, 
-	1470, 1470, 1437, 1477, 1478, 1437, 1471, 1472, 
-	1423, 1482, 1482, 1483, 1484, 1485, 1486, 1463, 
-	1429, 1456, 1457, 1423, 1489, 1490, 1491, 1492, 
-	1493, 1489, 1490, 1491, 1492, 1493, 1488, 1487, 
-	1494, 1494, 1494, 1494, 1495, 1487, 1496, 1497, 
-	1496, 1497, 1487, 1498, 1498, 1498, 1498, 1487, 
-	1499, 1499, 1487, 1500, 1501, 1500, 1500, 1500, 
-	1487, 1502, 1502, 1487, 1503, 1503, 1487, 1504, 
-	1504, 1487, 1505, 1505, 1487, 1506, 1487, 1507, 
-	1487, 1508, 1508, 1487, 1509, 1509, 1487, 1510, 
-	1510, 1487, 1511, 1487, 1512, 1512, 1512, 1512, 
-	1487, 1513, 1513, 1487, 1514, 1515, 1514, 1515, 
-	1487, 1516, 1487, 1517, 1517, 1487, 1518, 1518, 
-	1487, 1519, 1519, 1487, 1520, 1520, 1487, 1521, 
-	1521, 1521, 1521, 1487, 1522, 1522, 1487, 1523, 
-	1523, 1487, 1524, 1524, 1487, 1525, 1526, 1487, 
-	1527, 1487, 1528, 1487, 1529, 1529, 1487, 1530, 
-	1530, 1487, 1531, 1531, 1487, 1532, 1487, 1533, 
-	1487, 1534, 1487, 1535, 1535, 1535, 1535, 1487, 
-	1536, 1487, 1537, 1487, 1538, 1538, 1487, 1539, 
-	1539, 1487, 1540, 1540, 1487, 1541, 1487, 1542, 
-	1487, 1543, 1487, 1544, 1544, 1544, 1544, 1487, 
-	1545, 1545, 1487, 1546, 1546, 1487, 1547, 1547, 
-	1487, 1548, 1548, 1487, 1549, 1549, 1487, 1550, 
-	1550, 1487, 1551, 1551, 1487, 1552, 1552, 1552, 
-	1552, 1487, 1553, 1553, 1487, 1554, 1554, 1487, 
-	1555, 1555, 1487, 1556, 1556, 1487, 1557, 1557, 
-	1487, 1558, 1558, 1487, 1559, 1560, 1559, 1560, 
-	1487, 1561, 1561, 1487, 1562, 1562, 1487, 1563, 
-	1563, 1563, 1563, 1487, 1564, 1564, 1487, 1565, 
-	1565, 1487, 1566, 1566, 1566, 1566, 1487, 1567, 
-	1567, 1487, 1568, 1568, 1487, 1569, 1570, 1569, 
-	1570, 1487, 1571, 1571, 1487, 1572, 1487, 1573, 
-	1573, 1573, 1573, 1487, 1574, 1574, 1487, 1575, 
-	1575, 1487, 1576, 1577, 1578, 1487, 1579, 1580, 
-	1579, 1579, 1579, 1487, 1581, 1581, 1487, 1582, 
-	1582, 1487, 1583, 1583, 1487, 1584, 1584, 1487, 
-	1585, 1487, 1586, 1487, 1587, 1587, 1487, 1588, 
-	1588, 1487, 1589, 1589, 1487, 1590, 1487, 1591, 
-	1591, 1591, 1591, 1487, 1592, 1487, 1593, 1487, 
-	1594, 1594, 1594, 1594, 1487, 1595, 1487, 1596, 
-	1487, 1597, 1597, 1597, 1597, 1487, 1600, 1601, 
-	1602, 1603, 1604, 1605, 1600, 1601, 1602, 1603, 
-	1604, 1605, 1599, 1598, 1606, 1606, 1606, 1606, 
-	1607, 1598, 1608, 1608, 1598, 1609, 1609, 1598, 
-	1610, 1610, 1598, 1611, 1611, 1598, 1612, 1612, 
-	1598, 1613, 1613, 1613, 1613, 1598, 1614, 1615, 
-	1616, 1614, 1615, 1616, 1598, 1617, 1617, 1598, 
-	1618, 1618, 1598, 1619, 1619, 1598, 1620, 1620, 
-	1598, 1621, 1621, 1598, 1622, 1622, 1622, 1622, 
-	1598, 1623, 1624, 1623, 1624, 1598, 1625, 1625, 
-	1598, 1626, 1626, 1626, 1626, 1598, 1627, 1627, 
-	1598, 1628, 1628, 1598, 1629, 1629, 1629, 1629, 
-	1598, 1630, 1630, 1598, 1631, 1631, 1598, 1632, 
-	1632, 1598, 1633, 1633, 1633, 1633, 1598, 1634, 
-	1634, 1598, 1635, 1635, 1598, 1636, 1636, 1636, 
-	1636, 1598, 1637, 1638, 1637, 1638, 1598, 1639, 
-	1639, 1598, 1640, 1640, 1640, 1640, 1598, 1641, 
-	1641, 1598, 1642, 1642, 1598, 1643, 1643, 1643, 
-	1643, 1598, 1644, 1644, 1598, 1645, 1645, 1598, 
-	1646, 1646, 1598, 1647, 1647, 1647, 1647, 1598, 
-	1648, 1648, 1598, 1649, 1649, 1598, 1650, 1650, 
-	1650, 1650, 1598, 1652, 1652, 1651, 1653, 1654, 
-	1653, 1653, 1653, 1654, 1651, 1655, 1655, 1655, 
-	1655, 1655, 1655, 69, 1656, 1656, 1656, 1656, 
-	69, 1657, 1657, 1657, 1657, 1657, 1657, 69, 
-	1658, 1658, 1659, 1660, 1661, 1662, 69, 1663, 
-	1663, 1664, 1665, 1666, 1666, 1666, 1667, 1668, 
-	1666, 1666, 1666, 69, 1669, 1669, 1670, 1671, 
-	1672, 1673, 69, 1675, 1675, 1676, 1677, 1679, 
-	1680, 1678, 1674, 1681, 1681, 1682, 1683, 1685, 
-	1686, 1684, 1674, 1687, 1687, 1688, 1689, 1691, 
-	1692, 1690, 1674, 1694, 1694, 1695, 1696, 1698, 
-	1699, 1700, 1701, 1702, 1698, 1699, 1700, 1701, 
-	1702, 1703, 1704, 1697, 1693, 1705, 1705, 1706, 
-	1707, 1709, 1710, 1708, 1674, 1711, 1711, 1712, 
-	1713, 1715, 1716, 1717, 1718, 1719, 1715, 1716, 
-	1717, 1718, 1719, 1720, 1721, 1714, 1693, 1722, 
-	1722, 1723, 1724, 1726, 1727, 1725, 1674, 1728, 
-	1728, 1729, 1730, 1732, 1733, 1734, 1735, 1736, 
-	1732, 1733, 1734, 1735, 1736, 1737, 1738, 1731, 
-	1693, 1739, 1739, 1740, 1741, 1743, 1744, 1742, 
-	1674, 1745, 1745, 1747, 1748, 1749, 1750, 1751, 
-	1747, 1748, 1749, 1750, 1751, 1745, 1745, 1746, 
-	1693, 1745, 1745, 1745, 1745, 1752, 1693, 1754, 
-	1755, 1756, 1757, 1758, 1754, 1755, 1756, 1757, 
-	1758, 1753, 1693, 1759, 1759, 1759, 1759, 1760, 
-	1693, 1743, 1744, 69, 1728, 1728, 1729, 1730, 
-	1737, 1738, 1761, 1693, 1763, 1764, 1765, 1766, 
-	1767, 1763, 1764, 1765, 1766, 1767, 1762, 1693, 
-	1768, 1768, 1769, 1770, 1772, 1773, 1771, 1693, 
-	1726, 1727, 69, 1711, 1711, 1712, 1713, 1720, 
-	1721, 1774, 1693, 1776, 1777, 1778, 1779, 1780, 
-	1776, 1777, 1778, 1779, 1780, 1775, 1693, 1781, 
-	1781, 1782, 1783, 1785, 1786, 1784, 1693, 1709, 
-	1710, 69, 1694, 1694, 1695, 1696, 1703, 1704, 
-	1787, 1693, 1789, 1790, 1791, 1792, 1793, 1789, 
-	1790, 1791, 1792, 1793, 1788, 1693, 1794, 1794, 
-	1795, 1796, 1798, 1799, 1797, 1693, 1691, 1692, 
-	69, 1679, 1680, 69, 1667, 1668, 69, 69, 
-	69, 69, 69, 1800, 1801, 1801, 1802, 1803, 
-	1804, 1805, 69, 1807, 1807, 1808, 1809, 1810, 
-	1811, 1806, 1806, 1806, 69, 1812, 1812, 1812, 
-	1812, 69, 1810, 1811, 69, 1813, 1813, 1813, 
-	1813, 1813, 1813, 69, 1814, 1814, 1815, 1816, 
-	1817, 1818, 69, 1819, 1819, 1820, 1821, 1822, 
-	1822, 1822, 1823, 1824, 1822, 1822, 1822, 69, 
-	1825, 1825, 1825, 1825, 69, 1823, 1824, 69, 
-	1826, 1674, 1827, 1827, 1828, 1829, 1831, 1832, 
-	1830, 1674, 1833, 1833, 1834, 1835, 1836, 1836, 
-	1836, 1837, 1838, 1836, 1836, 1836, 69, 1839, 
-	1839, 1839, 1839, 69, 1837, 1838, 69, 69, 
-	69, 69, 69, 1840, 1841, 1841, 1842, 1843, 
-	1844, 1845, 1846, 1846, 1847, 1848, 69, 1849, 
-	1849, 1850, 1851, 1852, 1853, 1854, 1854, 1855, 
-	1856, 1840, 1840, 1840, 69, 1852, 1853, 69, 
-	1857, 1857, 1857, 1857, 1651, 1858, 1859, 1858, 
-	1858, 1858, 1859, 1859, 1859, 1651, 1861, 1860, 
-	1862, 1862, 1863, 1864, 1866, 1867, 1865, 1860, 
-	1868, 1868, 1869, 1870, 1872, 1873, 1874, 1875, 
-	1871, 1860, 1876, 1876, 1877, 1878, 1880, 1881, 
-	1879, 1860, 1882, 1882, 1883, 1884, 1872, 1873, 
-	1886, 1887, 1885, 1860, 1888, 1888, 1889, 1890, 
-	1891, 1893, 1894, 1892, 1860, 1895, 1895, 1896, 
-	1897, 1872, 1873, 1898, 1899, 1860, 1900, 1900, 
-	1901, 1902, 1903, 1904, 1860, 1900, 1900, 1901, 
-	1902, 1903, 1904, 1905, 1860, 1906, 1906, 1907, 
-	1908, 1910, 1911, 1909, 1860, 1912, 1912, 1913, 
-	1914, 1916, 1917, 1918, 1919, 1915, 1860, 1920, 
-	1920, 1921, 1922, 1924, 1925, 1923, 1860, 1926, 
-	1926, 1927, 1928, 1916, 1917, 1930, 1931, 1929, 
-	1860, 1932, 1932, 1933, 1934, 1935, 1937, 1938, 
-	1936, 1860, 1939, 1939, 1940, 1941, 1916, 1917, 
-	1942, 1943, 1860, 1944, 1944, 1945, 1946, 1947, 
-	1948, 1860, 1944, 1944, 1945, 1946, 1949, 1947, 
-	1948, 1950, 1860, 1951, 1860, 1952, 1952, 1953, 
-	1954, 1955, 1957, 1958, 1959, 1960, 1960, 1961, 
-	1962, 1956, 1860, 1963, 1963, 1964, 1965, 1967, 
-	1968, 1969, 1969, 1970, 1971, 1966, 1860, 1972, 
-	1972, 1973, 1974, 1975, 1977, 1978, 1979, 1980, 
-	1980, 1981, 1982, 1976, 1860, 1983, 1983, 1984, 
-	1985, 1987, 1988, 1969, 1969, 1989, 1990, 1986, 
-	1860, 1991, 1991, 1992, 1993, 1994, 1996, 1997, 
-	1998, 1999, 1999, 2000, 2001, 1995, 1860, 2002, 
-	2002, 2003, 2004, 2006, 2007, 1969, 1969, 2008, 
-	2009, 2005, 1860, 2010, 2010, 2011, 2012, 2013, 
-	2010, 2015, 2016, 2017, 2017, 2018, 2019, 2014, 
-	1860, 2020, 2020, 2021, 2022, 2023, 2024, 1969, 
-	1969, 2025, 2026, 1860, 2023, 2024, 1860, 2027, 
-	2027, 2028, 2029, 2027, 2031, 2032, 2033, 2033, 
-	2034, 2035, 2030, 1860, 2010, 2010, 2011, 2012, 
-	2010, 2015, 2016, 2017, 2017, 2018, 2019, 2036, 
-	1860, 2006, 2007, 1860, 2037, 2037, 2038, 2039, 
-	2041, 2042, 2043, 2044, 2044, 2045, 2046, 2040, 
-	1860, 1991, 1991, 1992, 1993, 1996, 1997, 1998, 
-	1999, 1999, 2000, 2001, 2047, 1860, 2002, 2002, 
-	2003, 2004, 2006, 2007, 1969, 1969, 2008, 2009, 
-	1860, 1987, 1988, 1860, 2048, 2048, 2049, 2050, 
-	2052, 2053, 2054, 2055, 2055, 2056, 2057, 2051, 
-	1860, 1972, 1972, 1973, 1974, 1977, 1978, 1979, 
-	1980, 1980, 1981, 1982, 2058, 1860, 1983, 1983, 
-	1984, 1985, 1987, 1988, 1969, 1969, 1989, 1990, 
-	1860, 1967, 1968, 1860, 2059, 2059, 2060, 2061, 
-	2063, 2064, 2065, 2066, 2066, 2067, 2068, 2062, 
-	1860, 1952, 1952, 1953, 1954, 1957, 1958, 1959, 
-	1960, 1960, 1961, 1962, 2069, 1860, 1963, 1963, 
-	1964, 1965, 1967, 1968, 1969, 1969, 1970, 1971, 
-	1860, 1947, 1948, 1860, 2070, 2070, 2071, 2072, 
-	2073, 2074, 1860, 1942, 1943, 1860, 2075, 2075, 
-	2076, 2077, 2079, 2080, 2078, 1860, 1932, 1932, 
-	1933, 1934, 1937, 1938, 2081, 1860, 1930, 1931, 
-	1860, 1918, 1919, 1860, 1903, 1904, 1860, 2082, 
-	2082, 2083, 2084, 2085, 2086, 1860, 1898, 1899, 
-	1860, 2087, 2087, 2088, 2089, 2091, 2092, 2090, 
-	1860, 1888, 1888, 1889, 1890, 1893, 1894, 2093, 
-	1860, 1886, 1887, 1860, 1874, 1875, 1860, 2094, 
-	1674, 2095, 2095, 2096, 2097, 2099, 2100, 2098, 
-	1674, 2101, 2101, 2102, 2103, 2105, 2106, 2104, 
-	1674, 2107, 2107, 2108, 2109, 2111, 2112, 2110, 
-	1674, 2113, 2113, 2114, 2115, 2117, 2118, 2116, 
-	1674, 2119, 2119, 2120, 2121, 2123, 2124, 2122, 
-	1674, 2125, 2125, 2126, 2127, 2128, 2128, 2128, 
-	2129, 2130, 2128, 2128, 2128, 69, 2131, 2131, 
-	2131, 2131, 69, 2129, 2130, 69, 2117, 2118, 
-	69, 2105, 2106, 69, 2132, 1674, 2133, 2133, 
-	2134, 2135, 2137, 2138, 2136, 1674, 2139, 2139, 
-	2140, 2141, 2143, 2144, 2142, 1674, 2145, 2145, 
-	2146, 2147, 2149, 2150, 2148, 1674, 2152, 2152, 
-	2153, 2154, 2155, 2156, 2151, 2151, 2151, 69, 
-	2157, 2157, 2158, 2159, 2160, 2161, 69, 2163, 
-	2163, 2164, 2165, 2166, 2167, 2162, 2162, 2162, 
-	69, 2168, 2168, 2169, 2170, 2171, 2172, 69, 
-	2174, 2174, 2175, 2176, 2177, 2178, 2173, 2173, 
-	2173, 69, 2179, 2179, 2180, 2181, 2182, 2183, 
-	69, 2184, 2184, 2185, 2186, 2187, 2187, 2187, 
-	2188, 2189, 2187, 2187, 2187, 69, 2190, 2190, 
-	2190, 2190, 69, 2188, 2189, 69, 2177, 2178, 
-	69, 2166, 2167, 69, 2155, 2156, 69, 2143, 
-	2144, 69, 2191, 2191, 2191, 69, 2192, 2192, 
-	2193, 2194, 2195, 2196, 69, 2192, 2192, 2193, 
-	2194, 2195, 2196, 2197, 1674, 2198, 2198, 2199, 
-	2200, 2202, 2203, 2201, 1674, 2204, 2204, 2205, 
-	2206, 2208, 2209, 2207, 2207, 2207, 69, 2210, 
-	2210, 2211, 2212, 2213, 2214, 69, 2210, 2210, 
-	2211, 2212, 2215, 2213, 2214, 2215, 2215, 2215, 
-	69, 2216, 2216, 69, 2213, 2214, 69, 2208, 
-	2209, 69, 2195, 2196, 69, 2218, 2218, 2219, 
-	2220, 2221, 2222, 2223, 2224, 2225, 2226, 2226, 
-	2227, 2228, 2217, 2229, 2230, 2217, 2231, 2217, 
-	2233, 2233, 2232, 2235, 2234, 2232, 2236, 2217, 
-	2237, 2237, 2238, 2239, 2241, 2242, 2243, 2243, 
-	2244, 2245, 2240, 2217, 2224, 2225, 69, 2246, 
-	2217, 2247, 2247, 2247, 2247, 2232, 2249, 2248, 
-	2248, 2248, 2232, 2250, 1674, 2251, 2251, 2252, 
-	2253, 2255, 2256, 2254, 1674, 2257, 2257, 2258, 
-	2259, 2261, 2262, 2260, 2260, 2260, 69, 2263, 
-	2263, 2264, 2265, 2266, 2267, 69, 2263, 2263, 
-	2264, 2265, 2266, 2267, 2268, 1674, 2269, 2269, 
-	2270, 2271, 2273, 2274, 2272, 1674, 2276, 2276, 
-	2277, 2278, 2280, 2281, 2279, 2279, 2279, 2275, 
-	2282, 2282, 2282, 2275, 2283, 2283, 2284, 2285, 
-	2286, 2287, 2288, 2288, 2289, 2290, 2279, 2279, 
-	2279, 2275, 2286, 2287, 2275, 2280, 2281, 69, 
-	2266, 2267, 69, 2261, 2262, 69, 2291, 1674, 
-	2292, 2292, 2293, 2294, 2296, 2297, 2295, 1674, 
-	2298, 2298, 2299, 2300, 2302, 2303, 2301, 1674, 
-	2304, 2304, 2305, 2306, 2308, 2309, 2307, 1674, 
-	2310, 2310, 2311, 2312, 2314, 2315, 2313, 2313, 
-	2313, 2275, 2316, 2316, 2316, 2275, 2317, 2317, 
-	2318, 2319, 2320, 2321, 2322, 2322, 2323, 2324, 
-	2313, 2313, 2313, 2275, 2320, 2321, 2275, 2314, 
-	2315, 69, 2302, 2303, 69, 2325, 1674, 2326, 
-	2326, 2327, 2328, 2330, 2331, 2329, 1674, 2333, 
-	2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 
-	2341, 2332, 2342, 2342, 2343, 2344, 2345, 2346, 
-	2332, 2342, 2342, 2343, 2344, 2345, 2346, 2348, 
-	2347, 2349, 2349, 2350, 2351, 2353, 2354, 2352, 
-	2347, 2355, 2355, 2356, 2357, 2358, 2359, 2360, 
-	2332, 2362, 2362, 2363, 2364, 2365, 2365, 2366, 
-	2367, 2368, 2368, 2369, 2370, 2371, 2371, 2371, 
-	2371, 2372, 2373, 2371, 2371, 2374, 2375, 2376, 
-	2377, 2371, 2371, 2378, 2379, 2380, 2381, 2371, 
-	2371, 2382, 2383, 2361, 2362, 2362, 2363, 2364, 
-	2385, 2372, 2373, 2385, 2385, 2385, 2384, 2371, 
-	2371, 2384, 2372, 2373, 2384, 2365, 2365, 2366, 
-	2367, 2371, 2371, 2371, 2371, 2376, 2377, 2371, 
-	2371, 2378, 2379, 2384, 2376, 2377, 2384, 2385, 
-	2362, 2362, 2363, 2364, 2365, 2365, 2366, 2367, 
-	2368, 2368, 2369, 2370, 2371, 2371, 2371, 2371, 
-	2372, 2373, 2371, 2371, 2374, 2375, 2376, 2377, 
-	2371, 2371, 2378, 2379, 2380, 2381, 2371, 2371, 
-	2382, 2383, 2385, 2385, 2385, 2384, 2372, 2376, 
-	2380, 2373, 2377, 2381, 2384, 2359, 2360, 2332, 
-	2345, 2346, 2332, 2386, 2386, 2387, 2388, 2389, 
-	2390, 2332, 2386, 2386, 2387, 2388, 2389, 2390, 
-	2391, 2347, 2392, 2392, 2393, 2394, 2396, 2397, 
-	2395, 2347, 2399, 2399, 2400, 2401, 2402, 2403, 
-	2404, 2402, 2398, 2406, 2407, 2407, 2408, 2409, 
-	2410, 2410, 2411, 2412, 2413, 2413, 2414, 2415, 
-	2416, 2416, 2416, 2416, 2417, 2418, 2416, 2416, 
-	2419, 2420, 2421, 2422, 2416, 2416, 2423, 2424, 
-	2425, 2426, 2416, 2416, 2427, 2428, 2406, 2405, 
-	2403, 2404, 2332, 2389, 2390, 2332, 2429, 2429, 
-	2430, 2431, 2432, 2433, 2332, 2429, 2429, 2430, 
-	2431, 2432, 2433, 2434, 2347, 2435, 2435, 2436, 
-	2437, 2439, 2440, 2438, 2347, 2441, 2441, 2442, 
-	2443, 2444, 2445, 2446, 2444, 2444, 2444, 2398, 
-	2447, 2448, 2448, 2449, 2450, 2451, 2451, 2452, 
-	2453, 2454, 2454, 2455, 2456, 2457, 2457, 2457, 
-	2457, 2458, 2459, 2457, 2457, 2460, 2461, 2462, 
-	2463, 2457, 2457, 2464, 2465, 2466, 2467, 2457, 
-	2457, 2468, 2469, 2447, 2447, 2447, 2405, 2445, 
-	2446, 2332, 2432, 2433, 2332, 2470, 2470, 2471, 
-	2472, 2473, 2474, 2332, 2470, 2470, 2471, 2472, 
-	2473, 2474, 2475, 2347, 2476, 2476, 2477, 2478, 
-	2480, 2481, 2479, 2347, 2482, 2482, 2483, 2484, 
-	2485, 2485, 2485, 2486, 2487, 2485, 2485, 2485, 
-	2332, 2488, 2488, 2489, 2490, 2491, 2491, 2492, 
-	2493, 2494, 2494, 2495, 2496, 2497, 2497, 2497, 
-	2497, 2498, 2499, 2497, 2497, 2500, 2501, 2502, 
-	2503, 2497, 2497, 2504, 2505, 2506, 2507, 2497, 
-	2497, 2508, 2509, 2361, 2486, 2487, 2332, 2473, 
-	2474, 2332, 2340, 2341, 69, 2511, 2512, 2513, 
-	2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 
-	2522, 2523, 2524, 2511, 2512, 2513, 2514, 2515, 
-	2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 
-	2524, 2510, 2525, 2525, 2526, 2527, 2528, 2529, 
-	2530, 2528, 2529, 2530, 2531, 2532, 2510, 2533, 
-	2533, 2534, 2535, 2537, 2538, 2536, 2536, 2536, 
-	69, 2539, 2539, 2540, 2541, 2542, 2543, 69, 
-	2539, 2539, 2540, 2541, 2542, 2543, 2544, 1674, 
-	2545, 2545, 2546, 2547, 2549, 2550, 2548, 1674, 
-	2551, 2551, 2552, 2553, 2555, 2556, 2554, 1674, 
-	2557, 2557, 2558, 2559, 2561, 2562, 2560, 1674, 
-	2564, 2564, 2565, 2566, 2568, 2569, 2567, 2563, 
-	2570, 2570, 2571, 2572, 2574, 2575, 2573, 2563, 
-	2576, 2576, 2577, 2578, 2580, 2581, 2579, 2563, 
-	2582, 2582, 2583, 2584, 2586, 2587, 2585, 2563, 
-	2588, 2588, 2589, 2590, 2592, 2593, 2591, 1674, 
-	2594, 2594, 2595, 2596, 2598, 2599, 2597, 1674, 
-	2600, 2600, 2601, 2602, 2603, 2603, 2603, 2604, 
-	2605, 2603, 2603, 2603, 69, 2606, 2606, 2607, 
-	2608, 2609, 2610, 69, 2611, 2611, 2612, 2613, 
-	2614, 2615, 2616, 2614, 2614, 2614, 69, 2617, 
-	2617, 69, 2615, 2616, 69, 2604, 2605, 69, 
-	2592, 2593, 69, 2580, 2581, 69, 2568, 2569, 
-	69, 2555, 2556, 69, 2542, 2543, 69, 2537, 
-	2538, 69, 2618, 2618, 2510, 2619, 2619, 2510, 
-	2620, 2620, 2621, 2622, 2623, 2624, 2510, 2625, 
-	2625, 2510, 2626, 2626, 2510, 2627, 2627, 2510, 
-	2628, 2628, 2629, 2630, 2631, 2632, 2510, 2633, 
-	2633, 2510, 2634, 2634, 2635, 2636, 2637, 2638, 
-	2510, 2639, 2640, 2639, 2640, 2510, 2641, 2641, 
-	2510, 2642, 2642, 2510, 2643, 2643, 2644, 2645, 
-	2646, 2647, 2510, 2648, 2648, 2510, 2649, 2649, 
-	2510, 2650, 2650, 2510, 2651, 2651, 2652, 2653, 
-	2654, 2655, 2510, 2656, 2657, 2658, 2656, 2657, 
-	2658, 2510, 2659, 2659, 2510, 2660, 2660, 2510, 
-	2661, 2661, 2510, 2662, 2662, 2663, 2664, 2665, 
-	2666, 2510, 2667, 2668, 2667, 2668, 2510, 2669, 
-	2669, 2510, 2670, 2670, 2510, 2671, 2671, 2672, 
-	2673, 2674, 2675, 2510, 2676, 2676, 2510, 2677, 
-	2677, 2510, 2678, 2678, 2510, 2679, 2679, 2680, 
-	2681, 2682, 2683, 2510, 2684, 2684, 2685, 2686, 
-	2687, 2688, 2510, 2689, 2689, 2510, 2690, 2690, 
-	2510, 2691, 2692, 2510, 2693, 2510, 2694, 2694, 
-	2695, 2696, 2697, 2698, 2510, 2699, 2510, 2700, 
-	2700, 2701, 2702, 2703, 2704, 2510, 2705, 2705, 
-	2510, 2706, 2706, 2510, 2707, 2707, 2510, 2708, 
-	2708, 2510, 2709, 2709, 2710, 2711, 2712, 2713, 
-	2510, 2714, 2714, 2510, 2715, 2715, 2510, 2716, 
-	2716, 2510, 2717, 2717, 2510, 2718, 2718, 2510, 
-	2719, 2719, 2510, 2720, 2720, 2510, 2721, 2721, 
-	2722, 2723, 2724, 2725, 2510, 2726, 2727, 2726, 
-	2727, 2510, 2728, 2728, 2510, 2729, 2729, 2730, 
-	2731, 2732, 2733, 2510, 2734, 2734, 2735, 2736, 
-	2737, 2738, 2510, 2739, 2740, 2741, 2742, 2741, 
-	2742, 2510, 2743, 2510, 2744, 2744, 2745, 2746, 
-	2747, 2748, 2510, 2749, 2510, 2750, 2750, 2751, 
-	2752, 2753, 2754, 2510, 2755, 2755, 2510, 2756, 
-	2756, 2757, 2758, 2759, 2760, 2510, 2761, 2761, 
-	2762, 2763, 2764, 2765, 2510, 2766, 2767, 2766, 
-	2767, 2510, 2768, 2768, 2510, 2769, 2769, 2510, 
-	2770, 2770, 2510, 2771, 2771, 2772, 2773, 2774, 
-	2775, 2510, 2776, 2776, 2777, 2778, 2779, 2780, 
-	2510, 2781, 2782, 2783, 2781, 2782, 2783, 2510, 
-	2784, 2784, 2510, 2785, 2785, 2510, 2786, 2786, 
-	2510, 2787, 2787, 2788, 2789, 2790, 2791, 2510, 
-	2792, 2792, 2510, 2793, 2793, 2794, 2795, 2796, 
-	2797, 2510, 2798, 2798, 2799, 2800, 2801, 2801, 
-	2802, 2803, 2510, 2804, 2804, 2510, 2805, 2805, 
-	2806, 2807, 2808, 2809, 2810, 2510, 2811, 2811, 
-	2812, 2813, 2814, 2814, 2815, 2816, 2510, 2817, 
-	2817, 2510, 2818, 2818, 2510, 2819, 2819, 2510, 
-	2820, 2820, 2510, 2821, 2821, 2822, 2823, 2824, 
-	2825, 2510, 2826, 2826, 2510, 2827, 2827, 2510, 
-	2828, 2828, 2829, 2830, 2831, 2832, 2510, 2833, 
-	2834, 2835, 2833, 2834, 2835, 2510, 2836, 2836, 
-	2837, 2838, 2839, 2840, 2510, 2841, 2841, 2510, 
-	2842, 2842, 2510, 2843, 2843, 2510, 2844, 2844, 
-	2845, 2846, 2847, 2848, 2510, 2849, 2849, 2850, 
-	2851, 2852, 2853, 2510, 2854, 2855, 2856, 2857, 
-	2854, 2855, 2856, 2857, 2510, 2858, 2858, 2510, 
-	2859, 2859, 2860, 2861, 2862, 2863, 2510, 2864, 
-	2864, 2510, 2865, 2865, 2866, 2867, 2868, 2869, 
-	2510, 2870, 2870, 2510, 2871, 2871, 2872, 2873, 
-	2874, 2875, 2510, 2876, 2876, 2510, 2877, 2877, 
-	2510, 2878, 2878, 2510, 2879, 2879, 2880, 2881, 
-	2882, 2883, 2510, 2884, 2885, 2886, 2884, 2885, 
-	2886, 2510, 2887, 2887, 2510, 2888, 2888, 2510, 
-	2889, 2889, 2890, 2891, 2892, 2893, 2510, 2894, 
-	2894, 2510, 2895, 2895, 2896, 2897, 2898, 2899, 
-	2510, 2900, 2900, 2510, 2901, 2901, 2510, 2903, 
-	2902, 2904, 2904, 2905, 2906, 2908, 2909, 2907, 
-	2902, 2910, 2910, 2910, 2910, 2910, 2910, 69, 
-	2911, 2911, 2911, 2911, 69, 2912, 2912, 2912, 
-	2912, 69, 2913, 1674, 2914, 2914, 2915, 2916, 
-	2918, 2919, 2917, 1674, 2920, 2920, 2921, 2922, 
-	2924, 2925, 2923, 1674, 2926, 2926, 2927, 2928, 
-	2930, 2931, 2929, 1674, 2932, 2932, 2933, 2934, 
-	2936, 2937, 2935, 2935, 2935, 69, 2938, 2938, 
-	2939, 2940, 2941, 2942, 69, 2938, 2938, 2939, 
-	2940, 2943, 2941, 2942, 2943, 2943, 2943, 69, 
-	2944, 2944, 69, 2941, 2942, 69, 2936, 2937, 
-	69, 2924, 2925, 69, 2945, 2945, 2945, 2945, 
-	69, 2946, 2946, 69, 2947, 1674, 2948, 2948, 
-	2949, 2950, 2952, 2953, 2951, 1674, 2954, 2954, 
-	2955, 2956, 2958, 2959, 2957, 1674, 2960, 2960, 
-	2961, 2962, 2964, 2965, 2963, 1674, 2966, 2966, 
-	2967, 2968, 2970, 2971, 2969, 1674, 2972, 2972, 
-	2973, 2974, 2976, 2977, 2975, 1674, 2978, 2978, 
-	2979, 2980, 2981, 2983, 2984, 2982, 2982, 2982, 
-	2275, 2985, 2985, 2986, 2987, 2988, 2989, 2275, 
-	2991, 2991, 2992, 2993, 2995, 2996, 2994, 2994, 
-	2994, 2990, 2997, 2997, 2997, 2990, 2999, 2998, 
-	2998, 2998, 2990, 3000, 3000, 3000, 2990, 3002, 
-	3001, 3001, 3001, 2990, 3004, 3003, 3003, 3003, 
-	2990, 3005, 3005, 3005, 2990, 3007, 3006, 3006, 
-	3006, 2990, 3008, 3008, 3008, 3008, 3009, 3009, 
-	3009, 2990, 3010, 3010, 3010, 3010, 69, 3011, 
-	2990, 3007, 2990, 3004, 2990, 3012, 2990, 3002, 
-	2990, 2995, 2996, 69, 3013, 3013, 3013, 2275, 
-	2985, 2985, 2986, 2987, 2988, 2989, 3014, 3014, 
-	3014, 2275, 2983, 2984, 69, 2970, 2971, 69, 
-	2958, 2959, 69, 3015, 1674, 3016, 3016, 3017, 
-	3018, 3020, 3021, 3019, 1674, 3022, 3022, 3023, 
-	3024, 3026, 3027, 3025, 1674, 3028, 3028, 3029, 
-	3030, 3032, 3033, 3031, 1674, 3034, 3034, 3035, 
-	3036, 3038, 3039, 3037, 1674, 3040, 3040, 3041, 
-	3042, 3044, 3045, 3043, 1674, 3046, 3046, 3047, 
-	3048, 3049, 3051, 3052, 3050, 3050, 3050, 2275, 
-	3053, 3053, 3053, 3053, 2275, 3054, 3054, 3054, 
-	2275, 3053, 3053, 3053, 3053, 3055, 3055, 3055, 
-	2275, 3051, 3052, 69, 3038, 3039, 69, 3026, 
-	3027, 69, 3056, 1674, 3057, 3057, 3058, 3059, 
-	3061, 3062, 3060, 1674, 3063, 3063, 3064, 3065, 
-	3067, 3068, 3066, 1674, 3069, 3069, 3070, 3071, 
-	3073, 3074, 3072, 1674, 3075, 3075, 3076, 3077, 
-	3079, 3080, 3078, 1674, 3081, 3081, 3082, 3083, 
-	3085, 3086, 3084, 1674, 3087, 3087, 3088, 3089, 
-	3091, 3092, 3090, 3090, 3090, 2275, 3093, 3093, 
-	3093, 2275, 3094, 3094, 3095, 3096, 3097, 3098, 
-	3099, 3099, 3100, 3101, 3090, 3090, 3090, 2275, 
-	3097, 3098, 2275, 3091, 3092, 69, 3079, 3080, 
-	69, 3067, 3068, 69, 3102, 1674, 3103, 3103, 
-	3104, 3105, 3107, 3108, 3106, 1674, 3109, 3109, 
-	3110, 3111, 3112, 3113, 3114, 3112, 1651, 3115, 
-	3116, 3115, 3115, 3115, 3116, 1651, 3113, 3114, 
-	69, 3117, 1674, 3118, 3118, 3119, 3120, 3122, 
-	3123, 3121, 1674, 3124, 3124, 3125, 3126, 3128, 
-	3129, 3127, 3127, 3127, 2275, 3130, 3130, 3130, 
-	2275, 3131, 3131, 3131, 2275, 3132, 3132, 3132, 
-	2275, 3134, 3133, 3135, 3135, 3135, 2275, 3136, 
-	3136, 3136, 2275, 3137, 3137, 3137, 2275, 3138, 
-	3138, 3138, 2275, 3140, 3134, 3140, 3140, 3140, 
-	3139, 3128, 3129, 69, 3141, 3141, 3141, 2275, 
-	3142, 3142, 3142, 2275, 3144, 3143, 3145, 3145, 
-	3145, 2275, 3146, 3146, 3146, 2275, 3147, 3144, 
-	3147, 3147, 3147, 3143, 3148, 3148, 3148, 2275, 
-	3149, 3149, 3149, 2275, 3150, 3143, 3151, 3151, 
-	3151, 2275, 3152, 3152, 3152, 2275, 3153, 3150, 
-	3153, 3153, 3153, 3143, 3155, 3155, 3156, 3157, 
-	3158, 3159, 3159, 3159, 19, 20, 3160, 3161, 
-	3159, 3159, 3159, 3154, 3162, 3162, 3163, 3164, 
-	3165, 3166, 67, 3167, 3166, 19, 20, 3168, 
-	3169, 121, 122, 3170, 3171, 67, 67, 3166, 
-	67, 3166, 67, 3166, 67, 697, 3173, 3173, 
-	3156, 3174, 3175, 3159, 3176, 3177, 3178, 3179, 
-	3180, 3181, 3182, 3183, 3184, 3185, 3186, 3187, 
-	3188, 3189, 3159, 3159, 3176, 3177, 3178, 3179, 
-	3180, 3181, 3182, 3183, 3184, 3185, 3186, 3187, 
-	3188, 3189, 19, 20, 3190, 3191, 3159, 3159, 
-	3159, 3172, 3173, 3173, 3156, 3174, 3175, 3159, 
-	3194, 3195, 3196, 3197, 3198, 3199, 3200, 3201, 
-	3202, 3203, 3204, 3205, 3206, 3207, 3159, 3159, 
-	3194, 3195, 3196, 3197, 3198, 3199, 3200, 3201, 
-	3202, 3203, 3204, 3205, 3206, 3207, 19, 20, 
-	3190, 3191, 3159, 3193, 3159, 3159, 3192, 3209, 
-	3209, 3210, 3211, 3212, 3213, 3213, 3213, 3214, 
-	3215, 3216, 3217, 3213, 3213, 3213, 3208, 3155, 
-	3155, 3156, 3218, 3219, 3159, 3194, 3195, 3196, 
-	3197, 3198, 3199, 3200, 3201, 3202, 3203, 3204, 
-	3205, 3206, 3207, 3159, 3159, 3194, 3195, 3196, 
-	3197, 3198, 3199, 3200, 3201, 3202, 3203, 3204, 
-	3205, 3206, 3207, 19, 20, 3220, 3161, 3159, 
-	3193, 3159, 3159, 3192, 3221, 3221, 3156, 3222, 
-	3223, 3159, 3159, 3159, 88, 89, 3224, 3225, 
-	3159, 3159, 3159, 697, 3226, 3226, 3163, 3227, 
-	3228, 3166, 67, 3167, 3166, 88, 89, 3224, 
-	3229, 96, 97, 3224, 3230, 67, 67, 3166, 
-	67, 3166, 67, 3166, 67, 697, 3162, 3162, 
-	3163, 3164, 3165, 3166, 67, 3233, 3234, 3235, 
-	3236, 3237, 3238, 3239, 3240, 3241, 3242, 3243, 
-	3244, 3245, 3246, 3167, 3166, 3233, 3234, 3235, 
-	3236, 3237, 3238, 3239, 3240, 3241, 3242, 3243, 
-	3244, 3245, 3246, 19, 20, 3168, 3169, 121, 
-	122, 3170, 3171, 67, 67, 3166, 3232, 67, 
-	3166, 67, 3166, 67, 3231, 3226, 3226, 3163, 
-	3227, 3228, 3166, 67, 3233, 3234, 3235, 3236, 
-	3237, 3238, 3239, 3240, 3241, 3242, 3243, 3244, 
-	3245, 3246, 3167, 3166, 3233, 3234, 3235, 3236, 
-	3237, 3238, 3239, 3240, 3241, 3242, 3243, 3244, 
-	3245, 3246, 88, 89, 3247, 3229, 96, 97, 
-	3247, 3230, 67, 67, 3166, 3232, 67, 3166, 
-	67, 3166, 67, 3231, 619, 619, 619, 820, 
-	817, 820, 819, 820, 817, 821, 817, 816, 
-	619, 619, 619, 957, 957, 958, 959, 927, 
-	929, 930, 928, 960, 961, 928, 928, 928, 
-	934, 619, 968, 968, 969, 970, 963, 971, 
-	972, 973, 973, 974, 975, 963, 963, 963, 
-	962, 971, 972, 962, 619, 985, 985, 986, 
-	987, 988, 989, 990, 991, 992, 993, 994, 
-	995, 996, 997, 998, 999, 1000, 1001, 988, 
-	989, 990, 991, 992, 993, 994, 995, 996, 
-	997, 998, 999, 1000, 1001, 1002, 1003, 1004, 
-	1004, 1005, 1006, 976, 1002, 1003, 976, 619, 
-	1445, 1445, 1446, 1447, 1448, 1449, 1450, 1450, 
-	1451, 1452, 1441, 1441, 1441, 1437, 1448, 1449, 
-	1437, 1467, 1467, 1468, 1469, 1471, 1472, 1470, 
-	1470, 1470, 1437, 1423, 1474, 1474, 1475, 1476, 
-	1477, 1478, 1479, 1479, 1480, 1481, 1470, 1470, 
-	1470, 1437, 1477, 1478, 1437, 1471, 1472, 1423, 
-	619, 619, 619, 619, 619, 619, 619, 619, 
-	619, 1849, 1849, 1850, 1851, 1852, 1853, 1854, 
-	1854, 1855, 1856, 1840, 1840, 1840, 69, 1852, 
-	1853, 69, 619, 619, 2020, 2020, 2021, 2022, 
-	2023, 2024, 1969, 1969, 2025, 2026, 1860, 2023, 
-	2024, 1860, 2002, 2002, 2003, 2004, 2006, 2007, 
-	1969, 1969, 2008, 2009, 2005, 1860, 2006, 2007, 
-	1860, 1983, 1983, 1984, 1985, 1987, 1988, 1969, 
-	1969, 1989, 1990, 1986, 1860, 1987, 1988, 1860, 
-	1963, 1963, 1964, 1965, 1967, 1968, 1969, 1969, 
-	1970, 1971, 1966, 1860, 1967, 1968, 1860, 619, 
-	619, 619, 619, 2218, 2218, 2219, 2220, 2221, 
-	2222, 2223, 2224, 2225, 2226, 2226, 2227, 2228, 
-	2217, 2224, 2225, 69, 619, 2283, 2283, 2284, 
-	2285, 2286, 2287, 2288, 2288, 2289, 2290, 2279, 
-	2279, 2279, 2275, 2286, 2287, 2275, 619, 2317, 
-	2317, 2318, 2319, 2320, 2321, 2322, 2322, 2323, 
-	2324, 2313, 2313, 2313, 2275, 2320, 2321, 2275, 
-	619, 2365, 2365, 2366, 2367, 2371, 2371, 2371, 
-	2371, 2376, 2377, 2371, 2371, 2378, 2379, 2384, 
-	2376, 2377, 2384, 2362, 2362, 2363, 2364, 2385, 
-	2372, 2373, 2385, 2385, 2385, 2384, 2372, 2373, 
-	2384, 2385, 2362, 2362, 2363, 2364, 2365, 2365, 
-	2366, 2367, 2368, 2368, 2369, 2370, 2371, 2371, 
-	2371, 2371, 2372, 2373, 2371, 2371, 2374, 2375, 
-	2376, 2377, 2371, 2371, 2378, 2379, 2380, 2381, 
-	2371, 2371, 2382, 2383, 2385, 2385, 2385, 2384, 
-	2372, 2376, 2380, 2373, 2377, 2381, 2384, 619, 
-	619, 619, 619, 619, 619, 619, 3094, 3094, 
-	3095, 3096, 3097, 3098, 3099, 3099, 3100, 3101, 
-	3090, 3090, 3090, 2275, 3097, 3098, 2275, 619, 
-	619, 619, 619, 0
+	15, 16, 17, 18, 611, 612, 4, 524, 
+	611, 612, 613, 615, 615, 616, 617, 28, 
+	29, 30, 31, 32, 28, 29, 30, 31, 
+	32, 618, 619, 27, 614, 620, 620, 621, 
+	622, 5, 6, 7, 8, 9, 10, 11, 
+	12, 13, 14, 15, 16, 17, 18, 5, 
+	6, 7, 8, 9, 10, 11, 12, 13, 
+	14, 15, 16, 17, 18, 623, 624, 4, 
+	524, 623, 624, 35, 24, 24, 25, 26, 
+	33, 34, 625, 23, 627, 628, 629, 630, 
+	631, 627, 628, 629, 630, 631, 626, 23, 
+	632, 632, 633, 634, 636, 637, 635, 23, 
+	53, 54, 35, 639, 639, 640, 641, 59, 
+	60, 61, 59, 60, 61, 642, 643, 64, 
+	64, 644, 645, 638, 646, 646, 647, 648, 
+	67, 99, 100, 101, 102, 103, 104, 105, 
+	106, 107, 108, 109, 110, 111, 112, 72, 
+	99, 100, 101, 102, 103, 104, 105, 106, 
+	107, 108, 109, 110, 111, 112, 649, 650, 
+	67, 67, 651, 652, 67, 67, 98, 67, 
+	526, 654, 654, 655, 656, 59, 60, 61, 
+	59, 60, 61, 81, 82, 657, 658, 659, 
+	660, 661, 662, 653, 663, 663, 664, 665, 
+	72, 81, 82, 93, 89, 90, 91, 666, 
+	92, 67, 67, 67, 77, 667, 667, 668, 
+	669, 5, 6, 7, 8, 9, 10, 11, 
+	12, 13, 14, 15, 16, 17, 18, 5, 
+	6, 7, 8, 9, 10, 11, 12, 13, 
+	14, 15, 16, 17, 18, 81, 82, 670, 
+	671, 4, 0, 81, 670, 82, 671, 83, 
+	672, 672, 673, 674, 81, 82, 81, 82, 
+	82, 82, 82, 77, 672, 672, 673, 674, 
+	81, 82, 81, 82, 82, 82, 82, 83, 
+	675, 675, 676, 677, 67, 99, 100, 101, 
+	102, 103, 104, 105, 106, 107, 108, 109, 
+	110, 111, 112, 72, 99, 100, 101, 102, 
+	103, 104, 105, 106, 107, 108, 109, 110, 
+	111, 112, 81, 82, 670, 678, 90, 91, 
+	670, 679, 67, 67, 98, 67, 95, 78, 
+	78, 79, 80, 131, 132, 131, 132, 81, 
+	82, 81, 82, 653, 78, 78, 79, 80, 
+	157, 158, 159, 157, 158, 159, 81, 82, 
+	81, 82, 653, 78, 78, 79, 80, 202, 
+	202, 81, 82, 81, 82, 653, 78, 78, 
+	79, 80, 224, 224, 81, 82, 81, 82, 
+	653, 78, 78, 79, 80, 541, 237, 541, 
+	237, 81, 82, 81, 82, 653, 78, 78, 
+	79, 80, 276, 277, 276, 277, 81, 82, 
+	81, 82, 653, 78, 78, 79, 80, 295, 
+	296, 297, 298, 297, 298, 81, 82, 81, 
+	82, 653, 78, 78, 79, 80, 334, 335, 
+	334, 335, 81, 82, 81, 82, 653, 78, 
+	78, 79, 80, 355, 356, 357, 355, 356, 
+	357, 81, 82, 81, 82, 653, 78, 78, 
+	79, 80, 418, 418, 81, 82, 81, 82, 
+	653, 78, 78, 79, 80, 428, 429, 430, 
+	428, 429, 430, 81, 82, 81, 82, 653, 
+	78, 78, 79, 80, 458, 459, 460, 461, 
+	458, 459, 460, 461, 81, 82, 81, 82, 
+	653, 78, 78, 79, 80, 500, 501, 502, 
+	500, 501, 502, 81, 82, 81, 82, 653, 
+	81, 680, 82, 678, 77, 78, 78, 79, 
+	80, 592, 593, 594, 592, 593, 594, 81, 
+	82, 81, 82, 681, 682, 682, 591, 683, 
+	683, 591, 684, 684, 591, 685, 685, 591, 
+	686, 686, 591, 687, 687, 687, 687, 591, 
+	688, 688, 591, 689, 689, 591, 690, 690, 
+	690, 690, 591, 692, 692, 693, 694, 81, 
+	82, 695, 696, 691, 698, 698, 699, 700, 
+	28, 29, 30, 31, 32, 28, 29, 30, 
+	31, 32, 81, 82, 701, 702, 27, 697, 
+	703, 703, 704, 705, 5, 6, 7, 8, 
+	9, 10, 11, 12, 13, 14, 15, 16, 
+	17, 18, 5, 6, 7, 8, 9, 10, 
+	11, 12, 13, 14, 15, 16, 17, 18, 
+	81, 82, 706, 707, 4, 0, 81, 706, 
+	82, 707, 125, 709, 709, 710, 711, 59, 
+	60, 61, 59, 60, 61, 81, 82, 712, 
+	713, 659, 660, 714, 715, 708, 716, 716, 
+	717, 718, 67, 99, 100, 101, 102, 103, 
+	104, 105, 106, 107, 108, 109, 110, 111, 
+	112, 72, 99, 100, 101, 102, 103, 104, 
+	105, 106, 107, 108, 109, 110, 111, 112, 
+	81, 82, 680, 678, 90, 91, 719, 679, 
+	67, 67, 98, 67, 95, 692, 692, 693, 
+	694, 131, 132, 131, 132, 81, 82, 695, 
+	696, 708, 692, 692, 693, 694, 157, 158, 
+	159, 157, 158, 159, 81, 82, 695, 696, 
+	708, 692, 692, 693, 694, 202, 202, 81, 
+	82, 695, 696, 708, 692, 692, 693, 694, 
+	224, 224, 81, 82, 695, 696, 708, 692, 
+	692, 693, 694, 541, 237, 541, 237, 81, 
+	82, 695, 696, 708, 692, 692, 693, 694, 
+	276, 277, 276, 277, 81, 82, 695, 696, 
+	708, 692, 692, 693, 694, 295, 296, 297, 
+	298, 297, 298, 81, 82, 695, 696, 708, 
+	692, 692, 693, 694, 334, 335, 334, 335, 
+	81, 82, 695, 696, 708, 692, 692, 693, 
+	694, 355, 356, 357, 355, 356, 357, 81, 
+	82, 695, 696, 708, 692, 692, 693, 694, 
+	418, 418, 81, 82, 695, 696, 708, 692, 
+	692, 693, 694, 428, 429, 430, 428, 429, 
+	430, 81, 82, 695, 696, 708, 692, 692, 
+	693, 694, 458, 459, 460, 461, 458, 459, 
+	460, 461, 81, 82, 695, 696, 708, 692, 
+	692, 693, 694, 500, 501, 502, 500, 501, 
+	502, 81, 82, 695, 696, 708, 720, 720, 
+	85, 722, 723, 84, 84, 724, 725, 84, 
+	84, 84, 721, 667, 667, 668, 669, 5, 
+	6, 7, 8, 9, 10, 11, 12, 13, 
+	14, 15, 16, 17, 18, 5, 6, 7, 
+	8, 9, 10, 11, 12, 13, 14, 15, 
+	16, 17, 18, 81, 82, 670, 671, 4, 
+	95, 78, 81, 78, 79, 80, 82, 726, 
+	670, 726, 727, 728, 671, 612, 77, 78, 
+	81, 78, 79, 80, 82, 726, 670, 726, 
+	727, 728, 671, 612, 83, 672, 81, 672, 
+	673, 674, 729, 670, 729, 730, 731, 82, 
+	678, 77, 732, 732, 35, 733, 733, 35, 
+	734, 734, 735, 736, 737, 738, 739, 739, 
+	740, 741, 55, 742, 742, 35, 743, 743, 
+	35, 744, 744, 35, 745, 745, 746, 747, 
+	748, 749, 750, 750, 751, 752, 55, 753, 
+	753, 35, 754, 754, 755, 756, 757, 758, 
+	759, 759, 760, 761, 55, 19, 113, 20, 
+	114, 77, 762, 762, 763, 764, 81, 82, 
+	81, 82, 20, 20, 20, 77, 762, 762, 
+	763, 764, 81, 82, 81, 82, 20, 20, 
+	20, 83, 762, 81, 762, 763, 764, 82, 
+	765, 670, 765, 766, 767, 678, 20, 114, 
+	77, 672, 81, 672, 673, 674, 768, 81, 
+	768, 769, 770, 82, 89, 77, 649, 650, 
+	69, 78, 81, 78, 79, 80, 82, 771, 
+	670, 771, 772, 773, 678, 650, 77, 603, 
+	603, 604, 605, 131, 132, 131, 132, 606, 
+	607, 774, 603, 603, 604, 605, 157, 158, 
+	159, 157, 158, 159, 606, 607, 774, 603, 
+	603, 604, 605, 202, 202, 606, 607, 774, 
+	603, 603, 604, 605, 224, 224, 606, 607, 
+	774, 603, 603, 604, 605, 541, 237, 541, 
+	237, 606, 607, 774, 603, 603, 604, 605, 
+	276, 277, 276, 277, 606, 607, 774, 603, 
+	603, 604, 605, 295, 296, 297, 298, 297, 
+	298, 606, 607, 774, 603, 603, 604, 605, 
+	334, 335, 334, 335, 606, 607, 774, 603, 
+	603, 604, 605, 355, 356, 357, 355, 356, 
+	357, 606, 607, 774, 603, 603, 604, 605, 
+	418, 418, 606, 607, 774, 603, 603, 604, 
+	605, 428, 429, 430, 428, 429, 430, 606, 
+	607, 774, 603, 603, 604, 605, 458, 459, 
+	460, 461, 458, 459, 460, 461, 606, 607, 
+	774, 603, 603, 604, 605, 500, 501, 502, 
+	500, 501, 502, 606, 607, 774, 19, 21, 
+	20, 22, 83, 19, 539, 20, 540, 125, 
+	603, 603, 604, 605, 236, 237, 236, 237, 
+	606, 607, 774, 81, 129, 82, 130, 125, 
+	776, 775, 778, 777, 780, 782, 783, 784, 
+	781, 781, 781, 781, 779, 785, 786, 785, 
+	785, 785, 779, 787, 788, 789, 787, 790, 
+	789, 787, 787, 789, 789, 789, 779, 785, 
+	786, 785, 792, 791, 785, 785, 791, 791, 
+	791, 779, 793, 791, 795, 794, 796, 794, 
+	797, 799, 797, 800, 798, 797, 797, 798, 
+	798, 798, 794, 787, 787, 787, 787, 779, 
+	801, 801, 801, 801, 779, 804, 805, 803, 
+	803, 802, 806, 803, 806, 805, 806, 803, 
+	807, 803, 802, 808, 803, 810, 809, 811, 
+	809, 812, 813, 812, 815, 812, 813, 814, 
+	813, 809, 816, 817, 818, 816, 816, 802, 
+	806, 806, 806, 806, 819, 820, 816, 821, 
+	809, 822, 809, 823, 824, 825, 823, 823, 
+	809, 827, 827, 828, 829, 830, 831, 826, 
+	827, 827, 828, 829, 830, 831, 833, 832, 
+	835, 835, 836, 837, 839, 840, 841, 842, 
+	843, 839, 840, 841, 842, 843, 844, 845, 
+	844, 845, 838, 834, 835, 835, 836, 837, 
+	844, 845, 844, 845, 846, 844, 845, 835, 
+	835, 836, 837, 844, 845, 844, 845, 847, 
+	834, 850, 851, 852, 853, 854, 850, 851, 
+	852, 853, 854, 849, 848, 855, 855, 856, 
+	857, 859, 860, 859, 860, 858, 834, 830, 
+	831, 826, 862, 862, 863, 864, 865, 866, 
+	861, 862, 862, 863, 864, 867, 869, 870, 
+	868, 865, 866, 868, 868, 868, 861, 871, 
+	861, 873, 873, 874, 875, 876, 877, 878, 
+	877, 879, 880, 879, 880, 877, 877, 877, 
+	872, 881, 881, 882, 883, 884, 885, 884, 
+	885, 872, 884, 885, 871, 887, 886, 886, 
+	886, 886, 861, 888, 886, 890, 889, 891, 
+	889, 893, 894, 892, 892, 892, 892, 889, 
+	873, 873, 874, 875, 879, 880, 879, 880, 
+	872, 865, 866, 861, 895, 895, 896, 897, 
+	898, 899, 613, 895, 895, 896, 897, 898, 
+	899, 900, 900, 900, 901, 903, 903, 904, 
+	905, 906, 907, 906, 908, 902, 910, 910, 
+	911, 912, 913, 915, 916, 914, 917, 918, 
+	917, 919, 914, 914, 914, 909, 921, 920, 
+	922, 922, 923, 924, 925, 926, 927, 926, 
+	928, 929, 928, 929, 926, 926, 926, 909, 
+	930, 930, 931, 932, 917, 918, 917, 918, 
+	83, 917, 918, 921, 934, 933, 933, 933, 
+	933, 920, 935, 933, 937, 936, 938, 936, 
+	940, 941, 939, 939, 939, 939, 936, 922, 
+	922, 923, 924, 928, 929, 928, 929, 909, 
+	917, 942, 918, 919, 83, 943, 943, 944, 
+	945, 913, 915, 916, 914, 946, 947, 914, 
+	914, 914, 920, 946, 947, 613, 898, 899, 
+	613, 949, 949, 949, 949, 948, 950, 950, 
+	950, 950, 948, 951, 952, 951, 951, 951, 
+	948, 953, 954, 953, 953, 953, 948, 954, 
+	954, 955, 956, 949, 957, 958, 959, 959, 
+	960, 961, 949, 949, 949, 948, 957, 958, 
+	948, 954, 948, 963, 963, 964, 965, 966, 
+	967, 968, 968, 969, 970, 962, 971, 971, 
+	972, 973, 974, 975, 976, 977, 978, 979, 
+	980, 981, 982, 983, 984, 985, 986, 987, 
+	974, 975, 976, 977, 978, 979, 980, 981, 
+	982, 983, 984, 985, 986, 987, 988, 989, 
+	990, 990, 991, 992, 962, 993, 993, 994, 
+	995, 996, 997, 998, 996, 997, 998, 999, 
+	1000, 1001, 1001, 1002, 1003, 962, 1004, 1004, 
+	962, 1005, 1005, 962, 1006, 1006, 1007, 1008, 
+	1009, 1010, 1011, 1011, 1012, 1013, 962, 988, 
+	989, 962, 1014, 1015, 1014, 1015, 962, 1016, 
+	1016, 962, 1017, 1017, 962, 1018, 1018, 1019, 
+	1020, 1021, 1022, 1023, 1023, 1024, 1025, 962, 
+	1026, 1026, 962, 1027, 1027, 962, 1028, 1028, 
+	962, 1029, 1029, 1030, 1031, 1032, 1033, 1034, 
+	1034, 1035, 1036, 962, 1037, 1038, 1039, 1037, 
+	1038, 1039, 962, 1040, 1040, 962, 1041, 1041, 
+	962, 1042, 1042, 962, 1043, 1043, 1044, 1045, 
+	1046, 1047, 1048, 1048, 1049, 1050, 962, 1051, 
+	1052, 1051, 1052, 962, 1053, 1053, 962, 1054, 
+	1054, 962, 1055, 1055, 1056, 1057, 1058, 1059, 
+	1060, 1060, 1061, 1062, 962, 1063, 1063, 962, 
+	1064, 1064, 962, 1065, 1065, 962, 1066, 1066, 
+	1067, 1068, 1069, 1070, 1071, 1071, 1072, 1073, 
+	962, 1074, 1074, 1075, 1076, 1077, 1078, 1079, 
+	1079, 1080, 1081, 962, 1082, 1082, 962, 1083, 
+	1083, 962, 1084, 1085, 962, 1086, 962, 1087, 
+	1087, 1088, 1089, 1090, 1091, 1092, 1092, 1093, 
+	1094, 962, 1095, 962, 1096, 1096, 1097, 1098, 
+	1099, 1100, 1101, 1101, 1102, 1103, 962, 1104, 
+	1104, 962, 1105, 1105, 962, 1106, 1106, 962, 
+	1107, 1107, 962, 1108, 1108, 1109, 1110, 1111, 
+	1112, 1113, 1113, 1114, 1115, 962, 1116, 1116, 
+	962, 1117, 1117, 962, 1118, 1118, 962, 1119, 
+	1119, 962, 1120, 1120, 962, 1121, 1121, 962, 
+	1122, 1122, 962, 1123, 1123, 1124, 1125, 1126, 
+	1127, 1128, 1128, 1129, 1130, 962, 1131, 1132, 
+	1131, 1132, 962, 1133, 1133, 962, 1134, 1134, 
+	1135, 1136, 1137, 1138, 1139, 1139, 1140, 1141, 
+	962, 1142, 1142, 1143, 1144, 1145, 1146, 1147, 
+	1147, 1148, 1149, 962, 1150, 1151, 1152, 1153, 
+	1152, 1153, 962, 1154, 962, 1155, 1155, 1156, 
+	1157, 1158, 1159, 1160, 1160, 1161, 1162, 962, 
+	1163, 962, 1164, 1164, 1165, 1166, 1167, 1168, 
+	1169, 1169, 1170, 1171, 962, 1172, 1172, 962, 
+	1173, 1173, 1174, 1175, 1176, 1177, 1178, 1178, 
+	1179, 1180, 962, 1181, 1181, 1182, 1183, 1184, 
+	1185, 1186, 1186, 1187, 1188, 962, 1189, 1190, 
+	1189, 1190, 962, 1191, 1191, 962, 1192, 1192, 
+	962, 1193, 1193, 962, 1194, 1194, 1195, 1196, 
+	1197, 1198, 1199, 1199, 1200, 1201, 962, 1202, 
+	1202, 1203, 1204, 1205, 1206, 1207, 1207, 1208, 
+	1209, 962, 1210, 1211, 1212, 1210, 1211, 1212, 
+	962, 1213, 1213, 962, 1214, 1214, 962, 1215, 
+	1215, 962, 1216, 1216, 1217, 1218, 1219, 1220, 
+	1221, 1221, 1222, 1223, 962, 1224, 1224, 962, 
+	1225, 1225, 1226, 1227, 1228, 1229, 1230, 1230, 
+	1231, 1232, 962, 1233, 1233, 1234, 1235, 1236, 
+	1236, 1237, 1238, 1239, 1239, 1240, 1241, 962, 
+	1242, 1242, 962, 1243, 1243, 1244, 1245, 1246, 
+	1247, 1248, 1249, 1249, 1250, 1251, 962, 1252, 
+	1252, 1253, 1254, 1255, 1255, 1256, 1257, 1258, 
+	1258, 1259, 1260, 962, 1261, 1261, 962, 1262, 
+	1262, 962, 1263, 1263, 962, 1264, 1264, 962, 
+	1265, 1265, 1266, 1267, 1268, 1269, 1270, 1270, 
+	1271, 1272, 962, 1273, 1273, 962, 1274, 1274, 
+	962, 1275, 1275, 1276, 1277, 1278, 1279, 1280, 
+	1280, 1281, 1282, 962, 1283, 1284, 1285, 1283, 
+	1284, 1285, 962, 1286, 1286, 1287, 1288, 1289, 
+	1290, 1291, 1291, 1292, 1293, 962, 1294, 1294, 
+	962, 1295, 1295, 962, 1296, 1296, 962, 1297, 
+	1297, 1298, 1299, 1300, 1301, 1302, 1302, 1303, 
+	1304, 962, 1305, 1305, 1306, 1307, 1308, 1309, 
+	1310, 1310, 1311, 1312, 962, 1313, 1314, 1315, 
+	1316, 1313, 1314, 1315, 1316, 962, 1317, 1317, 
+	962, 1318, 1318, 1319, 1320, 1321, 1322, 1323, 
+	1323, 1324, 1325, 962, 1326, 1326, 962, 1327, 
+	1327, 1328, 1329, 1330, 1331, 1332, 1332, 1333, 
+	1334, 962, 1335, 1335, 962, 1336, 1336, 1337, 
+	1338, 1339, 1340, 1341, 1341, 1342, 1343, 962, 
+	1344, 1344, 962, 1345, 1345, 962, 1346, 1346, 
+	962, 1347, 1347, 1348, 1349, 1350, 1351, 1352, 
+	1352, 1353, 1354, 962, 1355, 1356, 1357, 1355, 
+	1356, 1357, 962, 1358, 1358, 962, 1359, 1359, 
+	962, 1360, 1360, 1361, 1362, 1363, 1364, 1365, 
+	1365, 1366, 1367, 962, 1368, 1368, 962, 1369, 
+	1369, 1370, 1371, 1372, 1373, 1374, 1374, 1375, 
+	1376, 962, 1377, 1377, 962, 1378, 1378, 962, 
+	1379, 962, 1380, 1380, 1381, 1382, 1384, 1385, 
+	1386, 1386, 1387, 1388, 1383, 962, 1389, 1389, 
+	962, 1390, 1390, 962, 1391, 1391, 962, 1392, 
+	1392, 1393, 1394, 1395, 1396, 1397, 1397, 1398, 
+	1399, 962, 1400, 1400, 962, 1401, 1401, 1402, 
+	1403, 1404, 1405, 1406, 1406, 1407, 1408, 962, 
+	1410, 1410, 1411, 1412, 1413, 1414, 1409, 1410, 
+	1410, 1411, 1412, 1413, 1414, 1416, 1415, 1417, 
+	1417, 1418, 1419, 1421, 1422, 1420, 1415, 1424, 
+	1424, 1425, 1426, 1428, 1429, 1427, 1427, 1427, 
+	1423, 1430, 1430, 1430, 1423, 1431, 1431, 1432, 
+	1433, 1434, 1435, 1436, 1436, 1437, 1438, 1427, 
+	1427, 1427, 1423, 1434, 1435, 1423, 1428, 1429, 
+	1409, 1413, 1414, 1409, 1439, 1439, 1440, 1441, 
+	1442, 1443, 1409, 1439, 1439, 1440, 1441, 1444, 
+	1442, 1443, 1445, 1415, 1446, 1446, 1447, 1448, 
+	1450, 1450, 1451, 1452, 1449, 1415, 1453, 1453, 
+	1454, 1455, 1457, 1458, 1456, 1456, 1456, 1423, 
+	1459, 1459, 1459, 1423, 1460, 1460, 1461, 1462, 
+	1463, 1464, 1465, 1465, 1466, 1467, 1456, 1456, 
+	1456, 1423, 1463, 1464, 1423, 1457, 1458, 1409, 
+	1468, 1468, 1469, 1470, 1471, 1472, 1449, 1415, 
+	1442, 1443, 1409, 1475, 1476, 1477, 1478, 1479, 
+	1475, 1476, 1477, 1478, 1479, 1474, 1473, 1480, 
+	1480, 1480, 1480, 1481, 1473, 1482, 1483, 1482, 
+	1483, 1473, 1484, 1484, 1484, 1484, 1473, 1485, 
+	1485, 1473, 1486, 1487, 1486, 1486, 1486, 1473, 
+	1488, 1488, 1473, 1489, 1489, 1473, 1490, 1490, 
+	1473, 1491, 1491, 1473, 1492, 1473, 1493, 1473, 
+	1494, 1494, 1473, 1495, 1495, 1473, 1496, 1496, 
+	1473, 1497, 1473, 1498, 1498, 1498, 1498, 1473, 
+	1499, 1499, 1473, 1500, 1501, 1500, 1501, 1473, 
+	1502, 1473, 1503, 1503, 1473, 1504, 1504, 1473, 
+	1505, 1505, 1473, 1506, 1506, 1473, 1507, 1507, 
+	1507, 1507, 1473, 1508, 1508, 1473, 1509, 1509, 
+	1473, 1510, 1510, 1473, 1511, 1512, 1473, 1513, 
+	1473, 1514, 1473, 1515, 1515, 1473, 1516, 1516, 
+	1473, 1517, 1517, 1473, 1518, 1473, 1519, 1473, 
+	1520, 1473, 1521, 1521, 1521, 1521, 1473, 1522, 
+	1473, 1523, 1473, 1524, 1524, 1473, 1525, 1525, 
+	1473, 1526, 1526, 1473, 1527, 1473, 1528, 1473, 
+	1529, 1473, 1530, 1530, 1530, 1530, 1473, 1531, 
+	1531, 1473, 1532, 1532, 1473, 1533, 1533, 1473, 
+	1534, 1534, 1473, 1535, 1535, 1473, 1536, 1536, 
+	1473, 1537, 1537, 1473, 1538, 1538, 1538, 1538, 
+	1473, 1539, 1539, 1473, 1540, 1540, 1473, 1541, 
+	1541, 1473, 1542, 1542, 1473, 1543, 1543, 1473, 
+	1544, 1544, 1473, 1545, 1546, 1545, 1546, 1473, 
+	1547, 1547, 1473, 1548, 1548, 1473, 1549, 1549, 
+	1549, 1549, 1473, 1550, 1550, 1473, 1551, 1551, 
+	1473, 1552, 1552, 1552, 1552, 1473, 1553, 1553, 
+	1473, 1554, 1554, 1473, 1555, 1556, 1555, 1556, 
+	1473, 1557, 1557, 1473, 1558, 1473, 1559, 1559, 
+	1559, 1559, 1473, 1560, 1560, 1473, 1561, 1561, 
+	1473, 1562, 1563, 1564, 1473, 1565, 1566, 1565, 
+	1565, 1565, 1473, 1567, 1567, 1473, 1568, 1568, 
+	1473, 1569, 1569, 1473, 1570, 1570, 1473, 1571, 
+	1473, 1572, 1473, 1573, 1573, 1473, 1574, 1574, 
+	1473, 1575, 1575, 1473, 1576, 1473, 1577, 1577, 
+	1577, 1577, 1473, 1578, 1473, 1579, 1473, 1580, 
+	1580, 1580, 1580, 1473, 1581, 1473, 1582, 1473, 
+	1583, 1583, 1583, 1583, 1473, 1586, 1587, 1588, 
+	1589, 1590, 1591, 1586, 1587, 1588, 1589, 1590, 
+	1591, 1585, 1584, 1592, 1592, 1592, 1592, 1593, 
+	1584, 1594, 1594, 1584, 1595, 1595, 1584, 1596, 
+	1596, 1584, 1597, 1597, 1584, 1598, 1598, 1584, 
+	1599, 1599, 1599, 1599, 1584, 1600, 1601, 1602, 
+	1600, 1601, 1602, 1584, 1603, 1603, 1584, 1604, 
+	1604, 1584, 1605, 1605, 1584, 1606, 1606, 1584, 
+	1607, 1607, 1584, 1608, 1608, 1608, 1608, 1584, 
+	1609, 1610, 1609, 1610, 1584, 1611, 1611, 1584, 
+	1612, 1612, 1612, 1612, 1584, 1613, 1613, 1584, 
+	1614, 1614, 1584, 1615, 1615, 1615, 1615, 1584, 
+	1616, 1616, 1584, 1617, 1617, 1584, 1618, 1618, 
+	1584, 1619, 1619, 1619, 1619, 1584, 1620, 1620, 
+	1584, 1621, 1621, 1584, 1622, 1622, 1622, 1622, 
+	1584, 1623, 1624, 1623, 1624, 1584, 1625, 1625, 
+	1584, 1626, 1626, 1626, 1626, 1584, 1627, 1627, 
+	1584, 1628, 1628, 1584, 1629, 1629, 1629, 1629, 
+	1584, 1630, 1630, 1584, 1631, 1631, 1584, 1632, 
+	1632, 1584, 1633, 1633, 1633, 1633, 1584, 1634, 
+	1634, 1584, 1635, 1635, 1584, 1636, 1636, 1636, 
+	1636, 1584, 1638, 1638, 1637, 1639, 1640, 1639, 
+	1639, 1639, 1640, 1637, 1641, 1641, 1641, 1641, 
+	1641, 1641, 69, 1642, 1642, 1642, 1642, 69, 
+	1643, 1643, 1643, 1643, 1643, 1643, 69, 1644, 
+	1644, 1645, 1646, 1647, 1648, 69, 1649, 1649, 
+	1650, 1651, 1652, 1652, 1652, 1653, 1654, 1652, 
+	1652, 1652, 69, 1655, 1655, 1656, 1657, 1658, 
+	1659, 69, 1661, 1661, 1662, 1663, 1665, 1666, 
+	1664, 1660, 1667, 1667, 1668, 1669, 1671, 1672, 
+	1670, 1660, 1673, 1673, 1674, 1675, 1677, 1678, 
+	1676, 1660, 1680, 1680, 1681, 1682, 1684, 1685, 
+	1686, 1687, 1688, 1684, 1685, 1686, 1687, 1688, 
+	1689, 1690, 1683, 1679, 1691, 1691, 1692, 1693, 
+	1695, 1696, 1694, 1660, 1697, 1697, 1698, 1699, 
+	1701, 1702, 1703, 1704, 1705, 1701, 1702, 1703, 
+	1704, 1705, 1706, 1707, 1700, 1679, 1708, 1708, 
+	1709, 1710, 1712, 1713, 1711, 1660, 1714, 1714, 
+	1715, 1716, 1718, 1719, 1720, 1721, 1722, 1718, 
+	1719, 1720, 1721, 1722, 1723, 1724, 1717, 1679, 
+	1725, 1725, 1726, 1727, 1729, 1730, 1728, 1660, 
+	1731, 1731, 1733, 1734, 1735, 1736, 1737, 1733, 
+	1734, 1735, 1736, 1737, 1731, 1731, 1732, 1679, 
+	1731, 1731, 1731, 1731, 1738, 1679, 1740, 1741, 
+	1742, 1743, 1744, 1740, 1741, 1742, 1743, 1744, 
+	1739, 1679, 1745, 1745, 1745, 1745, 1746, 1679, 
+	1729, 1730, 69, 1714, 1714, 1715, 1716, 1723, 
+	1724, 1747, 1679, 1749, 1750, 1751, 1752, 1753, 
+	1749, 1750, 1751, 1752, 1753, 1748, 1679, 1754, 
+	1754, 1755, 1756, 1758, 1759, 1757, 1679, 1712, 
+	1713, 69, 1697, 1697, 1698, 1699, 1706, 1707, 
+	1760, 1679, 1762, 1763, 1764, 1765, 1766, 1762, 
+	1763, 1764, 1765, 1766, 1761, 1679, 1767, 1767, 
+	1768, 1769, 1771, 1772, 1770, 1679, 1695, 1696, 
+	69, 1680, 1680, 1681, 1682, 1689, 1690, 1773, 
+	1679, 1775, 1776, 1777, 1778, 1779, 1775, 1776, 
+	1777, 1778, 1779, 1774, 1679, 1780, 1780, 1781, 
+	1782, 1784, 1785, 1783, 1679, 1677, 1678, 69, 
+	1665, 1666, 69, 1653, 1654, 69, 69, 69, 
+	69, 69, 1786, 1787, 1787, 1788, 1789, 1790, 
+	1791, 69, 1793, 1793, 1794, 1795, 1796, 1797, 
+	1792, 1792, 1792, 69, 1798, 1798, 1798, 1798, 
+	69, 1796, 1797, 69, 1799, 1799, 1799, 1799, 
+	1799, 1799, 69, 1800, 1800, 1801, 1802, 1803, 
+	1804, 69, 1805, 1805, 1806, 1807, 1808, 1808, 
+	1808, 1809, 1810, 1808, 1808, 1808, 69, 1811, 
+	1811, 1811, 1811, 69, 1809, 1810, 69, 1812, 
+	1660, 1813, 1813, 1814, 1815, 1817, 1818, 1816, 
+	1660, 1819, 1819, 1820, 1821, 1822, 1822, 1822, 
+	1823, 1824, 1822, 1822, 1822, 69, 1825, 1825, 
+	1825, 1825, 69, 1823, 1824, 69, 69, 69, 
+	69, 69, 1826, 1827, 1827, 1828, 1829, 1830, 
+	1831, 1832, 1832, 1833, 1834, 69, 1835, 1835, 
+	1836, 1837, 1838, 1839, 1840, 1840, 1841, 1842, 
+	1826, 1826, 1826, 69, 1838, 1839, 69, 1843, 
+	1843, 1843, 1843, 1637, 1844, 1845, 1844, 1844, 
+	1844, 1845, 1845, 1845, 1637, 1847, 1846, 1848, 
+	1848, 1849, 1850, 1852, 1853, 1851, 1846, 1854, 
+	1854, 1855, 1856, 1858, 1859, 1860, 1861, 1857, 
+	1846, 1862, 1862, 1863, 1864, 1866, 1867, 1865, 
+	1846, 1868, 1868, 1869, 1870, 1858, 1859, 1872, 
+	1873, 1871, 1846, 1874, 1874, 1875, 1876, 1877, 
+	1879, 1880, 1878, 1846, 1881, 1881, 1882, 1883, 
+	1858, 1859, 1884, 1885, 1846, 1886, 1886, 1887, 
+	1888, 1889, 1890, 1846, 1886, 1886, 1887, 1888, 
+	1889, 1890, 1891, 1846, 1892, 1892, 1893, 1894, 
+	1896, 1897, 1895, 1846, 1898, 1898, 1899, 1900, 
+	1902, 1903, 1904, 1905, 1901, 1846, 1906, 1906, 
+	1907, 1908, 1910, 1911, 1909, 1846, 1912, 1912, 
+	1913, 1914, 1902, 1903, 1916, 1917, 1915, 1846, 
+	1918, 1918, 1919, 1920, 1921, 1923, 1924, 1922, 
+	1846, 1925, 1925, 1926, 1927, 1902, 1903, 1928, 
+	1929, 1846, 1930, 1930, 1931, 1932, 1933, 1934, 
+	1846, 1930, 1930, 1931, 1932, 1935, 1933, 1934, 
+	1936, 1846, 1937, 1846, 1938, 1938, 1939, 1940, 
+	1941, 1943, 1944, 1945, 1946, 1946, 1947, 1948, 
+	1942, 1846, 1949, 1949, 1950, 1951, 1953, 1954, 
+	1955, 1955, 1956, 1957, 1952, 1846, 1958, 1958, 
+	1959, 1960, 1961, 1963, 1964, 1965, 1966, 1966, 
+	1967, 1968, 1962, 1846, 1969, 1969, 1970, 1971, 
+	1973, 1974, 1955, 1955, 1975, 1976, 1972, 1846, 
+	1977, 1977, 1978, 1979, 1980, 1982, 1983, 1984, 
+	1985, 1985, 1986, 1987, 1981, 1846, 1988, 1988, 
+	1989, 1990, 1992, 1993, 1955, 1955, 1994, 1995, 
+	1991, 1846, 1996, 1996, 1997, 1998, 1999, 1996, 
+	2001, 2002, 2003, 2003, 2004, 2005, 2000, 1846, 
+	2006, 2006, 2007, 2008, 2009, 2010, 1955, 1955, 
+	2011, 2012, 1846, 2009, 2010, 1846, 2013, 2013, 
+	2014, 2015, 2013, 2017, 2018, 2019, 2019, 2020, 
+	2021, 2016, 1846, 1996, 1996, 1997, 1998, 1996, 
+	2001, 2002, 2003, 2003, 2004, 2005, 2022, 1846, 
+	1992, 1993, 1846, 2023, 2023, 2024, 2025, 2027, 
+	2028, 2029, 2030, 2030, 2031, 2032, 2026, 1846, 
+	1977, 1977, 1978, 1979, 1982, 1983, 1984, 1985, 
+	1985, 1986, 1987, 2033, 1846, 1988, 1988, 1989, 
+	1990, 1992, 1993, 1955, 1955, 1994, 1995, 1846, 
+	1973, 1974, 1846, 2034, 2034, 2035, 2036, 2038, 
+	2039, 2040, 2041, 2041, 2042, 2043, 2037, 1846, 
+	1958, 1958, 1959, 1960, 1963, 1964, 1965, 1966, 
+	1966, 1967, 1968, 2044, 1846, 1969, 1969, 1970, 
+	1971, 1973, 1974, 1955, 1955, 1975, 1976, 1846, 
+	1953, 1954, 1846, 2045, 2045, 2046, 2047, 2049, 
+	2050, 2051, 2052, 2052, 2053, 2054, 2048, 1846, 
+	1938, 1938, 1939, 1940, 1943, 1944, 1945, 1946, 
+	1946, 1947, 1948, 2055, 1846, 1949, 1949, 1950, 
+	1951, 1953, 1954, 1955, 1955, 1956, 1957, 1846, 
+	1933, 1934, 1846, 2056, 2056, 2057, 2058, 2059, 
+	2060, 1846, 1928, 1929, 1846, 2061, 2061, 2062, 
+	2063, 2065, 2066, 2064, 1846, 1918, 1918, 1919, 
+	1920, 1923, 1924, 2067, 1846, 1916, 1917, 1846, 
+	1904, 1905, 1846, 1889, 1890, 1846, 2068, 2068, 
+	2069, 2070, 2071, 2072, 1846, 1884, 1885, 1846, 
+	2073, 2073, 2074, 2075, 2077, 2078, 2076, 1846, 
+	1874, 1874, 1875, 1876, 1879, 1880, 2079, 1846, 
+	1872, 1873, 1846, 1860, 1861, 1846, 2080, 1660, 
+	2081, 2081, 2082, 2083, 2085, 2086, 2084, 1660, 
+	2087, 2087, 2088, 2089, 2091, 2092, 2090, 1660, 
+	2093, 2093, 2094, 2095, 2097, 2098, 2096, 1660, 
+	2099, 2099, 2100, 2101, 2103, 2104, 2102, 1660, 
+	2105, 2105, 2106, 2107, 2109, 2110, 2108, 1660, 
+	2111, 2111, 2112, 2113, 2114, 2114, 2114, 2115, 
+	2116, 2114, 2114, 2114, 69, 2117, 2117, 2117, 
+	2117, 69, 2115, 2116, 69, 2103, 2104, 69, 
+	2091, 2092, 69, 2118, 1660, 2119, 2119, 2120, 
+	2121, 2123, 2124, 2122, 1660, 2125, 2125, 2126, 
+	2127, 2129, 2130, 2128, 1660, 2131, 2131, 2132, 
+	2133, 2135, 2136, 2134, 1660, 2138, 2138, 2139, 
+	2140, 2141, 2142, 2137, 2137, 2137, 69, 2143, 
+	2143, 2144, 2145, 2146, 2147, 69, 2149, 2149, 
+	2150, 2151, 2152, 2153, 2148, 2148, 2148, 69, 
+	2154, 2154, 2155, 2156, 2157, 2158, 69, 2160, 
+	2160, 2161, 2162, 2163, 2164, 2159, 2159, 2159, 
+	69, 2165, 2165, 2166, 2167, 2168, 2169, 69, 
+	2170, 2170, 2171, 2172, 2173, 2173, 2173, 2174, 
+	2175, 2173, 2173, 2173, 69, 2176, 2176, 2176, 
+	2176, 69, 2174, 2175, 69, 2163, 2164, 69, 
+	2152, 2153, 69, 2141, 2142, 69, 2129, 2130, 
+	69, 2177, 2177, 2177, 69, 2178, 2178, 2179, 
+	2180, 2181, 2182, 69, 2178, 2178, 2179, 2180, 
+	2181, 2182, 2183, 1660, 2184, 2184, 2185, 2186, 
+	2188, 2189, 2187, 1660, 2190, 2190, 2191, 2192, 
+	2194, 2195, 2193, 2193, 2193, 69, 2196, 2196, 
+	2197, 2198, 2199, 2200, 69, 2196, 2196, 2197, 
+	2198, 2201, 2199, 2200, 2201, 2201, 2201, 69, 
+	2202, 2202, 69, 2199, 2200, 69, 2194, 2195, 
+	69, 2181, 2182, 69, 2204, 2204, 2205, 2206, 
+	2207, 2208, 2209, 2210, 2211, 2212, 2212, 2213, 
+	2214, 2203, 2215, 2216, 2203, 2217, 2203, 2219, 
+	2219, 2218, 2221, 2220, 2218, 2222, 2203, 2223, 
+	2223, 2224, 2225, 2227, 2228, 2229, 2229, 2230, 
+	2231, 2226, 2203, 2210, 2211, 69, 2232, 2203, 
+	2233, 2233, 2233, 2233, 2218, 2235, 2234, 2234, 
+	2234, 2218, 2236, 1660, 2237, 2237, 2238, 2239, 
+	2241, 2242, 2240, 1660, 2243, 2243, 2244, 2245, 
+	2247, 2248, 2246, 2246, 2246, 69, 2249, 2249, 
+	2250, 2251, 2252, 2253, 69, 2249, 2249, 2250, 
+	2251, 2252, 2253, 2254, 1660, 2255, 2255, 2256, 
+	2257, 2259, 2260, 2258, 1660, 2262, 2262, 2263, 
+	2264, 2266, 2267, 2265, 2265, 2265, 2261, 2268, 
+	2268, 2268, 2261, 2269, 2269, 2270, 2271, 2272, 
+	2273, 2274, 2274, 2275, 2276, 2265, 2265, 2265, 
+	2261, 2272, 2273, 2261, 2266, 2267, 69, 2252, 
+	2253, 69, 2247, 2248, 69, 2277, 1660, 2278, 
+	2278, 2279, 2280, 2282, 2283, 2281, 1660, 2284, 
+	2284, 2285, 2286, 2288, 2289, 2287, 1660, 2290, 
+	2290, 2291, 2292, 2294, 2295, 2293, 1660, 2296, 
+	2296, 2297, 2298, 2300, 2301, 2299, 2299, 2299, 
+	2261, 2302, 2302, 2302, 2261, 2303, 2303, 2304, 
+	2305, 2306, 2307, 2308, 2308, 2309, 2310, 2299, 
+	2299, 2299, 2261, 2306, 2307, 2261, 2300, 2301, 
+	69, 2288, 2289, 69, 2311, 1660, 2312, 2312, 
+	2313, 2314, 2316, 2317, 2315, 1660, 2319, 2319, 
+	2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 
+	2318, 2328, 2328, 2329, 2330, 2331, 2332, 2318, 
+	2328, 2328, 2329, 2330, 2331, 2332, 2334, 2333, 
+	2335, 2335, 2336, 2337, 2339, 2340, 2338, 2333, 
+	2341, 2341, 2342, 2343, 2344, 2345, 2346, 2318, 
+	2348, 2348, 2349, 2350, 2351, 2351, 2352, 2353, 
+	2354, 2354, 2355, 2356, 2357, 2357, 2357, 2357, 
+	2358, 2359, 2357, 2357, 2360, 2361, 2362, 2363, 
+	2357, 2357, 2364, 2365, 2366, 2367, 2357, 2357, 
+	2368, 2369, 2347, 2348, 2348, 2349, 2350, 2371, 
+	2358, 2359, 2371, 2371, 2371, 2370, 2357, 2357, 
+	2370, 2358, 2359, 2370, 2351, 2351, 2352, 2353, 
+	2357, 2357, 2357, 2357, 2362, 2363, 2357, 2357, 
+	2364, 2365, 2370, 2362, 2363, 2370, 2371, 2348, 
+	2348, 2349, 2350, 2351, 2351, 2352, 2353, 2354, 
+	2354, 2355, 2356, 2357, 2357, 2357, 2357, 2358, 
+	2359, 2357, 2357, 2360, 2361, 2362, 2363, 2357, 
+	2357, 2364, 2365, 2366, 2367, 2357, 2357, 2368, 
+	2369, 2371, 2371, 2371, 2370, 2358, 2362, 2366, 
+	2359, 2363, 2367, 2370, 2345, 2346, 2318, 2331, 
+	2332, 2318, 2372, 2372, 2373, 2374, 2375, 2376, 
+	2318, 2372, 2372, 2373, 2374, 2375, 2376, 2377, 
+	2333, 2378, 2378, 2379, 2380, 2382, 2383, 2381, 
+	2333, 2385, 2385, 2386, 2387, 2388, 2389, 2390, 
+	2388, 2384, 2392, 2393, 2393, 2394, 2395, 2396, 
+	2396, 2397, 2398, 2399, 2399, 2400, 2401, 2402, 
+	2402, 2402, 2402, 2403, 2404, 2402, 2402, 2405, 
+	2406, 2407, 2408, 2402, 2402, 2409, 2410, 2411, 
+	2412, 2402, 2402, 2413, 2414, 2392, 2391, 2389, 
+	2390, 2318, 2375, 2376, 2318, 2415, 2415, 2416, 
+	2417, 2418, 2419, 2318, 2415, 2415, 2416, 2417, 
+	2418, 2419, 2420, 2333, 2421, 2421, 2422, 2423, 
+	2425, 2426, 2424, 2333, 2427, 2427, 2428, 2429, 
+	2430, 2431, 2432, 2430, 2430, 2430, 2384, 2433, 
+	2434, 2434, 2435, 2436, 2437, 2437, 2438, 2439, 
+	2440, 2440, 2441, 2442, 2443, 2443, 2443, 2443, 
+	2444, 2445, 2443, 2443, 2446, 2447, 2448, 2449, 
+	2443, 2443, 2450, 2451, 2452, 2453, 2443, 2443, 
+	2454, 2455, 2433, 2433, 2433, 2391, 2431, 2432, 
+	2318, 2418, 2419, 2318, 2456, 2456, 2457, 2458, 
+	2459, 2460, 2318, 2456, 2456, 2457, 2458, 2459, 
+	2460, 2461, 2333, 2462, 2462, 2463, 2464, 2466, 
+	2467, 2465, 2333, 2468, 2468, 2469, 2470, 2471, 
+	2471, 2471, 2472, 2473, 2471, 2471, 2471, 2318, 
+	2474, 2474, 2475, 2476, 2477, 2477, 2478, 2479, 
+	2480, 2480, 2481, 2482, 2483, 2483, 2483, 2483, 
+	2484, 2485, 2483, 2483, 2486, 2487, 2488, 2489, 
+	2483, 2483, 2490, 2491, 2492, 2493, 2483, 2483, 
+	2494, 2495, 2347, 2472, 2473, 2318, 2459, 2460, 
+	2318, 2326, 2327, 69, 2497, 2498, 2499, 2500, 
+	2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 
+	2509, 2510, 2497, 2498, 2499, 2500, 2501, 2502, 
+	2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 
+	2496, 2511, 2511, 2512, 2513, 2514, 2515, 2516, 
+	2514, 2515, 2516, 2517, 2518, 2496, 2519, 2519, 
+	2520, 2521, 2523, 2524, 2522, 2522, 2522, 69, 
+	2525, 2525, 2526, 2527, 2528, 2529, 69, 2525, 
+	2525, 2526, 2527, 2528, 2529, 2530, 1660, 2531, 
+	2531, 2532, 2533, 2535, 2536, 2534, 1660, 2537, 
+	2537, 2538, 2539, 2541, 2542, 2540, 1660, 2543, 
+	2543, 2544, 2545, 2547, 2548, 2546, 1660, 2550, 
+	2550, 2551, 2552, 2554, 2555, 2553, 2549, 2556, 
+	2556, 2557, 2558, 2560, 2561, 2559, 2549, 2562, 
+	2562, 2563, 2564, 2566, 2567, 2565, 2549, 2568, 
+	2568, 2569, 2570, 2572, 2573, 2571, 2549, 2574, 
+	2574, 2575, 2576, 2578, 2579, 2577, 1660, 2580, 
+	2580, 2581, 2582, 2584, 2585, 2583, 1660, 2586, 
+	2586, 2587, 2588, 2589, 2589, 2589, 2590, 2591, 
+	2589, 2589, 2589, 69, 2592, 2592, 2593, 2594, 
+	2595, 2596, 69, 2597, 2597, 2598, 2599, 2600, 
+	2601, 2602, 2600, 2600, 2600, 69, 2603, 2603, 
+	69, 2601, 2602, 69, 2590, 2591, 69, 2578, 
+	2579, 69, 2566, 2567, 69, 2554, 2555, 69, 
+	2541, 2542, 69, 2528, 2529, 69, 2523, 2524, 
+	69, 2604, 2604, 2496, 2605, 2605, 2496, 2606, 
+	2606, 2607, 2608, 2609, 2610, 2496, 2611, 2611, 
+	2496, 2612, 2612, 2496, 2613, 2613, 2496, 2614, 
+	2614, 2615, 2616, 2617, 2618, 2496, 2619, 2619, 
+	2496, 2620, 2620, 2621, 2622, 2623, 2624, 2496, 
+	2625, 2626, 2625, 2626, 2496, 2627, 2627, 2496, 
+	2628, 2628, 2496, 2629, 2629, 2630, 2631, 2632, 
+	2633, 2496, 2634, 2634, 2496, 2635, 2635, 2496, 
+	2636, 2636, 2496, 2637, 2637, 2638, 2639, 2640, 
+	2641, 2496, 2642, 2643, 2644, 2642, 2643, 2644, 
+	2496, 2645, 2645, 2496, 2646, 2646, 2496, 2647, 
+	2647, 2496, 2648, 2648, 2649, 2650, 2651, 2652, 
+	2496, 2653, 2654, 2653, 2654, 2496, 2655, 2655, 
+	2496, 2656, 2656, 2496, 2657, 2657, 2658, 2659, 
+	2660, 2661, 2496, 2662, 2662, 2496, 2663, 2663, 
+	2496, 2664, 2664, 2496, 2665, 2665, 2666, 2667, 
+	2668, 2669, 2496, 2670, 2670, 2671, 2672, 2673, 
+	2674, 2496, 2675, 2675, 2496, 2676, 2676, 2496, 
+	2677, 2678, 2496, 2679, 2496, 2680, 2680, 2681, 
+	2682, 2683, 2684, 2496, 2685, 2496, 2686, 2686, 
+	2687, 2688, 2689, 2690, 2496, 2691, 2691, 2496, 
+	2692, 2692, 2496, 2693, 2693, 2496, 2694, 2694, 
+	2496, 2695, 2695, 2696, 2697, 2698, 2699, 2496, 
+	2700, 2700, 2496, 2701, 2701, 2496, 2702, 2702, 
+	2496, 2703, 2703, 2496, 2704, 2704, 2496, 2705, 
+	2705, 2496, 2706, 2706, 2496, 2707, 2707, 2708, 
+	2709, 2710, 2711, 2496, 2712, 2713, 2712, 2713, 
+	2496, 2714, 2714, 2496, 2715, 2715, 2716, 2717, 
+	2718, 2719, 2496, 2720, 2720, 2721, 2722, 2723, 
+	2724, 2496, 2725, 2726, 2727, 2728, 2727, 2728, 
+	2496, 2729, 2496, 2730, 2730, 2731, 2732, 2733, 
+	2734, 2496, 2735, 2496, 2736, 2736, 2737, 2738, 
+	2739, 2740, 2496, 2741, 2741, 2496, 2742, 2742, 
+	2743, 2744, 2745, 2746, 2496, 2747, 2747, 2748, 
+	2749, 2750, 2751, 2496, 2752, 2753, 2752, 2753, 
+	2496, 2754, 2754, 2496, 2755, 2755, 2496, 2756, 
+	2756, 2496, 2757, 2757, 2758, 2759, 2760, 2761, 
+	2496, 2762, 2762, 2763, 2764, 2765, 2766, 2496, 
+	2767, 2768, 2769, 2767, 2768, 2769, 2496, 2770, 
+	2770, 2496, 2771, 2771, 2496, 2772, 2772, 2496, 
+	2773, 2773, 2774, 2775, 2776, 2777, 2496, 2778, 
+	2778, 2496, 2779, 2779, 2780, 2781, 2782, 2783, 
+	2496, 2784, 2784, 2785, 2786, 2787, 2787, 2788, 
+	2789, 2496, 2790, 2790, 2496, 2791, 2791, 2792, 
+	2793, 2794, 2795, 2796, 2496, 2797, 2797, 2798, 
+	2799, 2800, 2800, 2801, 2802, 2496, 2803, 2803, 
+	2496, 2804, 2804, 2496, 2805, 2805, 2496, 2806, 
+	2806, 2496, 2807, 2807, 2808, 2809, 2810, 2811, 
+	2496, 2812, 2812, 2496, 2813, 2813, 2496, 2814, 
+	2814, 2815, 2816, 2817, 2818, 2496, 2819, 2820, 
+	2821, 2819, 2820, 2821, 2496, 2822, 2822, 2823, 
+	2824, 2825, 2826, 2496, 2827, 2827, 2496, 2828, 
+	2828, 2496, 2829, 2829, 2496, 2830, 2830, 2831, 
+	2832, 2833, 2834, 2496, 2835, 2835, 2836, 2837, 
+	2838, 2839, 2496, 2840, 2841, 2842, 2843, 2840, 
+	2841, 2842, 2843, 2496, 2844, 2844, 2496, 2845, 
+	2845, 2846, 2847, 2848, 2849, 2496, 2850, 2850, 
+	2496, 2851, 2851, 2852, 2853, 2854, 2855, 2496, 
+	2856, 2856, 2496, 2857, 2857, 2858, 2859, 2860, 
+	2861, 2496, 2862, 2862, 2496, 2863, 2863, 2496, 
+	2864, 2864, 2496, 2865, 2865, 2866, 2867, 2868, 
+	2869, 2496, 2870, 2871, 2872, 2870, 2871, 2872, 
+	2496, 2873, 2873, 2496, 2874, 2874, 2496, 2875, 
+	2875, 2876, 2877, 2878, 2879, 2496, 2880, 2880, 
+	2496, 2881, 2881, 2882, 2883, 2884, 2885, 2496, 
+	2886, 2886, 2496, 2887, 2887, 2496, 2889, 2888, 
+	2890, 2890, 2891, 2892, 2894, 2895, 2893, 2888, 
+	2896, 2896, 2896, 2896, 2896, 2896, 69, 2897, 
+	2897, 2897, 2897, 69, 2898, 2898, 2898, 2898, 
+	69, 2899, 1660, 2900, 2900, 2901, 2902, 2904, 
+	2905, 2903, 1660, 2906, 2906, 2907, 2908, 2910, 
+	2911, 2909, 1660, 2912, 2912, 2913, 2914, 2916, 
+	2917, 2915, 1660, 2918, 2918, 2919, 2920, 2922, 
+	2923, 2921, 2921, 2921, 69, 2924, 2924, 2925, 
+	2926, 2927, 2928, 69, 2924, 2924, 2925, 2926, 
+	2929, 2927, 2928, 2929, 2929, 2929, 69, 2930, 
+	2930, 69, 2927, 2928, 69, 2922, 2923, 69, 
+	2910, 2911, 69, 2931, 2931, 2931, 2931, 69, 
+	2932, 2932, 69, 2933, 1660, 2934, 2934, 2935, 
+	2936, 2938, 2939, 2937, 1660, 2940, 2940, 2941, 
+	2942, 2944, 2945, 2943, 1660, 2946, 2946, 2947, 
+	2948, 2950, 2951, 2949, 1660, 2952, 2952, 2953, 
+	2954, 2956, 2957, 2955, 1660, 2958, 2958, 2959, 
+	2960, 2962, 2963, 2961, 1660, 2964, 2964, 2965, 
+	2966, 2967, 2969, 2970, 2968, 2968, 2968, 2261, 
+	2971, 2971, 2972, 2973, 2974, 2975, 2261, 2977, 
+	2977, 2978, 2979, 2981, 2982, 2980, 2980, 2980, 
+	2976, 2983, 2983, 2983, 2976, 2985, 2984, 2984, 
+	2984, 2976, 2986, 2986, 2986, 2976, 2988, 2987, 
+	2987, 2987, 2976, 2990, 2989, 2989, 2989, 2976, 
+	2991, 2991, 2991, 2976, 2993, 2992, 2992, 2992, 
+	2976, 2994, 2994, 2994, 2994, 2995, 2995, 2995, 
+	2976, 2996, 2996, 2996, 2996, 69, 2997, 2976, 
+	2993, 2976, 2990, 2976, 2998, 2976, 2988, 2976, 
+	2981, 2982, 69, 2999, 2999, 2999, 2261, 2971, 
+	2971, 2972, 2973, 2974, 2975, 3000, 3000, 3000, 
+	2261, 2969, 2970, 69, 2956, 2957, 69, 2944, 
+	2945, 69, 3001, 1660, 3002, 3002, 3003, 3004, 
+	3006, 3007, 3005, 1660, 3008, 3008, 3009, 3010, 
+	3012, 3013, 3011, 1660, 3014, 3014, 3015, 3016, 
+	3018, 3019, 3017, 1660, 3020, 3020, 3021, 3022, 
+	3024, 3025, 3023, 1660, 3026, 3026, 3027, 3028, 
+	3030, 3031, 3029, 1660, 3032, 3032, 3033, 3034, 
+	3035, 3037, 3038, 3036, 3036, 3036, 2261, 3039, 
+	3039, 3039, 3039, 2261, 3040, 3040, 3040, 2261, 
+	3039, 3039, 3039, 3039, 3041, 3041, 3041, 2261, 
+	3037, 3038, 69, 3024, 3025, 69, 3012, 3013, 
+	69, 3042, 1660, 3043, 3043, 3044, 3045, 3047, 
+	3048, 3046, 1660, 3049, 3049, 3050, 3051, 3053, 
+	3054, 3052, 1660, 3055, 3055, 3056, 3057, 3059, 
+	3060, 3058, 1660, 3061, 3061, 3062, 3063, 3065, 
+	3066, 3064, 1660, 3067, 3067, 3068, 3069, 3071, 
+	3072, 3070, 1660, 3073, 3073, 3074, 3075, 3077, 
+	3078, 3076, 3076, 3076, 2261, 3079, 3079, 3079, 
+	2261, 3080, 3080, 3081, 3082, 3083, 3084, 3085, 
+	3085, 3086, 3087, 3076, 3076, 3076, 2261, 3083, 
+	3084, 2261, 3077, 3078, 69, 3065, 3066, 69, 
+	3053, 3054, 69, 3088, 1660, 3089, 3089, 3090, 
+	3091, 3093, 3094, 3092, 1660, 3095, 3095, 3096, 
+	3097, 3098, 3099, 3100, 3098, 1637, 3101, 3102, 
+	3101, 3101, 3101, 3102, 1637, 3099, 3100, 69, 
+	3103, 1660, 3104, 3104, 3105, 3106, 3108, 3109, 
+	3107, 1660, 3110, 3110, 3111, 3112, 3114, 3115, 
+	3113, 3113, 3113, 2261, 3116, 3116, 3116, 2261, 
+	3117, 3117, 3117, 2261, 3118, 3118, 3118, 2261, 
+	3120, 3119, 3121, 3121, 3121, 2261, 3122, 3122, 
+	3122, 2261, 3123, 3123, 3123, 2261, 3124, 3124, 
+	3124, 2261, 3126, 3120, 3126, 3126, 3126, 3125, 
+	3114, 3115, 69, 3127, 3127, 3127, 2261, 3128, 
+	3128, 3128, 2261, 3130, 3129, 3131, 3131, 3131, 
+	2261, 3132, 3132, 3132, 2261, 3133, 3130, 3133, 
+	3133, 3133, 3129, 3134, 3134, 3134, 2261, 3135, 
+	3135, 3135, 2261, 3136, 3129, 3137, 3137, 3137, 
+	2261, 3138, 3138, 3138, 2261, 3139, 3136, 3139, 
+	3139, 3139, 3129, 3141, 3141, 3142, 3143, 3144, 
+	3145, 3145, 3145, 19, 20, 3146, 3147, 3145, 
+	3145, 3145, 3140, 3148, 3148, 3149, 3150, 3151, 
+	3152, 67, 3153, 3152, 19, 20, 3154, 3155, 
+	115, 116, 3156, 3157, 67, 67, 3152, 67, 
+	3152, 67, 3152, 67, 691, 3159, 3159, 3142, 
+	3160, 3161, 3145, 3162, 3163, 3164, 3165, 3166, 
+	3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 
+	3175, 3145, 3145, 3162, 3163, 3164, 3165, 3166, 
+	3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 
+	3175, 19, 20, 3176, 3177, 3145, 3145, 3145, 
+	3158, 3159, 3159, 3142, 3160, 3161, 3145, 3180, 
+	3181, 3182, 3183, 3184, 3185, 3186, 3187, 3188, 
+	3189, 3190, 3191, 3192, 3193, 3145, 3145, 3180, 
+	3181, 3182, 3183, 3184, 3185, 3186, 3187, 3188, 
+	3189, 3190, 3191, 3192, 3193, 19, 20, 3176, 
+	3177, 3145, 3179, 3145, 3145, 3178, 3195, 3195, 
+	3196, 3197, 3198, 3199, 3199, 3199, 3200, 3201, 
+	3202, 3203, 3199, 3199, 3199, 3194, 3141, 3141, 
+	3142, 3204, 3205, 3145, 3180, 3181, 3182, 3183, 
+	3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 
+	3192, 3193, 3145, 3145, 3180, 3181, 3182, 3183, 
+	3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 
+	3192, 3193, 19, 20, 3206, 3147, 3145, 3179, 
+	3145, 3145, 3178, 3207, 3207, 3142, 3208, 3209, 
+	3145, 3145, 3145, 81, 82, 3210, 3211, 3145, 
+	3145, 3145, 691, 3212, 3212, 3149, 3213, 3214, 
+	3152, 67, 3153, 3152, 81, 82, 3210, 3215, 
+	90, 91, 3210, 3216, 67, 67, 3152, 67, 
+	3152, 67, 3152, 67, 691, 3148, 3148, 3149, 
+	3150, 3151, 3152, 67, 3219, 3220, 3221, 3222, 
+	3223, 3224, 3225, 3226, 3227, 3228, 3229, 3230, 
+	3231, 3232, 3153, 3152, 3219, 3220, 3221, 3222, 
+	3223, 3224, 3225, 3226, 3227, 3228, 3229, 3230, 
+	3231, 3232, 19, 20, 3154, 3155, 115, 116, 
+	3156, 3157, 67, 67, 3152, 3218, 67, 3152, 
+	67, 3152, 67, 3217, 3212, 3212, 3149, 3213, 
+	3214, 3152, 67, 3219, 3220, 3221, 3222, 3223, 
+	3224, 3225, 3226, 3227, 3228, 3229, 3230, 3231, 
+	3232, 3153, 3152, 3219, 3220, 3221, 3222, 3223, 
+	3224, 3225, 3226, 3227, 3228, 3229, 3230, 3231, 
+	3232, 81, 82, 3233, 3215, 90, 91, 3233, 
+	3216, 67, 67, 3152, 3218, 67, 3152, 67, 
+	3152, 67, 3217, 613, 613, 613, 806, 803, 
+	806, 805, 806, 803, 807, 803, 802, 613, 
+	613, 613, 943, 943, 944, 945, 913, 915, 
+	916, 914, 946, 947, 914, 914, 914, 920, 
+	613, 954, 954, 955, 956, 949, 957, 958, 
+	959, 959, 960, 961, 949, 949, 949, 948, 
+	957, 958, 948, 613, 971, 971, 972, 973, 
+	974, 975, 976, 977, 978, 979, 980, 981, 
+	982, 983, 984, 985, 986, 987, 974, 975, 
+	976, 977, 978, 979, 980, 981, 982, 983, 
+	984, 985, 986, 987, 988, 989, 990, 990, 
+	991, 992, 962, 988, 989, 962, 613, 1431, 
+	1431, 1432, 1433, 1434, 1435, 1436, 1436, 1437, 
+	1438, 1427, 1427, 1427, 1423, 1434, 1435, 1423, 
+	1453, 1453, 1454, 1455, 1457, 1458, 1456, 1456, 
+	1456, 1423, 1409, 1460, 1460, 1461, 1462, 1463, 
+	1464, 1465, 1465, 1466, 1467, 1456, 1456, 1456, 
+	1423, 1463, 1464, 1423, 1457, 1458, 1409, 613, 
+	613, 613, 613, 613, 613, 613, 613, 613, 
+	1835, 1835, 1836, 1837, 1838, 1839, 1840, 1840, 
+	1841, 1842, 1826, 1826, 1826, 69, 1838, 1839, 
+	69, 613, 613, 2006, 2006, 2007, 2008, 2009, 
+	2010, 1955, 1955, 2011, 2012, 1846, 2009, 2010, 
+	1846, 1988, 1988, 1989, 1990, 1992, 1993, 1955, 
+	1955, 1994, 1995, 1991, 1846, 1992, 1993, 1846, 
+	1969, 1969, 1970, 1971, 1973, 1974, 1955, 1955, 
+	1975, 1976, 1972, 1846, 1973, 1974, 1846, 1949, 
+	1949, 1950, 1951, 1953, 1954, 1955, 1955, 1956, 
+	1957, 1952, 1846, 1953, 1954, 1846, 613, 613, 
+	613, 613, 2204, 2204, 2205, 2206, 2207, 2208, 
+	2209, 2210, 2211, 2212, 2212, 2213, 2214, 2203, 
+	2210, 2211, 69, 613, 2269, 2269, 2270, 2271, 
+	2272, 2273, 2274, 2274, 2275, 2276, 2265, 2265, 
+	2265, 2261, 2272, 2273, 2261, 613, 2303, 2303, 
+	2304, 2305, 2306, 2307, 2308, 2308, 2309, 2310, 
+	2299, 2299, 2299, 2261, 2306, 2307, 2261, 613, 
+	2351, 2351, 2352, 2353, 2357, 2357, 2357, 2357, 
+	2362, 2363, 2357, 2357, 2364, 2365, 2370, 2362, 
+	2363, 2370, 2348, 2348, 2349, 2350, 2371, 2358, 
+	2359, 2371, 2371, 2371, 2370, 2358, 2359, 2370, 
+	2371, 2348, 2348, 2349, 2350, 2351, 2351, 2352, 
+	2353, 2354, 2354, 2355, 2356, 2357, 2357, 2357, 
+	2357, 2358, 2359, 2357, 2357, 2360, 2361, 2362, 
+	2363, 2357, 2357, 2364, 2365, 2366, 2367, 2357, 
+	2357, 2368, 2369, 2371, 2371, 2371, 2370, 2358, 
+	2362, 2366, 2359, 2363, 2367, 2370, 613, 613, 
+	613, 613, 613, 613, 613, 3080, 3080, 3081, 
+	3082, 3083, 3084, 3085, 3085, 3086, 3087, 3076, 
+	3076, 3076, 2261, 3083, 3084, 2261, 613, 613, 
+	613, 613, 0
 };
 
 static const short _zone_scanner_trans_targs[] = {
-	0, 1, 1, 1, 2, 4, 17, 26, 
-	40, 47, 130, 63, 67, 75, 81, 97, 
-	100, 107, 118, 1060, 138, 1065, 243, 0, 
-	3, 3, 3, 2, 153, 153, 153, 153, 
-	153, 3, 156, 0, 3, 3, 3, 4, 
-	17, 26, 40, 47, 52, 63, 67, 75, 
-	81, 97, 100, 107, 118, 3, 156, 0, 
-	5, 5, 5, 214, 217, 221, 5, 11, 
-	10, 12, 21, 6, 5, 0, 5, 5, 
-	9, 5, 11, 10, 12, 21, 0, 7, 
-	7, 7, 1060, 8, 0, 7, 7, 7, 
-	1060, 8, 6, 10, 12, 12, 12, 13, 
-	1066, 163, 227, 1061, 14, 0, 14, 14, 
-	15, 159, 166, 167, 168, 169, 170, 171, 
-	172, 173, 174, 175, 176, 177, 178, 1068, 
-	223, 1066, 224, 1069, 226, 0, 16, 16, 
-	16, 1062, 246, 0, 16, 16, 16, 1062, 
-	246, 18, 22, 19, 20, 5, 5, 5, 
-	5, 11, 10, 12, 21, 21, 21, 21, 
-	23, 24, 25, 5, 5, 5, 5, 11, 
-	10, 12, 21, 27, 31, 39, 28, 29, 
-	30, 5, 5, 5, 5, 11, 10, 12, 
-	21, 32, 35, 33, 34, 5, 5, 5, 
-	5, 11, 10, 12, 21, 36, 37, 38, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	41, 42, 43, 45, 44, 5, 5, 5, 
-	5, 11, 10, 12, 21, 46, 5, 5, 
-	5, 5, 11, 10, 12, 21, 48, 49, 
-	50, 51, 5, 5, 5, 5, 11, 10, 
-	12, 21, 53, 56, 54, 54, 54, 54, 
-	128, 54, 54, 54, 4, 17, 26, 40, 
-	47, 55, 63, 67, 75, 81, 97, 100, 
-	107, 118, 54, 128, 57, 58, 59, 60, 
-	61, 62, 5, 5, 5, 5, 11, 10, 
-	12, 21, 64, 66, 65, 5, 5, 5, 
-	5, 11, 10, 12, 21, 5, 5, 5, 
-	5, 11, 10, 12, 21, 68, 70, 72, 
-	74, 69, 5, 5, 5, 5, 11, 10, 
-	12, 21, 71, 5, 5, 5, 5, 11, 
-	10, 12, 21, 73, 5, 5, 5, 5, 
-	11, 10, 12, 21, 5, 5, 5, 5, 
-	11, 10, 12, 21, 76, 80, 77, 78, 
-	79, 5, 5, 5, 5, 11, 10, 12, 
-	21, 5, 5, 5, 5, 11, 10, 12, 
-	21, 82, 86, 88, 83, 84, 85, 5, 
-	5, 5, 5, 11, 10, 12, 21, 87, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	5, 5, 5, 89, 5, 11, 10, 12, 
-	21, 90, 5, 5, 5, 91, 5, 11, 
-	10, 12, 21, 5, 5, 5, 92, 5, 
-	11, 10, 12, 21, 93, 94, 95, 96, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	98, 99, 5, 5, 5, 5, 11, 10, 
-	12, 21, 101, 102, 106, 5, 5, 5, 
-	5, 11, 10, 12, 21, 103, 104, 105, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	108, 110, 112, 114, 109, 5, 5, 5, 
-	5, 11, 10, 12, 21, 111, 5, 5, 
-	5, 5, 11, 10, 12, 21, 113, 5, 
-	5, 5, 5, 11, 10, 12, 21, 115, 
-	116, 117, 5, 5, 5, 5, 11, 10, 
-	12, 21, 119, 122, 124, 120, 121, 5, 
-	5, 5, 5, 11, 10, 12, 21, 123, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	125, 126, 0, 127, 0, 5, 5, 5, 
-	127, 5, 11, 10, 12, 21, 129, 129, 
-	129, 1063, 244, 131, 132, 132, 132, 132, 
-	137, 132, 132, 132, 133, 4, 17, 26, 
-	40, 47, 55, 63, 67, 75, 81, 97, 
-	100, 107, 118, 132, 137, 54, 54, 54, 
-	133, 134, 134, 134, 134, 134, 54, 128, 
-	135, 135, 136, 136, 136, 136, 136, 54, 
-	54, 54, 135, 54, 128, 0, 140, 181, 
-	187, 141, 142, 143, 144, 145, 146, 1064, 
-	0, 148, 148, 148, 148, 149, 148, 148, 
-	148, 148, 149, 0, 0, 151, 151, 151, 
-	151, 152, 151, 151, 151, 151, 152, 154, 
-	154, 155, 155, 155, 155, 155, 3, 3, 
-	3, 154, 3, 156, 0, 158, 158, 158, 
-	158, 228, 165, 229, 158, 158, 158, 158, 
-	228, 165, 229, 0, 160, 160, 160, 1061, 
-	13, 1066, 163, 1067, 227, 160, 160, 160, 
-	1067, 161, 161, 161, 1065, 162, 164, 164, 
-	164, 165, 165, 165, 179, 213, 1068, 0, 
-	182, 183, 184, 185, 186, 1064, 188, 189, 
-	1064, 0, 161, 161, 161, 1065, 162, 0, 
-	192, 192, 192, 1063, 193, 192, 192, 192, 
-	1063, 193, 0, 195, 195, 195, 1068, 179, 
-	1069, 213, 195, 195, 195, 1069, 210, 0, 
-	210, 210, 210, 211, 161, 161, 161, 1065, 
-	162, 212, 212, 212, 212, 212, 212, 213, 
-	213, 213, 215, 216, 5, 5, 5, 5, 
-	11, 10, 12, 21, 218, 219, 220, 5, 
-	5, 5, 5, 11, 10, 12, 21, 222, 
-	5, 5, 5, 5, 11, 10, 12, 21, 
-	225, 225, 225, 226, 226, 226, 227, 227, 
-	227, 229, 229, 229, 0, 248, 1070, 248, 
-	1070, 0, 250, 252, 257, 258, 253, 1071, 
-	251, 1071, 250, 252, 253, 252, 253, 254, 
-	0, 255, 256, 1071, 252, 251, 253, 1071, 
-	0, 260, 265, 261, 1072, 1073, 262, 0, 
-	263, 264, 1072, 260, 1073, 261, 265, 266, 
-	267, 0, 268, 269, 270, 265, 266, 267, 
-	0, 272, 272, 272, 272, 279, 0, 273, 
-	0, 274, 274, 274, 273, 276, 276, 276, 
-	276, 276, 1074, 275, 0, 277, 0, 277, 
-	278, 278, 278, 278, 278, 274, 274, 274, 
-	277, 1074, 275, 0, 281, 281, 281, 281, 
-	292, 282, 286, 291, 287, 283, 0, 284, 
-	284, 284, 282, 286, 287, 1075, 285, 284, 
-	284, 284, 1075, 285, 286, 287, 288, 0, 
-	289, 290, 286, 283, 287, 294, 294, 294, 
-	294, 310, 295, 0, 0, 296, 296, 296, 
-	1076, 300, 307, 0, 296, 296, 296, 297, 
-	301, 306, 302, 1076, 300, 307, 0, 298, 
-	299, 299, 299, 297, 301, 302, 1076, 300, 
-	299, 299, 299, 301, 302, 303, 0, 304, 
-	305, 301, 298, 302, 1077, 308, 308, 308, 
-	308, 309, 0, 312, 313, 314, 317, 315, 
-	315, 315, 315, 315, 316, 1078, 1079, 1080, 
-	0, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 319, 319, 319, 320, 325, 333, 347, 
-	354, 359, 367, 371, 379, 385, 401, 404, 
-	411, 422, 319, 324, 1081, 1082, 1083, 319, 
-	319, 319, 321, 432, 436, 319, 324, 1081, 
-	1082, 1083, 322, 323, 319, 319, 319, 319, 
-	324, 1081, 1082, 1083, 326, 329, 327, 328, 
-	319, 319, 319, 319, 324, 1081, 1082, 1083, 
-	330, 331, 332, 319, 319, 319, 319, 324, 
-	1081, 1082, 1083, 334, 338, 346, 335, 336, 
-	337, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 339, 342, 340, 341, 319, 319, 319, 
-	319, 324, 1081, 1082, 1083, 343, 344, 345, 
-	319, 319, 319, 319, 324, 1081, 1082, 1083, 
-	319, 319, 319, 319, 324, 1081, 1082, 1083, 
-	348, 349, 350, 352, 351, 319, 319, 319, 
-	319, 324, 1081, 1082, 1083, 353, 319, 319, 
-	319, 319, 324, 1081, 1082, 1083, 355, 356, 
-	357, 358, 319, 319, 319, 319, 324, 1081, 
-	1082, 1083, 360, 361, 362, 363, 364, 365, 
-	366, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 368, 370, 369, 319, 319, 319, 319, 
-	324, 1081, 1082, 1083, 319, 319, 319, 319, 
-	324, 1081, 1082, 1083, 372, 374, 376, 378, 
-	373, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 375, 319, 319, 319, 319, 324, 1081, 
-	1082, 1083, 377, 319, 319, 319, 319, 324, 
-	1081, 1082, 1083, 319, 319, 319, 319, 324, 
-	1081, 1082, 1083, 380, 384, 381, 382, 383, 
-	319, 319, 319, 319, 324, 1081, 1082, 1083, 
-	319, 319, 319, 319, 324, 1081, 1082, 1083, 
-	386, 390, 392, 387, 388, 389, 319, 319, 
-	319, 319, 324, 1081, 1082, 1083, 391, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 319, 
-	319, 319, 393, 319, 324, 1081, 1082, 1083, 
-	394, 319, 319, 319, 395, 319, 324, 1081, 
-	1082, 1083, 319, 319, 319, 396, 319, 324, 
-	1081, 1082, 1083, 397, 398, 399, 400, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 402, 
-	403, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 405, 406, 410, 319, 319, 319, 319, 
-	324, 1081, 1082, 1083, 407, 408, 409, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 412, 
-	414, 416, 418, 413, 319, 319, 319, 319, 
-	324, 1081, 1082, 1083, 415, 319, 319, 319, 
-	319, 324, 1081, 1082, 1083, 417, 319, 319, 
-	319, 319, 324, 1081, 1082, 1083, 419, 420, 
-	421, 319, 319, 319, 319, 324, 1081, 1082, 
-	1083, 423, 426, 428, 424, 425, 319, 319, 
-	319, 319, 324, 1081, 1082, 1083, 427, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 429, 
-	430, 431, 319, 319, 319, 431, 319, 324, 
-	1081, 1082, 1083, 433, 434, 435, 319, 319, 
-	319, 319, 324, 1081, 1082, 1083, 437, 319, 
-	319, 319, 319, 324, 1081, 1082, 1083, 0, 
-	439, 439, 439, 439, 446, 0, 440, 441, 
-	441, 441, 440, 441, 445, 0, 441, 441, 
-	441, 442, 441, 445, 443, 443, 443, 443, 
-	443, 444, 1084, 1085, 1086, 448, 448, 448, 
-	448, 456, 449, 455, 1087, 1087, 1087, 455, 
-	1088, 1087, 1091, 450, 450, 450, 451, 450, 
-	454, 452, 452, 452, 452, 452, 453, 1088, 
-	1089, 1090, 450, 450, 450, 450, 454, 0, 
-	458, 459, 474, 504, 512, 525, 1092, 458, 
-	460, 461, 1092, 462, 1092, 463, 464, 465, 
-	466, 467, 468, 469, 470, 471, 472, 473, 
-	1092, 475, 476, 482, 477, 478, 479, 480, 
-	481, 1092, 483, 484, 485, 486, 495, 487, 
-	488, 489, 490, 491, 492, 493, 494, 1092, 
-	496, 497, 498, 499, 500, 501, 502, 503, 
-	1092, 505, 506, 507, 508, 509, 510, 511, 
-	1092, 513, 514, 515, 516, 517, 518, 519, 
-	522, 520, 521, 1092, 523, 524, 1092, 526, 
-	527, 528, 531, 529, 530, 1092, 532, 533, 
-	534, 546, 549, 1092, 535, 536, 537, 538, 
-	539, 540, 541, 542, 543, 544, 545, 1092, 
-	547, 548, 1092, 550, 551, 1092, 0, 553, 
-	554, 560, 577, 580, 586, 590, 1093, 553, 
-	555, 556, 557, 558, 559, 1093, 561, 567, 
-	573, 562, 563, 564, 565, 566, 1093, 568, 
-	570, 569, 1093, 571, 572, 1093, 574, 575, 
-	576, 1093, 578, 579, 1093, 581, 583, 582, 
-	1093, 584, 585, 1093, 587, 588, 589, 1093, 
-	591, 592, 1093, 0, 594, 1094, 594, 596, 
-	1095, 598, 599, 599, 599, 599, 628, 599, 
-	599, 599, 600, 599, 628, 601, 601, 601, 
-	601, 627, 0, 601, 601, 601, 602, 601, 
-	627, 603, 603, 603, 602, 603, 626, 603, 
-	603, 603, 604, 603, 626, 0, 605, 605, 
-	605, 604, 623, 623, 623, 623, 623, 605, 
-	622, 605, 605, 605, 606, 605, 622, 607, 
-	607, 607, 606, 619, 619, 619, 619, 619, 
-	607, 618, 607, 607, 607, 608, 607, 618, 
-	609, 609, 609, 608, 615, 615, 615, 615, 
-	615, 609, 614, 609, 609, 609, 610, 609, 
-	614, 1096, 610, 611, 611, 611, 611, 611, 
-	612, 612, 613, 613, 613, 613, 613, 1096, 
-	612, 616, 616, 617, 617, 617, 617, 617, 
-	609, 609, 609, 616, 609, 614, 620, 620, 
-	621, 621, 621, 621, 621, 607, 607, 607, 
-	620, 607, 618, 624, 624, 625, 625, 625, 
-	625, 625, 605, 605, 605, 624, 605, 622, 
-	630, 631, 631, 631, 631, 633, 632, 631, 
-	631, 631, 631, 633, 1097, 635, 636, 636, 
-	636, 636, 638, 636, 636, 636, 637, 636, 
-	638, 1098, 640, 641, 641, 641, 640, 641, 
-	643, 641, 641, 641, 642, 641, 643, 1099, 
-	645, 646, 646, 646, 646, 647, 1100, 1101, 
-	1102, 646, 646, 646, 646, 647, 1100, 1101, 
-	1102, 649, 1103, 649, 0, 651, 652, 652, 
-	652, 651, 652, 704, 652, 652, 652, 653, 
-	657, 699, 652, 704, 654, 654, 654, 653, 
-	654, 703, 654, 654, 654, 655, 654, 703, 
-	656, 656, 656, 701, 655, 656, 700, 656, 
-	656, 656, 656, 700, 658, 658, 658, 658, 
-	698, 659, 660, 660, 660, 659, 660, 697, 
-	660, 660, 660, 661, 665, 692, 660, 697, 
-	662, 662, 662, 661, 662, 696, 662, 662, 
-	662, 663, 662, 696, 664, 664, 664, 694, 
-	663, 664, 693, 664, 664, 664, 664, 693, 
-	666, 666, 666, 666, 691, 667, 668, 668, 
-	669, 669, 669, 688, 668, 690, 669, 687, 
-	1104, 1111, 1112, 669, 669, 669, 670, 669, 
-	687, 1104, 1111, 1112, 671, 671, 671, 684, 
-	670, 686, 671, 683, 1104, 1109, 1110, 671, 
-	671, 671, 672, 671, 683, 1109, 1110, 673, 
-	673, 673, 680, 672, 682, 673, 679, 1104, 
-	1107, 1108, 673, 673, 673, 674, 673, 679, 
-	1107, 1108, 675, 675, 675, 677, 674, 675, 
-	676, 1104, 1105, 1106, 675, 675, 675, 675, 
-	676, 1105, 1106, 675, 675, 675, 678, 675, 
-	676, 1104, 1105, 1106, 678, 673, 673, 673, 
-	681, 682, 673, 679, 1104, 1107, 1108, 681, 
-	671, 671, 671, 685, 686, 671, 683, 1104, 
-	1109, 1110, 685, 669, 669, 669, 689, 690, 
-	669, 687, 1104, 1111, 1112, 689, 666, 666, 
-	666, 666, 691, 664, 664, 664, 695, 664, 
-	693, 695, 658, 658, 658, 658, 698, 656, 
-	656, 656, 702, 656, 700, 702, 706, 707, 
-	707, 707, 706, 707, 715, 707, 707, 707, 
-	708, 707, 715, 709, 709, 709, 708, 709, 
-	714, 709, 709, 709, 710, 709, 714, 711, 
-	711, 711, 710, 711, 713, 711, 711, 711, 
-	712, 711, 713, 1113, 717, 718, 718, 718, 
-	717, 718, 732, 718, 718, 718, 719, 718, 
-	732, 720, 720, 720, 719, 720, 731, 721, 
-	720, 720, 720, 720, 731, 722, 722, 722, 
-	722, 730, 723, 722, 722, 722, 722, 730, 
-	724, 724, 724, 724, 729, 725, 724, 724, 
-	724, 724, 729, 726, 726, 726, 726, 728, 
-	726, 726, 726, 727, 726, 728, 1114, 734, 
-	735, 735, 735, 735, 743, 736, 737, 737, 
-	737, 736, 737, 742, 737, 737, 737, 738, 
-	737, 742, 739, 739, 739, 739, 741, 740, 
-	1115, 0, 744, 745, 744, 744, 746, 752, 
-	744, 751, 1116, 1117, 1118, 746, 752, 747, 
-	0, 748, 748, 749, 750, 744, 744, 744, 
-	750, 744, 751, 1116, 1117, 1118, 753, 754, 
-	754, 749, 756, 757, 757, 757, 756, 757, 
-	767, 757, 757, 757, 758, 757, 767, 759, 
-	759, 759, 759, 766, 760, 761, 761, 761, 
-	760, 761, 765, 0, 761, 761, 761, 762, 
-	761, 765, 763, 763, 763, 763, 763, 764, 
-	1119, 1120, 1121, 769, 770, 770, 770, 769, 
-	770, 777, 770, 770, 770, 771, 770, 777, 
-	772, 772, 772, 771, 772, 776, 772, 772, 
-	772, 773, 772, 776, 774, 774, 774, 774, 
-	774, 775, 1122, 1123, 1124, 779, 780, 780, 
-	780, 779, 780, 816, 0, 780, 780, 780, 
-	781, 795, 802, 809, 780, 816, 782, 782, 
-	782, 782, 794, 0, 783, 784, 784, 784, 
-	783, 784, 793, 784, 784, 784, 785, 784, 
-	793, 0, 786, 786, 786, 789, 789, 789, 
-	791, 791, 791, 1125, 786, 788, 1128, 1129, 
-	789, 790, 1126, 1127, 791, 792, 1130, 1131, 
-	0, 787, 796, 796, 796, 796, 801, 797, 
-	798, 798, 798, 797, 798, 800, 0, 798, 
-	798, 798, 799, 798, 800, 0, 799, 786, 
-	786, 786, 789, 789, 789, 791, 791, 791, 
-	1125, 786, 788, 1128, 1129, 789, 790, 1126, 
-	1127, 791, 792, 1130, 1131, 803, 803, 803, 
-	803, 808, 804, 805, 805, 805, 804, 805, 
-	807, 805, 805, 805, 806, 805, 807, 806, 
-	786, 786, 786, 789, 789, 789, 791, 791, 
-	791, 1125, 786, 788, 1128, 1129, 789, 790, 
-	1126, 1127, 791, 792, 1130, 1131, 810, 810, 
-	810, 810, 815, 811, 812, 812, 812, 811, 
-	812, 814, 812, 812, 812, 813, 812, 814, 
-	786, 786, 786, 789, 789, 789, 791, 791, 
-	791, 1125, 786, 788, 1128, 1129, 789, 790, 
-	1126, 1127, 791, 792, 1130, 1131, 0, 818, 
-	852, 860, 874, 881, 886, 894, 898, 906, 
-	912, 928, 931, 938, 949, 819, 819, 819, 
-	843, 846, 850, 819, 842, 819, 819, 819, 
-	820, 819, 842, 821, 821, 821, 821, 841, 
-	822, 823, 823, 823, 822, 823, 840, 823, 
-	823, 823, 824, 823, 840, 825, 825, 825, 
-	824, 825, 839, 0, 825, 825, 825, 826, 
-	825, 839, 827, 827, 827, 826, 827, 838, 
-	827, 827, 827, 828, 827, 838, 829, 829, 
-	829, 828, 829, 837, 829, 829, 829, 830, 
-	829, 837, 831, 831, 831, 830, 831, 836, 
-	831, 831, 831, 832, 831, 836, 833, 833, 
-	833, 833, 835, 833, 833, 833, 834, 833, 
-	835, 1132, 844, 845, 819, 819, 819, 819, 
-	842, 847, 848, 849, 819, 819, 819, 819, 
-	842, 851, 819, 819, 819, 819, 842, 853, 
-	856, 854, 855, 819, 819, 819, 819, 842, 
-	857, 858, 859, 819, 819, 819, 819, 842, 
-	861, 865, 873, 862, 863, 864, 819, 819, 
-	819, 819, 842, 866, 869, 867, 868, 819, 
-	819, 819, 819, 842, 870, 871, 872, 819, 
-	819, 819, 819, 842, 819, 819, 819, 819, 
-	842, 875, 876, 877, 879, 878, 819, 819, 
-	819, 819, 842, 880, 819, 819, 819, 819, 
-	842, 882, 883, 884, 885, 819, 819, 819, 
-	819, 842, 887, 888, 889, 890, 891, 892, 
-	893, 819, 819, 819, 819, 842, 895, 897, 
-	896, 819, 819, 819, 819, 842, 819, 819, 
-	819, 819, 842, 899, 901, 903, 905, 900, 
-	819, 819, 819, 819, 842, 902, 819, 819, 
-	819, 819, 842, 904, 819, 819, 819, 819, 
-	842, 819, 819, 819, 819, 842, 907, 911, 
-	908, 909, 910, 819, 819, 819, 819, 842, 
-	819, 819, 819, 819, 842, 913, 917, 919, 
-	914, 915, 916, 819, 819, 819, 819, 842, 
-	918, 819, 819, 819, 819, 842, 819, 819, 
-	819, 920, 819, 842, 921, 819, 819, 819, 
-	922, 819, 842, 819, 819, 819, 923, 819, 
-	842, 924, 925, 926, 927, 819, 819, 819, 
-	819, 842, 929, 930, 819, 819, 819, 819, 
-	842, 932, 933, 937, 819, 819, 819, 819, 
-	842, 934, 935, 936, 819, 819, 819, 819, 
-	842, 819, 819, 819, 819, 842, 939, 941, 
-	943, 945, 940, 819, 819, 819, 819, 842, 
-	942, 819, 819, 819, 819, 842, 944, 819, 
-	819, 819, 819, 842, 946, 947, 948, 819, 
-	819, 819, 819, 842, 950, 953, 955, 951, 
-	952, 819, 819, 819, 819, 842, 954, 819, 
-	819, 819, 819, 842, 956, 957, 0, 958, 
-	819, 819, 819, 958, 819, 842, 960, 961, 
-	1133, 963, 964, 964, 964, 963, 964, 972, 
-	964, 964, 964, 965, 964, 972, 966, 966, 
-	966, 965, 966, 971, 966, 966, 966, 967, 
-	966, 971, 968, 968, 968, 968, 970, 969, 
-	1134, 974, 1135, 976, 977, 977, 977, 976, 
-	977, 1003, 977, 977, 977, 978, 977, 1003, 
-	979, 979, 979, 978, 979, 1002, 979, 979, 
-	979, 980, 979, 1002, 981, 981, 981, 980, 
-	981, 1001, 981, 981, 981, 982, 999, 981, 
-	1001, 983, 983, 983, 983, 998, 0, 983, 
-	983, 983, 984, 983, 998, 985, 986, 996, 
-	987, 988, 995, 989, 993, 990, 991, 991, 
-	992, 984, 1136, 994, 997, 1000, 999, 1005, 
-	1006, 1006, 1006, 1005, 1006, 1016, 1006, 1006, 
-	1006, 1007, 1006, 1016, 1008, 1008, 1008, 1007, 
-	1008, 1015, 1008, 1008, 1008, 1009, 1008, 1015, 
-	1010, 1010, 1010, 1009, 1010, 1014, 1010, 1010, 
-	1010, 1011, 1012, 1010, 1014, 1137, 1013, 1012, 
-	1018, 1019, 1019, 1019, 1018, 1019, 1029, 1019, 
-	1019, 1019, 1020, 1019, 1029, 1021, 1021, 1021, 
-	1020, 1021, 1028, 1021, 1021, 1021, 1022, 1021, 
-	1028, 1023, 1023, 1023, 1022, 1023, 1027, 1023, 
-	1023, 1023, 1024, 1023, 1027, 1025, 1025, 1025, 
-	1025, 1025, 1026, 1138, 1139, 1140, 1031, 1032, 
-	1032, 1032, 1031, 1032, 1034, 1032, 1032, 1032, 
-	1033, 1032, 1034, 1141, 1033, 1036, 1037, 1037, 
-	1037, 1036, 1037, 1047, 1037, 1037, 1037, 1038, 
-	1037, 1047, 1039, 1040, 1041, 0, 1042, 1043, 
-	1044, 1045, 1046, 0, 1142, 1049, 1050, 0, 
-	1051, 1052, 1053, 1143, 1055, 1056, 1057, 1058, 
-	1059, 1144, 0, 1, 139, 1, 1, 147, 
-	1065, 243, 14, 180, 14, 14, 190, 209, 
-	1068, 223, 1069, 226, 0, 129, 129, 129, 
-	157, 230, 231, 232, 233, 245, 235, 236, 
-	237, 238, 239, 240, 241, 242, 1063, 244, 
-	0, 150, 157, 230, 231, 232, 233, 234, 
-	235, 236, 237, 238, 239, 240, 241, 242, 
-	0, 1, 139, 1, 1, 147, 1060, 138, 
-	1065, 243, 1, 1, 1065, 161, 161, 161, 
-	1065, 162, 165, 165, 165, 179, 213, 0, 
-	191, 194, 196, 197, 198, 199, 200, 201, 
-	202, 203, 204, 205, 206, 207, 208, 1065
+	0, 1, 1, 1, 2, 4, 16, 25, 
+	39, 46, 129, 62, 66, 74, 80, 96, 
+	99, 106, 117, 1059, 137, 1064, 242, 0, 
+	3, 3, 3, 2, 152, 152, 152, 152, 
+	152, 3, 155, 0, 3, 3, 3, 4, 
+	16, 25, 39, 46, 51, 62, 66, 74, 
+	80, 96, 99, 106, 117, 3, 155, 0, 
+	5, 5, 5, 213, 216, 220, 5, 10, 
+	6, 11, 20, 6, 5, 0, 5, 5, 
+	9, 5, 10, 11, 20, 0, 7, 7, 
+	7, 1059, 8, 0, 6, 6, 11, 11, 
+	11, 12, 1065, 162, 226, 1060, 13, 0, 
+	13, 13, 14, 158, 165, 166, 167, 168, 
+	169, 170, 171, 172, 173, 174, 175, 176, 
+	177, 1067, 222, 1065, 223, 1068, 225, 0, 
+	15, 15, 15, 1061, 245, 0, 15, 15, 
+	15, 1061, 245, 17, 21, 18, 19, 5, 
+	5, 5, 5, 10, 6, 11, 20, 20, 
+	20, 20, 22, 23, 24, 5, 5, 5, 
+	5, 10, 6, 11, 20, 26, 30, 38, 
+	27, 28, 29, 5, 5, 5, 5, 10, 
+	6, 11, 20, 31, 34, 32, 33, 5, 
+	5, 5, 5, 10, 6, 11, 20, 35, 
+	36, 37, 5, 5, 5, 5, 10, 6, 
+	11, 20, 5, 5, 5, 5, 10, 6, 
+	11, 20, 40, 41, 42, 44, 43, 5, 
+	5, 5, 5, 10, 6, 11, 20, 45, 
+	5, 5, 5, 5, 10, 6, 11, 20, 
+	47, 48, 49, 50, 5, 5, 5, 5, 
+	10, 6, 11, 20, 52, 55, 53, 53, 
+	53, 53, 127, 53, 53, 53, 4, 16, 
+	25, 39, 46, 54, 62, 66, 74, 80, 
+	96, 99, 106, 117, 53, 127, 56, 57, 
+	58, 59, 60, 61, 5, 5, 5, 5, 
+	10, 6, 11, 20, 63, 65, 64, 5, 
+	5, 5, 5, 10, 6, 11, 20, 5, 
+	5, 5, 5, 10, 6, 11, 20, 67, 
+	69, 71, 73, 68, 5, 5, 5, 5, 
+	10, 6, 11, 20, 70, 5, 5, 5, 
+	5, 10, 6, 11, 20, 72, 5, 5, 
+	5, 5, 10, 6, 11, 20, 5, 5, 
+	5, 5, 10, 6, 11, 20, 75, 79, 
+	76, 77, 78, 5, 5, 5, 5, 10, 
+	6, 11, 20, 5, 5, 5, 5, 10, 
+	6, 11, 20, 81, 85, 87, 82, 83, 
+	84, 5, 5, 5, 5, 10, 6, 11, 
+	20, 86, 5, 5, 5, 5, 10, 6, 
+	11, 20, 5, 5, 5, 88, 5, 10, 
+	6, 11, 20, 89, 5, 5, 5, 90, 
+	5, 10, 6, 11, 20, 5, 5, 5, 
+	91, 5, 10, 6, 11, 20, 92, 93, 
+	94, 95, 5, 5, 5, 5, 10, 6, 
+	11, 20, 97, 98, 5, 5, 5, 5, 
+	10, 6, 11, 20, 100, 101, 105, 5, 
+	5, 5, 5, 10, 6, 11, 20, 102, 
+	103, 104, 5, 5, 5, 5, 10, 6, 
+	11, 20, 5, 5, 5, 5, 10, 6, 
+	11, 20, 107, 109, 111, 113, 108, 5, 
+	5, 5, 5, 10, 6, 11, 20, 110, 
+	5, 5, 5, 5, 10, 6, 11, 20, 
+	112, 5, 5, 5, 5, 10, 6, 11, 
+	20, 114, 115, 116, 5, 5, 5, 5, 
+	10, 6, 11, 20, 118, 121, 123, 119, 
+	120, 5, 5, 5, 5, 10, 6, 11, 
+	20, 122, 5, 5, 5, 5, 10, 6, 
+	11, 20, 124, 125, 0, 126, 0, 5, 
+	5, 5, 126, 5, 10, 6, 11, 20, 
+	128, 128, 128, 1062, 243, 130, 131, 131, 
+	131, 131, 136, 131, 131, 131, 132, 4, 
+	16, 25, 39, 46, 54, 62, 66, 74, 
+	80, 96, 99, 106, 117, 131, 136, 53, 
+	53, 53, 132, 133, 133, 133, 133, 133, 
+	53, 127, 134, 134, 135, 135, 135, 135, 
+	135, 53, 53, 53, 134, 53, 127, 0, 
+	139, 180, 186, 140, 141, 142, 143, 144, 
+	145, 1063, 0, 147, 147, 147, 147, 148, 
+	147, 147, 147, 147, 148, 0, 0, 150, 
+	150, 150, 150, 151, 150, 150, 150, 150, 
+	151, 153, 153, 154, 154, 154, 154, 154, 
+	3, 3, 3, 153, 3, 155, 0, 157, 
+	157, 157, 157, 227, 164, 228, 157, 157, 
+	157, 157, 227, 164, 228, 0, 159, 159, 
+	159, 1060, 12, 1065, 162, 1066, 226, 159, 
+	159, 159, 1066, 160, 160, 160, 1064, 161, 
+	163, 163, 163, 164, 164, 164, 178, 212, 
+	1067, 0, 181, 182, 183, 184, 185, 1063, 
+	187, 188, 1063, 0, 160, 160, 160, 1064, 
+	161, 0, 191, 191, 191, 1062, 192, 191, 
+	191, 191, 1062, 192, 0, 194, 194, 194, 
+	1067, 178, 1068, 212, 194, 194, 194, 1068, 
+	209, 0, 209, 209, 209, 210, 211, 211, 
+	211, 212, 212, 212, 214, 215, 5, 5, 
+	5, 5, 10, 6, 11, 20, 217, 218, 
+	219, 5, 5, 5, 5, 10, 6, 11, 
+	20, 221, 5, 5, 5, 5, 10, 6, 
+	11, 20, 224, 224, 224, 225, 225, 225, 
+	226, 226, 226, 228, 228, 228, 0, 247, 
+	1069, 247, 1069, 0, 249, 251, 256, 257, 
+	252, 1070, 250, 1070, 249, 251, 252, 251, 
+	252, 253, 0, 254, 255, 1070, 251, 250, 
+	252, 1070, 0, 259, 264, 260, 1071, 1072, 
+	261, 0, 262, 263, 1071, 259, 1072, 260, 
+	264, 265, 266, 0, 267, 268, 269, 264, 
+	265, 266, 0, 271, 271, 271, 271, 278, 
+	0, 272, 0, 273, 273, 273, 272, 275, 
+	275, 275, 275, 275, 1073, 274, 0, 276, 
+	0, 276, 277, 277, 277, 277, 277, 273, 
+	273, 273, 276, 1073, 274, 0, 280, 280, 
+	280, 280, 291, 281, 285, 290, 286, 282, 
+	0, 283, 283, 283, 281, 285, 286, 1074, 
+	284, 283, 283, 283, 1074, 284, 285, 286, 
+	287, 0, 288, 289, 285, 282, 286, 293, 
+	293, 293, 293, 309, 294, 0, 0, 295, 
+	295, 295, 1075, 299, 306, 0, 295, 295, 
+	295, 296, 300, 305, 301, 1075, 299, 306, 
+	0, 297, 298, 298, 298, 296, 300, 301, 
+	1075, 299, 298, 298, 298, 300, 301, 302, 
+	0, 303, 304, 300, 297, 301, 1076, 307, 
+	307, 307, 307, 308, 0, 311, 312, 313, 
+	316, 314, 314, 314, 314, 314, 315, 1077, 
+	1078, 1079, 0, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 318, 318, 318, 319, 324, 
+	332, 346, 353, 358, 366, 370, 378, 384, 
+	400, 403, 410, 421, 318, 323, 1080, 1081, 
+	1082, 318, 318, 318, 320, 431, 435, 318, 
+	323, 1080, 1081, 1082, 321, 322, 318, 318, 
+	318, 318, 323, 1080, 1081, 1082, 325, 328, 
+	326, 327, 318, 318, 318, 318, 323, 1080, 
+	1081, 1082, 329, 330, 331, 318, 318, 318, 
+	318, 323, 1080, 1081, 1082, 333, 337, 345, 
+	334, 335, 336, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 338, 341, 339, 340, 318, 
+	318, 318, 318, 323, 1080, 1081, 1082, 342, 
+	343, 344, 318, 318, 318, 318, 323, 1080, 
+	1081, 1082, 318, 318, 318, 318, 323, 1080, 
+	1081, 1082, 347, 348, 349, 351, 350, 318, 
+	318, 318, 318, 323, 1080, 1081, 1082, 352, 
+	318, 318, 318, 318, 323, 1080, 1081, 1082, 
+	354, 355, 356, 357, 318, 318, 318, 318, 
+	323, 1080, 1081, 1082, 359, 360, 361, 362, 
+	363, 364, 365, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 367, 369, 368, 318, 318, 
+	318, 318, 323, 1080, 1081, 1082, 318, 318, 
+	318, 318, 323, 1080, 1081, 1082, 371, 373, 
+	375, 377, 372, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 374, 318, 318, 318, 318, 
+	323, 1080, 1081, 1082, 376, 318, 318, 318, 
+	318, 323, 1080, 1081, 1082, 318, 318, 318, 
+	318, 323, 1080, 1081, 1082, 379, 383, 380, 
+	381, 382, 318, 318, 318, 318, 323, 1080, 
+	1081, 1082, 318, 318, 318, 318, 323, 1080, 
+	1081, 1082, 385, 389, 391, 386, 387, 388, 
+	318, 318, 318, 318, 323, 1080, 1081, 1082, 
+	390, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 318, 318, 318, 392, 318, 323, 1080, 
+	1081, 1082, 393, 318, 318, 318, 394, 318, 
+	323, 1080, 1081, 1082, 318, 318, 318, 395, 
+	318, 323, 1080, 1081, 1082, 396, 397, 398, 
+	399, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 401, 402, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 404, 405, 409, 318, 318, 
+	318, 318, 323, 1080, 1081, 1082, 406, 407, 
+	408, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 411, 413, 415, 417, 412, 318, 318, 
+	318, 318, 323, 1080, 1081, 1082, 414, 318, 
+	318, 318, 318, 323, 1080, 1081, 1082, 416, 
+	318, 318, 318, 318, 323, 1080, 1081, 1082, 
+	418, 419, 420, 318, 318, 318, 318, 323, 
+	1080, 1081, 1082, 422, 425, 427, 423, 424, 
+	318, 318, 318, 318, 323, 1080, 1081, 1082, 
+	426, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 428, 429, 430, 318, 318, 318, 430, 
+	318, 323, 1080, 1081, 1082, 432, 433, 434, 
+	318, 318, 318, 318, 323, 1080, 1081, 1082, 
+	436, 318, 318, 318, 318, 323, 1080, 1081, 
+	1082, 0, 438, 438, 438, 438, 445, 0, 
+	439, 440, 440, 440, 439, 440, 444, 0, 
+	440, 440, 440, 441, 440, 444, 442, 442, 
+	442, 442, 442, 443, 1083, 1084, 1085, 447, 
+	447, 447, 447, 455, 448, 454, 1086, 1086, 
+	1086, 454, 1087, 1086, 1090, 449, 449, 449, 
+	450, 449, 453, 451, 451, 451, 451, 451, 
+	452, 1087, 1088, 1089, 449, 449, 449, 449, 
+	453, 0, 457, 458, 473, 503, 511, 524, 
+	1091, 457, 459, 460, 1091, 461, 1091, 462, 
+	463, 464, 465, 466, 467, 468, 469, 470, 
+	471, 472, 1091, 474, 475, 481, 476, 477, 
+	478, 479, 480, 1091, 482, 483, 484, 485, 
+	494, 486, 487, 488, 489, 490, 491, 492, 
+	493, 1091, 495, 496, 497, 498, 499, 500, 
+	501, 502, 1091, 504, 505, 506, 507, 508, 
+	509, 510, 1091, 512, 513, 514, 515, 516, 
+	517, 518, 521, 519, 520, 1091, 522, 523, 
+	1091, 525, 526, 527, 530, 528, 529, 1091, 
+	531, 532, 533, 545, 548, 1091, 534, 535, 
+	536, 537, 538, 539, 540, 541, 542, 543, 
+	544, 1091, 546, 547, 1091, 549, 550, 1091, 
+	0, 552, 553, 559, 576, 579, 585, 589, 
+	1092, 552, 554, 555, 556, 557, 558, 1092, 
+	560, 566, 572, 561, 562, 563, 564, 565, 
+	1092, 567, 569, 568, 1092, 570, 571, 1092, 
+	573, 574, 575, 1092, 577, 578, 1092, 580, 
+	582, 581, 1092, 583, 584, 1092, 586, 587, 
+	588, 1092, 590, 591, 1092, 0, 593, 1093, 
+	593, 595, 1094, 597, 598, 598, 598, 598, 
+	627, 598, 598, 598, 599, 598, 627, 600, 
+	600, 600, 600, 626, 0, 600, 600, 600, 
+	601, 600, 626, 602, 602, 602, 601, 602, 
+	625, 602, 602, 602, 603, 602, 625, 0, 
+	604, 604, 604, 603, 622, 622, 622, 622, 
+	622, 604, 621, 604, 604, 604, 605, 604, 
+	621, 606, 606, 606, 605, 618, 618, 618, 
+	618, 618, 606, 617, 606, 606, 606, 607, 
+	606, 617, 608, 608, 608, 607, 614, 614, 
+	614, 614, 614, 608, 613, 608, 608, 608, 
+	609, 608, 613, 1095, 609, 610, 610, 610, 
+	610, 610, 611, 611, 612, 612, 612, 612, 
+	612, 1095, 611, 615, 615, 616, 616, 616, 
+	616, 616, 608, 608, 608, 615, 608, 613, 
+	619, 619, 620, 620, 620, 620, 620, 606, 
+	606, 606, 619, 606, 617, 623, 623, 624, 
+	624, 624, 624, 624, 604, 604, 604, 623, 
+	604, 621, 629, 630, 630, 630, 630, 632, 
+	631, 630, 630, 630, 630, 632, 1096, 634, 
+	635, 635, 635, 635, 637, 635, 635, 635, 
+	636, 635, 637, 1097, 639, 640, 640, 640, 
+	639, 640, 642, 640, 640, 640, 641, 640, 
+	642, 1098, 644, 645, 645, 645, 645, 646, 
+	1099, 1100, 1101, 645, 645, 645, 645, 646, 
+	1099, 1100, 1101, 648, 1102, 648, 0, 650, 
+	651, 651, 651, 650, 651, 703, 651, 651, 
+	651, 652, 656, 698, 651, 703, 653, 653, 
+	653, 652, 653, 702, 653, 653, 653, 654, 
+	653, 702, 655, 655, 655, 700, 654, 655, 
+	699, 655, 655, 655, 655, 699, 657, 657, 
+	657, 657, 697, 658, 659, 659, 659, 658, 
+	659, 696, 659, 659, 659, 660, 664, 691, 
+	659, 696, 661, 661, 661, 660, 661, 695, 
+	661, 661, 661, 662, 661, 695, 663, 663, 
+	663, 693, 662, 663, 692, 663, 663, 663, 
+	663, 692, 665, 665, 665, 665, 690, 666, 
+	667, 667, 668, 668, 668, 687, 667, 689, 
+	668, 686, 1103, 1110, 1111, 668, 668, 668, 
+	669, 668, 686, 1103, 1110, 1111, 670, 670, 
+	670, 683, 669, 685, 670, 682, 1103, 1108, 
+	1109, 670, 670, 670, 671, 670, 682, 1108, 
+	1109, 672, 672, 672, 679, 671, 681, 672, 
+	678, 1103, 1106, 1107, 672, 672, 672, 673, 
+	672, 678, 1106, 1107, 674, 674, 674, 676, 
+	673, 674, 675, 1103, 1104, 1105, 674, 674, 
+	674, 674, 675, 1104, 1105, 674, 674, 674, 
+	677, 674, 675, 1103, 1104, 1105, 677, 672, 
+	672, 672, 680, 681, 672, 678, 1103, 1106, 
+	1107, 680, 670, 670, 670, 684, 685, 670, 
+	682, 1103, 1108, 1109, 684, 668, 668, 668, 
+	688, 689, 668, 686, 1103, 1110, 1111, 688, 
+	665, 665, 665, 665, 690, 663, 663, 663, 
+	694, 663, 692, 694, 657, 657, 657, 657, 
+	697, 655, 655, 655, 701, 655, 699, 701, 
+	705, 706, 706, 706, 705, 706, 714, 706, 
+	706, 706, 707, 706, 714, 708, 708, 708, 
+	707, 708, 713, 708, 708, 708, 709, 708, 
+	713, 710, 710, 710, 709, 710, 712, 710, 
+	710, 710, 711, 710, 712, 1112, 716, 717, 
+	717, 717, 716, 717, 731, 717, 717, 717, 
+	718, 717, 731, 719, 719, 719, 718, 719, 
+	730, 720, 719, 719, 719, 719, 730, 721, 
+	721, 721, 721, 729, 722, 721, 721, 721, 
+	721, 729, 723, 723, 723, 723, 728, 724, 
+	723, 723, 723, 723, 728, 725, 725, 725, 
+	725, 727, 725, 725, 725, 726, 725, 727, 
+	1113, 733, 734, 734, 734, 734, 742, 735, 
+	736, 736, 736, 735, 736, 741, 736, 736, 
+	736, 737, 736, 741, 738, 738, 738, 738, 
+	740, 739, 1114, 0, 743, 744, 743, 743, 
+	745, 751, 743, 750, 1115, 1116, 1117, 745, 
+	751, 746, 0, 747, 747, 748, 749, 743, 
+	743, 743, 749, 743, 750, 1115, 1116, 1117, 
+	752, 753, 753, 748, 755, 756, 756, 756, 
+	755, 756, 766, 756, 756, 756, 757, 756, 
+	766, 758, 758, 758, 758, 765, 759, 760, 
+	760, 760, 759, 760, 764, 0, 760, 760, 
+	760, 761, 760, 764, 762, 762, 762, 762, 
+	762, 763, 1118, 1119, 1120, 768, 769, 769, 
+	769, 768, 769, 776, 769, 769, 769, 770, 
+	769, 776, 771, 771, 771, 770, 771, 775, 
+	771, 771, 771, 772, 771, 775, 773, 773, 
+	773, 773, 773, 774, 1121, 1122, 1123, 778, 
+	779, 779, 779, 778, 779, 815, 0, 779, 
+	779, 779, 780, 794, 801, 808, 779, 815, 
+	781, 781, 781, 781, 793, 0, 782, 783, 
+	783, 783, 782, 783, 792, 783, 783, 783, 
+	784, 783, 792, 0, 785, 785, 785, 788, 
+	788, 788, 790, 790, 790, 1124, 785, 787, 
+	1127, 1128, 788, 789, 1125, 1126, 790, 791, 
+	1129, 1130, 0, 786, 795, 795, 795, 795, 
+	800, 796, 797, 797, 797, 796, 797, 799, 
+	0, 797, 797, 797, 798, 797, 799, 0, 
+	798, 785, 785, 785, 788, 788, 788, 790, 
+	790, 790, 1124, 785, 787, 1127, 1128, 788, 
+	789, 1125, 1126, 790, 791, 1129, 1130, 802, 
+	802, 802, 802, 807, 803, 804, 804, 804, 
+	803, 804, 806, 804, 804, 804, 805, 804, 
+	806, 805, 785, 785, 785, 788, 788, 788, 
+	790, 790, 790, 1124, 785, 787, 1127, 1128, 
+	788, 789, 1125, 1126, 790, 791, 1129, 1130, 
+	809, 809, 809, 809, 814, 810, 811, 811, 
+	811, 810, 811, 813, 811, 811, 811, 812, 
+	811, 813, 785, 785, 785, 788, 788, 788, 
+	790, 790, 790, 1124, 785, 787, 1127, 1128, 
+	788, 789, 1125, 1126, 790, 791, 1129, 1130, 
+	0, 817, 851, 859, 873, 880, 885, 893, 
+	897, 905, 911, 927, 930, 937, 948, 818, 
+	818, 818, 842, 845, 849, 818, 841, 818, 
+	818, 818, 819, 818, 841, 820, 820, 820, 
+	820, 840, 821, 822, 822, 822, 821, 822, 
+	839, 822, 822, 822, 823, 822, 839, 824, 
+	824, 824, 823, 824, 838, 0, 824, 824, 
+	824, 825, 824, 838, 826, 826, 826, 825, 
+	826, 837, 826, 826, 826, 827, 826, 837, 
+	828, 828, 828, 827, 828, 836, 828, 828, 
+	828, 829, 828, 836, 830, 830, 830, 829, 
+	830, 835, 830, 830, 830, 831, 830, 835, 
+	832, 832, 832, 832, 834, 832, 832, 832, 
+	833, 832, 834, 1131, 843, 844, 818, 818, 
+	818, 818, 841, 846, 847, 848, 818, 818, 
+	818, 818, 841, 850, 818, 818, 818, 818, 
+	841, 852, 855, 853, 854, 818, 818, 818, 
+	818, 841, 856, 857, 858, 818, 818, 818, 
+	818, 841, 860, 864, 872, 861, 862, 863, 
+	818, 818, 818, 818, 841, 865, 868, 866, 
+	867, 818, 818, 818, 818, 841, 869, 870, 
+	871, 818, 818, 818, 818, 841, 818, 818, 
+	818, 818, 841, 874, 875, 876, 878, 877, 
+	818, 818, 818, 818, 841, 879, 818, 818, 
+	818, 818, 841, 881, 882, 883, 884, 818, 
+	818, 818, 818, 841, 886, 887, 888, 889, 
+	890, 891, 892, 818, 818, 818, 818, 841, 
+	894, 896, 895, 818, 818, 818, 818, 841, 
+	818, 818, 818, 818, 841, 898, 900, 902, 
+	904, 899, 818, 818, 818, 818, 841, 901, 
+	818, 818, 818, 818, 841, 903, 818, 818, 
+	818, 818, 841, 818, 818, 818, 818, 841, 
+	906, 910, 907, 908, 909, 818, 818, 818, 
+	818, 841, 818, 818, 818, 818, 841, 912, 
+	916, 918, 913, 914, 915, 818, 818, 818, 
+	818, 841, 917, 818, 818, 818, 818, 841, 
+	818, 818, 818, 919, 818, 841, 920, 818, 
+	818, 818, 921, 818, 841, 818, 818, 818, 
+	922, 818, 841, 923, 924, 925, 926, 818, 
+	818, 818, 818, 841, 928, 929, 818, 818, 
+	818, 818, 841, 931, 932, 936, 818, 818, 
+	818, 818, 841, 933, 934, 935, 818, 818, 
+	818, 818, 841, 818, 818, 818, 818, 841, 
+	938, 940, 942, 944, 939, 818, 818, 818, 
+	818, 841, 941, 818, 818, 818, 818, 841, 
+	943, 818, 818, 818, 818, 841, 945, 946, 
+	947, 818, 818, 818, 818, 841, 949, 952, 
+	954, 950, 951, 818, 818, 818, 818, 841, 
+	953, 818, 818, 818, 818, 841, 955, 956, 
+	0, 957, 818, 818, 818, 957, 818, 841, 
+	959, 960, 1132, 962, 963, 963, 963, 962, 
+	963, 971, 963, 963, 963, 964, 963, 971, 
+	965, 965, 965, 964, 965, 970, 965, 965, 
+	965, 966, 965, 970, 967, 967, 967, 967, 
+	969, 968, 1133, 973, 1134, 975, 976, 976, 
+	976, 975, 976, 1002, 976, 976, 976, 977, 
+	976, 1002, 978, 978, 978, 977, 978, 1001, 
+	978, 978, 978, 979, 978, 1001, 980, 980, 
+	980, 979, 980, 1000, 980, 980, 980, 981, 
+	998, 980, 1000, 982, 982, 982, 982, 997, 
+	0, 982, 982, 982, 983, 982, 997, 984, 
+	985, 995, 986, 987, 994, 988, 992, 989, 
+	990, 990, 991, 983, 1135, 993, 996, 999, 
+	998, 1004, 1005, 1005, 1005, 1004, 1005, 1015, 
+	1005, 1005, 1005, 1006, 1005, 1015, 1007, 1007, 
+	1007, 1006, 1007, 1014, 1007, 1007, 1007, 1008, 
+	1007, 1014, 1009, 1009, 1009, 1008, 1009, 1013, 
+	1009, 1009, 1009, 1010, 1011, 1009, 1013, 1136, 
+	1012, 1011, 1017, 1018, 1018, 1018, 1017, 1018, 
+	1028, 1018, 1018, 1018, 1019, 1018, 1028, 1020, 
+	1020, 1020, 1019, 1020, 1027, 1020, 1020, 1020, 
+	1021, 1020, 1027, 1022, 1022, 1022, 1021, 1022, 
+	1026, 1022, 1022, 1022, 1023, 1022, 1026, 1024, 
+	1024, 1024, 1024, 1024, 1025, 1137, 1138, 1139, 
+	1030, 1031, 1031, 1031, 1030, 1031, 1033, 1031, 
+	1031, 1031, 1032, 1031, 1033, 1140, 1032, 1035, 
+	1036, 1036, 1036, 1035, 1036, 1046, 1036, 1036, 
+	1036, 1037, 1036, 1046, 1038, 1039, 1040, 0, 
+	1041, 1042, 1043, 1044, 1045, 0, 1141, 1048, 
+	1049, 0, 1050, 1051, 1052, 1142, 1054, 1055, 
+	1056, 1057, 1058, 1143, 0, 1, 138, 1, 
+	1, 146, 1064, 242, 13, 179, 13, 13, 
+	189, 208, 1067, 222, 1068, 225, 0, 128, 
+	128, 128, 156, 229, 230, 231, 232, 244, 
+	234, 235, 236, 237, 238, 239, 240, 241, 
+	1062, 243, 0, 149, 156, 229, 230, 231, 
+	232, 233, 234, 235, 236, 237, 238, 239, 
+	240, 241, 0, 1, 138, 1, 1, 146, 
+	1059, 137, 1064, 242, 1, 1, 1064, 160, 
+	160, 160, 1064, 161, 164, 164, 164, 178, 
+	212, 0, 190, 193, 195, 196, 197, 198, 
+	199, 200, 201, 202, 203, 204, 205, 206, 
+	207, 1064
 };
 
 static const short _zone_scanner_trans_actions[] = {
-	1872, 0, 5, 7, 506, 659, 659, 659, 
-	659, 659, 659, 659, 659, 659, 659, 659, 
-	659, 659, 659, 3, 0, 3, 0, 2012, 
-	101, 674, 677, 41, 55, 53, 51, 0, 
-	57, 671, 101, 345, 0, 5, 7, 95, 
+	1806, 0, 5, 7, 492, 642, 642, 642, 
+	642, 642, 642, 642, 642, 642, 642, 642, 
+	642, 642, 642, 3, 0, 3, 0, 1934, 
+	101, 657, 660, 41, 55, 53, 51, 0, 
+	57, 654, 101, 343, 0, 5, 7, 95, 
 	95, 95, 95, 95, 95, 95, 95, 95, 
-	95, 95, 95, 95, 95, 3, 0, 1592, 
-	1595, 2384, 2388, 0, 0, 0, 2380, 1595, 
-	2392, 3481, 2392, 339, 0, 337, 5, 7, 
-	0, 3, 0, 339, 356, 339, 1571, 343, 
-	1580, 1583, 2372, 343, 9, 0, 5, 7, 
-	1709, 0, 1586, 341, 0, 5, 7, 0, 
-	2368, 339, 339, 359, 0, 3086, 5, 7, 
-	1860, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 
-	2096, 2096, 2096, 2096, 2096, 2096, 2096, 3, 
-	0, 1574, 339, 356, 339, 3782, 680, 2100, 
-	2104, 3256, 680, 1589, 0, 5, 7, 359, 
-	0, 0, 0, 0, 0, 1649, 2676, 2680, 
-	2672, 1649, 2684, 3586, 2684, 0, 5, 7, 
-	0, 0, 0, 1601, 2420, 2424, 2416, 1601, 
-	2428, 3506, 2428, 0, 0, 0, 0, 0, 
-	0, 1676, 2820, 2824, 2816, 1676, 2828, 3631, 
-	2828, 0, 0, 0, 0, 1652, 2692, 2696, 
-	2688, 1652, 2700, 3591, 2700, 0, 0, 0, 
-	1673, 2804, 2808, 2800, 1673, 2812, 3626, 2812, 
-	1658, 2724, 2728, 2720, 1658, 2732, 3601, 2732, 
-	0, 0, 0, 0, 0, 1703, 2964, 2968, 
-	2960, 1703, 2972, 3676, 2972, 0, 1706, 2980, 
-	2984, 2976, 1706, 2988, 3681, 2988, 0, 0, 
-	0, 0, 1610, 2468, 2472, 2464, 1610, 2476, 
-	3521, 2476, 0, 0, 99, 665, 668, 662, 
-	99, 0, 5, 7, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 3, 0, 0, 0, 0, 0, 
-	0, 0, 1664, 2756, 2760, 2752, 1664, 2764, 
-	3611, 2764, 0, 0, 0, 1631, 2580, 2584, 
-	2576, 1631, 2588, 3556, 2588, 1646, 2660, 2664, 
-	2656, 1646, 2668, 3581, 2668, 0, 0, 0, 
-	0, 0, 1694, 2916, 2920, 2912, 1694, 2924, 
-	3661, 2924, 0, 1697, 2932, 2936, 2928, 1697, 
-	2940, 3666, 2940, 0, 1637, 2612, 2616, 2608, 
-	1637, 2620, 3566, 2620, 1700, 2948, 2952, 2944, 
-	1700, 2956, 3671, 2956, 0, 0, 0, 0, 
-	0, 1613, 2484, 2488, 2480, 1613, 2492, 3526, 
-	2492, 1616, 2500, 2504, 2496, 1616, 2508, 3531, 
-	2508, 0, 0, 0, 0, 0, 0, 1643, 
-	2644, 2648, 2640, 1643, 2652, 3576, 2652, 0, 
-	1691, 2900, 2904, 2896, 1691, 2908, 3656, 2908, 
-	1598, 2404, 2408, 0, 2400, 1598, 2412, 3501, 
-	2412, 0, 1670, 2788, 2792, 0, 2784, 1670, 
-	2796, 3621, 2796, 1679, 2836, 2840, 0, 2832, 
-	1679, 2844, 3636, 2844, 0, 0, 0, 0, 
-	1682, 2852, 2856, 2848, 1682, 2860, 3641, 2860, 
-	0, 0, 1607, 2452, 2456, 2448, 1607, 2460, 
-	3516, 2460, 0, 0, 0, 1622, 2532, 2536, 
-	2528, 1622, 2540, 3541, 2540, 0, 0, 0, 
-	1667, 2772, 2776, 2768, 1667, 2780, 3616, 2780, 
-	1628, 2564, 2568, 2560, 1628, 2572, 3551, 2572, 
-	0, 0, 0, 0, 0, 1604, 2436, 2440, 
-	2432, 1604, 2444, 3511, 2444, 0, 1688, 2884, 
-	2888, 2880, 1688, 2892, 3651, 2892, 0, 1640, 
-	2628, 2632, 2624, 1640, 2636, 3571, 2636, 0, 
-	0, 0, 1661, 2740, 2744, 2736, 1661, 2748, 
-	3606, 2748, 0, 0, 0, 0, 0, 1685, 
-	2868, 2872, 2864, 1685, 2876, 3646, 2876, 0, 
-	1619, 2516, 2520, 2512, 1619, 2524, 3536, 2524, 
-	0, 0, 518, 506, 1876, 578, 1992, 1996, 
-	41, 1988, 578, 2000, 3216, 2000, 0, 5, 
-	7, 3, 0, 0, 99, 665, 668, 662, 
-	99, 0, 5, 7, 506, 97, 97, 97, 
+	95, 95, 95, 95, 95, 3, 0, 1566, 
+	1569, 2298, 2302, 0, 0, 0, 2294, 1569, 
+	2306, 3371, 2306, 339, 0, 337, 5, 7, 
+	0, 3, 0, 354, 339, 1551, 0, 5, 
+	7, 1683, 0, 9, 1560, 341, 0, 5, 
+	7, 0, 2286, 339, 339, 357, 0, 2981, 
+	5, 7, 1794, 2018, 2018, 2018, 2018, 2018, 
+	2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 
+	2018, 3, 0, 1554, 339, 354, 339, 3644, 
+	101, 657, 660, 2022, 101, 1563, 0, 5, 
+	7, 357, 0, 0, 0, 0, 0, 1623, 
+	2586, 2590, 2582, 1623, 2594, 3466, 2594, 0, 
+	5, 7, 0, 0, 0, 1575, 2330, 2334, 
+	2326, 1575, 2338, 3386, 2338, 0, 0, 0, 
+	0, 0, 0, 1650, 2730, 2734, 2726, 1650, 
+	2738, 3511, 2738, 0, 0, 0, 0, 1626, 
+	2602, 2606, 2598, 1626, 2610, 3471, 2610, 0, 
+	0, 0, 1647, 2714, 2718, 2710, 1647, 2722, 
+	3506, 2722, 1632, 2634, 2638, 2630, 1632, 2642, 
+	3481, 2642, 0, 0, 0, 0, 0, 1677, 
+	2874, 2878, 2870, 1677, 2882, 3556, 2882, 0, 
+	1680, 2890, 2894, 2886, 1680, 2898, 3561, 2898, 
+	0, 0, 0, 0, 1584, 2378, 2382, 2374, 
+	1584, 2386, 3401, 2386, 0, 0, 99, 648, 
+	651, 645, 99, 0, 5, 7, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 3, 0, 0, 0, 
+	0, 0, 0, 0, 1638, 2666, 2670, 2662, 
+	1638, 2674, 3491, 2674, 0, 0, 0, 1605, 
+	2490, 2494, 2486, 1605, 2498, 3436, 2498, 1620, 
+	2570, 2574, 2566, 1620, 2578, 3461, 2578, 0, 
+	0, 0, 0, 0, 1668, 2826, 2830, 2822, 
+	1668, 2834, 3541, 2834, 0, 1671, 2842, 2846, 
+	2838, 1671, 2850, 3546, 2850, 0, 1611, 2522, 
+	2526, 2518, 1611, 2530, 3446, 2530, 1674, 2858, 
+	2862, 2854, 1674, 2866, 3551, 2866, 0, 0, 
+	0, 0, 0, 1587, 2394, 2398, 2390, 1587, 
+	2402, 3406, 2402, 1590, 2410, 2414, 2406, 1590, 
+	2418, 3411, 2418, 0, 0, 0, 0, 0, 
+	0, 1617, 2554, 2558, 2550, 1617, 2562, 3456, 
+	2562, 0, 1665, 2810, 2814, 2806, 1665, 2818, 
+	3536, 2818, 1572, 2314, 2318, 0, 2310, 1572, 
+	2322, 3381, 2322, 0, 1644, 2698, 2702, 0, 
+	2694, 1644, 2706, 3501, 2706, 1653, 2746, 2750, 
+	0, 2742, 1653, 2754, 3516, 2754, 0, 0, 
+	0, 0, 1656, 2762, 2766, 2758, 1656, 2770, 
+	3521, 2770, 0, 0, 1581, 2362, 2366, 2358, 
+	1581, 2370, 3396, 2370, 0, 0, 0, 1596, 
+	2442, 2446, 2438, 1596, 2450, 3421, 2450, 0, 
+	0, 0, 1641, 2682, 2686, 2678, 1641, 2690, 
+	3496, 2690, 1602, 2474, 2478, 2470, 1602, 2482, 
+	3431, 2482, 0, 0, 0, 0, 0, 1578, 
+	2346, 2350, 2342, 1578, 2354, 3391, 2354, 0, 
+	1662, 2794, 2798, 2790, 1662, 2802, 3531, 2802, 
+	0, 1614, 2538, 2542, 2534, 1614, 2546, 3451, 
+	2546, 0, 0, 0, 1635, 2650, 2654, 2646, 
+	1635, 2658, 3486, 2658, 0, 0, 0, 0, 
+	0, 1659, 2778, 2782, 2774, 1659, 2786, 3526, 
+	2786, 0, 1593, 2426, 2430, 2422, 1593, 2434, 
+	3416, 2434, 0, 0, 504, 492, 1810, 561, 
+	1914, 1918, 41, 1910, 561, 1922, 3111, 1922, 
+	0, 5, 7, 3, 0, 0, 99, 648, 
+	651, 645, 99, 0, 5, 7, 492, 97, 
 	97, 97, 97, 97, 97, 97, 97, 97, 
-	97, 97, 97, 3, 0, 101, 674, 677, 
-	41, 55, 53, 51, 0, 57, 671, 101, 
-	2016, 41, 55, 53, 51, 0, 57, 599, 
-	2040, 2044, 3226, 2036, 599, 93, 89, 89, 
-	89, 0, 0, 0, 0, 0, 0, 87, 
-	37, 33, 455, 458, 452, 33, 0, 5, 
-	7, 3, 0, 0, 3071, 461, 1796, 1804, 
-	1792, 461, 0, 5, 7, 3, 0, 2016, 
-	41, 55, 53, 51, 0, 57, 599, 2040, 
-	2044, 3226, 2036, 599, 1836, 1820, 3036, 3051, 
-	3021, 1820, 3698, 3056, 0, 5, 7, 3, 
-	0, 356, 339, 2376, 2396, 3486, 3491, 3800, 
-	2396, 3855, 3496, 3848, 3496, 0, 5, 7, 
-	1744, 0, 5, 7, 359, 0, 0, 5, 
-	7, 0, 5, 7, 0, 339, 359, 2360, 
-	0, 0, 0, 0, 0, 79, 0, 0, 
-	75, 1828, 464, 1800, 1808, 3011, 464, 3813, 
-	1812, 3026, 3041, 3692, 1812, 0, 5, 7, 
-	359, 0, 3076, 3061, 3704, 3710, 3806, 3061, 
-	3862, 3716, 0, 5, 7, 1744, 1816, 479, 
-	3031, 3046, 3016, 1816, 343, 365, 371, 1748, 
-	343, 343, 1580, 1583, 0, 5, 7, 0, 
-	5, 7, 0, 0, 1634, 2596, 2600, 2592, 
-	1634, 2604, 3561, 2604, 0, 0, 0, 1625, 
-	2548, 2552, 2544, 1625, 2556, 3546, 2556, 0, 
-	1655, 2708, 2712, 2704, 1655, 2716, 3596, 2716, 
-	0, 5, 7, 0, 5, 7, 0, 5, 
-	7, 0, 5, 7, 482, 380, 2996, 11, 
-	1756, 27, 1772, 1772, 25, 0, 419, 1760, 
-	17, 404, 383, 383, 13, 15, 0, 386, 
-	395, 19, 19, 3001, 389, 392, 21, 416, 
-	620, 65, 0, 0, 1, 617, 623, 2052, 
-	69, 69, 626, 629, 2048, 71, 65, 0, 
-	0, 67, 623, 69, 69, 629, 71, 71, 
-	73, 0, 5, 7, 3, 0, 509, 506, 
-	3221, 0, 5, 7, 41, 55, 53, 51, 
-	0, 57, 2056, 0, 374, 2016, 2004, 41, 
-	55, 53, 51, 0, 57, 59, 590, 593, 
-	3226, 3231, 59, 77, 0, 5, 7, 3, 
-	0, 3236, 3236, 632, 2060, 17, 377, 23, 
-	407, 410, 383, 383, 13, 3006, 23, 0, 
-	5, 7, 2064, 0, 15, 0, 386, 398, 
-	19, 19, 389, 392, 21, 0, 5, 7, 
-	3, 0, 635, 83, 644, 81, 638, 641, 
-	3241, 81, 81, 650, 0, 5, 7, 3246, 
-	3246, 647, 2068, 2072, 0, 0, 85, 17, 
-	413, 1764, 1768, 383, 383, 13, 3686, 413, 
-	0, 5, 7, 15, 0, 386, 401, 19, 
-	19, 389, 392, 21, 1724, 0, 5, 7, 
-	3, 0, 125, 117, 119, 121, 0, 123, 
-	0, 5, 7, 3, 0, 1, 350, 1, 
-	309, 307, 1496, 1499, 1493, 307, 2332, 3476, 
-	2332, 0, 5, 7, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 3, 0, 1502, 1732, 1502, 231, 
-	1154, 1157, 0, 0, 0, 1151, 231, 2180, 
-	3286, 2180, 0, 0, 257, 1271, 1274, 1268, 
-	257, 2232, 3351, 2232, 0, 0, 0, 0, 
-	267, 1316, 1319, 1313, 267, 2252, 3376, 2252, 
-	0, 0, 0, 235, 1172, 1175, 1169, 235, 
-	2188, 3296, 2188, 0, 0, 0, 0, 0, 
-	0, 285, 1397, 1400, 1394, 285, 2288, 3421, 
-	2288, 0, 0, 0, 0, 269, 1325, 1328, 
-	1322, 269, 2256, 3381, 2256, 0, 0, 0, 
-	283, 1388, 1391, 1385, 283, 2284, 3416, 2284, 
-	273, 1343, 1346, 1340, 273, 2264, 3391, 2264, 
-	0, 0, 0, 0, 0, 303, 1478, 1481, 
-	1475, 303, 2324, 3466, 2324, 0, 305, 1487, 
-	1490, 1484, 305, 2328, 3471, 2328, 0, 0, 
-	0, 0, 241, 1199, 1202, 1196, 241, 2200, 
-	3311, 2200, 0, 0, 0, 0, 0, 0, 
-	0, 277, 1361, 1364, 1358, 277, 2272, 3401, 
-	2272, 0, 0, 0, 255, 1262, 1265, 1259, 
-	255, 2228, 3346, 2228, 265, 1307, 1310, 1304, 
-	265, 2248, 3371, 2248, 0, 0, 0, 0, 
-	0, 297, 1451, 1454, 1448, 297, 2312, 3451, 
-	2312, 0, 299, 1460, 1463, 1457, 299, 2316, 
-	3456, 2316, 0, 259, 1280, 1283, 1277, 259, 
-	2236, 3356, 2236, 301, 1469, 1472, 1466, 301, 
-	2320, 3461, 2320, 0, 0, 0, 0, 0, 
-	243, 1208, 1211, 1205, 243, 2204, 3316, 2204, 
-	245, 1217, 1220, 1214, 245, 2208, 3321, 2208, 
-	0, 0, 0, 0, 0, 0, 263, 1298, 
-	1301, 1295, 263, 2244, 3366, 2244, 0, 295, 
-	1442, 1445, 1439, 295, 2308, 3446, 2308, 233, 
-	1163, 1166, 0, 1160, 233, 2184, 3291, 2184, 
-	0, 281, 1379, 1382, 0, 1376, 281, 2280, 
-	3411, 2280, 287, 1406, 1409, 0, 1403, 287, 
-	2292, 3426, 2292, 0, 0, 0, 0, 289, 
-	1415, 1418, 1412, 289, 2296, 3431, 2296, 0, 
-	0, 239, 1190, 1193, 1187, 239, 2196, 3306, 
-	2196, 0, 0, 0, 249, 1235, 1238, 1232, 
-	249, 2216, 3331, 2216, 0, 0, 0, 279, 
-	1370, 1373, 1367, 279, 2276, 3406, 2276, 253, 
-	1253, 1256, 1250, 253, 2224, 3341, 2224, 0, 
-	0, 0, 0, 0, 237, 1181, 1184, 1178, 
-	237, 2192, 3301, 2192, 0, 293, 1433, 1436, 
-	1430, 293, 2304, 3441, 2304, 0, 261, 1289, 
-	1292, 1286, 261, 2240, 3361, 2240, 0, 0, 
-	0, 275, 1352, 1355, 1349, 275, 2268, 3396, 
-	2268, 0, 0, 0, 0, 0, 291, 1424, 
-	1427, 1421, 291, 2300, 3436, 2300, 0, 247, 
-	1226, 1229, 1223, 247, 2212, 3326, 2212, 0, 
-	0, 506, 229, 1145, 1148, 41, 1142, 229, 
-	2176, 3281, 2176, 0, 0, 0, 251, 1244, 
-	1247, 1241, 251, 2220, 3336, 2220, 0, 271, 
-	1334, 1337, 1331, 271, 2260, 3386, 2260, 323, 
-	0, 5, 7, 3, 0, 512, 506, 49, 
-	584, 587, 41, 581, 49, 2164, 0, 5, 
+	97, 97, 97, 97, 97, 3, 0, 101, 
+	657, 660, 41, 55, 53, 51, 0, 57, 
+	654, 101, 1938, 41, 55, 53, 51, 0, 
+	57, 582, 1962, 1966, 3121, 1958, 582, 93, 
+	89, 89, 89, 0, 0, 0, 0, 0, 
+	0, 87, 37, 33, 444, 447, 441, 33, 
+	0, 5, 7, 3, 0, 0, 2966, 450, 
+	1758, 1762, 1750, 450, 0, 5, 7, 3, 
+	0, 1938, 41, 55, 53, 51, 0, 57, 
+	582, 1962, 1966, 3121, 1958, 582, 1786, 1770, 
+	2941, 2951, 2931, 1770, 3572, 2956, 0, 5, 
+	7, 3, 0, 354, 339, 2290, 1569, 2298, 
+	2302, 3376, 1569, 3668, 2306, 3662, 2306, 0, 
+	5, 7, 1718, 0, 5, 7, 357, 0, 
+	0, 5, 7, 0, 5, 7, 0, 339, 
+	357, 2278, 0, 0, 0, 0, 0, 79, 
+	0, 0, 75, 1778, 33, 444, 447, 1754, 
+	33, 3681, 450, 1758, 1762, 2921, 450, 0, 
+	5, 7, 357, 0, 2971, 1770, 2941, 2951, 
+	3578, 1770, 3674, 2956, 0, 5, 7, 1718, 
+	1766, 465, 2936, 2946, 2926, 1766, 0, 5, 
+	7, 0, 5, 7, 0, 0, 1608, 2506, 
+	2510, 2502, 1608, 2514, 3441, 2514, 0, 0, 
+	0, 1599, 2458, 2462, 2454, 1599, 2466, 3426, 
+	2466, 0, 1629, 2618, 2622, 2614, 1629, 2626, 
+	3476, 2626, 0, 5, 7, 0, 5, 7, 
+	0, 5, 7, 0, 5, 7, 468, 372, 
+	2906, 11, 1726, 27, 1742, 1742, 25, 0, 
+	411, 1730, 17, 396, 375, 375, 13, 15, 
+	0, 378, 387, 19, 19, 2911, 381, 384, 
+	21, 408, 603, 65, 0, 0, 1, 600, 
+	606, 1974, 69, 69, 609, 612, 1970, 71, 
+	65, 0, 0, 67, 606, 69, 69, 612, 
+	71, 71, 73, 0, 5, 7, 3, 0, 
+	495, 492, 3116, 0, 5, 7, 41, 55, 
+	53, 51, 0, 57, 1978, 0, 366, 1938, 
+	1926, 41, 55, 53, 51, 0, 57, 59, 
+	573, 576, 3121, 3126, 59, 77, 0, 5, 
+	7, 3, 0, 3131, 3131, 615, 1982, 17, 
+	369, 23, 399, 402, 375, 375, 13, 2916, 
+	23, 0, 5, 7, 1986, 0, 15, 0, 
+	378, 390, 19, 19, 381, 384, 21, 0, 
+	5, 7, 3, 0, 618, 83, 627, 81, 
+	621, 624, 3136, 81, 81, 633, 0, 5, 
+	7, 3141, 3141, 630, 1990, 1994, 0, 0, 
+	85, 17, 405, 1734, 1738, 375, 375, 13, 
+	3566, 405, 0, 5, 7, 15, 0, 378, 
+	393, 19, 19, 381, 384, 21, 1698, 0, 
+	5, 7, 3, 0, 125, 117, 119, 121, 
+	0, 123, 0, 5, 7, 3, 0, 1, 
+	348, 1, 309, 307, 1476, 1479, 1473, 307, 
+	2250, 3366, 2250, 0, 5, 7, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 3, 0, 1482, 1706, 
+	1482, 231, 1134, 1137, 0, 0, 0, 1131, 
+	231, 2098, 3176, 2098, 0, 0, 257, 1251, 
+	1254, 1248, 257, 2150, 3241, 2150, 0, 0, 
+	0, 0, 267, 1296, 1299, 1293, 267, 2170, 
+	3266, 2170, 0, 0, 0, 235, 1152, 1155, 
+	1149, 235, 2106, 3186, 2106, 0, 0, 0, 
+	0, 0, 0, 285, 1377, 1380, 1374, 285, 
+	2206, 3311, 2206, 0, 0, 0, 0, 269, 
+	1305, 1308, 1302, 269, 2174, 3271, 2174, 0, 
+	0, 0, 283, 1368, 1371, 1365, 283, 2202, 
+	3306, 2202, 273, 1323, 1326, 1320, 273, 2182, 
+	3281, 2182, 0, 0, 0, 0, 0, 303, 
+	1458, 1461, 1455, 303, 2242, 3356, 2242, 0, 
+	305, 1467, 1470, 1464, 305, 2246, 3361, 2246, 
+	0, 0, 0, 0, 241, 1179, 1182, 1176, 
+	241, 2118, 3201, 2118, 0, 0, 0, 0, 
+	0, 0, 0, 277, 1341, 1344, 1338, 277, 
+	2190, 3291, 2190, 0, 0, 0, 255, 1242, 
+	1245, 1239, 255, 2146, 3236, 2146, 265, 1287, 
+	1290, 1284, 265, 2166, 3261, 2166, 0, 0, 
+	0, 0, 0, 297, 1431, 1434, 1428, 297, 
+	2230, 3341, 2230, 0, 299, 1440, 1443, 1437, 
+	299, 2234, 3346, 2234, 0, 259, 1260, 1263, 
+	1257, 259, 2154, 3246, 2154, 301, 1449, 1452, 
+	1446, 301, 2238, 3351, 2238, 0, 0, 0, 
+	0, 0, 243, 1188, 1191, 1185, 243, 2122, 
+	3206, 2122, 245, 1197, 1200, 1194, 245, 2126, 
+	3211, 2126, 0, 0, 0, 0, 0, 0, 
+	263, 1278, 1281, 1275, 263, 2162, 3256, 2162, 
+	0, 295, 1422, 1425, 1419, 295, 2226, 3336, 
+	2226, 233, 1143, 1146, 0, 1140, 233, 2102, 
+	3181, 2102, 0, 281, 1359, 1362, 0, 1356, 
+	281, 2198, 3301, 2198, 287, 1386, 1389, 0, 
+	1383, 287, 2210, 3316, 2210, 0, 0, 0, 
+	0, 289, 1395, 1398, 1392, 289, 2214, 3321, 
+	2214, 0, 0, 239, 1170, 1173, 1167, 239, 
+	2114, 3196, 2114, 0, 0, 0, 249, 1215, 
+	1218, 1212, 249, 2134, 3221, 2134, 0, 0, 
+	0, 279, 1350, 1353, 1347, 279, 2194, 3296, 
+	2194, 253, 1233, 1236, 1230, 253, 2142, 3231, 
+	2142, 0, 0, 0, 0, 0, 237, 1161, 
+	1164, 1158, 237, 2110, 3191, 2110, 0, 293, 
+	1413, 1416, 1410, 293, 2222, 3331, 2222, 0, 
+	261, 1269, 1272, 1266, 261, 2158, 3251, 2158, 
+	0, 0, 0, 275, 1332, 1335, 1329, 275, 
+	2186, 3286, 2186, 0, 0, 0, 0, 0, 
+	291, 1404, 1407, 1401, 291, 2218, 3326, 2218, 
+	0, 247, 1206, 1209, 1203, 247, 2130, 3216, 
+	2130, 0, 0, 492, 229, 1125, 1128, 41, 
+	1122, 229, 2094, 3171, 2094, 0, 0, 0, 
+	251, 1224, 1227, 1221, 251, 2138, 3226, 2138, 
+	0, 271, 1314, 1317, 1311, 271, 2178, 3276, 
+	2178, 323, 0, 5, 7, 3, 0, 498, 
+	492, 49, 567, 570, 41, 564, 49, 2082, 
+	0, 5, 7, 113, 3, 0, 115, 0, 
+	5, 7, 3, 0, 693, 1702, 693, 0, 
+	5, 7, 3, 0, 492, 492, 345, 1690, 
+	1694, 41, 1, 1686, 345, 0, 5, 7, 
+	113, 3, 0, 115, 0, 5, 7, 3, 
+	0, 693, 1702, 693, 49, 567, 570, 564, 
+	49, 329, 492, 0, 0, 0, 0, 0, 
+	525, 41, 0, 0, 702, 0, 705, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 711, 0, 0, 0, 0, 0, 
+	0, 0, 0, 723, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 726, 0, 0, 0, 0, 0, 0, 
+	0, 0, 729, 0, 0, 0, 0, 0, 
+	0, 0, 732, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 735, 0, 0, 
+	738, 0, 0, 0, 0, 0, 0, 699, 
+	0, 0, 0, 0, 0, 708, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 714, 0, 0, 717, 0, 0, 720, 
+	331, 492, 0, 0, 0, 0, 0, 0, 
+	537, 41, 0, 0, 0, 0, 0, 759, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	762, 0, 0, 0, 756, 0, 0, 750, 
+	0, 0, 0, 753, 0, 0, 768, 0, 
+	0, 0, 747, 0, 0, 741, 0, 0, 
+	0, 744, 0, 0, 765, 666, 663, 2034, 
+	103, 471, 474, 471, 39, 480, 483, 477, 
+	39, 0, 5, 7, 471, 3, 0, 39, 
+	480, 483, 477, 39, 501, 0, 5, 7, 
+	492, 3, 0, 47, 555, 558, 41, 552, 
+	47, 0, 5, 7, 492, 3, 0, 1930, 
+	47, 555, 558, 41, 55, 53, 51, 0, 
+	57, 552, 47, 0, 5, 7, 492, 3, 
+	0, 47, 555, 558, 41, 55, 53, 51, 
+	0, 57, 552, 47, 0, 5, 7, 492, 
+	3, 0, 47, 555, 558, 41, 55, 53, 
+	51, 0, 57, 552, 47, 0, 5, 7, 
+	492, 3, 0, 549, 41, 55, 53, 51, 
+	0, 57, 1938, 41, 55, 53, 51, 0, 
+	57, 1942, 3121, 1938, 41, 55, 53, 51, 
+	0, 57, 579, 1950, 1954, 3121, 1946, 579, 
+	1938, 41, 55, 53, 51, 0, 57, 579, 
+	1950, 1954, 3121, 1946, 579, 1938, 41, 55, 
+	53, 51, 0, 57, 579, 1950, 1954, 3121, 
+	1946, 579, 414, 31, 429, 432, 426, 31, 
+	414, 0, 5, 7, 3, 0, 423, 471, 
+	39, 480, 483, 477, 39, 0, 5, 7, 
+	471, 3, 0, 474, 492, 45, 543, 546, 
+	41, 540, 45, 0, 5, 7, 471, 3, 
+	0, 474, 414, 31, 429, 432, 426, 31, 
+	423, 1746, 423, 0, 5, 7, 3, 0, 
+	1, 348, 1, 663, 2050, 103, 1542, 2254, 
+	311, 1488, 1491, 41, 1485, 311, 0, 5, 
+	7, 492, 0, 0, 3, 0, 315, 1506, 
+	1509, 41, 1503, 315, 0, 5, 7, 3106, 
+	3, 0, 507, 1842, 1846, 0, 41, 1838, 
+	507, 0, 5, 7, 3, 0, 0, 5, 
+	7, 3, 0, 492, 313, 1497, 1500, 41, 
+	1494, 313, 0, 5, 7, 492, 0, 0, 
+	3, 0, 317, 1515, 1518, 41, 1512, 317, 
+	0, 5, 7, 3106, 3, 0, 510, 1854, 
+	1858, 0, 41, 1850, 510, 0, 5, 7, 
+	3, 0, 0, 5, 7, 3, 0, 0, 
+	3101, 3656, 513, 1866, 1870, 0, 41, 513, 
+	1862, 513, 3081, 3620, 3081, 0, 5, 7, 
+	3101, 3, 0, 1539, 1710, 1539, 516, 1878, 
+	1882, 0, 41, 516, 1874, 516, 3086, 3626, 
+	3086, 0, 5, 7, 3101, 3, 0, 1710, 
+	1539, 519, 1890, 1894, 0, 41, 519, 1886, 
+	519, 3091, 3632, 3091, 0, 5, 7, 3101, 
+	3, 0, 1710, 1539, 522, 1902, 1906, 0, 
+	41, 1898, 522, 3096, 3638, 3096, 0, 5, 
+	7, 3, 0, 1710, 1539, 1834, 3071, 3076, 
+	2986, 3066, 1834, 3614, 3709, 3614, 489, 1830, 
+	3056, 3061, 2986, 1830, 3051, 1830, 3608, 3702, 
+	3608, 489, 1826, 3041, 3046, 2986, 1826, 3036, 
+	1826, 3602, 3695, 3602, 489, 1822, 3026, 3031, 
+	2986, 1822, 3021, 1822, 3596, 3688, 3596, 489, 
+	321, 1533, 1536, 1530, 321, 1818, 3011, 3016, 
+	2986, 3006, 1818, 489, 319, 1524, 1527, 1521, 
+	319, 1814, 2996, 3001, 2986, 2991, 1814, 489, 
+	492, 45, 543, 546, 41, 540, 45, 0, 
+	5, 7, 492, 3, 0, 45, 543, 546, 
+	41, 540, 45, 0, 5, 7, 492, 3, 
+	0, 45, 543, 546, 41, 540, 45, 0, 
+	5, 7, 471, 3, 0, 474, 492, 45, 
+	543, 546, 41, 540, 45, 0, 5, 7, 
+	492, 3, 0, 45, 543, 546, 41, 540, 
+	45, 414, 0, 5, 7, 3, 0, 31, 
+	429, 432, 426, 31, 414, 0, 5, 7, 
+	3, 0, 31, 429, 432, 426, 31, 414, 
+	0, 5, 7, 3, 0, 31, 429, 432, 
+	426, 31, 0, 5, 7, 471, 3, 0, 
+	474, 335, 0, 5, 7, 3, 0, 492, 
+	45, 543, 546, 41, 540, 45, 0, 5, 
+	7, 333, 3, 0, 0, 5, 7, 3, 
+	0, 127, 1, 687, 0, 675, 5, 7, 
+	678, 681, 3, 0, 1, 348, 1, 109, 
+	111, 0, 2026, 663, 103, 105, 492, 684, 
+	2074, 2078, 41, 2070, 684, 2066, 3166, 2066, 
+	0, 663, 103, 107, 492, 45, 543, 546, 
+	41, 540, 45, 0, 5, 7, 333, 3, 
+	0, 0, 5, 7, 3, 0, 492, 43, 
+	531, 534, 41, 528, 43, 690, 0, 5, 
 	7, 113, 3, 0, 115, 0, 5, 7, 
-	3, 0, 713, 1728, 713, 0, 5, 7, 
-	3, 0, 506, 506, 347, 1716, 1720, 41, 
-	1, 1712, 347, 0, 5, 7, 113, 3, 
-	0, 115, 0, 5, 7, 3, 0, 713, 
-	1728, 713, 49, 584, 587, 581, 49, 329, 
-	506, 0, 0, 0, 0, 0, 539, 41, 
-	0, 0, 722, 0, 725, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	731, 0, 0, 0, 0, 0, 0, 0, 
-	0, 743, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 746, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	749, 0, 0, 0, 0, 0, 0, 0, 
-	752, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 755, 0, 0, 758, 0, 
-	0, 0, 0, 0, 0, 719, 0, 0, 
-	0, 0, 0, 728, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 734, 
-	0, 0, 737, 0, 0, 740, 331, 506, 
-	0, 0, 0, 0, 0, 0, 551, 41, 
-	0, 0, 0, 0, 0, 779, 0, 0, 
-	0, 0, 0, 0, 0, 0, 782, 0, 
-	0, 0, 776, 0, 0, 770, 0, 0, 
-	0, 773, 0, 0, 788, 0, 0, 0, 
-	767, 0, 0, 761, 0, 0, 0, 764, 
-	0, 0, 785, 686, 683, 2116, 103, 485, 
-	488, 485, 500, 1848, 1852, 1844, 500, 0, 
-	5, 7, 485, 3, 0, 500, 1848, 1852, 
-	1844, 500, 515, 0, 5, 7, 506, 3, 
-	0, 47, 572, 575, 41, 569, 47, 0, 
-	5, 7, 506, 3, 0, 2008, 47, 572, 
-	575, 41, 55, 53, 51, 0, 57, 569, 
-	47, 0, 5, 7, 506, 3, 0, 47, 
-	572, 575, 41, 55, 53, 51, 0, 57, 
-	569, 47, 0, 5, 7, 506, 3, 0, 
-	47, 572, 575, 41, 55, 53, 51, 0, 
-	57, 569, 47, 0, 5, 7, 506, 3, 
-	0, 566, 41, 55, 53, 51, 0, 57, 
-	2016, 41, 55, 53, 51, 0, 57, 2020, 
-	3226, 2016, 41, 55, 53, 51, 0, 57, 
-	596, 2028, 2032, 3226, 2024, 596, 2016, 41, 
-	55, 53, 51, 0, 57, 596, 2028, 2032, 
-	3226, 2024, 596, 2016, 41, 55, 53, 51, 
-	0, 57, 596, 2028, 2032, 3226, 2024, 596, 
-	422, 31, 437, 440, 434, 31, 422, 0, 
-	5, 7, 3, 0, 431, 485, 500, 1848, 
-	1852, 1844, 500, 0, 5, 7, 485, 3, 
-	0, 488, 506, 563, 1980, 1984, 41, 1976, 
-	563, 0, 5, 7, 485, 3, 0, 488, 
-	422, 31, 437, 440, 434, 31, 431, 1776, 
-	431, 0, 5, 7, 3, 0, 1, 350, 
-	1, 683, 2132, 103, 1562, 2336, 311, 1508, 
-	1511, 41, 1505, 311, 0, 5, 7, 506, 
-	0, 0, 3, 0, 315, 1526, 1529, 41, 
-	1523, 315, 0, 5, 7, 3211, 3, 0, 
-	521, 1908, 1912, 0, 41, 1904, 521, 0, 
-	5, 7, 3, 0, 0, 5, 7, 3, 
-	0, 506, 313, 1517, 1520, 41, 1514, 313, 
-	0, 5, 7, 506, 0, 0, 3, 0, 
-	317, 1535, 1538, 41, 1532, 317, 0, 5, 
-	7, 3211, 3, 0, 524, 1920, 1924, 0, 
-	41, 1916, 524, 0, 5, 7, 3, 0, 
-	0, 5, 7, 3, 0, 0, 3206, 3794, 
-	527, 1932, 1936, 0, 41, 527, 1928, 527, 
-	3186, 3758, 3186, 0, 5, 7, 3206, 3, 
-	0, 1559, 1736, 1559, 530, 1944, 1948, 0, 
-	41, 530, 1940, 530, 3191, 3764, 3191, 0, 
-	5, 7, 3206, 3, 0, 1736, 1559, 533, 
-	1956, 1960, 0, 41, 533, 1952, 533, 3196, 
-	3770, 3196, 0, 5, 7, 3206, 3, 0, 
-	1736, 1559, 536, 1968, 1972, 0, 41, 1964, 
-	536, 3201, 3776, 3201, 0, 5, 7, 3, 
-	0, 1736, 1559, 1900, 3176, 3181, 3091, 3171, 
-	1900, 3752, 3841, 3752, 503, 1896, 3161, 3166, 
-	3091, 1896, 3156, 1896, 3746, 3834, 3746, 503, 
-	1892, 3146, 3151, 3091, 1892, 3141, 1892, 3740, 
-	3827, 3740, 503, 1888, 3131, 3136, 3091, 1888, 
-	3126, 1888, 3734, 3820, 3734, 503, 321, 1553, 
-	1556, 1550, 321, 1884, 3116, 3121, 3091, 3111, 
-	1884, 503, 319, 1544, 1547, 1541, 319, 1880, 
-	3101, 3106, 3091, 3096, 1880, 503, 506, 45, 
-	557, 560, 41, 554, 45, 0, 5, 7, 
-	506, 3, 0, 45, 557, 560, 41, 554, 
-	45, 0, 5, 7, 506, 3, 0, 563, 
-	1980, 1984, 41, 1976, 563, 0, 5, 7, 
-	485, 3, 0, 488, 506, 45, 557, 560, 
-	41, 554, 45, 0, 5, 7, 506, 3, 
-	0, 45, 557, 560, 41, 554, 45, 422, 
-	0, 5, 7, 3, 0, 31, 437, 440, 
-	434, 31, 422, 0, 5, 7, 3, 0, 
-	31, 437, 440, 434, 31, 422, 0, 5, 
-	7, 3, 0, 443, 1784, 1788, 1780, 443, 
-	0, 5, 7, 485, 3, 0, 488, 335, 
-	0, 5, 7, 3, 0, 506, 45, 557, 
-	560, 41, 554, 45, 0, 5, 7, 333, 
-	3, 0, 0, 5, 7, 3, 0, 127, 
-	1, 707, 0, 695, 5, 7, 698, 701, 
-	3, 0, 1, 350, 1, 109, 111, 0, 
-	2108, 683, 103, 105, 506, 704, 2156, 2160, 
-	41, 2152, 704, 2148, 3276, 2148, 0, 683, 
-	103, 107, 506, 45, 557, 560, 41, 554, 
-	45, 0, 5, 7, 333, 3, 0, 0, 
-	5, 7, 3, 0, 506, 43, 545, 548, 
-	41, 542, 43, 710, 0, 5, 7, 113, 
-	3, 0, 115, 0, 5, 7, 3, 0, 
-	1, 350, 1, 506, 43, 545, 548, 41, 
-	542, 43, 0, 5, 7, 506, 3, 0, 
-	43, 545, 548, 41, 542, 43, 0, 5, 
-	7, 113, 3, 0, 115, 0, 5, 7, 
-	3, 0, 1, 350, 1, 506, 43, 545, 
-	548, 41, 542, 43, 791, 0, 5, 7, 
-	145, 147, 149, 151, 3, 0, 0, 5, 
-	7, 3, 0, 1864, 506, 43, 545, 548, 
-	41, 542, 43, 0, 5, 7, 0, 3, 
-	0, 2172, 0, 5, 7, 0, 5, 7, 
-	0, 5, 7, 1, 3, 0, 350, 1, 
-	3, 0, 350, 1, 3, 0, 350, 1, 
-	794, 127, 0, 5, 7, 3, 0, 506, 
-	43, 545, 548, 41, 542, 43, 2112, 0, 
-	5, 7, 683, 3, 0, 3261, 103, 689, 
-	2124, 2128, 689, 2124, 2128, 689, 2124, 2128, 
-	2116, 2120, 689, 3266, 2116, 2120, 689, 3266, 
-	2116, 2120, 689, 3266, 2116, 0, 5, 7, 
-	3, 0, 506, 43, 545, 548, 41, 542, 
-	43, 0, 5, 7, 683, 3, 0, 103, 
-	692, 2140, 2144, 692, 2140, 2144, 692, 2140, 
-	2144, 2132, 2136, 692, 3271, 2132, 2136, 692, 
-	3271, 2132, 2136, 692, 3271, 2132, 0, 5, 
-	7, 3, 0, 506, 43, 545, 548, 41, 
-	542, 43, 0, 5, 7, 485, 3, 0, 
-	39, 494, 497, 39, 494, 497, 39, 494, 
-	497, 488, 491, 39, 1840, 488, 491, 39, 
-	1840, 488, 491, 39, 1840, 488, 797, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 153, 803, 806, 
-	0, 0, 0, 800, 153, 0, 5, 7, 
-	333, 3, 0, 0, 5, 7, 3, 0, 
-	506, 43, 545, 548, 41, 542, 43, 0, 
-	5, 7, 506, 3, 0, 47, 572, 575, 
-	41, 569, 47, 614, 0, 5, 7, 602, 
-	3, 0, 63, 608, 611, 61, 605, 63, 
-	0, 5, 7, 602, 3, 0, 63, 608, 
-	611, 61, 605, 63, 0, 5, 7, 506, 
-	3, 0, 563, 1980, 1984, 41, 1976, 563, 
-	0, 5, 7, 485, 3, 0, 500, 1848, 
-	1852, 1844, 500, 0, 5, 7, 127, 3, 
-	0, 1, 0, 0, 179, 920, 923, 917, 
-	179, 0, 0, 0, 173, 893, 896, 890, 
-	173, 0, 193, 983, 986, 980, 193, 0, 
-	0, 0, 0, 189, 965, 968, 962, 189, 
-	0, 0, 0, 157, 821, 824, 818, 157, 
-	0, 0, 0, 0, 0, 0, 207, 1046, 
-	1049, 1043, 207, 0, 0, 0, 0, 191, 
-	974, 977, 971, 191, 0, 0, 0, 205, 
-	1037, 1040, 1034, 205, 195, 992, 995, 989, 
-	195, 0, 0, 0, 0, 0, 225, 1127, 
-	1130, 1124, 225, 0, 227, 1136, 1139, 1133, 
-	227, 0, 0, 0, 0, 163, 848, 851, 
-	845, 163, 0, 0, 0, 0, 0, 0, 
-	0, 199, 1010, 1013, 1007, 199, 0, 0, 
-	0, 177, 911, 914, 908, 177, 187, 956, 
-	959, 953, 187, 0, 0, 0, 0, 0, 
-	219, 1100, 1103, 1097, 219, 0, 221, 1109, 
-	1112, 1106, 221, 0, 181, 929, 932, 926, 
-	181, 223, 1118, 1121, 1115, 223, 0, 0, 
-	0, 0, 0, 165, 857, 860, 854, 165, 
-	167, 866, 869, 863, 167, 0, 0, 0, 
-	0, 0, 0, 185, 947, 950, 944, 185, 
-	0, 217, 1091, 1094, 1088, 217, 155, 812, 
-	815, 0, 809, 155, 0, 203, 1028, 1031, 
-	0, 1025, 203, 209, 1055, 1058, 0, 1052, 
-	209, 0, 0, 0, 0, 211, 1064, 1067, 
-	1061, 211, 0, 0, 161, 839, 842, 836, 
-	161, 0, 0, 0, 171, 884, 887, 881, 
-	171, 0, 0, 0, 201, 1019, 1022, 1016, 
-	201, 175, 902, 905, 899, 175, 0, 0, 
-	0, 0, 0, 159, 830, 833, 827, 159, 
-	0, 215, 1082, 1085, 1079, 215, 0, 183, 
-	938, 941, 935, 183, 0, 0, 0, 197, 
-	1001, 1004, 998, 197, 0, 0, 0, 0, 
-	0, 213, 1073, 1076, 1070, 213, 0, 169, 
-	875, 878, 872, 169, 0, 0, 1868, 506, 
-	45, 557, 560, 41, 554, 45, 485, 1856, 
-	1, 506, 45, 557, 560, 41, 554, 45, 
-	0, 5, 7, 506, 3, 0, 43, 545, 
-	548, 41, 542, 43, 0, 5, 7, 333, 
-	3, 0, 0, 5, 7, 3, 0, 127, 
-	1, 127, 1, 506, 43, 545, 548, 41, 
-	542, 43, 0, 5, 7, 506, 3, 0, 
-	43, 545, 548, 41, 542, 43, 0, 5, 
-	7, 506, 3, 0, 45, 557, 560, 41, 
-	554, 45, 0, 5, 7, 29, 425, 3, 
-	0, 31, 437, 440, 434, 31, 716, 0, 
-	5, 7, 428, 3, 0, 131, 133, 0, 
-	135, 137, 0, 139, 0, 141, 143, 0, 
-	446, 129, 1, 0, 0, 115, 113, 506, 
-	43, 545, 548, 41, 542, 43, 0, 5, 
-	7, 506, 3, 0, 43, 545, 548, 41, 
-	542, 43, 0, 5, 7, 506, 3, 0, 
-	45, 557, 560, 41, 554, 45, 0, 5, 
-	7, 29, 425, 3, 0, 431, 115, 113, 
-	506, 43, 545, 548, 41, 542, 43, 0, 
-	5, 7, 506, 3, 0, 43, 545, 548, 
-	41, 542, 43, 0, 5, 7, 506, 3, 
-	0, 43, 545, 548, 41, 542, 43, 0, 
-	5, 7, 113, 3, 0, 115, 0, 5, 
-	7, 3, 0, 1, 350, 1, 506, 45, 
-	557, 560, 41, 554, 45, 0, 5, 7, 
-	683, 3, 0, 2116, 103, 506, 45, 557, 
-	560, 41, 554, 45, 0, 5, 7, 1568, 
-	3, 0, 115, 113, 115, 2168, 327, 113, 
-	115, 113, 115, 2356, 2352, 1565, 115, 2348, 
-	325, 113, 115, 2340, 1565, 115, 325, 113, 
-	115, 2344, 476, 35, 0, 470, 473, 449, 
-	467, 35, 35, 339, 362, 368, 2364, 449, 
-	353, 35, 1740, 1577, 1832, 35, 362, 368, 
-	2092, 2092, 2092, 2092, 2092, 2092, 2092, 2092, 
-	2092, 2092, 2092, 2092, 2092, 2092, 353, 35, 
-	3066, 3081, 3251, 3251, 3251, 3251, 3251, 3251, 
-	3251, 3251, 3251, 3251, 3251, 3251, 3251, 3251, 
-	1824, 656, 91, 2084, 2088, 2076, 653, 91, 
-	2080, 656, 362, 368, 353, 35, 362, 368, 
-	2992, 35, 35, 362, 368, 35, 1577, 3722, 
-	3728, 3788, 3788, 3788, 3788, 3788, 3788, 3788, 
-	3788, 3788, 3788, 3788, 3788, 3788, 3788, 1752
+	3, 0, 1, 348, 1, 492, 43, 531, 
+	534, 41, 528, 43, 0, 5, 7, 492, 
+	3, 0, 43, 531, 534, 41, 528, 43, 
+	0, 5, 7, 113, 3, 0, 115, 0, 
+	5, 7, 3, 0, 1, 348, 1, 492, 
+	43, 531, 534, 41, 528, 43, 771, 0, 
+	5, 7, 145, 147, 149, 151, 3, 0, 
+	0, 5, 7, 3, 0, 1798, 492, 43, 
+	531, 534, 41, 528, 43, 0, 5, 7, 
+	0, 3, 0, 2090, 0, 5, 7, 0, 
+	5, 7, 0, 5, 7, 1, 3, 0, 
+	348, 1, 3, 0, 348, 1, 3, 0, 
+	348, 1, 774, 127, 0, 5, 7, 3, 
+	0, 492, 43, 531, 534, 41, 528, 43, 
+	2030, 0, 5, 7, 663, 3, 0, 3151, 
+	103, 669, 2042, 2046, 669, 2042, 2046, 669, 
+	2042, 2046, 2034, 2038, 669, 3156, 2034, 2038, 
+	669, 3156, 2034, 2038, 669, 3156, 2034, 0, 
+	5, 7, 3, 0, 492, 43, 531, 534, 
+	41, 528, 43, 0, 5, 7, 663, 3, 
+	0, 103, 672, 2058, 2062, 672, 2058, 2062, 
+	672, 2058, 2062, 2050, 2054, 672, 3161, 2050, 
+	2054, 672, 3161, 2050, 2054, 672, 3161, 2050, 
+	0, 5, 7, 3, 0, 492, 43, 531, 
+	534, 41, 528, 43, 0, 5, 7, 471, 
+	3, 0, 39, 480, 483, 39, 480, 483, 
+	39, 480, 483, 474, 477, 39, 1790, 474, 
+	477, 39, 1790, 474, 477, 39, 1790, 474, 
+	777, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 153, 
+	783, 786, 0, 0, 0, 780, 153, 0, 
+	5, 7, 333, 3, 0, 0, 5, 7, 
+	3, 0, 492, 43, 531, 534, 41, 528, 
+	43, 0, 5, 7, 492, 3, 0, 47, 
+	555, 558, 41, 552, 47, 597, 0, 5, 
+	7, 585, 3, 0, 63, 591, 594, 61, 
+	588, 63, 0, 5, 7, 585, 3, 0, 
+	63, 591, 594, 61, 588, 63, 0, 5, 
+	7, 492, 3, 0, 45, 543, 546, 41, 
+	540, 45, 0, 5, 7, 471, 3, 0, 
+	39, 480, 483, 477, 39, 0, 5, 7, 
+	127, 3, 0, 1, 0, 0, 179, 900, 
+	903, 897, 179, 0, 0, 0, 173, 873, 
+	876, 870, 173, 0, 193, 963, 966, 960, 
+	193, 0, 0, 0, 0, 189, 945, 948, 
+	942, 189, 0, 0, 0, 157, 801, 804, 
+	798, 157, 0, 0, 0, 0, 0, 0, 
+	207, 1026, 1029, 1023, 207, 0, 0, 0, 
+	0, 191, 954, 957, 951, 191, 0, 0, 
+	0, 205, 1017, 1020, 1014, 205, 195, 972, 
+	975, 969, 195, 0, 0, 0, 0, 0, 
+	225, 1107, 1110, 1104, 225, 0, 227, 1116, 
+	1119, 1113, 227, 0, 0, 0, 0, 163, 
+	828, 831, 825, 163, 0, 0, 0, 0, 
+	0, 0, 0, 199, 990, 993, 987, 199, 
+	0, 0, 0, 177, 891, 894, 888, 177, 
+	187, 936, 939, 933, 187, 0, 0, 0, 
+	0, 0, 219, 1080, 1083, 1077, 219, 0, 
+	221, 1089, 1092, 1086, 221, 0, 181, 909, 
+	912, 906, 181, 223, 1098, 1101, 1095, 223, 
+	0, 0, 0, 0, 0, 165, 837, 840, 
+	834, 165, 167, 846, 849, 843, 167, 0, 
+	0, 0, 0, 0, 0, 185, 927, 930, 
+	924, 185, 0, 217, 1071, 1074, 1068, 217, 
+	155, 792, 795, 0, 789, 155, 0, 203, 
+	1008, 1011, 0, 1005, 203, 209, 1035, 1038, 
+	0, 1032, 209, 0, 0, 0, 0, 211, 
+	1044, 1047, 1041, 211, 0, 0, 161, 819, 
+	822, 816, 161, 0, 0, 0, 171, 864, 
+	867, 861, 171, 0, 0, 0, 201, 999, 
+	1002, 996, 201, 175, 882, 885, 879, 175, 
+	0, 0, 0, 0, 0, 159, 810, 813, 
+	807, 159, 0, 215, 1062, 1065, 1059, 215, 
+	0, 183, 918, 921, 915, 183, 0, 0, 
+	0, 197, 981, 984, 978, 197, 0, 0, 
+	0, 0, 0, 213, 1053, 1056, 1050, 213, 
+	0, 169, 855, 858, 852, 169, 0, 0, 
+	1802, 492, 45, 543, 546, 41, 540, 45, 
+	471, 486, 1, 492, 45, 543, 546, 41, 
+	540, 45, 0, 5, 7, 492, 3, 0, 
+	43, 531, 534, 41, 528, 43, 0, 5, 
+	7, 333, 3, 0, 0, 5, 7, 3, 
+	0, 127, 1, 127, 1, 492, 43, 531, 
+	534, 41, 528, 43, 0, 5, 7, 492, 
+	3, 0, 43, 531, 534, 41, 528, 43, 
+	0, 5, 7, 492, 3, 0, 45, 543, 
+	546, 41, 540, 45, 0, 5, 7, 29, 
+	417, 3, 0, 31, 429, 432, 426, 31, 
+	696, 0, 5, 7, 420, 3, 0, 131, 
+	133, 0, 135, 137, 0, 139, 0, 141, 
+	143, 0, 435, 129, 1, 0, 0, 115, 
+	113, 492, 43, 531, 534, 41, 528, 43, 
+	0, 5, 7, 492, 3, 0, 43, 531, 
+	534, 41, 528, 43, 0, 5, 7, 492, 
+	3, 0, 45, 543, 546, 41, 540, 45, 
+	0, 5, 7, 29, 417, 3, 0, 423, 
+	115, 113, 492, 43, 531, 534, 41, 528, 
+	43, 0, 5, 7, 492, 3, 0, 43, 
+	531, 534, 41, 528, 43, 0, 5, 7, 
+	492, 3, 0, 43, 531, 534, 41, 528, 
+	43, 0, 5, 7, 113, 3, 0, 115, 
+	0, 5, 7, 3, 0, 1, 348, 1, 
+	492, 45, 543, 546, 41, 540, 45, 0, 
+	5, 7, 663, 3, 0, 2034, 103, 492, 
+	45, 543, 546, 41, 540, 45, 0, 5, 
+	7, 1548, 3, 0, 115, 113, 115, 2086, 
+	327, 113, 115, 113, 115, 2274, 2270, 1545, 
+	115, 2266, 325, 113, 115, 2258, 1545, 115, 
+	325, 113, 115, 2262, 462, 35, 0, 456, 
+	459, 438, 453, 35, 35, 339, 360, 363, 
+	2282, 438, 351, 35, 1714, 1557, 1782, 35, 
+	360, 363, 2014, 2014, 2014, 2014, 2014, 2014, 
+	2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 
+	351, 35, 2961, 2976, 3146, 3146, 3146, 3146, 
+	3146, 3146, 3146, 3146, 3146, 3146, 3146, 3146, 
+	3146, 3146, 1774, 639, 91, 2006, 2010, 1998, 
+	636, 91, 2002, 639, 360, 363, 351, 35, 
+	360, 363, 2902, 35, 35, 360, 363, 35, 
+	1557, 3584, 3590, 3650, 3650, 3650, 3650, 3650, 
+	3650, 3650, 3650, 3650, 3650, 3650, 3650, 3650, 
+	3650, 1722
 };
 
 static const short _zone_scanner_eof_actions[] = {
-	0, 1872, 2012, 345, 1592, 337, 1571, 9, 
-	9, 337, 1571, 337, 1571, 1571, 3086, 3782, 
-	1589, 345, 345, 345, 1592, 1571, 345, 345, 
-	345, 1592, 345, 345, 345, 345, 1592, 345, 
-	345, 345, 1592, 345, 345, 345, 1592, 1592, 
-	345, 345, 345, 345, 1592, 345, 1592, 345, 
-	345, 345, 345, 1592, 345, 345, 345, 345, 
-	345, 345, 345, 345, 345, 345, 1592, 345, 
-	345, 1592, 1592, 345, 345, 1592, 345, 1592, 
-	345, 1592, 1592, 345, 345, 345, 345, 1592, 
-	1592, 345, 345, 345, 345, 1592, 345, 1592, 
-	1592, 345, 1592, 1592, 345, 345, 345, 345, 
-	1592, 345, 345, 1592, 345, 1592, 345, 345, 
-	345, 1592, 1592, 345, 345, 1592, 345, 1592, 
-	345, 1592, 345, 345, 345, 1592, 345, 345, 
-	345, 1592, 345, 1592, 345, 345, 518, 1876, 
-	345, 1872, 345, 345, 518, 2012, 2012, 2012, 
-	2012, 345, 9, 93, 93, 93, 93, 93, 
-	93, 93, 93, 37, 518, 0, 3071, 518, 
-	345, 2012, 2012, 2012, 345, 1836, 1876, 2376, 
-	1571, 1872, 9, 1571, 9, 3086, 2376, 2376, 
-	2376, 2376, 2376, 2376, 2376, 2376, 2376, 2376, 
-	2376, 2376, 2376, 1571, 2360, 93, 93, 93, 
-	93, 93, 93, 93, 93, 93, 1828, 3813, 
-	1872, 1589, 3076, 3086, 3076, 3076, 3076, 3076, 
-	3076, 3076, 3076, 3076, 3076, 3076, 3076, 3076, 
-	3076, 479, 3086, 1571, 9, 1571, 345, 345, 
-	1592, 345, 345, 345, 1592, 345, 1592, 1571, 
-	1571, 9, 1571, 1571, 337, 1571, 482, 482, 
-	482, 482, 482, 482, 482, 482, 482, 482, 
-	482, 482, 482, 9, 1589, 482, 1589, 0, 
-	0, 27, 27, 27, 27, 395, 395, 395, 
-	395, 27, 27, 620, 620, 2052, 2052, 2052, 
-	2052, 620, 67, 2052, 2052, 2052, 2052, 73, 
-	509, 3221, 374, 374, 3221, 2004, 3221, 73, 
-	77, 77, 77, 377, 377, 377, 77, 398, 
-	398, 398, 398, 377, 77, 0, 83, 644, 
-	650, 85, 650, 9, 9, 85, 401, 401, 
-	401, 401, 650, 9, 85, 0, 0, 125, 
-	125, 125, 125, 125, 125, 125, 309, 309, 
+	0, 1806, 1934, 343, 1566, 337, 1551, 9, 
+	9, 337, 337, 1551, 1551, 2981, 3644, 1563, 
+	343, 343, 343, 1566, 1551, 343, 343, 343, 
+	1566, 343, 343, 343, 343, 1566, 343, 343, 
+	343, 1566, 343, 343, 343, 1566, 1566, 343, 
+	343, 343, 343, 1566, 343, 1566, 343, 343, 
+	343, 343, 1566, 343, 343, 343, 343, 343, 
+	343, 343, 343, 343, 343, 1566, 343, 343, 
+	1566, 1566, 343, 343, 1566, 343, 1566, 343, 
+	1566, 1566, 343, 343, 343, 343, 1566, 1566, 
+	343, 343, 343, 343, 1566, 343, 1566, 1566, 
+	343, 1566, 1566, 343, 343, 343, 343, 1566, 
+	343, 343, 1566, 343, 1566, 343, 343, 343, 
+	1566, 1566, 343, 343, 1566, 343, 1566, 343, 
+	1566, 343, 343, 343, 1566, 343, 343, 343, 
+	1566, 343, 1566, 343, 343, 504, 1810, 343, 
+	1806, 343, 343, 504, 1934, 1934, 1934, 1934, 
+	343, 9, 93, 93, 93, 93, 93, 93, 
+	93, 93, 37, 504, 0, 2966, 504, 343, 
+	1934, 1934, 1934, 343, 1786, 1810, 2290, 1551, 
+	1806, 9, 1551, 9, 2981, 2290, 2290, 2290, 
+	2290, 2290, 2290, 2290, 2290, 2290, 2290, 2290, 
+	2290, 2290, 1551, 2278, 93, 93, 93, 93, 
+	93, 93, 93, 93, 93, 1778, 3681, 1806, 
+	1563, 2971, 2981, 2971, 2971, 2971, 2971, 2971, 
+	2971, 2971, 2971, 2971, 2971, 2971, 2971, 2971, 
+	465, 2981, 1551, 9, 1551, 343, 343, 1566, 
+	343, 343, 343, 1566, 343, 1566, 1551, 1551, 
+	9, 1551, 1551, 337, 1551, 468, 468, 468, 
+	468, 468, 468, 468, 468, 468, 468, 468, 
+	468, 468, 9, 1563, 468, 1563, 0, 0, 
+	27, 27, 27, 27, 387, 387, 387, 387, 
+	27, 27, 603, 603, 1974, 1974, 1974, 1974, 
+	603, 67, 1974, 1974, 1974, 1974, 73, 495, 
+	3116, 366, 366, 3116, 1926, 3116, 73, 77, 
+	77, 77, 369, 369, 369, 77, 390, 390, 
+	390, 390, 369, 77, 0, 83, 627, 633, 
+	85, 633, 9, 9, 85, 393, 393, 393, 
+	393, 633, 9, 85, 0, 0, 125, 125, 
+	125, 125, 125, 125, 125, 309, 309, 309, 
 	309, 309, 309, 309, 309, 309, 309, 309, 
 	309, 309, 309, 309, 309, 309, 309, 309, 
 	309, 309, 309, 309, 309, 309, 309, 309, 
@@ -4811,10 +4788,9 @@ static const short _zone_scanner_eof_actions[] = {
 	309, 309, 309, 309, 309, 309, 309, 309, 
 	309, 309, 309, 309, 309, 309, 309, 309, 
 	309, 309, 309, 309, 309, 309, 309, 309, 
-	309, 309, 309, 309, 309, 309, 323, 512, 
-	512, 2164, 2164, 2164, 2164, 323, 323, 323, 
-	512, 512, 2164, 2164, 2164, 2164, 323, 512, 
-	323, 329, 329, 329, 329, 329, 329, 329, 
+	309, 309, 309, 309, 309, 323, 498, 498, 
+	2082, 2082, 2082, 2082, 323, 323, 323, 498, 
+	498, 2082, 2082, 2082, 2082, 323, 498, 323, 
 	329, 329, 329, 329, 329, 329, 329, 329, 
 	329, 329, 329, 329, 329, 329, 329, 329, 
 	329, 329, 329, 329, 329, 329, 329, 329, 
@@ -4826,71 +4802,71 @@ static const short _zone_scanner_eof_actions[] = {
 	329, 329, 329, 329, 329, 329, 329, 329, 
 	329, 329, 329, 329, 329, 329, 329, 329, 
 	329, 329, 329, 329, 329, 329, 329, 329, 
+	329, 329, 329, 329, 329, 329, 329, 331, 
 	331, 331, 331, 331, 331, 331, 331, 331, 
 	331, 331, 331, 331, 331, 331, 331, 331, 
 	331, 331, 331, 331, 331, 331, 331, 331, 
 	331, 331, 331, 331, 331, 331, 331, 331, 
 	331, 331, 331, 331, 331, 331, 331, 331, 
-	331, 686, 686, 337, 337, 337, 337, 337, 
-	337, 515, 515, 515, 2008, 515, 2008, 515, 
-	2008, 515, 2008, 2008, 2008, 2008, 337, 2008, 
-	2008, 2008, 337, 2008, 2008, 2008, 337, 2008, 
-	2008, 2008, 337, 337, 337, 337, 337, 337, 
-	337, 337, 337, 337, 337, 337, 337, 515, 
-	515, 337, 337, 337, 337, 337, 337, 337, 
-	686, 686, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 
-	1562, 515, 515, 515, 515, 515, 515, 337, 
-	337, 337, 337, 337, 515, 515, 515, 515, 
+	666, 666, 337, 337, 337, 337, 337, 337, 
+	501, 501, 501, 1930, 501, 1930, 501, 1930, 
+	501, 1930, 1930, 1930, 1930, 337, 1930, 1930, 
+	1930, 337, 1930, 1930, 1930, 337, 1930, 1930, 
+	1930, 337, 337, 337, 337, 337, 337, 337, 
+	337, 337, 337, 337, 337, 337, 501, 501, 
+	337, 337, 337, 337, 337, 337, 337, 666, 
+	666, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 
+	501, 501, 501, 501, 501, 501, 337, 337, 
+	337, 337, 337, 501, 501, 501, 501, 337, 
 	337, 337, 337, 337, 337, 337, 337, 337, 
-	337, 337, 337, 337, 337, 337, 337, 515, 
-	515, 337, 337, 337, 337, 337, 337, 337, 
-	707, 707, 707, 2108, 2108, 707, 707, 337, 
-	707, 2108, 2108, 515, 515, 337, 337, 515, 
-	515, 710, 710, 710, 710, 337, 337, 337, 
-	515, 515, 515, 515, 710, 710, 710, 710, 
-	337, 337, 515, 515, 791, 791, 1864, 1864, 
-	791, 2172, 794, 794, 794, 794, 794, 794, 
-	794, 791, 791, 791, 1864, 1864, 2112, 3261, 
-	791, 791, 791, 1864, 1864, 2112, 3261, 791, 
-	791, 791, 1864, 1864, 791, 2172, 791, 791, 
-	337, 797, 797, 337, 337, 515, 515, 515, 
-	515, 614, 614, 614, 614, 515, 515, 337, 
+	337, 337, 337, 337, 337, 337, 501, 501, 
+	337, 337, 337, 337, 337, 337, 337, 687, 
+	687, 687, 2026, 2026, 687, 687, 337, 687, 
+	2026, 2026, 501, 501, 337, 337, 501, 501, 
+	690, 690, 690, 690, 337, 337, 337, 501, 
+	501, 501, 501, 690, 690, 690, 690, 337, 
+	337, 501, 501, 771, 771, 1798, 1798, 771, 
+	2090, 774, 774, 774, 774, 774, 774, 774, 
+	771, 771, 771, 1798, 1798, 2030, 3151, 771, 
+	771, 771, 1798, 1798, 2030, 3151, 771, 771, 
+	771, 1798, 1798, 771, 2090, 771, 771, 337, 
+	777, 777, 337, 337, 501, 501, 501, 501, 
+	597, 597, 597, 597, 501, 501, 337, 337, 
 	337, 337, 337, 337, 337, 337, 337, 337, 
-	337, 337, 337, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 797, 797, 797, 
-	797, 797, 797, 797, 797, 1868, 1868, 337, 
-	337, 337, 515, 515, 515, 515, 337, 337, 
-	337, 337, 337, 337, 337, 337, 337, 515, 
-	515, 515, 515, 515, 515, 710, 710, 716, 
-	716, 716, 716, 716, 716, 716, 716, 716, 
-	337, 716, 716, 716, 716, 716, 337, 710, 
-	710, 337, 337, 337, 515, 515, 515, 515, 
-	515, 515, 710, 710, 710, 710, 337, 337, 
-	337, 515, 515, 515, 515, 515, 515, 710, 
-	710, 710, 710, 337, 337, 337, 515, 515, 
-	686, 686, 337, 515, 515, 710, 710, 710, 
-	710, 2168, 710, 710, 710, 710, 2356, 337, 
-	710, 710, 2348, 710, 710, 2348, 710, 710, 
-	2348, 710, 710, 2348, 0, 0, 0, 0, 
-	91, 0, 0, 0, 0, 0, 0, 0, 
+	337, 337, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 777, 777, 777, 777, 
+	777, 777, 777, 777, 1802, 1802, 337, 337, 
+	337, 501, 501, 501, 501, 337, 337, 337, 
+	337, 337, 337, 337, 337, 337, 501, 501, 
+	501, 501, 501, 501, 690, 690, 696, 696, 
+	696, 696, 696, 696, 696, 696, 696, 337, 
+	696, 696, 696, 696, 696, 337, 690, 690, 
+	337, 337, 337, 501, 501, 501, 501, 501, 
+	501, 690, 690, 690, 690, 337, 337, 337, 
+	501, 501, 501, 501, 501, 501, 690, 690, 
+	690, 690, 337, 337, 337, 501, 501, 666, 
+	666, 337, 501, 501, 690, 690, 690, 690, 
+	2086, 690, 690, 690, 690, 2274, 337, 690, 
+	690, 2266, 690, 690, 2266, 690, 690, 2266, 
+	690, 690, 2266, 0, 0, 0, 0, 91, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -4900,71 +4876,113 @@ static const short _zone_scanner_eof_actions[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0
+	0, 0, 0, 0, 0, 0, 0, 0
 };
 
-static const int zone_scanner_start = 1060;
-static const int zone_scanner_first_final = 1060;
+static const int zone_scanner_start = 1059;
+static const int zone_scanner_first_final = 1059;
 static const int zone_scanner_error = 0;
 
-static const int zone_scanner_en_err_line = 247;
-static const int zone_scanner_en_dname_ = 249;
-static const int zone_scanner_en_text_ = 259;
-static const int zone_scanner_en_default_ttl_ = 271;
-static const int zone_scanner_en_zone_origin_ = 280;
-static const int zone_scanner_en_include_file_ = 293;
-static const int zone_scanner_en_base64_ = 311;
-static const int zone_scanner_en_bitmap_ = 318;
-static const int zone_scanner_en_nonempty_hex_r_data = 438;
-static const int zone_scanner_en_hex_r_data = 447;
-static const int zone_scanner_en_dns_alg_ = 457;
-static const int zone_scanner_en_cert_type_ = 552;
-static const int zone_scanner_en_r_data_a = 593;
-static const int zone_scanner_en_r_data_ns = 595;
-static const int zone_scanner_en_r_data_soa = 597;
-static const int zone_scanner_en_r_data_hinfo = 629;
-static const int zone_scanner_en_r_data_minfo = 634;
-static const int zone_scanner_en_r_data_mx = 639;
-static const int zone_scanner_en_r_data_txt = 644;
-static const int zone_scanner_en_r_data_aaaa = 648;
-static const int zone_scanner_en_r_data_loc = 650;
-static const int zone_scanner_en_r_data_srv = 705;
-static const int zone_scanner_en_r_data_naptr = 716;
-static const int zone_scanner_en_r_data_cert = 733;
-static const int zone_scanner_en_r_data_apl = 744;
-static const int zone_scanner_en_r_data_ds = 755;
-static const int zone_scanner_en_r_data_sshfp = 768;
-static const int zone_scanner_en_r_data_ipseckey = 778;
-static const int zone_scanner_en_r_data_rrsig = 817;
-static const int zone_scanner_en_r_data_nsec = 959;
-static const int zone_scanner_en_r_data_dnskey = 962;
-static const int zone_scanner_en_r_data_dhcid = 973;
-static const int zone_scanner_en_r_data_nsec3 = 975;
-static const int zone_scanner_en_r_data_nsec3param = 1004;
-static const int zone_scanner_en_r_data_tlsa = 1017;
-static const int zone_scanner_en_r_data_l32 = 1030;
-static const int zone_scanner_en_r_data_l64 = 1035;
-static const int zone_scanner_en_r_data_eui48 = 1048;
-static const int zone_scanner_en_r_data_eui64 = 1054;
-static const int zone_scanner_en_main = 1060;
+static const int zone_scanner_en_err_line = 246;
+static const int zone_scanner_en_dname_ = 248;
+static const int zone_scanner_en_text_ = 258;
+static const int zone_scanner_en_default_ttl_ = 270;
+static const int zone_scanner_en_zone_origin_ = 279;
+static const int zone_scanner_en_include_file_ = 292;
+static const int zone_scanner_en_base64_ = 310;
+static const int zone_scanner_en_bitmap_ = 317;
+static const int zone_scanner_en_nonempty_hex_r_data = 437;
+static const int zone_scanner_en_hex_r_data = 446;
+static const int zone_scanner_en_dns_alg_ = 456;
+static const int zone_scanner_en_cert_type_ = 551;
+static const int zone_scanner_en_r_data_a = 592;
+static const int zone_scanner_en_r_data_ns = 594;
+static const int zone_scanner_en_r_data_soa = 596;
+static const int zone_scanner_en_r_data_hinfo = 628;
+static const int zone_scanner_en_r_data_minfo = 633;
+static const int zone_scanner_en_r_data_mx = 638;
+static const int zone_scanner_en_r_data_txt = 643;
+static const int zone_scanner_en_r_data_aaaa = 647;
+static const int zone_scanner_en_r_data_loc = 649;
+static const int zone_scanner_en_r_data_srv = 704;
+static const int zone_scanner_en_r_data_naptr = 715;
+static const int zone_scanner_en_r_data_cert = 732;
+static const int zone_scanner_en_r_data_apl = 743;
+static const int zone_scanner_en_r_data_ds = 754;
+static const int zone_scanner_en_r_data_sshfp = 767;
+static const int zone_scanner_en_r_data_ipseckey = 777;
+static const int zone_scanner_en_r_data_rrsig = 816;
+static const int zone_scanner_en_r_data_nsec = 958;
+static const int zone_scanner_en_r_data_dnskey = 961;
+static const int zone_scanner_en_r_data_dhcid = 972;
+static const int zone_scanner_en_r_data_nsec3 = 974;
+static const int zone_scanner_en_r_data_nsec3param = 1003;
+static const int zone_scanner_en_r_data_tlsa = 1016;
+static const int zone_scanner_en_r_data_l32 = 1029;
+static const int zone_scanner_en_r_data_l64 = 1034;
+static const int zone_scanner_en_r_data_eui48 = 1047;
+static const int zone_scanner_en_r_data_eui64 = 1053;
+static const int zone_scanner_en_main = 1059;
 
 
-#line 82 "./zscanner/scanner.rl"
+#line 90 "./scanner.rl"
 
 
-scanner_t* scanner_create(const char *file_name)
+scanner_t* scanner_create(const char     *file_name,
+                          const char     *origin,
+                          const uint16_t rclass,
+                          const uint32_t ttl,
+                          void (*process_record)(const scanner_t *),
+                          void (*process_error)(const scanner_t *),
+                          void *data)
 {
+	char settings[1024];
+
 	scanner_t *s = calloc(1, sizeof(scanner_t));
 	if (s == NULL) {
 		return NULL;
 	}
 
-	s->file_name = strdup(file_name);
-	s->line_counter = 1;
+	if (file_name != NULL) {
+		// Get absolute path of the zone file.
+		if (realpath(file_name, (char*)(s->buffer)) != NULL) {
+			char *full_name = strdup((char*)(s->buffer));
+			s->path = strdup(dirname(full_name));
+			free(full_name);
+		} else {
+			free(s);
+			return NULL;
+		}
+
+		s->file_name = strdup(file_name);
+	} else {
+		s->path = strdup(".");
+		s->file_name = strdup("<NULL>");
+	}
 
 	// Nonzero initial scanner state.
 	s->cs = zone_scanner_start;
 
+	// Disable processing during parsing of settings.
+	s->process_record = &noop;
+	s->process_error = &noop;
+
+	// Create ORIGIN directive and parse it using scanner to set up origin.
+	int ret = snprintf(settings, sizeof(settings), "$ORIGIN %s\n", origin);
+	if (ret <= 0 || (size_t)ret >= sizeof(settings) ||
+	    scanner_process(settings, settings + ret, true, s) != 0) {
+		scanner_free(s);
+		return NULL;
+	}
+
+	// Set scanner defaults.
+	s->default_class = rclass;
+	s->default_ttl = ttl;
+	s->process_record = process_record ? process_record : &noop;
+	s->process_error = process_error ? process_error : &noop;
+	s->data = data;
+	s->line_counter = 1;
+
 	return s;
 }
 
@@ -4972,14 +4990,15 @@ void scanner_free(scanner_t *s)
 {
 	if (s != NULL) {
 		free(s->file_name);
+		free(s->path);
 		free(s);
 	}
 }
 
 int scanner_process(const char *start,
-		    const char *end,
-		    const bool is_last_block,
-		    scanner_t  *s)
+                    const char *end,
+                    const bool is_complete,
+                    scanner_t  *s)
 {
 	// Necessary scanner variables.
 	const char *p = start;
@@ -4992,7 +5011,7 @@ int scanner_process(const char *start,
 	struct in6_addr addr6;
 	uint32_t timestamp;
 	int16_t  window;
-	int	 ret;
+	int      ret;
 
 	// Next 2 variables are for better performance.
 	// Restoring r_data pointer to next free space.
@@ -5006,13 +5025,13 @@ int scanner_process(const char *start,
 	memcpy(stack, s->stack, sizeof(stack));
 
 	// End of file check.
-	if (is_last_block == true) {
+	if (is_complete == true) {
 		eof = (char *)pe;
 	}
 
 	// Writing scanner body (in C).
 	
-#line 5016 "zscanner/scanner.c"
+#line 5035 "scanner.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -5047,117 +5066,117 @@ _resume:
 	case 0: {
 		_widec = (short)(640 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		break;
 	}
 	case 1: {
 		_widec = (short)(1152 + ((*p) - -128));
 		if ( 
-#line 63 "./zscanner/scanner_body.rl"
+#line 63 "./scanner_body.rl"
  !s->multiline  ) _widec += 256;
 		break;
 	}
 	case 2: {
 		_widec = (short)(128 + ((*p) - -128));
 		if ( 
-#line 559 "./zscanner/scanner_body.rl"
+#line 551 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		break;
 	}
 	case 3: {
 		_widec = (short)(2688 + ((*p) - -128));
 		if ( 
-#line 1197 "./zscanner/scanner_body.rl"
+#line 1170 "./scanner_body.rl"
  s->number64 != 0  ) _widec += 256;
 		break;
 	}
 	case 4: {
 		_widec = (short)(4224 + ((*p) - -128));
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 256;
 		break;
 	}
 	case 5: {
 		_widec = (short)(1664 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 63 "./zscanner/scanner_body.rl"
+#line 63 "./scanner_body.rl"
  !s->multiline  ) _widec += 512;
 		break;
 	}
 	case 6: {
 		_widec = (short)(3200 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 1197 "./zscanner/scanner_body.rl"
+#line 1170 "./scanner_body.rl"
  s->number64 != 0  ) _widec += 512;
 		break;
 	}
 	case 7: {
 		_widec = (short)(4736 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 512;
 		break;
 	}
 	case 8: {
 		_widec = (short)(5760 + ((*p) - -128));
 		if ( 
-#line 1197 "./zscanner/scanner_body.rl"
+#line 1170 "./scanner_body.rl"
  s->number64 != 0  ) _widec += 256;
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 512;
 		break;
 	}
 	case 9: {
 		_widec = (short)(12928 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 63 "./zscanner/scanner_body.rl"
+#line 63 "./scanner_body.rl"
  !s->multiline  ) _widec += 512;
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 1024;
 		break;
 	}
 	case 10: {
 		_widec = (short)(6784 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 1197 "./zscanner/scanner_body.rl"
+#line 1170 "./scanner_body.rl"
  s->number64 != 0  ) _widec += 512;
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 1024;
 		break;
 	}
 	case 11: {
 		_widec = (short)(8832 + ((*p) - -128));
 		if ( 
-#line 54 "./zscanner/scanner_body.rl"
+#line 54 "./scanner_body.rl"
  s->multiline  ) _widec += 256;
 		if ( 
-#line 63 "./zscanner/scanner_body.rl"
+#line 63 "./scanner_body.rl"
  !s->multiline  ) _widec += 512;
 		if ( 
-#line 1197 "./zscanner/scanner_body.rl"
+#line 1170 "./scanner_body.rl"
  s->number64 != 0  ) _widec += 1024;
 		if ( 
-#line 1198 "./zscanner/scanner_body.rl"
+#line 1171 "./scanner_body.rl"
  s->number64 == 0  ) _widec += 2048;
 		break;
 	}
@@ -5229,52 +5248,52 @@ _match:
 		switch ( *_acts++ )
 		{
 	case 0:
-#line 20 "./zscanner/scanner_body.rl"
+#line 20 "./scanner_body.rl"
 	{
 		p--; {cs = stack[--top]; goto _again;}
 	}
 	break;
 	case 1:
-#line 25 "./zscanner/scanner_body.rl"
+#line 25 "./scanner_body.rl"
 	{
 		s->line_counter++;
 	}
 	break;
 	case 2:
-#line 29 "./zscanner/scanner_body.rl"
+#line 29 "./scanner_body.rl"
 	{
 		if (s->multiline == true) {
 			SCANNER_ERROR(ZSCANNER_ELEFT_PARENTHESIS);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 		s->multiline = true;
 	}
 	break;
 	case 3:
-#line 36 "./zscanner/scanner_body.rl"
+#line 36 "./scanner_body.rl"
 	{
 		if (s->multiline == false) {
 			SCANNER_ERROR(ZSCANNER_ERIGHT_PARENTHESIS);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 		s->multiline = false;
 	}
 	break;
 	case 4:
-#line 44 "./zscanner/scanner_body.rl"
+#line 44 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_REST);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
 	case 5:
-#line 67 "./zscanner/scanner_body.rl"
+#line 67 "./scanner_body.rl"
 	{
 		s->buffer_length = 0;
 	}
 	break;
 	case 6:
-#line 70 "./zscanner/scanner_body.rl"
+#line 70 "./scanner_body.rl"
 	{
 		if (s->buffer_length < sizeof(s->buffer) - 1) {
 			s->buffer[s->buffer_length++] = (*p);
@@ -5282,7 +5301,7 @@ _match:
 	}
 	break;
 	case 7:
-#line 75 "./zscanner/scanner_body.rl"
+#line 75 "./scanner_body.rl"
 	{
 		// Ending string in buffer.
 		s->buffer[s->buffer_length++] = 0;
@@ -5297,7 +5316,7 @@ _match:
 		s->process_error(s);
 
 		// Reset.
-		s->error_code = KNOT_EOK;
+		s->error_code = ZSCANNER_OK;
 		s->multiline = false;
 
 		// In case of serious error, stop scanner.
@@ -5307,80 +5326,80 @@ _match:
 	}
 	break;
 	case 8:
-#line 100 "./zscanner/scanner_body.rl"
-	{ {cs = 1060; goto _again;} }
+#line 100 "./scanner_body.rl"
+	{ {cs = 1059; goto _again;} }
 	break;
 	case 9:
-#line 104 "./zscanner/scanner_body.rl"
+#line 104 "./scanner_body.rl"
 	{
 		s->item_length = 0;
 		s->item_length_position = s->dname_tmp_length++;
 	}
 	break;
 	case 10:
-#line 108 "./zscanner/scanner_body.rl"
+#line 108 "./scanner_body.rl"
 	{
 		if (s->item_length < MAX_LABEL_LENGTH) {
 			(s->dname)[s->dname_tmp_length++] = (*p);
 			s->item_length++;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ELABEL_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
 	case 11:
-#line 117 "./zscanner/scanner_body.rl"
+#line 117 "./scanner_body.rl"
 	{
 		if (s->dname_tmp_length < MAX_DNAME_LENGTH) {
 			(s->dname)[s->item_length_position] =
 				(uint8_t)(s->item_length);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EDNAME_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
 	case 12:
-#line 127 "./zscanner/scanner_body.rl"
+#line 127 "./scanner_body.rl"
 	{
 		if (s->item_length < MAX_LABEL_LENGTH) {
 			(s->dname)[s->dname_tmp_length] = 0;
 			s->item_length++;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ELABEL_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
 	case 13:
-#line 136 "./zscanner/scanner_body.rl"
+#line 136 "./scanner_body.rl"
 	{
 		(s->dname)[s->dname_tmp_length] *= 10;
 		(s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
 	}
 	break;
 	case 14:
-#line 140 "./zscanner/scanner_body.rl"
+#line 140 "./scanner_body.rl"
 	{
 		s->dname_tmp_length++;
 	}
 	break;
 	case 15:
-#line 143 "./zscanner/scanner_body.rl"
+#line 143 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
 	case 16:
-#line 162 "./zscanner/scanner_body.rl"
+#line 162 "./scanner_body.rl"
 	{
 		(s->dname)[s->dname_tmp_length++] = 0;
 	}
 	break;
 	case 17:
-#line 165 "./zscanner/scanner_body.rl"
+#line 165 "./scanner_body.rl"
 	{
 		memcpy(s->dname + s->dname_tmp_length,
 		       s->zone_origin,
@@ -5390,12 +5409,12 @@ _match:
 
 		if (s->dname_tmp_length > MAX_DNAME_LENGTH) {
 			SCANNER_WARNING(ZSCANNER_EDNAME_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
 	case 18:
-#line 177 "./zscanner/scanner_body.rl"
+#line 177 "./scanner_body.rl"
 	{
 		memcpy(s->dname,
 		       s->zone_origin,
@@ -5405,31 +5424,31 @@ _match:
 	}
 	break;
 	case 19:
-#line 185 "./zscanner/scanner_body.rl"
+#line 185 "./scanner_body.rl"
 	{
 		s->item_length_position = 0;
 		s->dname_tmp_length = 0;
 	}
 	break;
 	case 20:
-#line 189 "./zscanner/scanner_body.rl"
+#line 189 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_DNAME_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
 	case 21:
-#line 201 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 249; goto _again;} }
+#line 201 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 248; goto _again;} }
 	break;
 	case 22:
-#line 205 "./zscanner/scanner_body.rl"
+#line 205 "./scanner_body.rl"
 	{
 		s->item_length_location = rdata_tail++;
 	}
 	break;
 	case 23:
-#line 208 "./zscanner/scanner_body.rl"
+#line 208 "./scanner_body.rl"
 	{
 		s->item_length = rdata_tail - s->item_length_location - 1;
 
@@ -5437,61 +5456,54 @@ _match:
 			*(s->item_length_location) = (uint8_t)(s->item_length);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EITEM_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
 	case 24:
-#line 219 "./zscanner/scanner_body.rl"
-	{
-		s->r_data_blocks[++(s->r_data_blocks_count)] =
-			rdata_tail - s->r_data;
-	}
-	break;
-	case 25:
-#line 229 "./zscanner/scanner_body.rl"
+#line 221 "./scanner_body.rl"
 	{
 		s->dname = s->r_owner;
 		s->r_owner_length = 0;
 	}
 	break;
-	case 26:
-#line 233 "./zscanner/scanner_body.rl"
+	case 25:
+#line 225 "./scanner_body.rl"
 	{
 		s->r_owner_length = s->dname_tmp_length;
 	}
 	break;
-	case 27:
-#line 236 "./zscanner/scanner_body.rl"
+	case 26:
+#line 228 "./scanner_body.rl"
 	{
 		if (s->r_owner_length == 0) {
 			SCANNER_WARNING(ZSCANNER_EBAD_PREVIOUS_OWNER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 28:
-#line 242 "./zscanner/scanner_body.rl"
+	case 27:
+#line 234 "./scanner_body.rl"
 	{
 		s->r_owner_length = 0;
 		SCANNER_WARNING(ZSCANNER_EBAD_OWNER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 29:
-#line 254 "./zscanner/scanner_body.rl"
+	case 28:
+#line 246 "./scanner_body.rl"
 	{
 		s->dname = rdata_tail;
 	}
 	break;
-	case 30:
-#line 257 "./zscanner/scanner_body.rl"
+	case 29:
+#line 249 "./scanner_body.rl"
 	{
 		rdata_tail += s->dname_tmp_length;
 	}
 	break;
-	case 31:
-#line 265 "./zscanner/scanner_body.rl"
+	case 30:
+#line 257 "./scanner_body.rl"
 	{
 		// Overflow check: 10*(s->number64) + fc - ASCII_0 <= UINT64_MAX
 		if ((s->number64 < (UINT64_MAX / 10)) ||   // Dominant fast check.
@@ -5503,43 +5515,43 @@ _match:
 			s->number64 += digit_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER64_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 32:
-#line 282 "./zscanner/scanner_body.rl"
+	case 31:
+#line 274 "./scanner_body.rl"
 	{
 		s->number64 = 0;
 	}
 	break;
-	case 33:
-#line 285 "./zscanner/scanner_body.rl"
+	case 32:
+#line 277 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 34:
-#line 293 "./zscanner/scanner_body.rl"
+	case 33:
+#line 285 "./scanner_body.rl"
 	{
 		s->decimal_counter = 0;
 	}
 	break;
-	case 35:
-#line 296 "./zscanner/scanner_body.rl"
+	case 34:
+#line 288 "./scanner_body.rl"
 	{
 		s->number64_tmp = s->number64;
 	}
 	break;
-	case 36:
-#line 299 "./zscanner/scanner_body.rl"
+	case 35:
+#line 291 "./scanner_body.rl"
 	{
 		s->decimal_counter++;
 	}
 	break;
-	case 37:
-#line 303 "./zscanner/scanner_body.rl"
+	case 36:
+#line 295 "./scanner_body.rl"
 	{
 		if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
 			s->number64 *= pow(10, s->decimals);
@@ -5549,175 +5561,175 @@ _match:
 			s->number64 += s->number64_tmp * pow(10, s->decimals);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EFLOAT_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 38:
-#line 320 "./zscanner/scanner_body.rl"
+	case 37:
+#line 312 "./scanner_body.rl"
 	{
 		s->decimals = 2;
 	}
 	break;
-	case 39:
-#line 323 "./zscanner/scanner_body.rl"
+	case 38:
+#line 315 "./scanner_body.rl"
 	{
 		s->decimals = 3;
 	}
 	break;
-	case 40:
-#line 332 "./zscanner/scanner_body.rl"
+	case 39:
+#line 324 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT8_MAX) {
 			*rdata_tail = (uint8_t)(s->number64);
 			rdata_tail += 1;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER8_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 41:
-#line 341 "./zscanner/scanner_body.rl"
+	case 40:
+#line 333 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT16_MAX) {
 			*((uint16_t *)rdata_tail) = htons((uint16_t)(s->number64));
 			rdata_tail += 2;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER16_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 42:
-#line 350 "./zscanner/scanner_body.rl"
+	case 41:
+#line 342 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT32_MAX) {
 			*((uint32_t *)rdata_tail) = htonl((uint32_t)(s->number64));
 			rdata_tail += 4;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 43:
-#line 360 "./zscanner/scanner_body.rl"
+	case 42:
+#line 352 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT16_MAX) {
 			s->r_type = (uint16_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER16_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 44:
-#line 369 "./zscanner/scanner_body.rl"
+	case 43:
+#line 361 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT16_MAX) {
 			s->r_data_length = (uint16_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER16_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 45:
-#line 386 "./zscanner/scanner_body.rl"
+	case 44:
+#line 378 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TIME_UNIT);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 46:
-#line 393 "./zscanner/scanner_body.rl"
+	case 45:
+#line 385 "./scanner_body.rl"
 	{ if (s->number64 <= (UINT32_MAX / 60)) {
 	                  s->number64 *= 60;
 	              } else {
 	                  SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-	                  p--; {cs = 247; goto _again;}
+	                  p--; {cs = 246; goto _again;}
 	              }
 	            }
 	break;
-	case 47:
-#line 400 "./zscanner/scanner_body.rl"
+	case 46:
+#line 392 "./scanner_body.rl"
 	{ if (s->number64 <= (UINT32_MAX / 3600)) {
 	                  s->number64 *= 3600;
 	              } else {
 	                  SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-	                  p--; {cs = 247; goto _again;}
+	                  p--; {cs = 246; goto _again;}
 	              }
 	            }
 	break;
-	case 48:
-#line 407 "./zscanner/scanner_body.rl"
+	case 47:
+#line 399 "./scanner_body.rl"
 	{ if (s->number64 <= (UINT32_MAX / 86400)) {
 	                  s->number64 *= 86400;
 	              } else {
 	                  SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-	                  p--; {cs = 247; goto _again;}
+	                  p--; {cs = 246; goto _again;}
 	              }
 	            }
 	break;
-	case 49:
-#line 414 "./zscanner/scanner_body.rl"
+	case 48:
+#line 406 "./scanner_body.rl"
 	{ if (s->number64 <= (UINT32_MAX / 604800)) {
 	                  s->number64 *= 604800;
 	              } else {
 	                  SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-	                  p--; {cs = 247; goto _again;}
+	                  p--; {cs = 246; goto _again;}
 	              }
 	            }
 	break;
-	case 50:
-#line 424 "./zscanner/scanner_body.rl"
+	case 49:
+#line 416 "./scanner_body.rl"
 	{
 		s->number64_tmp = s->number64;
 	}
 	break;
-	case 51:
-#line 427 "./zscanner/scanner_body.rl"
+	case 50:
+#line 419 "./scanner_body.rl"
 	{
 		if (s->number64 + s->number64_tmp < UINT32_MAX) {
 			s->number64 += s->number64_tmp;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 52:
-#line 445 "./zscanner/scanner_body.rl"
+	case 51:
+#line 437 "./scanner_body.rl"
 	{
 		s->buffer_length = 0;
 	}
 	break;
-	case 53:
-#line 448 "./zscanner/scanner_body.rl"
+	case 52:
+#line 440 "./scanner_body.rl"
 	{
 		if (s->buffer_length < MAX_RDATA_LENGTH) {
 			s->buffer[s->buffer_length++] = (*p);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 54:
-#line 456 "./zscanner/scanner_body.rl"
+	case 53:
+#line 448 "./scanner_body.rl"
 	{
 		s->buffer[s->buffer_length] = 0;
 
 		if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
 			ret = date_to_timestamp(s->buffer, &timestamp);
 
-			if (ret == KNOT_EOK) {
+			if (ret == ZSCANNER_OK) {
 				*((uint32_t *)rdata_tail) = htonl(timestamp);
 				rdata_tail += 4;
 			} else {
 				SCANNER_WARNING(ret);
-				p--; {cs = 247; goto _again;}
+				p--; {cs = 246; goto _again;}
 			}
 		} else if (s->buffer_length <= 10) { // Timestamp format.
 			char *end;
@@ -5726,7 +5738,7 @@ _match:
 
 			if (end == (char *)(s->buffer) || *end != '\0') {
 				SCANNER_WARNING(ZSCANNER_EBAD_TIMESTAMP);
-				p--; {cs = 247; goto _again;}
+				p--; {cs = 246; goto _again;}
 			}
 
 			if (s->number64 <= UINT32_MAX) {
@@ -5734,60 +5746,60 @@ _match:
 				rdata_tail += 4;
 			} else {
 				SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-				p--; {cs = 247; goto _again;}
+				p--; {cs = 246; goto _again;}
 			}
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_TIMESTAMP_LENGTH);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 55:
-#line 491 "./zscanner/scanner_body.rl"
+	case 54:
+#line 483 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TIMESTAMP_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 56:
-#line 501 "./zscanner/scanner_body.rl"
+	case 55:
+#line 493 "./scanner_body.rl"
 	{
 		if (rdata_tail <= rdata_stop) {
 			*(rdata_tail++) = (*p);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ETEXT_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 57:
-#line 509 "./zscanner/scanner_body.rl"
+	case 56:
+#line 501 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TEXT_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 58:
-#line 513 "./zscanner/scanner_body.rl"
+	case 57:
+#line 505 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TEXT);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 59:
-#line 518 "./zscanner/scanner_body.rl"
+	case 58:
+#line 510 "./scanner_body.rl"
 	{
 		if (rdata_tail <= rdata_stop) {
 			*rdata_tail = 0;
 			s->item_length++;
 		} else {
 			SCANNER_WARNING(ZSCANNER_ETEXT_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 60:
-#line 527 "./zscanner/scanner_body.rl"
+	case 59:
+#line 519 "./scanner_body.rl"
 	{
 		if ((*rdata_tail < (UINT8_MAX / 10)) ||   // Dominant fast check.
 			((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
@@ -5798,80 +5810,80 @@ _match:
 			*rdata_tail += digit_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER8_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 61:
-#line 540 "./zscanner/scanner_body.rl"
+	case 60:
+#line 532 "./scanner_body.rl"
 	{
 		rdata_tail++;
 	}
 	break;
-	case 62:
-#line 543 "./zscanner/scanner_body.rl"
+	case 61:
+#line 535 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 63:
-#line 565 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 259; goto _again;} }
+	case 62:
+#line 557 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 258; goto _again;} }
 	break;
-	case 64:
-#line 575 "./zscanner/scanner_body.rl"
+	case 63:
+#line 567 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT32_MAX) {
 			s->default_ttl = (uint32_t)(s->number64);
 		} else {
 			SCANNER_ERROR(ZSCANNER_ENUMBER32_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 65:
-#line 583 "./zscanner/scanner_body.rl"
+	case 64:
+#line 575 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_TTL);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 66:
-#line 590 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 271; goto _again;} }
+	case 65:
+#line 582 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 270; goto _again;} }
 	break;
-	case 67:
-#line 594 "./zscanner/scanner_body.rl"
+	case 66:
+#line 586 "./scanner_body.rl"
 	{
 		s->dname = s->zone_origin;
 	}
 	break;
-	case 68:
-#line 597 "./zscanner/scanner_body.rl"
+	case 67:
+#line 589 "./scanner_body.rl"
 	{
 		s->zone_origin_length = s->dname_tmp_length;
 	}
 	break;
-	case 69:
-#line 600 "./zscanner/scanner_body.rl"
+	case 68:
+#line 592 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_ORIGIN);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 70:
-#line 607 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 280; goto _again;} }
+	case 69:
+#line 599 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 279; goto _again;} }
 	break;
-	case 71:
-#line 611 "./zscanner/scanner_body.rl"
+	case 70:
+#line 603 "./scanner_body.rl"
 	{
 		rdata_tail = s->r_data;
 	}
 	break;
-	case 72:
-#line 614 "./zscanner/scanner_body.rl"
+	case 71:
+#line 606 "./scanner_body.rl"
 	{
 		*rdata_tail = 0; // Ending filename string.
 		strncpy((char*)(s->include_filename), (char*)(s->r_data),
@@ -5881,72 +5893,59 @@ _match:
 		if (strlen(s->include_filename) !=
 		    (size_t)(rdata_tail - s->r_data)) {
 			SCANNER_ERROR(ZSCANNER_EBAD_INCLUDE_FILENAME);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 
 		// For detection whether origin is not present.
 		s->dname = NULL;
 	}
 	break;
-	case 73:
-#line 629 "./zscanner/scanner_body.rl"
+	case 72:
+#line 621 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_INCLUDE_FILENAME);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 74:
-#line 634 "./zscanner/scanner_body.rl"
+	case 73:
+#line 626 "./scanner_body.rl"
 	{
 		s->dname = s->r_data;
 	}
 	break;
-	case 75:
-#line 637 "./zscanner/scanner_body.rl"
+	case 74:
+#line 629 "./scanner_body.rl"
 	{
 		s->r_data_length = s->dname_tmp_length;
 	}
 	break;
-	case 76:
-#line 640 "./zscanner/scanner_body.rl"
+	case 75:
+#line 632 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_INCLUDE_ORIGIN);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 77:
-#line 645 "./zscanner/scanner_body.rl"
+	case 76:
+#line 637 "./scanner_body.rl"
 	{
-		char text_origin[MAX_DNAME_LENGTH];
+		char text_origin[4 * MAX_DNAME_LENGTH]; // Each char as \DDD.
 
 		// Origin conversion from wire to text form.
 		if (s->dname == NULL) { // Use current origin.
 			wire_dname_to_str(s->zone_origin,
 			                  s->zone_origin_length,
-					  text_origin);
+			                  text_origin);
 		} else { // Use specified origin.
 			wire_dname_to_str(s->r_data,
 			                  s->r_data_length,
-					  text_origin);
+			                  text_origin);
 		}
 
-		if (s->include_filename[0] != '/') { // Relative file path.
-			// Get absolute path of the current zone file.
-			if (realpath(s->file_name, (char*)(s->buffer)) != NULL) {
-				char *full_current_zone_file_name =
-					strdup((char*)(s->buffer));
-
-				// Creating full include file name.
-				snprintf((char*)(s->buffer), sizeof(s->buffer),
-				        "%s/%s",
-				        dirname(full_current_zone_file_name),
-				        s->include_filename);
-
-				free(full_current_zone_file_name);
-			} else {
-				SCANNER_ERROR(ZSCANNER_EUNPROCESSED_INCLUDE);
-				p--; {cs = 247; goto _again;}
-			}
+		// Relative file path.
+		if (s->include_filename[0] != '/') {
+			snprintf((char*)(s->buffer), sizeof(s->buffer),
+			         "%s/%s", s->path, s->include_filename);
 		} else {
 			strncpy((char*)(s->buffer), (char*)(s->include_filename),
 			        sizeof(s->buffer));
@@ -5955,8 +5954,8 @@ _match:
 		// Create new file loader for included zone file.
 		file_loader_t *fl = file_loader_create((char*)(s->buffer),
 		                                       text_origin,
-		                                       DEFAULT_CLASS,
-		                                       DEFAULT_TTL,
+		                                       s->default_class,
+		                                       s->default_ttl,
 		                                       s->process_record,
 		                                       s->process_error,
 		                                       s->data);
@@ -5967,165 +5966,165 @@ _match:
 
 			if (ret != 0) {
 				SCANNER_ERROR(ZSCANNER_EUNPROCESSED_INCLUDE);
-				p--; {cs = 247; goto _again;}
+				p--; {cs = 246; goto _again;}
 			}
 		} else {
 			SCANNER_ERROR(ZSCANNER_EUNOPENED_INCLUDE);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 78:
-#line 711 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 293; goto _again;} }
+	case 77:
+#line 690 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 292; goto _again;} }
 	break;
-	case 79:
-#line 717 "./zscanner/scanner_body.rl"
+	case 78:
+#line 696 "./scanner_body.rl"
 	{
 		s->stop = true;
 	}
 	break;
-	case 80:
-#line 721 "./zscanner/scanner_body.rl"
+	case 79:
+#line 700 "./scanner_body.rl"
 	{
 		s->stop = false;
 	}
 	break;
-	case 81:
-#line 724 "./zscanner/scanner_body.rl"
+	case 80:
+#line 703 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_DIRECTIVE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 82:
-#line 736 "./zscanner/scanner_body.rl"
+	case 81:
+#line 715 "./scanner_body.rl"
 	{
 		s->r_class = s->default_class;
 	}
 	break;
-	case 83:
-#line 740 "./zscanner/scanner_body.rl"
+	case 82:
+#line 719 "./scanner_body.rl"
 	{
 		s->r_ttl = s->default_ttl;
 	}
 	break;
-	case 84:
-#line 744 "./zscanner/scanner_body.rl"
+	case 83:
+#line 723 "./scanner_body.rl"
 	{
 		s->r_class = KNOT_CLASS_IN;
 	}
 	break;
-	case 85:
-#line 748 "./zscanner/scanner_body.rl"
+	case 84:
+#line 727 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT32_MAX) {
 			s->r_ttl = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER32_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 86:
-#line 763 "./zscanner/scanner_body.rl"
+	case 85:
+#line 742 "./scanner_body.rl"
 	{
 		s->buffer_length = 0;
 	}
 	break;
-	case 87:
-#line 766 "./zscanner/scanner_body.rl"
+	case 86:
+#line 745 "./scanner_body.rl"
 	{
 		if (s->buffer_length < MAX_RDATA_LENGTH) {
 			s->buffer[s->buffer_length++] = (*p);
 		}
 		else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 88:
-#line 775 "./zscanner/scanner_body.rl"
+	case 87:
+#line 754 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_ADDRESS_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 89:
-#line 780 "./zscanner/scanner_body.rl"
+	case 88:
+#line 759 "./scanner_body.rl"
 	{
 		s->buffer[s->buffer_length] = 0;
 
 		if (inet_pton(AF_INET, (char *)s->buffer, &addr4) <= 0) {
 			SCANNER_WARNING(ZSCANNER_EBAD_IPV4);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 90:
-#line 788 "./zscanner/scanner_body.rl"
+	case 89:
+#line 767 "./scanner_body.rl"
 	{
 		memcpy(rdata_tail, &(addr4.s_addr), INET4_ADDR_LENGTH);
 		rdata_tail += INET4_ADDR_LENGTH;
 	}
 	break;
-	case 91:
-#line 793 "./zscanner/scanner_body.rl"
+	case 90:
+#line 772 "./scanner_body.rl"
 	{
 		s->buffer[s->buffer_length] = 0;
 
 		if (inet_pton(AF_INET6, (char *)s->buffer, &addr6) <= 0) {
 			SCANNER_WARNING(ZSCANNER_EBAD_IPV6);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 92:
-#line 801 "./zscanner/scanner_body.rl"
+	case 91:
+#line 780 "./scanner_body.rl"
 	{
 		memcpy(rdata_tail, &(addr6.s6_addr), INET6_ADDR_LENGTH);
 		rdata_tail += INET6_ADDR_LENGTH;
 	}
 	break;
-	case 93:
-#line 818 "./zscanner/scanner_body.rl"
+	case 92:
+#line 797 "./scanner_body.rl"
 	{
 		memset(&(s->apl), 0, sizeof(s->apl));
 	}
 	break;
-	case 94:
-#line 821 "./zscanner/scanner_body.rl"
+	case 93:
+#line 800 "./scanner_body.rl"
 	{
 		s->apl.excl_flag = 128; // dec 128  = bin 10000000.
 	}
 	break;
-	case 95:
-#line 824 "./zscanner/scanner_body.rl"
+	case 94:
+#line 803 "./scanner_body.rl"
 	{
 		s->apl.addr_family = 1;
 	}
 	break;
-	case 96:
-#line 827 "./zscanner/scanner_body.rl"
+	case 95:
+#line 806 "./scanner_body.rl"
 	{
 		s->apl.addr_family = 2;
 	}
 	break;
-	case 97:
-#line 830 "./zscanner/scanner_body.rl"
+	case 96:
+#line 809 "./scanner_body.rl"
 	{
 		if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
 		    (s->apl.addr_family == 2 && s->number64 <= 128)) {
 			s->apl.prefix_length = (uint8_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_APL);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 98:
-#line 839 "./zscanner/scanner_body.rl"
+	case 97:
+#line 818 "./scanner_body.rl"
 	{
 		// Write address family.
 		*((uint16_t *)rdata_tail) = htons(s->apl.addr_family);
@@ -6146,7 +6145,7 @@ _match:
 			break;
 		default:
 			SCANNER_WARNING(ZSCANNER_EBAD_APL);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 		// Find prefix without trailing zeroes.
 		while (len > 0) {
@@ -6163,73 +6162,67 @@ _match:
 		rdata_tail += len;
 	}
 	break;
-	case 99:
-#line 875 "./zscanner/scanner_body.rl"
+	case 98:
+#line 854 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_APL);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 100:
-#line 893 "./zscanner/scanner_body.rl"
+	case 99:
+#line 872 "./scanner_body.rl"
 	{
 		if (rdata_tail <= rdata_stop) {
 			*rdata_tail = first_hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 101:
-#line 901 "./zscanner/scanner_body.rl"
+	case 100:
+#line 880 "./scanner_body.rl"
 	{
 		*rdata_tail += second_hex_to_num[(uint8_t)(*p)];
 		rdata_tail++;
 	}
 	break;
-	case 102:
-#line 905 "./zscanner/scanner_body.rl"
+	case 101:
+#line 884 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 103:
-#line 919 "./zscanner/scanner_body.rl"
+	case 102:
+#line 898 "./scanner_body.rl"
 	{
 		if ((rdata_tail - s->r_data) != s->r_data_length) {
 			SCANNER_WARNING(ZSCANNER_EBAD_RDATA_LENGTH);
-			p--; {cs = 247; goto _again;}
-		}
-
-		ret = find_rdata_blocks(s);
-		if (ret != KNOT_EOK) {
-			SCANNER_WARNING(ret);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 104:
-#line 932 "./zscanner/scanner_body.rl"
+	case 103:
+#line 905 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 105:
-#line 942 "./zscanner/scanner_body.rl"
+	case 104:
+#line 915 "./scanner_body.rl"
 	{
 		if (rdata_tail <= rdata_stop) {
 			*rdata_tail = first_base64_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 106:
-#line 950 "./zscanner/scanner_body.rl"
+	case 105:
+#line 923 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)];
 
@@ -6237,12 +6230,12 @@ _match:
 			*rdata_tail = second_right_base64_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 107:
-#line 960 "./zscanner/scanner_body.rl"
+	case 106:
+#line 933 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)];
 
@@ -6250,40 +6243,40 @@ _match:
 			*rdata_tail = third_right_base64_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 108:
-#line 970 "./zscanner/scanner_body.rl"
+	case 107:
+#line 943 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)];
 	}
 	break;
-	case 109:
-#line 974 "./zscanner/scanner_body.rl"
+	case 108:
+#line 947 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BASE64_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 110:
-#line 996 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 311; goto _again;} }
+	case 109:
+#line 969 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 310; goto _again;} }
 	break;
-	case 111:
-#line 1000 "./zscanner/scanner_body.rl"
+	case 110:
+#line 973 "./scanner_body.rl"
 	{
 		if (rdata_tail <= rdata_stop) {
 			*rdata_tail = first_base32hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 112:
-#line 1008 "./zscanner/scanner_body.rl"
+	case 111:
+#line 981 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)(*p)];
 
@@ -6291,18 +6284,18 @@ _match:
 			*rdata_tail = second_right_base32hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 113:
-#line 1018 "./zscanner/scanner_body.rl"
+	case 112:
+#line 991 "./scanner_body.rl"
 	{
 		*rdata_tail += third_base32hex_to_num[(uint8_t)(*p)];
 	}
 	break;
-	case 114:
-#line 1021 "./zscanner/scanner_body.rl"
+	case 113:
+#line 994 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)(*p)];
 
@@ -6310,12 +6303,12 @@ _match:
 			*rdata_tail = fourth_right_base32hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 115:
-#line 1031 "./zscanner/scanner_body.rl"
+	case 114:
+#line 1004 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)(*p)];
 
@@ -6323,18 +6316,18 @@ _match:
 			*rdata_tail = fifth_right_base32hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 116:
-#line 1041 "./zscanner/scanner_body.rl"
+	case 115:
+#line 1014 "./scanner_body.rl"
 	{
 		*rdata_tail += sixth_base32hex_to_num[(uint8_t)(*p)];
 	}
 	break;
-	case 117:
-#line 1044 "./zscanner/scanner_body.rl"
+	case 116:
+#line 1017 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)(*p)];
 
@@ -6342,528 +6335,528 @@ _match:
 			*rdata_tail = seventh_right_base32hex_to_num[(uint8_t)(*p)];
 		} else {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 118:
-#line 1054 "./zscanner/scanner_body.rl"
+	case 117:
+#line 1027 "./scanner_body.rl"
 	{
 		*(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)(*p)];
 	}
 	break;
-	case 119:
-#line 1058 "./zscanner/scanner_body.rl"
+	case 118:
+#line 1031 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BASE32HEX_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 120:
-#line 1093 "./zscanner/scanner_body.rl"
+	case 119:
+#line 1066 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 0;
 	}
 	break;
-	case 121:
-#line 1096 "./zscanner/scanner_body.rl"
+	case 120:
+#line 1069 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 1;
 	}
 	break;
-	case 122:
-#line 1099 "./zscanner/scanner_body.rl"
+	case 121:
+#line 1072 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 2;
 	}
 	break;
-	case 123:
-#line 1102 "./zscanner/scanner_body.rl"
+	case 122:
+#line 1075 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 3;
 	}
 	break;
-	case 124:
-#line 1105 "./zscanner/scanner_body.rl"
+	case 123:
+#line 1078 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 5;
 	}
 	break;
-	case 125:
-#line 1108 "./zscanner/scanner_body.rl"
+	case 124:
+#line 1081 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 6;
 	}
 	break;
-	case 126:
-#line 1111 "./zscanner/scanner_body.rl"
+	case 125:
+#line 1084 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 7;
 	}
 	break;
-	case 127:
-#line 1114 "./zscanner/scanner_body.rl"
+	case 126:
+#line 1087 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 8;
 	}
 	break;
-	case 128:
-#line 1117 "./zscanner/scanner_body.rl"
+	case 127:
+#line 1090 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 10;
 	}
 	break;
-	case 129:
-#line 1120 "./zscanner/scanner_body.rl"
+	case 128:
+#line 1093 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 12;
 	}
 	break;
-	case 130:
-#line 1123 "./zscanner/scanner_body.rl"
+	case 129:
+#line 1096 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 13;
 	}
 	break;
-	case 131:
-#line 1126 "./zscanner/scanner_body.rl"
+	case 130:
+#line 1099 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 14;
 	}
 	break;
-	case 132:
-#line 1129 "./zscanner/scanner_body.rl"
+	case 131:
+#line 1102 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 252;
 	}
 	break;
-	case 133:
-#line 1132 "./zscanner/scanner_body.rl"
+	case 132:
+#line 1105 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 253;
 	}
 	break;
-	case 134:
-#line 1135 "./zscanner/scanner_body.rl"
+	case 133:
+#line 1108 "./scanner_body.rl"
 	{
 		*(rdata_tail++) = 254;
 	}
 	break;
-	case 135:
-#line 1139 "./zscanner/scanner_body.rl"
+	case 134:
+#line 1112 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(1);
 		rdata_tail += 2;
 	}
 	break;
-	case 136:
-#line 1143 "./zscanner/scanner_body.rl"
+	case 135:
+#line 1116 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(2);
 		rdata_tail += 2;
 	}
 	break;
-	case 137:
-#line 1147 "./zscanner/scanner_body.rl"
+	case 136:
+#line 1120 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(3);
 		rdata_tail += 2;
 	}
 	break;
-	case 138:
-#line 1151 "./zscanner/scanner_body.rl"
+	case 137:
+#line 1124 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(4);
 		rdata_tail += 2;
 	}
 	break;
-	case 139:
-#line 1155 "./zscanner/scanner_body.rl"
+	case 138:
+#line 1128 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(5);
 		rdata_tail += 2;
 	}
 	break;
-	case 140:
-#line 1159 "./zscanner/scanner_body.rl"
+	case 139:
+#line 1132 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(6);
 		rdata_tail += 2;
 	}
 	break;
-	case 141:
-#line 1163 "./zscanner/scanner_body.rl"
+	case 140:
+#line 1136 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(7);
 		rdata_tail += 2;
 	}
 	break;
-	case 142:
-#line 1167 "./zscanner/scanner_body.rl"
+	case 141:
+#line 1140 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(8);
 		rdata_tail += 2;
 	}
 	break;
-	case 143:
-#line 1171 "./zscanner/scanner_body.rl"
+	case 142:
+#line 1144 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(253);
 		rdata_tail += 2;
 	}
 	break;
-	case 144:
-#line 1175 "./zscanner/scanner_body.rl"
+	case 143:
+#line 1148 "./scanner_body.rl"
 	{
 		*((uint16_t *)rdata_tail) = htons(254);
 		rdata_tail += 2;
 	}
 	break;
-	case 145:
-#line 1182 "./zscanner/scanner_body.rl"
+	case 144:
+#line 1155 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_GATEWAY);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 146:
-#line 1186 "./zscanner/scanner_body.rl"
+	case 145:
+#line 1159 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_GATEWAY_KEY);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 147:
-#line 1204 "./zscanner/scanner_body.rl"
+	case 146:
+#line 1177 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EUNSUPPORTED_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 148:
-#line 1210 "./zscanner/scanner_body.rl"
+	case 147:
+#line 1183 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_A, &rdata_tail); }
 	break;
-	case 149:
-#line 1211 "./zscanner/scanner_body.rl"
+	case 148:
+#line 1184 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NS, &rdata_tail); }
 	break;
-	case 150:
-#line 1212 "./zscanner/scanner_body.rl"
+	case 149:
+#line 1185 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
 	break;
-	case 151:
-#line 1213 "./zscanner/scanner_body.rl"
+	case 150:
+#line 1186 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
 	break;
-	case 152:
-#line 1214 "./zscanner/scanner_body.rl"
+	case 151:
+#line 1187 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
 	break;
-	case 153:
-#line 1215 "./zscanner/scanner_body.rl"
+	case 152:
+#line 1188 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
 	break;
-	case 154:
-#line 1216 "./zscanner/scanner_body.rl"
+	case 153:
+#line 1189 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
 	break;
-	case 155:
-#line 1217 "./zscanner/scanner_body.rl"
+	case 154:
+#line 1190 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_MX, &rdata_tail); }
 	break;
-	case 156:
-#line 1218 "./zscanner/scanner_body.rl"
+	case 155:
+#line 1191 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
 	break;
-	case 157:
-#line 1219 "./zscanner/scanner_body.rl"
+	case 156:
+#line 1192 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_RP, &rdata_tail); }
 	break;
-	case 158:
-#line 1220 "./zscanner/scanner_body.rl"
+	case 157:
+#line 1193 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
 	break;
-	case 159:
-#line 1221 "./zscanner/scanner_body.rl"
+	case 158:
+#line 1194 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_RT, &rdata_tail); }
 	break;
-	case 160:
-#line 1222 "./zscanner/scanner_body.rl"
+	case 159:
+#line 1195 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
 	break;
-	case 161:
-#line 1223 "./zscanner/scanner_body.rl"
+	case 160:
+#line 1196 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
 	break;
-	case 162:
-#line 1224 "./zscanner/scanner_body.rl"
+	case 161:
+#line 1197 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
 	break;
-	case 163:
-#line 1225 "./zscanner/scanner_body.rl"
+	case 162:
+#line 1198 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
 	break;
-	case 164:
-#line 1226 "./zscanner/scanner_body.rl"
+	case 163:
+#line 1199 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
 	break;
-	case 165:
-#line 1227 "./zscanner/scanner_body.rl"
+	case 164:
+#line 1200 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_KX, &rdata_tail); }
 	break;
-	case 166:
-#line 1228 "./zscanner/scanner_body.rl"
+	case 165:
+#line 1201 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
 	break;
-	case 167:
-#line 1229 "./zscanner/scanner_body.rl"
+	case 166:
+#line 1202 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
 	break;
-	case 168:
-#line 1230 "./zscanner/scanner_body.rl"
+	case 167:
+#line 1203 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_APL, &rdata_tail); }
 	break;
-	case 169:
-#line 1231 "./zscanner/scanner_body.rl"
+	case 168:
+#line 1204 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_DS, &rdata_tail); }
 	break;
-	case 170:
-#line 1232 "./zscanner/scanner_body.rl"
+	case 169:
+#line 1205 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
 	break;
-	case 171:
-#line 1233 "./zscanner/scanner_body.rl"
+	case 170:
+#line 1206 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
 	break;
-	case 172:
-#line 1234 "./zscanner/scanner_body.rl"
+	case 171:
+#line 1207 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
 	break;
-	case 173:
-#line 1235 "./zscanner/scanner_body.rl"
+	case 172:
+#line 1208 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
 	break;
-	case 174:
-#line 1236 "./zscanner/scanner_body.rl"
+	case 173:
+#line 1209 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
 	break;
-	case 175:
-#line 1237 "./zscanner/scanner_body.rl"
+	case 174:
+#line 1210 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
 	break;
-	case 176:
-#line 1238 "./zscanner/scanner_body.rl"
+	case 175:
+#line 1211 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
 	break;
-	case 177:
-#line 1239 "./zscanner/scanner_body.rl"
+	case 176:
+#line 1212 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
 	break;
-	case 178:
-#line 1240 "./zscanner/scanner_body.rl"
+	case 177:
+#line 1213 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
 	break;
-	case 179:
-#line 1241 "./zscanner/scanner_body.rl"
+	case 178:
+#line 1214 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
 	break;
-	case 180:
-#line 1242 "./zscanner/scanner_body.rl"
+	case 179:
+#line 1215 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_NID, &rdata_tail); }
 	break;
-	case 181:
-#line 1243 "./zscanner/scanner_body.rl"
+	case 180:
+#line 1216 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_L32, &rdata_tail); }
 	break;
-	case 182:
-#line 1244 "./zscanner/scanner_body.rl"
+	case 181:
+#line 1217 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_L64, &rdata_tail); }
 	break;
-	case 183:
-#line 1245 "./zscanner/scanner_body.rl"
+	case 182:
+#line 1218 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_LP, &rdata_tail); }
 	break;
-	case 184:
-#line 1246 "./zscanner/scanner_body.rl"
+	case 183:
+#line 1219 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
 	break;
-	case 185:
-#line 1247 "./zscanner/scanner_body.rl"
+	case 184:
+#line 1220 "./scanner_body.rl"
 	{ type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
 	break;
-	case 186:
-#line 1253 "./zscanner/scanner_body.rl"
+	case 185:
+#line 1226 "./scanner_body.rl"
 	{
 		if (s->number64 <= UINT16_MAX) {
 			window_add_bit(s->number64, s);
 		} else {
 			SCANNER_WARNING(ZSCANNER_ENUMBER16_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 187:
-#line 1266 "./zscanner/scanner_body.rl"
+	case 186:
+#line 1239 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_A, s); }
 	break;
-	case 188:
-#line 1267 "./zscanner/scanner_body.rl"
+	case 187:
+#line 1240 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NS, s); }
 	break;
-	case 189:
-#line 1268 "./zscanner/scanner_body.rl"
+	case 188:
+#line 1241 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_CNAME, s); }
 	break;
-	case 190:
-#line 1269 "./zscanner/scanner_body.rl"
+	case 189:
+#line 1242 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_SOA, s); }
 	break;
-	case 191:
-#line 1270 "./zscanner/scanner_body.rl"
+	case 190:
+#line 1243 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_PTR, s); }
 	break;
-	case 192:
-#line 1271 "./zscanner/scanner_body.rl"
+	case 191:
+#line 1244 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_HINFO, s); }
 	break;
-	case 193:
-#line 1272 "./zscanner/scanner_body.rl"
+	case 192:
+#line 1245 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_MINFO, s); }
 	break;
-	case 194:
-#line 1273 "./zscanner/scanner_body.rl"
+	case 193:
+#line 1246 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_MX, s); }
 	break;
-	case 195:
-#line 1274 "./zscanner/scanner_body.rl"
+	case 194:
+#line 1247 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_TXT, s); }
 	break;
-	case 196:
-#line 1275 "./zscanner/scanner_body.rl"
+	case 195:
+#line 1248 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_RP, s); }
 	break;
-	case 197:
-#line 1276 "./zscanner/scanner_body.rl"
+	case 196:
+#line 1249 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_AFSDB, s); }
 	break;
-	case 198:
-#line 1277 "./zscanner/scanner_body.rl"
+	case 197:
+#line 1250 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_RT, s); }
 	break;
-	case 199:
-#line 1278 "./zscanner/scanner_body.rl"
+	case 198:
+#line 1251 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_KEY, s); }
 	break;
-	case 200:
-#line 1279 "./zscanner/scanner_body.rl"
+	case 199:
+#line 1252 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_AAAA, s); }
 	break;
-	case 201:
-#line 1280 "./zscanner/scanner_body.rl"
+	case 200:
+#line 1253 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_LOC, s); }
 	break;
-	case 202:
-#line 1281 "./zscanner/scanner_body.rl"
+	case 201:
+#line 1254 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_SRV, s); }
 	break;
-	case 203:
-#line 1282 "./zscanner/scanner_body.rl"
+	case 202:
+#line 1255 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NAPTR, s); }
 	break;
-	case 204:
-#line 1283 "./zscanner/scanner_body.rl"
+	case 203:
+#line 1256 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_KX, s); }
 	break;
-	case 205:
-#line 1284 "./zscanner/scanner_body.rl"
+	case 204:
+#line 1257 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_CERT, s); }
 	break;
-	case 206:
-#line 1285 "./zscanner/scanner_body.rl"
+	case 205:
+#line 1258 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_DNAME, s); }
 	break;
-	case 207:
-#line 1286 "./zscanner/scanner_body.rl"
+	case 206:
+#line 1259 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_APL, s); }
 	break;
-	case 208:
-#line 1287 "./zscanner/scanner_body.rl"
+	case 207:
+#line 1260 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_DS, s); }
 	break;
-	case 209:
-#line 1288 "./zscanner/scanner_body.rl"
+	case 208:
+#line 1261 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_SSHFP, s); }
 	break;
-	case 210:
-#line 1289 "./zscanner/scanner_body.rl"
+	case 209:
+#line 1262 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
 	break;
-	case 211:
-#line 1290 "./zscanner/scanner_body.rl"
+	case 210:
+#line 1263 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_RRSIG, s); }
 	break;
-	case 212:
-#line 1291 "./zscanner/scanner_body.rl"
+	case 211:
+#line 1264 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NSEC, s); }
 	break;
-	case 213:
-#line 1292 "./zscanner/scanner_body.rl"
+	case 212:
+#line 1265 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
 	break;
-	case 214:
-#line 1293 "./zscanner/scanner_body.rl"
+	case 213:
+#line 1266 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_DHCID, s); }
 	break;
-	case 215:
-#line 1294 "./zscanner/scanner_body.rl"
+	case 214:
+#line 1267 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NSEC3, s); }
 	break;
-	case 216:
-#line 1295 "./zscanner/scanner_body.rl"
+	case 215:
+#line 1268 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
 	break;
-	case 217:
-#line 1296 "./zscanner/scanner_body.rl"
+	case 216:
+#line 1269 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_TLSA, s); }
 	break;
-	case 218:
-#line 1297 "./zscanner/scanner_body.rl"
+	case 217:
+#line 1270 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_SPF, s); }
 	break;
-	case 219:
-#line 1298 "./zscanner/scanner_body.rl"
+	case 218:
+#line 1271 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_NID, s); }
 	break;
-	case 220:
-#line 1299 "./zscanner/scanner_body.rl"
+	case 219:
+#line 1272 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_L32, s); }
 	break;
-	case 221:
-#line 1300 "./zscanner/scanner_body.rl"
+	case 220:
+#line 1273 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_L64, s); }
 	break;
-	case 222:
-#line 1301 "./zscanner/scanner_body.rl"
+	case 221:
+#line 1274 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_LP, s); }
 	break;
-	case 223:
-#line 1302 "./zscanner/scanner_body.rl"
+	case 222:
+#line 1275 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_EUI48, s); }
 	break;
-	case 224:
-#line 1303 "./zscanner/scanner_body.rl"
+	case 223:
+#line 1276 "./scanner_body.rl"
 	{ window_add_bit(KNOT_RRTYPE_EUI64, s); }
 	break;
-	case 225:
-#line 1307 "./zscanner/scanner_body.rl"
+	case 224:
+#line 1280 "./scanner_body.rl"
 	{
 		memset(s->windows, 0, sizeof(s->windows));
 		s->last_window = -1;
 	}
 	break;
-	case 226:
-#line 1311 "./zscanner/scanner_body.rl"
+	case 225:
+#line 1284 "./scanner_body.rl"
 	{
 		for (window = 0; window <= s->last_window; window++) {
 			if ((s->windows[window]).length > 0) {
@@ -6882,91 +6875,91 @@ _match:
 					rdata_tail += (s->windows[window]).length;
 				} else {
 					SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-					p--; {cs = 247; goto _again;}
+					p--; {cs = 246; goto _again;}
 				}
 			}
 		}
 	}
 	break;
-	case 227:
-#line 1334 "./zscanner/scanner_body.rl"
+	case 226:
+#line 1307 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BITMAP);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 228:
-#line 1342 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 318; goto _again;} }
+	case 227:
+#line 1315 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 317; goto _again;} }
 	break;
-	case 229:
-#line 1346 "./zscanner/scanner_body.rl"
+	case 228:
+#line 1319 "./scanner_body.rl"
 	{
 		if (s->number64 <= 90) {
 			s->loc.d1 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 230:
-#line 1354 "./zscanner/scanner_body.rl"
+	case 229:
+#line 1327 "./scanner_body.rl"
 	{
 		if (s->number64 <= 180) {
 			s->loc.d2 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 231:
-#line 1362 "./zscanner/scanner_body.rl"
+	case 230:
+#line 1335 "./scanner_body.rl"
 	{
 		if (s->number64 <= 59) {
 			s->loc.m1 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 232:
-#line 1370 "./zscanner/scanner_body.rl"
+	case 231:
+#line 1343 "./scanner_body.rl"
 	{
 		if (s->number64 <= 59) {
 			s->loc.m2 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 233:
-#line 1378 "./zscanner/scanner_body.rl"
+	case 232:
+#line 1351 "./scanner_body.rl"
 	{
 		if (s->number64 <= 59999) {
 			s->loc.s1 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 234:
-#line 1386 "./zscanner/scanner_body.rl"
+	case 233:
+#line 1359 "./scanner_body.rl"
 	{
 		if (s->number64 <= 59999) {
 			s->loc.s2 = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 235:
-#line 1394 "./zscanner/scanner_body.rl"
+	case 234:
+#line 1367 "./scanner_body.rl"
 	{
 		if ((s->loc.alt_sign ==  1 && s->number64 <= 4284967295) ||
 		    (s->loc.alt_sign == -1 && s->number64 <=   10000000))
@@ -6974,63 +6967,63 @@ _match:
 			s->loc.alt = (uint32_t)(s->number64);
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 236:
-#line 1404 "./zscanner/scanner_body.rl"
+	case 235:
+#line 1377 "./scanner_body.rl"
 	{
 		if (s->number64 <= 9000000000ULL) {
 			s->loc.siz = s->number64;
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 237:
-#line 1412 "./zscanner/scanner_body.rl"
+	case 236:
+#line 1385 "./scanner_body.rl"
 	{
 		if (s->number64 <= 9000000000ULL) {
 			s->loc.hp = s->number64;
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 238:
-#line 1420 "./zscanner/scanner_body.rl"
+	case 237:
+#line 1393 "./scanner_body.rl"
 	{
 		if (s->number64 <= 9000000000ULL) {
 			s->loc.vp = s->number64;
 		} else {
 			SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 239:
-#line 1428 "./zscanner/scanner_body.rl"
+	case 238:
+#line 1401 "./scanner_body.rl"
 	{
 		s->loc.lat_sign = -1;
 	}
 	break;
-	case 240:
-#line 1431 "./zscanner/scanner_body.rl"
+	case 239:
+#line 1404 "./scanner_body.rl"
 	{
 		s->loc.long_sign = -1;
 	}
 	break;
-	case 241:
-#line 1434 "./zscanner/scanner_body.rl"
+	case 240:
+#line 1407 "./scanner_body.rl"
 	{
 		s->loc.alt_sign = -1;
 	}
 	break;
-	case 242:
-#line 1451 "./zscanner/scanner_body.rl"
+	case 241:
+#line 1424 "./scanner_body.rl"
 	{
 		memset(&(s->loc), 0, sizeof(s->loc));
 		// Defaults.
@@ -7042,8 +7035,8 @@ _match:
 		s->loc.alt_sign  = 1;
 	}
 	break;
-	case 243:
-#line 1461 "./zscanner/scanner_body.rl"
+	case 242:
+#line 1434 "./scanner_body.rl"
 	{
 		// Write version.
 		*(rdata_tail) = 0;
@@ -7071,200 +7064,198 @@ _match:
 		rdata_tail += 4;
 	}
 	break;
-	case 244:
-#line 1487 "./zscanner/scanner_body.rl"
+	case 243:
+#line 1460 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_LOC_DATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 245:
-#line 1500 "./zscanner/scanner_body.rl"
+	case 244:
+#line 1473 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 246:
-#line 1518 "./zscanner/scanner_body.rl"
+	case 245:
+#line 1491 "./scanner_body.rl"
 	{
 		s->item_length = 0;
 	}
 	break;
-	case 247:
-#line 1521 "./zscanner/scanner_body.rl"
+	case 246:
+#line 1494 "./scanner_body.rl"
 	{
 		s->item_length++;
 	}
 	break;
-	case 248:
-#line 1524 "./zscanner/scanner_body.rl"
+	case 247:
+#line 1497 "./scanner_body.rl"
 	{
 		if (s->item_length != 6) {
 			SCANNER_WARNING(ZSCANNER_EBAD_EUI_LENGTH);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 249:
-#line 1530 "./zscanner/scanner_body.rl"
+	case 248:
+#line 1503 "./scanner_body.rl"
 	{
 		if (s->item_length != 8) {
 			SCANNER_WARNING(ZSCANNER_EBAD_EUI_LENGTH);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 250:
-#line 1536 "./zscanner/scanner_body.rl"
+	case 249:
+#line 1509 "./scanner_body.rl"
 	{
-		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);                       
-		p--; {cs = 247; goto _again;}
+		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 251:
-#line 1551 "./zscanner/scanner_body.rl"
+	case 250:
+#line 1524 "./scanner_body.rl"
 	{
 		s->item_length = 0;
 	}
 	break;
-	case 252:
-#line 1554 "./zscanner/scanner_body.rl"
+	case 251:
+#line 1527 "./scanner_body.rl"
 	{
 		s->item_length++;
 	}
 	break;
-	case 253:
-#line 1557 "./zscanner/scanner_body.rl"
+	case 252:
+#line 1530 "./scanner_body.rl"
 	{
 		if (s->item_length != 4) {
 			SCANNER_WARNING(ZSCANNER_EBAD_L64_LENGTH);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 254:
-#line 1563 "./zscanner/scanner_body.rl"
+	case 253:
+#line 1536 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_COLON);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 255:
-#line 1576 "./zscanner/scanner_body.rl"
+	case 254:
+#line 1549 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_ALGORITHM);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 256:
-#line 1580 "./zscanner/scanner_body.rl"
+	case 255:
+#line 1553 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_CERT_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
+	case 256:
+#line 1575 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 456; goto _again;} }
+	break;
 	case 257:
-#line 1602 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 457; goto _again;} }
+#line 1590 "./scanner_body.rl"
+	{ p--; {stack[top++] = cs; cs = 551; goto _again;} }
 	break;
 	case 258:
-#line 1617 "./zscanner/scanner_body.rl"
-	{ p--; {stack[top++] = cs; cs = 552; goto _again;} }
-	break;
-	case 259:
-#line 1621 "./zscanner/scanner_body.rl"
+#line 1594 "./scanner_body.rl"
 	{
-		s->r_data_blocks[0] = 0;
-		s->r_data_blocks_count = 0;
 		rdata_tail = s->r_data;
 	}
 	break;
-	case 260:
-#line 1626 "./zscanner/scanner_body.rl"
+	case 259:
+#line 1597 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 261:
-#line 1744 "./zscanner/scanner_body.rl"
+	case 260:
+#line 1715 "./scanner_body.rl"
 	{
 		p--;
 		switch (s->r_type) {
 		case KNOT_RRTYPE_A:
-			{stack[top++] = cs; cs = 593; goto _again;}
+			{stack[top++] = cs; cs = 592; goto _again;}
 		case KNOT_RRTYPE_NS:
 		case KNOT_RRTYPE_CNAME:
 		case KNOT_RRTYPE_PTR:
 		case KNOT_RRTYPE_DNAME:
-			{stack[top++] = cs; cs = 595; goto _again;}
+			{stack[top++] = cs; cs = 594; goto _again;}
 		case KNOT_RRTYPE_SOA:
-			{stack[top++] = cs; cs = 597; goto _again;}
+			{stack[top++] = cs; cs = 596; goto _again;}
 		case KNOT_RRTYPE_HINFO:
-			{stack[top++] = cs; cs = 629; goto _again;}
+			{stack[top++] = cs; cs = 628; goto _again;}
 		case KNOT_RRTYPE_MINFO:
 		case KNOT_RRTYPE_RP:
-			{stack[top++] = cs; cs = 634; goto _again;}
+			{stack[top++] = cs; cs = 633; goto _again;}
 		case KNOT_RRTYPE_MX:
 		case KNOT_RRTYPE_AFSDB:
 		case KNOT_RRTYPE_RT:
 		case KNOT_RRTYPE_KX:
 		case KNOT_RRTYPE_LP:
-			{stack[top++] = cs; cs = 639; goto _again;}
+			{stack[top++] = cs; cs = 638; goto _again;}
 		case KNOT_RRTYPE_TXT:
 		case KNOT_RRTYPE_SPF:
-			{stack[top++] = cs; cs = 644; goto _again;}
+			{stack[top++] = cs; cs = 643; goto _again;}
 		case KNOT_RRTYPE_AAAA:
-			{stack[top++] = cs; cs = 648; goto _again;}
+			{stack[top++] = cs; cs = 647; goto _again;}
 		case KNOT_RRTYPE_LOC:
-			{stack[top++] = cs; cs = 650; goto _again;}
+			{stack[top++] = cs; cs = 649; goto _again;}
 		case KNOT_RRTYPE_SRV:
-			{stack[top++] = cs; cs = 705; goto _again;}
+			{stack[top++] = cs; cs = 704; goto _again;}
 		case KNOT_RRTYPE_NAPTR:
-			{stack[top++] = cs; cs = 716; goto _again;}
+			{stack[top++] = cs; cs = 715; goto _again;}
 		case KNOT_RRTYPE_CERT:
-			{stack[top++] = cs; cs = 733; goto _again;}
+			{stack[top++] = cs; cs = 732; goto _again;}
 		case KNOT_RRTYPE_APL:
-			{stack[top++] = cs; cs = 744; goto _again;}
+			{stack[top++] = cs; cs = 743; goto _again;}
 		case KNOT_RRTYPE_DS:
-			{stack[top++] = cs; cs = 755; goto _again;}
+			{stack[top++] = cs; cs = 754; goto _again;}
 		case KNOT_RRTYPE_SSHFP:
-			{stack[top++] = cs; cs = 768; goto _again;}
+			{stack[top++] = cs; cs = 767; goto _again;}
 		case KNOT_RRTYPE_IPSECKEY:
-			{stack[top++] = cs; cs = 778; goto _again;}
+			{stack[top++] = cs; cs = 777; goto _again;}
 		case KNOT_RRTYPE_RRSIG:
-			{stack[top++] = cs; cs = 817; goto _again;}
+			{stack[top++] = cs; cs = 816; goto _again;}
 		case KNOT_RRTYPE_NSEC:
-			{stack[top++] = cs; cs = 959; goto _again;}
+			{stack[top++] = cs; cs = 958; goto _again;}
 		case KNOT_RRTYPE_KEY:
 		case KNOT_RRTYPE_DNSKEY:
-			{stack[top++] = cs; cs = 962; goto _again;}
+			{stack[top++] = cs; cs = 961; goto _again;}
 		case KNOT_RRTYPE_DHCID:
-			{stack[top++] = cs; cs = 973; goto _again;}
+			{stack[top++] = cs; cs = 972; goto _again;}
 		case KNOT_RRTYPE_NSEC3:
-			{stack[top++] = cs; cs = 975; goto _again;}
+			{stack[top++] = cs; cs = 974; goto _again;}
 		case KNOT_RRTYPE_NSEC3PARAM:
-			{stack[top++] = cs; cs = 1004; goto _again;}
+			{stack[top++] = cs; cs = 1003; goto _again;}
 		case KNOT_RRTYPE_TLSA:
-			{stack[top++] = cs; cs = 1017; goto _again;}
+			{stack[top++] = cs; cs = 1016; goto _again;}
 		case KNOT_RRTYPE_NID:
 		case KNOT_RRTYPE_L64:
-			{stack[top++] = cs; cs = 1035; goto _again;}
+			{stack[top++] = cs; cs = 1034; goto _again;}
 		case KNOT_RRTYPE_L32:
-			{stack[top++] = cs; cs = 1030; goto _again;}
+			{stack[top++] = cs; cs = 1029; goto _again;}
 		case KNOT_RRTYPE_EUI48:
-			{stack[top++] = cs; cs = 1048; goto _again;}
+			{stack[top++] = cs; cs = 1047; goto _again;}
 		case KNOT_RRTYPE_EUI64:
-			{stack[top++] = cs; cs = 1054; goto _again;}
+			{stack[top++] = cs; cs = 1053; goto _again;}
 		default:
 			SCANNER_WARNING(ZSCANNER_ECANNOT_TEXT_DATA);
-			{cs = 247; goto _again;}
+			{cs = 246; goto _again;}
 		}
 	}
 	break;
-	case 262:
-#line 1817 "./zscanner/scanner_body.rl"
+	case 261:
+#line 1788 "./scanner_body.rl"
 	{
 		switch (s->r_type) {
 		// Next types must not have empty rdata.
@@ -7305,197 +7296,190 @@ _match:
 		case KNOT_RRTYPE_LP:
 		case KNOT_RRTYPE_EUI48:
 		case KNOT_RRTYPE_EUI64:
-			{stack[top++] = cs; cs = 438; goto _again;}
+			{stack[top++] = cs; cs = 437; goto _again;}
 		// Next types can have empty rdata.
 		case KNOT_RRTYPE_APL:
 		default:
-			{stack[top++] = cs; cs = 447; goto _again;}
+			{stack[top++] = cs; cs = 446; goto _again;}
 		}
 	}
 	break;
-	case 263:
-#line 1865 "./zscanner/scanner_body.rl"
-	{
-		s->r_data_blocks[++(s->r_data_blocks_count)] =
-			(uint16_t)(rdata_tail - s->r_data);
-	}
-	break;
-	case 264:
-#line 1873 "./zscanner/scanner_body.rl"
+	case 262:
+#line 1839 "./scanner_body.rl"
 	{ p--; }
 	break;
-	case 265:
-#line 1880 "./zscanner/scanner_body.rl"
+	case 263:
+#line 1846 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EUNSUPPORTED_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 266:
-#line 1886 "./zscanner/scanner_body.rl"
+	case 264:
+#line 1852 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_A; }
 	break;
-	case 267:
-#line 1887 "./zscanner/scanner_body.rl"
+	case 265:
+#line 1853 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NS; }
 	break;
-	case 268:
-#line 1888 "./zscanner/scanner_body.rl"
+	case 266:
+#line 1854 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_CNAME; }
 	break;
-	case 269:
-#line 1889 "./zscanner/scanner_body.rl"
+	case 267:
+#line 1855 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_SOA; }
 	break;
-	case 270:
-#line 1890 "./zscanner/scanner_body.rl"
+	case 268:
+#line 1856 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_PTR; }
 	break;
-	case 271:
-#line 1891 "./zscanner/scanner_body.rl"
+	case 269:
+#line 1857 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_HINFO; }
 	break;
-	case 272:
-#line 1892 "./zscanner/scanner_body.rl"
+	case 270:
+#line 1858 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_MINFO; }
 	break;
-	case 273:
-#line 1893 "./zscanner/scanner_body.rl"
+	case 271:
+#line 1859 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_MX; }
 	break;
-	case 274:
-#line 1894 "./zscanner/scanner_body.rl"
+	case 272:
+#line 1860 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_TXT; }
 	break;
-	case 275:
-#line 1895 "./zscanner/scanner_body.rl"
+	case 273:
+#line 1861 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_RP; }
 	break;
-	case 276:
-#line 1896 "./zscanner/scanner_body.rl"
+	case 274:
+#line 1862 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_AFSDB; }
 	break;
-	case 277:
-#line 1897 "./zscanner/scanner_body.rl"
+	case 275:
+#line 1863 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_RT; }
 	break;
-	case 278:
-#line 1898 "./zscanner/scanner_body.rl"
+	case 276:
+#line 1864 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_KEY; }
 	break;
-	case 279:
-#line 1899 "./zscanner/scanner_body.rl"
+	case 277:
+#line 1865 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_AAAA; }
 	break;
-	case 280:
-#line 1900 "./zscanner/scanner_body.rl"
+	case 278:
+#line 1866 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_LOC; }
 	break;
-	case 281:
-#line 1901 "./zscanner/scanner_body.rl"
+	case 279:
+#line 1867 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_SRV; }
 	break;
-	case 282:
-#line 1902 "./zscanner/scanner_body.rl"
+	case 280:
+#line 1868 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NAPTR; }
 	break;
-	case 283:
-#line 1903 "./zscanner/scanner_body.rl"
+	case 281:
+#line 1869 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_KX; }
 	break;
-	case 284:
-#line 1904 "./zscanner/scanner_body.rl"
+	case 282:
+#line 1870 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_CERT; }
 	break;
-	case 285:
-#line 1905 "./zscanner/scanner_body.rl"
+	case 283:
+#line 1871 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_DNAME; }
 	break;
-	case 286:
-#line 1906 "./zscanner/scanner_body.rl"
+	case 284:
+#line 1872 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_APL; }
 	break;
-	case 287:
-#line 1907 "./zscanner/scanner_body.rl"
+	case 285:
+#line 1873 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_DS; }
 	break;
-	case 288:
-#line 1908 "./zscanner/scanner_body.rl"
+	case 286:
+#line 1874 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_SSHFP; }
 	break;
-	case 289:
-#line 1909 "./zscanner/scanner_body.rl"
+	case 287:
+#line 1875 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_IPSECKEY; }
 	break;
-	case 290:
-#line 1910 "./zscanner/scanner_body.rl"
+	case 288:
+#line 1876 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_RRSIG; }
 	break;
-	case 291:
-#line 1911 "./zscanner/scanner_body.rl"
+	case 289:
+#line 1877 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NSEC; }
 	break;
-	case 292:
-#line 1912 "./zscanner/scanner_body.rl"
+	case 290:
+#line 1878 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_DNSKEY; }
 	break;
-	case 293:
-#line 1913 "./zscanner/scanner_body.rl"
+	case 291:
+#line 1879 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_DHCID; }
 	break;
-	case 294:
-#line 1914 "./zscanner/scanner_body.rl"
+	case 292:
+#line 1880 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NSEC3; }
 	break;
-	case 295:
-#line 1915 "./zscanner/scanner_body.rl"
+	case 293:
+#line 1881 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
 	break;
-	case 296:
-#line 1916 "./zscanner/scanner_body.rl"
+	case 294:
+#line 1882 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_TLSA; }
 	break;
-	case 297:
-#line 1917 "./zscanner/scanner_body.rl"
+	case 295:
+#line 1883 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_SPF; }
 	break;
-	case 298:
-#line 1918 "./zscanner/scanner_body.rl"
+	case 296:
+#line 1884 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_NID; }
 	break;
-	case 299:
-#line 1919 "./zscanner/scanner_body.rl"
+	case 297:
+#line 1885 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_L32; }
 	break;
-	case 300:
-#line 1920 "./zscanner/scanner_body.rl"
+	case 298:
+#line 1886 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_L64; }
 	break;
-	case 301:
-#line 1921 "./zscanner/scanner_body.rl"
+	case 299:
+#line 1887 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_LP; }
 	break;
-	case 302:
-#line 1922 "./zscanner/scanner_body.rl"
+	case 300:
+#line 1888 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_EUI48; }
 	break;
-	case 303:
-#line 1923 "./zscanner/scanner_body.rl"
+	case 301:
+#line 1889 "./scanner_body.rl"
 	{ s->r_type = KNOT_RRTYPE_EUI64; }
 	break;
-	case 304:
-#line 1929 "./zscanner/scanner_body.rl"
+	case 302:
+#line 1895 "./scanner_body.rl"
 	{
 		if (rdata_tail - s->r_data > UINT16_MAX) {
 			SCANNER_WARNING(ZSCANNER_ERDATA_OVERFLOW);
-			p--; {cs = 247; goto _again;}
+			p--; {cs = 246; goto _again;}
 		}
 		s->r_data_length = rdata_tail - s->r_data;
 
 		s->process_record(s);
 	}
 	break;
-#line 7499 "zscanner/scanner.c"
+#line 7483 "scanner.c"
 		}
 	}
 
@@ -7512,244 +7496,244 @@ _again:
 	while ( __nacts-- > 0 ) {
 		switch ( *__acts++ ) {
 	case 4:
-#line 44 "./zscanner/scanner_body.rl"
+#line 44 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_REST);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
 	case 15:
-#line 143 "./zscanner/scanner_body.rl"
+#line 143 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
 	case 20:
-#line 189 "./zscanner/scanner_body.rl"
+#line 189 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_DNAME_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 28:
-#line 242 "./zscanner/scanner_body.rl"
+	case 27:
+#line 234 "./scanner_body.rl"
 	{
 		s->r_owner_length = 0;
 		SCANNER_WARNING(ZSCANNER_EBAD_OWNER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 33:
-#line 285 "./zscanner/scanner_body.rl"
+	case 32:
+#line 277 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 45:
-#line 386 "./zscanner/scanner_body.rl"
+	case 44:
+#line 378 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TIME_UNIT);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 55:
-#line 491 "./zscanner/scanner_body.rl"
+	case 54:
+#line 483 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TIMESTAMP_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 57:
-#line 509 "./zscanner/scanner_body.rl"
+	case 56:
+#line 501 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TEXT_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 58:
-#line 513 "./zscanner/scanner_body.rl"
+	case 57:
+#line 505 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_TEXT);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 62:
-#line 543 "./zscanner/scanner_body.rl"
+	case 61:
+#line 535 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_NUMBER);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 65:
-#line 583 "./zscanner/scanner_body.rl"
+	case 64:
+#line 575 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_TTL);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 69:
-#line 600 "./zscanner/scanner_body.rl"
+	case 68:
+#line 592 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_ORIGIN);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 73:
-#line 629 "./zscanner/scanner_body.rl"
+	case 72:
+#line 621 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_INCLUDE_FILENAME);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 76:
-#line 640 "./zscanner/scanner_body.rl"
+	case 75:
+#line 632 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_INCLUDE_ORIGIN);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 80:
-#line 721 "./zscanner/scanner_body.rl"
+	case 79:
+#line 700 "./scanner_body.rl"
 	{
 		s->stop = false;
 	}
 	break;
-	case 81:
-#line 724 "./zscanner/scanner_body.rl"
+	case 80:
+#line 703 "./scanner_body.rl"
 	{
 		SCANNER_ERROR(ZSCANNER_EBAD_DIRECTIVE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 88:
-#line 775 "./zscanner/scanner_body.rl"
+	case 87:
+#line 754 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_ADDRESS_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 99:
-#line 875 "./zscanner/scanner_body.rl"
+	case 98:
+#line 854 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_APL);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 102:
-#line 905 "./zscanner/scanner_body.rl"
+	case 101:
+#line 884 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 104:
-#line 932 "./zscanner/scanner_body.rl"
+	case 103:
+#line 905 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 109:
-#line 974 "./zscanner/scanner_body.rl"
+	case 108:
+#line 947 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BASE64_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 119:
-#line 1058 "./zscanner/scanner_body.rl"
+	case 118:
+#line 1031 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BASE32HEX_CHAR);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 145:
-#line 1182 "./zscanner/scanner_body.rl"
+	case 144:
+#line 1155 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_GATEWAY);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 146:
-#line 1186 "./zscanner/scanner_body.rl"
+	case 145:
+#line 1159 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_GATEWAY_KEY);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 147:
-#line 1204 "./zscanner/scanner_body.rl"
+	case 146:
+#line 1177 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EUNSUPPORTED_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 227:
-#line 1334 "./zscanner/scanner_body.rl"
+	case 226:
+#line 1307 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_BITMAP);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 244:
-#line 1487 "./zscanner/scanner_body.rl"
+	case 243:
+#line 1460 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_LOC_DATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 245:
-#line 1500 "./zscanner/scanner_body.rl"
+	case 244:
+#line 1473 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_HEX_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 250:
-#line 1536 "./zscanner/scanner_body.rl"
+	case 249:
+#line 1509 "./scanner_body.rl"
 	{
-		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);                       
-		p--; {cs = 247; goto _again;}
+		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 254:
-#line 1563 "./zscanner/scanner_body.rl"
+	case 253:
+#line 1536 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_COLON);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 255:
-#line 1576 "./zscanner/scanner_body.rl"
+	case 254:
+#line 1549 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_ALGORITHM);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 256:
-#line 1580 "./zscanner/scanner_body.rl"
+	case 255:
+#line 1553 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_CERT_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 260:
-#line 1626 "./zscanner/scanner_body.rl"
+	case 259:
+#line 1597 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EBAD_RDATA);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-	case 265:
-#line 1880 "./zscanner/scanner_body.rl"
+	case 263:
+#line 1846 "./scanner_body.rl"
 	{
 		SCANNER_WARNING(ZSCANNER_EUNSUPPORTED_TYPE);
-		p--; {cs = 247; goto _again;}
+		p--; {cs = 246; goto _again;}
 	}
 	break;
-#line 7753 "zscanner/scanner.c"
+#line 7737 "scanner.c"
 		}
 	}
 	}
@@ -7757,7 +7741,7 @@ _again:
 	_out: {}
 	}
 
-#line 144 "./zscanner/scanner.rl"
+#line 195 "./scanner.rl"
 
 	// Check if scanner state machine is in uncovered state.
 	if (cs == zone_scanner_error) {
@@ -7787,7 +7771,7 @@ _again:
 	}
 
 	// Check unclosed multiline record.
-	if (is_last_block && s->multiline) {
+	if (is_complete && s->multiline) {
 		SCANNER_ERROR(ZSCANNER_UNCLOSED_MULTILINE);
 		s->error_counter++;
 		s->process_error(s);
diff --git a/src/zscanner/scanner.h b/src/zscanner/scanner.h
index e8ff355f7036367c85158b9d63eb8d3bc0099a90..d7689f78939d40b476bd00c6a162adb85ac7a3df 100644
--- a/src/zscanner/scanner.h
+++ b/src/zscanner/scanner.h
@@ -31,36 +31,36 @@
 #include <stdbool.h>			// bool
 
 /*! \brief Maximal length of rdata. */
-#define MAX_RDATA_LENGTH	       65535
+#define MAX_RDATA_LENGTH		65535
 /*! \brief Maximal length of rdata item. */
-#define MAX_ITEM_LENGTH			 255
+#define MAX_ITEM_LENGTH			255
 /*! \brief Maximal length of domain name. */
-#define MAX_DNAME_LENGTH		 255
+#define MAX_DNAME_LENGTH		255
 /*! \brief Maximal length of domain name label. */
-#define MAX_LABEL_LENGTH		  63
+#define MAX_LABEL_LENGTH		63
 /*! \brief Maximal number or rdata items. */
-#define MAX_RDATA_ITEMS			  64
+#define MAX_RDATA_ITEMS			64
 
 /*! \brief Number of bitmap windows. */
-#define BITMAP_WINDOWS			 256
+#define BITMAP_WINDOWS			256
 
 /*! \brief Length of ipv4 address in wire format. */
-#define INET4_ADDR_LENGTH		   4
+#define INET4_ADDR_LENGTH		4
 /*! \brief Length of ipv6 address in wire format. */
-#define INET6_ADDR_LENGTH		  16
+#define INET6_ADDR_LENGTH		16
 
 /*! \brief Ragel call stack size (see Ragel internals). */
-#define RAGEL_STACK_SIZE		  16
+#define RAGEL_STACK_SIZE		16
 
 /*! \brief ASCII value of '0' character. */
-#define ASCII_0				  48
+#define ASCII_0				48
 
 /*! \brief Latitude value for equator (2^31). */
 #define LOC_LAT_ZERO	(uint32_t)2147483648
 /*! \brief Longitude value for meridian (2^31). */
 #define LOC_LONG_ZERO	(uint32_t)2147483648
 /*! \brief Zero level altitude value. */
-#define LOC_ALT_ZERO	  (uint32_t)10000000
+#define LOC_ALT_ZERO	(uint32_t)10000000
 
 /*! \brief Auxiliary structure for storing bitmap window items (see RFC4034). */
 typedef struct {
@@ -86,49 +86,27 @@ typedef struct {
 } loc_t;
 
 /*!
- * \brief Context structure for Ragel scanner.
+ * \brief Context structure for zone scanner.
  *
  * This structure contains folowing items:
- *  - Copies of Ragel internal variables. The scanner is called many times
- *    for each block of zone file. So it is necessary to preserve internal
- *    values between subsequent scanner callings.
+ *  - Copies of Ragel internal variables. The scanner can be called many times
+ *    on smaller parts of zone file/memory. So it is necessary to preserve
+ *    internal values between subsequent scanner callings.
  *  - Auxiliary variables which are used during processing zone data.
- *  - Zone file and error information.
  *  - Pointers to callback functions and pointer to any arbitrary data which
  *    can be used in callback functions.
- *  - Output variables containing all parts of zone record. These data are
- *    usefull during processing via callback function.
+ *  - Zone file and error information.
+ *  - Output variables (r_ prefix) containing all parts of zone record. These
+ *    data are usefull during processing via callback function.
  */
 typedef struct scanner scanner_t; // Forward declaration due to arguments.
 struct scanner {
 	/*! Current state (Ragel internals). */
-	int	 cs;
+	int      cs;
 	/*! Stack top (Ragel internals). */
-	int	 top;
+	int      top;
 	/*! Call stack (Ragel internals). */
-	int	 stack[RAGEL_STACK_SIZE];
-
-	/*! Zone file name. */
-	char     *file_name;
-	/*! Zone file line counter. */
-	uint64_t line_counter;
-
-	/*! Last occured error/warning code. */
-	int      error_code;
-	/*! Errors/warnings counter. */
-	uint64_t error_counter;
-	/*!
-	 * Indicates serious warning which is considered as an error and
-	 * forces zone processing to stop.
-	 */
-	bool     stop;
-
-	/*! Callback function for correct zone record. */
-	void (*process_record)(const scanner_t *);
-	/*! Callback function for wrong situations. */
-	void (*process_error)(const scanner_t *);
-	/*! Arbitrary data useful inside callback functions. */
-	void *data;
+	int      stack[RAGEL_STACK_SIZE];
 
 	/*! Indicates whether current record is multiline. */
 	bool     multiline;
@@ -184,6 +162,29 @@ struct scanner {
 	/*! Value of the current default ttl (TTL directive sets this). */
 	uint32_t default_ttl;
 
+	/*! Callback function for correct zone record. */
+	void (*process_record)(const scanner_t *);
+	/*! Callback function for wrong situations. */
+	void (*process_error)(const scanner_t *);
+	/*! Arbitrary data useful inside callback functions. */
+	void *data;
+
+	/*! Absolute path for relative includes. */
+	char     *path;
+	/*! Zone file name, if specified. */
+	char     *file_name;
+	/*! Zone file line counter. */
+	uint64_t line_counter;
+	/*! Last occured error/warning code. */
+	int      error_code;
+	/*! Errors/warnings counter. */
+	uint64_t error_counter;
+	/*!
+	 * Indicates serious warning which is considered as an error and
+	 * forces zone processing to stop.
+	 */
+	bool     stop;
+
 	/*!
 	 * Owner of the current record.
 	 *
@@ -203,10 +204,6 @@ struct scanner {
 	uint8_t  r_data[MAX_RDATA_LENGTH];
 	/*! Length of the current rdata. */
 	uint32_t r_data_length;
-	/*! Indexes of the current rdata blocks. */
-	uint16_t r_data_blocks[MAX_RDATA_ITEMS];
-	/*! Number or the current rdata blocks. */
-	uint32_t r_data_blocks_count;
 
 	/*
 	 * Example: a. IN 60 MX 1 b.
@@ -218,25 +215,31 @@ struct scanner {
 	 *          r_type = 15
 	 *          r_data = 0001016200
 	 *          r_data_length = 5
-	 *          r_data_blocks_count = 2
-	 *          r_data_blocks = [0, 2, 5]
 	 */
 };
 
 /*!
  * \brief Creates zone scanner structure.
  *
- * \note After creation scanner structure, it is necessary to set next items:
- *       process_record, process_error, default_class, default_ttl, zone_origin.
- *
- * \param file_name	Zone file name. If parsing from memory use arbitrary
- *                      directory name (like "."), because relative includes
- *                      are considered to this file.
+ * \param file_name		Name of file to process (NULL if parsing from
+ * 				memory).
+ * \param origin		Initial zone origin.
+ * \param rclass		Zone class value.
+ * \param ttl			Initial ttl value.
+ * \param process_record	Processing callback function.
+ * \param process_error 	Error callback function.
+ * \param data			Arbitrary data useful in callback functions.
  *
  * \retval scanner	if success.
  * \retval 0		if error.
  */
-scanner_t* scanner_create(const char *file_name);
+scanner_t* scanner_create(const char     *file_name,
+                          const char     *origin,
+                          const uint16_t rclass,
+                          const uint32_t ttl,
+                          void (*process_record)(const scanner_t *),
+                          void (*process_error)(const scanner_t *),
+                          void *data);
 
 /*!
  * \brief Destroys zone scanner structure.
@@ -253,17 +256,17 @@ void scanner_free(scanner_t *scanner);
  *
  * \param start		First byte of the zone data to scan.
  * \param end		Last byte of the zone data to scan.
- * \param is_last_block	Indicates if current block is last.
+ * \param is_complete	Indicates if the current block is complete i.e. the
+ * 			last record line doesn't continue in the next block.
  * \param scanner	Zone scanner structure.
  *
  * \retval  0		if success.
  * \retval -1		if error.
  */
 int scanner_process(const char *start,
-		    const char *end,
-		    const bool is_last_block,
-		    scanner_t  *scanner);
-
+                    const char *end,
+                    const bool is_complete,
+                    scanner_t  *scanner);
 
 #endif // _ZSCANNER__SCANNER_H_
 
diff --git a/src/zscanner/scanner.rl b/src/zscanner/scanner.rl
index a204814b2f7a1c8b8f4ed417ad3e2ec6ebe275a4..e3a9ed0ea79c508b02d8baa3547fdd64fbf55e2e 100644
--- a/src/zscanner/scanner.rl
+++ b/src/zscanner/scanner.rl
@@ -14,8 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "zscanner/scanner.h"
-
+#include <config.h>
 #include <stdint.h>			// uint32_t
 #include <stdlib.h>			// calloc
 #include <stdio.h>			// sprintf
@@ -28,16 +27,25 @@
 #include <netinet/in.h>			// in_addr (BSD)
 #include <arpa/inet.h>			// inet_pton
 
-#include "common/errcode.h"		// error codes
-#include "common/descriptor.h"		// KNOT_RRTYPE_A
+#include "zscanner/scanner.h"
+#include "zscanner/error.h"		// error codes
 #include "zscanner/file_loader.h"	// file_loader
 #include "zscanner/scanner_functions.h"	// Base64
+#include "zscanner/descriptor.h"	// KNOT_RRTYPE_A
 
 /*! \brief Shorthand for setting warning data. */
 #define SCANNER_WARNING(code) { s->error_code = code; }
 /*! \brief Shorthand for setting error data. */
 #define SCANNER_ERROR(code)   { s->error_code = code; s->stop = true; }
 
+/*!
+ * \brief Empty function which is called if no callback function is specified.
+ */
+static inline void noop(const scanner_t *s)
+{
+	(void)s;
+}
+
 /*!
  * \brief Writes record type number to r_data.
  *
@@ -81,19 +89,61 @@ static inline void window_add_bit(const uint16_t type, scanner_t *s) {
 	write data;
 }%%
 
-scanner_t* scanner_create(const char *file_name)
+scanner_t* scanner_create(const char     *file_name,
+                          const char     *origin,
+                          const uint16_t rclass,
+                          const uint32_t ttl,
+                          void (*process_record)(const scanner_t *),
+                          void (*process_error)(const scanner_t *),
+                          void *data)
 {
+	char settings[1024];
+
 	scanner_t *s = calloc(1, sizeof(scanner_t));
 	if (s == NULL) {
 		return NULL;
 	}
 
-	s->file_name = strdup(file_name);
-	s->line_counter = 1;
+	if (file_name != NULL) {
+		// Get absolute path of the zone file.
+		if (realpath(file_name, (char*)(s->buffer)) != NULL) {
+			char *full_name = strdup((char*)(s->buffer));
+			s->path = strdup(dirname(full_name));
+			free(full_name);
+		} else {
+			free(s);
+			return NULL;
+		}
+
+		s->file_name = strdup(file_name);
+	} else {
+		s->path = strdup(".");
+		s->file_name = strdup("<NULL>");
+	}
 
 	// Nonzero initial scanner state.
 	s->cs = zone_scanner_start;
 
+	// Disable processing during parsing of settings.
+	s->process_record = &noop;
+	s->process_error = &noop;
+
+	// Create ORIGIN directive and parse it using scanner to set up origin.
+	int ret = snprintf(settings, sizeof(settings), "$ORIGIN %s\n", origin);
+	if (ret <= 0 || (size_t)ret >= sizeof(settings) ||
+	    scanner_process(settings, settings + ret, true, s) != 0) {
+		scanner_free(s);
+		return NULL;
+	}
+
+	// Set scanner defaults.
+	s->default_class = rclass;
+	s->default_ttl = ttl;
+	s->process_record = process_record ? process_record : &noop;
+	s->process_error = process_error ? process_error : &noop;
+	s->data = data;
+	s->line_counter = 1;
+
 	return s;
 }
 
@@ -101,14 +151,15 @@ void scanner_free(scanner_t *s)
 {
 	if (s != NULL) {
 		free(s->file_name);
+		free(s->path);
 		free(s);
 	}
 }
 
 int scanner_process(const char *start,
-		    const char *end,
-		    const bool is_last_block,
-		    scanner_t  *s)
+                    const char *end,
+                    const bool is_complete,
+                    scanner_t  *s)
 {
 	// Necessary scanner variables.
 	const char *p = start;
@@ -121,7 +172,7 @@ int scanner_process(const char *start,
 	struct in6_addr addr6;
 	uint32_t timestamp;
 	int16_t  window;
-	int	 ret;
+	int      ret;
 
 	// Next 2 variables are for better performance.
 	// Restoring r_data pointer to next free space.
@@ -135,7 +186,7 @@ int scanner_process(const char *start,
 	memcpy(stack, s->stack, sizeof(stack));
 
 	// End of file check.
-	if (is_last_block == true) {
+	if (is_complete == true) {
 		eof = (char *)pe;
 	}
 
@@ -170,7 +221,7 @@ int scanner_process(const char *start,
 	}
 
 	// Check unclosed multiline record.
-	if (is_last_block && s->multiline) {
+	if (is_complete && s->multiline) {
 		SCANNER_ERROR(ZSCANNER_UNCLOSED_MULTILINE);
 		s->error_counter++;
 		s->process_error(s);
diff --git a/src/zscanner/scanner_body.rl b/src/zscanner/scanner_body.rl
index ea76c5487effc93cea0be70641060106dcb8db2d..22dffc0339528623eaaf33ca8c04e2f092f46f16 100644
--- a/src/zscanner/scanner_body.rl
+++ b/src/zscanner/scanner_body.rl
@@ -86,7 +86,7 @@
 		s->process_error(s);
 
 		// Reset.
-		s->error_code = KNOT_EOK;
+		s->error_code = ZSCANNER_OK;
 		s->multiline = false;
 
 		// In case of serious error, stop scanner.
@@ -215,14 +215,6 @@
 			fhold; fgoto err_line;
 		}
 	}
-
-	action _separate {
-		s->r_data_blocks[++(s->r_data_blocks_count)] =
-			rdata_tail - s->r_data;
-	}
-
-	# Rdata blocks dividing.
-	blk_sep = zlen >_separate;
 	# END
 
 	# BEGIN - Owner processing
@@ -435,7 +427,7 @@
 
 	time_block = (number . time_unit) >_time_block_init %_time_block_exit;
 
-	# Time is either a number or a sequence of time blocks (1w1h1m)
+	# Time is either a number or a sequence of time blocks (1w1h1m).
 	time = (number . (time_unit . (time_block)*)?) $!_number_error;
 
 	time32 = time %_num32_write;
@@ -459,7 +451,7 @@
 		if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
 			ret = date_to_timestamp(s->buffer, &timestamp);
 
-			if (ret == KNOT_EOK) {
+			if (ret == ZSCANNER_OK) {
 				*((uint32_t *)rdata_tail) = htonl(timestamp);
 				rdata_tail += 4;
 			} else {
@@ -643,36 +635,23 @@
 	}
 
 	action _include_exit {
-		char text_origin[MAX_DNAME_LENGTH];
+		char text_origin[4 * MAX_DNAME_LENGTH]; // Each char as \DDD.
 
 		// Origin conversion from wire to text form.
 		if (s->dname == NULL) { // Use current origin.
 			wire_dname_to_str(s->zone_origin,
 			                  s->zone_origin_length,
-					  text_origin);
+			                  text_origin);
 		} else { // Use specified origin.
 			wire_dname_to_str(s->r_data,
 			                  s->r_data_length,
-					  text_origin);
+			                  text_origin);
 		}
 
-		if (s->include_filename[0] != '/') { // Relative file path.
-			// Get absolute path of the current zone file.
-			if (realpath(s->file_name, (char*)(s->buffer)) != NULL) {
-				char *full_current_zone_file_name =
-					strdup((char*)(s->buffer));
-
-				// Creating full include file name.
-				snprintf((char*)(s->buffer), sizeof(s->buffer),
-				        "%s/%s",
-				        dirname(full_current_zone_file_name),
-				        s->include_filename);
-
-				free(full_current_zone_file_name);
-			} else {
-				SCANNER_ERROR(ZSCANNER_EUNPROCESSED_INCLUDE);
-				fhold; fgoto err_line;
-			}
+		// Relative file path.
+		if (s->include_filename[0] != '/') {
+			snprintf((char*)(s->buffer), sizeof(s->buffer),
+			         "%s/%s", s->path, s->include_filename);
 		} else {
 			strncpy((char*)(s->buffer), (char*)(s->include_filename),
 			        sizeof(s->buffer));
@@ -681,8 +660,8 @@
 		// Create new file loader for included zone file.
 		file_loader_t *fl = file_loader_create((char*)(s->buffer),
 		                                       text_origin,
-		                                       DEFAULT_CLASS,
-		                                       DEFAULT_TTL,
+		                                       s->default_class,
+		                                       s->default_ttl,
 		                                       s->process_record,
 		                                       s->process_error,
 		                                       s->data);
@@ -921,12 +900,6 @@
 			SCANNER_WARNING(ZSCANNER_EBAD_RDATA_LENGTH);
 			fhold; fgoto err_line;
 		}
-
-		ret = find_rdata_blocks(s);
-		if (ret != KNOT_EOK) {
-			SCANNER_WARNING(ret);
-			fhold; fgoto err_line;
-		}
 	}
 
 	action _type_data_error {
@@ -1534,7 +1507,7 @@
 		}
 	}
 	action _eui_sep_error {
-		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);                       
+		SCANNER_WARNING(ZSCANNER_EBAD_CHAR_DASH);
 		fhold; fgoto err_line;
 	}
 
@@ -1619,8 +1592,6 @@
 
 	# BEGIN - Rdata processing
 	action _r_data_init {
-		s->r_data_blocks[0] = 0;
-		s->r_data_blocks_count = 0;
 		rdata_tail = s->r_data;
 	}
 	action _r_data_error {
@@ -1637,8 +1608,8 @@
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_soa :=
-		(r_dname . blk_sep .  sep . r_dname . blk_sep .  sep . num32 .
-		 sep . time32 . sep . time32 . sep . time32 . sep . time32)
+		(r_dname . sep . r_dname . sep . num32 . sep . time32 .
+		 sep . time32 . sep . time32 . sep . time32)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_hinfo :=
@@ -1646,11 +1617,11 @@
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_minfo :=
-		(r_dname . blk_sep .  sep . r_dname)
+		(r_dname . sep . r_dname)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_mx :=
-		(num16 . blk_sep .  sep . r_dname)
+		(num16 . sep . r_dname)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_txt :=
@@ -1666,12 +1637,12 @@
 		$!_r_data_error %_ret . end_wchar;
 
 	r_data_srv :=
-		(num16 . sep . num16 . sep . num16 . blk_sep .  sep . r_dname)
+		(num16 . sep . num16 . sep . num16 . sep . r_dname)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_naptr :=
 		(num16 . sep . num16 . sep . text_string . sep . text_string .
-		 sep . text_string . blk_sep .  sep . r_dname)
+		 sep . text_string . sep . r_dname)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_cert :=
@@ -1696,12 +1667,12 @@
 
 	r_data_rrsig :=
 		(type_num . sep . dns_alg . sep . num8 . sep . num32 . sep .
-		 timestamp . sep . timestamp . sep . num16 . blk_sep .  sep .
-		 r_dname . blk_sep . sep . base64)
+		 timestamp . sep . timestamp . sep . num16 . sep . r_dname .
+		 sep . base64)
 		$!_r_data_error %_ret . end_wchar;
 
 	r_data_nsec :=
-		(r_dname . blk_sep . bitmap)
+		(r_dname . bitmap)
 		$!_r_data_error %_ret . all_wchar;
 
 	r_data_dnskey :=
@@ -1862,15 +1833,10 @@
 		}
 	}
 
-	action _text_r_data_exit {
-		s->r_data_blocks[++(s->r_data_blocks_count)] =
-			(uint16_t)(rdata_tail - s->r_data);
-	}
-
-	# rdata can be in text or hex format with leading "\#" string
+	# rdata can be in text or hex format with leading "\#" string.
 	r_data =
-		( sep  . ^('\\' | all_wchar)     $_text_r_data %_text_r_data_exit
-		| sep  . '\\' . ^'#' ${ fhold; } $_text_r_data %_text_r_data_exit
+		( sep  . ^('\\' | all_wchar)     $_text_r_data
+		| sep  . '\\' . ^'#' ${ fhold; } $_text_r_data
 		| sep  . '\\' .  '#'             $_hex_r_data   # Hex format.
 		| sep? . end_wchar               $_text_r_data  # Empty rdata.
 		) >_r_data_init $!_r_data_error;
diff --git a/src/zscanner/scanner_functions.c b/src/zscanner/scanner_functions.c
index 7c68348fedb705467594b1e26a05057b58c749ce..a57eac5b4a7eee5791b913c9879c456ca826eb77 100644
--- a/src/zscanner/scanner_functions.c
+++ b/src/zscanner/scanner_functions.c
@@ -15,14 +15,13 @@
  */
 
 #include <config.h>
-#include "zscanner/scanner_functions.h"
-
 #include <stdint.h>
 #include <stdlib.h>
 
-#include "common/errcode.h"
-#include "common/descriptor.h"
-#include "zscanner/scanner.h"
+#include "zscanner/scanner_functions.h"
+#include "zscanner/error.h"		// error codes
+#include "zscanner/scanner.h"		// scanner_t
+#include "zscanner/descriptor.h"	// KNOT_RDATA_WF_END
 
 const uint8_t digit_to_num[] = {
     ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
@@ -766,16 +765,16 @@ int date_to_timestamp(uint8_t *buff, uint32_t *timestamp)
 	}
 
 	*timestamp = hour * 3600 + minute * 60 + second +
-		     (days_across_years[year] +
-		     days_across_months[month] +
-		     day - 1 + leap_day) * 86400;
+	             (days_across_years[year] +
+	             days_across_months[month] +
+	             day - 1 + leap_day) * 86400;
 
-	return KNOT_EOK;
+	return ZSCANNER_OK;
 }
 
 void wire_dname_to_str(const uint8_t  *data,
-		       const uint32_t data_len,
-		       char *text)
+                       const uint32_t data_len,
+                       char           *text)
 {
 	uint32_t i = 0, text_len = 0;
 
@@ -823,162 +822,3 @@ uint8_t loc64to8(uint64_t number)
 	// First 4 bits are mantisa, second 4 bits are exponent.
 	return ((uint8_t)number << 4) + (exponent & 15);
 }
-
-/*!
- * \brief Returns domain name length in wire-format.
- *
- * \param data		Data array.
- * \param data_len	Length of data array.
- *
- * \retval >0		if success.
- * \retval 0		if error.
- */
-static uint32_t get_dname_length(const uint8_t  *data,
-				 const uint32_t data_len)
-{
-	uint8_t  label_len = data[0];
-	uint32_t dname_len = 0;
-
-	while (label_len > 0) {
-		// Label overflow check.
-		if (label_len > MAX_LABEL_LENGTH) {
-			return 0;
-		}
-
-		dname_len += 1 + label_len;
-
-		// Data overflow check.
-		if (dname_len > data_len) {
-			return 0;
-		}
-
-		label_len = data[dname_len];
-        }
-
-	dname_len++; // Last label length byte.
-
-	// Dname overflow check.
-	if (dname_len <= MAX_DNAME_LENGTH) {
-		return dname_len;
-	} else {
-		return 0;
-	}
-}
-
-/*!
- * \brief Returns length of the leading NAPTR block in wire-format.
- *
- * \param data		Data array.
- * \param data_len	Length of data array.
- *
- * \retval >0		if success.
- * \retval 0		if error.
- */
-static uint32_t get_naptr_header_length(const uint8_t  *data,
-					const uint32_t data_len)
-{
-	uint32_t naptr_len = 0;
-
-	// 2B order + 2B preference.
-	naptr_len += 2 + 2;
-
-	// Flags - text string with forward 1B length.
-	naptr_len += data[naptr_len] + 1;
-
-	// Services - text string with forward 1B length.
-	naptr_len += data[naptr_len] + 1;
-
-	// Regexp - text string with forward 1B length.
-	naptr_len += data[naptr_len] + 1;
-
-	// Data overflow check.
-	if (naptr_len <= data_len) {
-		return naptr_len;
-	} else {
-		return 0;
-	}
-}
-
-/*!
- * \brief Returns block length in wire-format.
- *
- * \param data		Data array.
- * \param data_len	Length of data array.
- * \param offset	Start of the block in data array.
- * \param type		Record type.
- *
- * \retval >=0		if success.
- * \retval <0		if error.
- */
-static int32_t get_block_length(const uint8_t  *data,
-				const uint32_t data_len,
-				const uint32_t offset,
-				const int      type)
-{
-	uint32_t ret;
-
-	switch (type) {
-	case KNOT_RDATA_WF_COMPRESSED_DNAME:
-	case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
-		ret = get_dname_length(data + offset, data_len - offset);
-
-		if (ret > 0) {
-			return ret;
-		} else {
-			return -1;
-		}
-	case KNOT_RDATA_WF_NAPTR_HEADER:
-		ret = get_naptr_header_length(data + offset, data_len - offset);
-
-                if (ret > 0) {
-                        return ret;
-                } else {
-                        return -1;
-                }
-	case KNOT_RDATA_WF_REMAINDER:
-		return data_len - offset;
-	default:
-		return 0;
-	}
-}
-
-int find_rdata_blocks(scanner_t *s)
-{
-	int32_t  ret;
-	uint32_t position = 0;
-
-	// Initialization of block items.
-	s->r_data_blocks_count = 0;
-	s->r_data_blocks[0] = 0;
-
-	// Getting appropriate descriptor array.
-	const rdata_descriptor_t *descriptor = get_rdata_descriptor(s->r_type);
-	const int *type = descriptor->block_types;
-
-	// Loop over descriptor array.
-	while (*type != KNOT_RDATA_WF_END) {
-		if (*type > KNOT_RDATA_WF_END) {
-			position += *type;
-		} else {
-			ret = get_block_length(s->r_data,
-					       s->r_data_length,
-					       position,
-					       *type);
-			if (ret < 0) {
-				return ZSCANNER_EUNKNOWN_BLOCK;
-			}
-
-			position += ret;
-		}
-		s->r_data_blocks[++(s->r_data_blocks_count)] = position;
-		type++;
-	}
-
-	// Checking processed rdata length.
-	if (s->r_data_blocks[s->r_data_blocks_count] != s->r_data_length) {
-		return ZSCANNER_EBAD_HEX_RDATA;
-	}
-	else {
-		return KNOT_EOK;
-	}
-}
diff --git a/src/zscanner/scanner_functions.h b/src/zscanner/scanner_functions.h
index 5d219d7143efa18cb1fffc39b202657291b9bc09..036ef7afe2c26933b55b030da06f949bab2d7fc7 100644
--- a/src/zscanner/scanner_functions.h
+++ b/src/zscanner/scanner_functions.h
@@ -96,8 +96,8 @@ int date_to_timestamp(uint8_t *buff, uint32_t *timestamp);
  * \param text		Text output.
  */
 void wire_dname_to_str(const uint8_t  *data,
-		       const uint32_t data_len,
-		       char *text);
+                       const uint32_t data_len,
+                       char *text);
 
 /*!
  * \brief Converts unsigned integer to mantisa*10^(exponent).
@@ -112,16 +112,6 @@ void wire_dname_to_str(const uint8_t  *data,
  */
 uint8_t loc64to8(uint64_t number);
 
-/*!
- * \brief Finds rdata blocks according to rdata descriptors.
- *
- * \param s		Zone scanner.
- *
- * \retval KNOT_EOK	if success.
- * \retval error_code	if error.
- */
-int find_rdata_blocks(scanner_t *s);
-
 #endif // _ZSCANNER__SCANNER_FUNCTIONS_H_
 
 /*! @} */
diff --git a/src/zscanner/test/cases/10_A.in b/src/zscanner/test/cases/10_A.in
index 69ae11d90ea12daf74f130380cb99f2234e57f87..6c7c90a61fd8ec1d1faf941ab3fb88e97b8ca95a 100644
--- a/src/zscanner/test/cases/10_A.in
+++ b/src/zscanner/test/cases/10_A.in
@@ -13,10 +13,7 @@ $TTL	1
 @	A
 @	A			; Empty rdata
 @	A	\# 0		; Hex empty rdata
-@	A	\#		; Missing hex length
 @	A	0.0.0.256	; 8bit overflow
 @	A	0.0.0		; Short address
 @	A	0.0.0.A		; Bad character
 @	A	0.0.0.0 1.1.1.1	; Unexpected item
-@	A	\# 5 0000000000	; Too long rdata
-@	A	\# 5 00000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/10_A.out b/src/zscanner/test/cases/10_A.out
index c36823c14b44e7a13eec5a18a928288f2e18d33c..b4dee87a24e1792ab0e85de30605e28631beea83 100644
--- a/src/zscanner/test/cases/10_A.out
+++ b/src/zscanner/test/cases/10_A.out
@@ -40,8 +40,6 @@ WARNG=ZSCANNER_EBAD_ADDRESS_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_IPV4
 ------
 WARNG=ZSCANNER_EBAD_IPV4
@@ -50,7 +48,3 @@ WARNG=ZSCANNER_EBAD_ADDRESS_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/11_AAAA.in b/src/zscanner/test/cases/11_AAAA.in
index c43d375c444612920f0435f20340336c7492cc54..48c4b37fa6f19dfb557f9aad7c4d4ef32bdc3ae0 100644
--- a/src/zscanner/test/cases/11_AAAA.in
+++ b/src/zscanner/test/cases/11_AAAA.in
@@ -15,10 +15,7 @@ $TTL	1
 @	AAAA
 @	AAAA				; Empty rdata
 @	AAAA	\# 0			; Hex empty rdata
-@	AAAA	\#			; Missing hex length
 @	AAAA	0::FFFFF		; 16bit overflow
 @	AAAA	0:0:0:0:0:0:0		; Short address
 @	AAAA	0:0:0:0:0:0:0:X		; Bad character
 @	AAAA	:: ::			; Unexpected item
-@	AAAA	\# 17 00000000 00000000 00000000 00000000 00	; Too long rdata
-@	AAAA	\# 17 00000000 00000000 00000000 00000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/11_AAAA.out b/src/zscanner/test/cases/11_AAAA.out
index c4006bf3c024e897cdd01157c20c6632b4bde276..0003ddbdefbde74c615309709f0c129b240efca4 100644
--- a/src/zscanner/test/cases/11_AAAA.out
+++ b/src/zscanner/test/cases/11_AAAA.out
@@ -52,8 +52,6 @@ WARNG=ZSCANNER_EBAD_ADDRESS_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_IPV6
 ------
 WARNG=ZSCANNER_EBAD_IPV6
@@ -62,7 +60,3 @@ WARNG=ZSCANNER_EBAD_ADDRESS_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/12_TXT.in b/src/zscanner/test/cases/12_TXT.in
index 36519ff70b12de58bda07261e20535f755ca6359..b71dc0d10e62a9f549c4c5ddc6dd15e1a6caf41d 100644
--- a/src/zscanner/test/cases/12_TXT.in
+++ b/src/zscanner/test/cases/12_TXT.in
@@ -30,10 +30,7 @@ third						; Second string
 @	TXT
 @	TXT			; Empty rdata
 @	TXT	\# 0		; Hex empty rdata
-@	TXT	\#		; Missing hex length
-@	TXT	\# second	; Hex notation with second text string
 @	TXT	\01		; Missing digit in decimal notation
 @	TXT	\256		; 8bit overflow in decimal notation
-@	TXT	\# 2 ""		; Bad length
 @	TXT	"""		; '"' char without forward slash
 @	TXT	"abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536"	; Maximal length overflow
diff --git a/src/zscanner/test/cases/12_TXT.out b/src/zscanner/test/cases/12_TXT.out
index ba573eda2be92f64992ba72f69740fe6cbdd7b16..3110fa7e6f50951b5f19cfd1f023bb3ab6ec79b0 100644
--- a/src/zscanner/test/cases/12_TXT.out
+++ b/src/zscanner/test/cases/12_TXT.out
@@ -120,14 +120,8 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
-WARNG=ZSCANNER_EBAD_HEX_CHAR
-------
 WARNG=ZSCANNER_EBAD_TEXT
 ------
 WARNG=ZSCANNER_EITEM_OVERFLOW
diff --git a/src/zscanner/test/cases/14_NS.in b/src/zscanner/test/cases/14_NS.in
index fe51ef2388aadbd1f6cb85d9654b4f89541727de..41054647e76b3ccfa74cf0226b4437ee370c6bbc 100644
--- a/src/zscanner/test/cases/14_NS.in
+++ b/src/zscanner/test/cases/14_NS.in
@@ -26,7 +26,6 @@ $TTL	1
 @	NS
 @	NS					; Empty rdata
 @	NS	\# 0				; Hex empty rdata
-@	NS	\#				; Missing hex length
 @	NS	&				; Bad (unslashed) character
 @	NS	a*a				; * char can substitute whole label only
 @	NS	@@				; Double @@
@@ -37,5 +36,3 @@ $TTL	1
 @	NS	123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012.	; Domain name exceeded maximal length
 @	NS	123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012	; Domain name exceeded maximal length (after appending origin)
 @	NS	. x				; Unexpected item
-@	NS	\# 2 0001			; Too long rdata
-@	NS	\# 2 00				; Bad rdata length
diff --git a/src/zscanner/test/cases/14_NS.out b/src/zscanner/test/cases/14_NS.out
index 08c0104c59c3fac8f48fe64d83187272b0255508..f73f34371f33ba48bc4c6b116cd7c0ba39c30b8e 100644
--- a/src/zscanner/test/cases/14_NS.out
+++ b/src/zscanner/test/cases/14_NS.out
@@ -118,8 +118,6 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_DNAME_CHAR
@@ -140,7 +138,3 @@ WARNG=ZSCANNER_EDNAME_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/18_MX.in b/src/zscanner/test/cases/18_MX.in
index 52bf995662049083539cae97e0a849d646a290aa..61e6afbdebbc57cbacd74fe09d599543da3bd1aa 100644
--- a/src/zscanner/test/cases/18_MX.in
+++ b/src/zscanner/test/cases/18_MX.in
@@ -17,10 +17,7 @@ $TTL	1
 @	MX
 @	MX				; Empty rdata
 @	MX	\# 0			; Hex empty rdata
-@	MX	\#			; Missing hex length
 @	MX	-1	@		; Negative number
 @	MX	65536	@		; 16bit overflow
 @	MX	1	$		; Bad dname
 @	MX	0	@ x		; Unexpected item
-@	MX	\# 4 0001 0001		; Too long rdata
-@	MX	\# 4 0001 00		; Bad rdata length
diff --git a/src/zscanner/test/cases/18_MX.out b/src/zscanner/test/cases/18_MX.out
index 50b1b7008d6232d37643289b595e84cae4f6425c..0cae318089e8be5dee70c54b371348d3379c4eb3 100644
--- a/src/zscanner/test/cases/18_MX.out
+++ b/src/zscanner/test/cases/18_MX.out
@@ -2,49 +2,49 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0000 00
+RDATA=000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=FFFF 00
+RDATA=FFFF00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 046D61696C00
+RDATA=0001046D61696C00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 046D61696C03746C6400
+RDATA=0001046D61696C03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000F
-RDATA=0001 00
+RDATA=000100
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
@@ -54,15 +54,9 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/19_AFSDB.out b/src/zscanner/test/cases/19_AFSDB.out
index 957abc704810510033a494336b0300e602b7df54..a6dcd0d780d50bc958a8d53920e46e7ad69f3b06 100644
--- a/src/zscanner/test/cases/19_AFSDB.out
+++ b/src/zscanner/test/cases/19_AFSDB.out
@@ -2,31 +2,31 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0012
-RDATA=0001 046D61696C00
+RDATA=0001046D61696C00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0012
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0012
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0012
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0012
-RDATA=0001 00
+RDATA=000100
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
diff --git a/src/zscanner/test/cases/20_RT.out b/src/zscanner/test/cases/20_RT.out
index 490ab2aba8140d3d8b35c2bf02f3e081ba339eb8..d3f8cb0dc07196343eba50977a301b4bfacc2086 100644
--- a/src/zscanner/test/cases/20_RT.out
+++ b/src/zscanner/test/cases/20_RT.out
@@ -2,31 +2,31 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0015
-RDATA=0001 046D61696C00
+RDATA=0001046D61696C00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0015
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0015
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0015
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0015
-RDATA=0001 00
+RDATA=000100
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
diff --git a/src/zscanner/test/cases/21_KX.out b/src/zscanner/test/cases/21_KX.out
index 1c5f323e9a9a0a13333056f223389c70be1a8768..8ccd2e46382a31c02c3664d8e1eddbc4f2931584 100644
--- a/src/zscanner/test/cases/21_KX.out
+++ b/src/zscanner/test/cases/21_KX.out
@@ -2,31 +2,31 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0024
-RDATA=0001 046D61696C00
+RDATA=0001046D61696C00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0024
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0024
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0024
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0024
-RDATA=0001 00
+RDATA=000100
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
diff --git a/src/zscanner/test/cases/22_HINFO.in b/src/zscanner/test/cases/22_HINFO.in
index 8939e2d407462e1b05de690132c5d7c59d06bf18..4cd5ab83dba70e59f614cc3cab0ca1434b902b4d 100644
--- a/src/zscanner/test/cases/22_HINFO.in
+++ b/src/zscanner/test/cases/22_HINFO.in
@@ -19,11 +19,8 @@ $TTL	1
 @	HINFO
 @	HINFO			; Empty rdata
 @	HINFO	\# 0		; Hex empty rdata
-@	HINFO	\#		; Missing hex length
-@	HINFO	\# second	; Hex notation with second text string
 @	HINFO	\01 ""		; Missing digit in decimal notation
 @	HINFO	\256 ""		; 8bit overflow in decimal notation
-@	HINFO	\# 3 0000	; Bad rdata length
 @	HINFO	""" ""		; '"' char without forward slash
 @	HINFO	"" "" ""	; Unexpected item
 @	HINFO	"abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" ""	; Maximal length overflow
diff --git a/src/zscanner/test/cases/22_HINFO.out b/src/zscanner/test/cases/22_HINFO.out
index 66863ef8f7484958a9566a41afc9b6df9f13d3e4..8cb034d32a065f5fb130662a8470b2403807f42d 100644
--- a/src/zscanner/test/cases/22_HINFO.out
+++ b/src/zscanner/test/cases/22_HINFO.out
@@ -78,14 +78,8 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
 WARNG=ZSCANNER_EBAD_TEXT
 ------
 WARNG=ZSCANNER_EBAD_REST
diff --git a/src/zscanner/test/cases/23_MINFO.in b/src/zscanner/test/cases/23_MINFO.in
index 4f0076513451ce174ce62404c7b90918829c42ca..7b34c6008fa79617f4966df5bfc8d16795214bab 100644
--- a/src/zscanner/test/cases/23_MINFO.in
+++ b/src/zscanner/test/cases/23_MINFO.in
@@ -16,6 +16,3 @@ $TTL	1
 @	MINFO
 @	MINFO				; Empty rdata
 @	MINFO	\# 0			; Hex empty rdata
-@	MINFO	\#			; Missing hex length
-@	MINFO	\# 3 00 0001		; Too long rdata
-@	MINFO	\# 3 00 00		; Bad rdata length
diff --git a/src/zscanner/test/cases/23_MINFO.out b/src/zscanner/test/cases/23_MINFO.out
index 026a7d23220af1773839f16153926e41bf8d2ffa..b0ff2dff4eaefa215303de24f6908e5d2cc16797 100644
--- a/src/zscanner/test/cases/23_MINFO.out
+++ b/src/zscanner/test/cases/23_MINFO.out
@@ -2,43 +2,43 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=046D61696C00 046D61696C03746C6400
+RDATA=046D61696C00046D61696C03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=000E
-RDATA=00 00
+RDATA=0000
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
@@ -46,9 +46,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/24_RP.out b/src/zscanner/test/cases/24_RP.out
index 0565c7df9b1d0b585821ac2fba103f2d8d83d3a0..7f1d08c6817714eff887efdf49acc50684da4a3b 100644
--- a/src/zscanner/test/cases/24_RP.out
+++ b/src/zscanner/test/cases/24_RP.out
@@ -2,31 +2,31 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0011
-RDATA=046D61696C00 046D61696C03746C6400
+RDATA=046D61696C00046D61696C03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0011
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0011
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0011
-RDATA=00 00
+RDATA=0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0011
-RDATA=00 00
+RDATA=0000
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
diff --git a/src/zscanner/test/cases/25_SOA.in b/src/zscanner/test/cases/25_SOA.in
index 197cb20c62a5aebe7d1ea6ebac62a71bf8c14872..bf093d47e62c9705d443cd4eb068bd25d8260efe 100644
--- a/src/zscanner/test/cases/25_SOA.in
+++ b/src/zscanner/test/cases/25_SOA.in
@@ -22,7 +22,6 @@ $TTL	1
 @	SOA
 @	SOA						; Empty rdata
 @	SOA	\# 0					; Hex empty rdata
-@	SOA	\#					; Missing hex length
 @	SOA	@	@	1h	0 0 0 0		; Bad number
 @	SOA	@	@	4294967296 0 0 0 0	; Serial overflow
 @	SOA	@	@	0 4294967296 0 0 0	; Refresh overflow
@@ -30,7 +29,3 @@ $TTL	1
 @	SOA	@	@	0 0 0 4294967296 0	; Expire overflow
 @	SOA	@	@	0 0 0 0 4294967296	; Minimum overflow
 @	SOA	@	@	0 0 0 0 0 x		; Unexpected item
-@	SOA	\# 23 0001 00 0000000000000000000000000000000000000000	; Too long rdata (mname)
-@	SOA	\# 23 00 0001 0000000000000000000000000000000000000000	; Too long rdata (rname)
-@	SOA	\# 23 00 00 000000000000000000000000000000000000000000	; Too long rdata
-@	SOA	\# 23 00 00   0000000000000000000000000000000000000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/25_SOA.out b/src/zscanner/test/cases/25_SOA.out
index 0833c68b7bfda78b70a576b05c403541ecb8255b..9bdd69d6839c9855f2f231f43daa690aca7e7017 100644
--- a/src/zscanner/test/cases/25_SOA.out
+++ b/src/zscanner/test/cases/25_SOA.out
@@ -2,55 +2,55 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 0000000000000000000000000000000000000000
+RDATA=00000000000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=03746C6400 03746C6400 0000000000000000000000000000000000000000
+RDATA=03746C640003746C64000000000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 FFFFFFFF00000000000000000000000000000000
+RDATA=0000FFFFFFFF00000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+RDATA=000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 000000000001518000000E100000003C00000001
+RDATA=0000000000000001518000000E100000003C00000001
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 0000000000000000000000000000000000000000
+RDATA=00000000000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 0000000000000000000000000000000000000000
+RDATA=00000000000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=026E7303746C6400 0C66697273742E7365636F6E6403746C6400 77A23B46000C08A500001C20000000B400000004
+RDATA=026E7303746C64000C66697273742E7365636F6E6403746C640077A23B46000C08A500001C20000000B400000004
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0006
-RDATA=00 00 0000000000000000000000000000000000000000
+RDATA=00000000000000000000000000000000000000000000
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
@@ -60,8 +60,6 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER32_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER32_OVERFLOW
@@ -74,11 +72,3 @@ WARNG=ZSCANNER_ENUMBER32_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/26_SRV.in b/src/zscanner/test/cases/26_SRV.in
index 43abb3f69bf764990a2488803c5fe62f836b4dc3..001e1e65400e0c72c925f3668d5fa55d9ef4ba31 100644
--- a/src/zscanner/test/cases/26_SRV.in
+++ b/src/zscanner/test/cases/26_SRV.in
@@ -14,7 +14,6 @@ _ldap._tcp.test.tld.	SRV	0	0	0	@ 	; Underscores in owner
 @	SRV
 @	SRV						; Empty rdata
 @	SRV	\# 0					; Hex empty rdata
-@	SRV	\#					; Missing hex length
 @	SRV	1h	0	0	@		; Bad priority
 @	SRV	0	1h	0	@		; Bad weight
 @	SRV	0	0	1h	@		; Bad port
@@ -24,5 +23,3 @@ _ldap._tcp.test.tld.	SRV	0	0	0	@ 	; Underscores in owner
 @	SRV	0	0	65536	@		; Port overflow
 @	SRV	0	0	0	@ x		; Unexpected item
 @	SRV	0	0	0			; Missing item
-@	SRV	\# 8 000000000000 00 00			; Too long rdata
-@	SRV	\# 8 000000000000 00			; Bad rdata length
diff --git a/src/zscanner/test/cases/26_SRV.out b/src/zscanner/test/cases/26_SRV.out
index fa63eb0d6e516372070dd320dc1b52dfb3f9a340..7db652bdc9fc49997537508f104e971dbd3d5a48 100644
--- a/src/zscanner/test/cases/26_SRV.out
+++ b/src/zscanner/test/cases/26_SRV.out
@@ -2,43 +2,43 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 00
+RDATA=00000000000000
 ------
 OWNER=055F6C646170045F746370047465737403746C6400
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 00
+RDATA=00000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=FFFFFFFFFFFF 00
+RDATA=FFFFFFFFFFFF00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 082030205C222E402A03746C6400
+RDATA=000000000000082030205C222E402A03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 00
+RDATA=00000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 00
+RDATA=00000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0021
-RDATA=000000000000 00
+RDATA=00000000000000
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
@@ -52,8 +52,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
@@ -66,7 +64,3 @@ WARNG=ZSCANNER_EBAD_REST
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/27_NAPTR.in b/src/zscanner/test/cases/27_NAPTR.in
index 0f8a132b57416aaeb245909cc83bc9bf240a3cdf..44fd34608bb9076f40fd5019233aa4d43b39a68a 100644
--- a/src/zscanner/test/cases/27_NAPTR.in
+++ b/src/zscanner/test/cases/27_NAPTR.in
@@ -14,10 +14,7 @@ $TTL	1
 @	NAPTR
 @	NAPTR							; Empty rdata
 @	NAPTR	\# 0						; Hex empty rdata
-@	NAPTR	\#						; Missing hex length
 @	NAPTR	65536	0	""	""	""	@	; Order overflow
 @	NAPTR	0	65536	""	""	""	@	; Preference overflow
 @	NAPTR	0	0	""	""	""	@  x	; Unexpected item
 @	NAPTR	0	0	""	""	""		; Missing item
-@	NAPTR	\# 9 00000000000000 00 00			; Too long rdata
-@	NAPTR	\# 9 00000000000000 00				; Bad rdata length
diff --git a/src/zscanner/test/cases/27_NAPTR.out b/src/zscanner/test/cases/27_NAPTR.out
index 42d99d0e1a9d9b9291e631e84777411df31c82e7..5db20ab7ae27ba7175f72b7b9a9dc94fbc13ea77 100644
--- a/src/zscanner/test/cases/27_NAPTR.out
+++ b/src/zscanner/test/cases/27_NAPTR.out
@@ -2,43 +2,43 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=00000000000000 00
+RDATA=0000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=FFFFFFFF000000 00
+RDATA=FFFFFFFF00000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=FFFFFFFF00001F215E75726E3A6369643A2E2B40285B5E2E5D2B2E29282E2A2924215C322169 00
+RDATA=FFFFFFFF00001F215E75726E3A6369643A2E2B40285B5E2E5D2B2E29282E2A2924215C32216900
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=00000000000000 082030205C222E402A03746C6400
+RDATA=00000000000000082030205C222E402A03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=00000000000000 00
+RDATA=0000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=00000000000000 00
+RDATA=0000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=0023
-RDATA=00000000000000 00
+RDATA=0000000000000000
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
@@ -46,8 +46,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
@@ -56,7 +54,3 @@ WARNG=ZSCANNER_EBAD_REST
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/29_CERT.in b/src/zscanner/test/cases/29_CERT.in
index c08c3f79de1792d727cad92c5eb6e53e5c180a3e..5a5daf6ae0d7dcdff8453f56dd73934fa44c0a87 100644
--- a/src/zscanner/test/cases/29_CERT.in
+++ b/src/zscanner/test/cases/29_CERT.in
@@ -40,7 +40,6 @@ $TTL	1
 @	CERT
 @	CERT						; Empty rdata
 @	CERT	\# 0					; Hex empty rdata
-@	CERT	\#					; Missing hex length
 @	CERT	65536	0	0	AA==		; Type overflow
 @	CERT	X	0	0	AA==		; Bad type mnemonic
 @	CERT	0	65536	0	AA==		; Key tag overflow
@@ -55,5 +54,3 @@ $TTL	1
 @	CERT	0	0	0	===		; Bad padding
 @	CERT	0	0	0	====		; Bad padding
 @	CERT	0	0	0			; Missing item
-@	CERT	\# 6 00000000000000			; Too long rdata
-@	CERT	\# 7 000000000000			; Bad rdata length
diff --git a/src/zscanner/test/cases/29_CERT.out b/src/zscanner/test/cases/29_CERT.out
index 5352d090bc67d85bc08e5d43213fbdb28fef9879..4e579efd26c0788ec7d1a8402ab6955efeffc915 100644
--- a/src/zscanner/test/cases/29_CERT.out
+++ b/src/zscanner/test/cases/29_CERT.out
@@ -202,8 +202,6 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_CERT_TYPE
@@ -232,7 +230,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/30_KEY.in b/src/zscanner/test/cases/30_KEY.in
index 392257fd896d3ce95bbbf98f099ee408960f2930..263fa03479874cbe0bbf9f9a39239e99be58b374 100644
--- a/src/zscanner/test/cases/30_KEY.in
+++ b/src/zscanner/test/cases/30_KEY.in
@@ -16,7 +16,6 @@ $TTL	1
 @	KEY
 @	KEY						; Empty rdata
 @	KEY	\# 0					; Hex empty rdata
-@	KEY	\#					; Missing hex length
 @	KEY	65536	0	0	AA==		; Type overflow
 @	KEY	0	256	0	AA==		; Key tag overflow
 @	KEY	0	0	256	AA==		; Algorithm overflow
@@ -30,5 +29,3 @@ $TTL	1
 @	KEY	0	0	0	===		; Bad padding
 @	KEY	0	0	0	====		; Bad padding
 @	KEY	0	0	0			; Missing item
-@	KEY	\# 5 000000000000			; Too long rdata
-@	KEY	\# 6 0000000000				; Bad rdata length
diff --git a/src/zscanner/test/cases/30_KEY.out b/src/zscanner/test/cases/30_KEY.out
index aa721d25b14d00219c56deaaacbbbc5c44b8d162..fb48735f6643088d04a78bd286c237aaed1c31d7 100644
--- a/src/zscanner/test/cases/30_KEY.out
+++ b/src/zscanner/test/cases/30_KEY.out
@@ -58,8 +58,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -86,7 +84,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/31_DNSKEY.in b/src/zscanner/test/cases/31_DNSKEY.in
index 0f99937473e814211416d9e3e5a4a969e7d32806..871de97e7696e9388f1fe1d7a9e7fa6891e9b64c 100644
--- a/src/zscanner/test/cases/31_DNSKEY.in
+++ b/src/zscanner/test/cases/31_DNSKEY.in
@@ -17,7 +17,6 @@ $TTL	1
 @	DNSKEY
 @	DNSKEY						; Empty rdata
 @	DNSKEY	\# 0					; Hex empty rdata
-@	DNSKEY	\#					; Missing hex length
 @	DNSKEY	65536	0	0	AA==		; Type overflow
 @	DNSKEY	0	256	0	AA==		; Key tag overflow
 @	DNSKEY	0	0	256	AA==		; Algorithm overflow
@@ -31,5 +30,3 @@ $TTL	1
 @	DNSKEY	0	0	0	===		; Bad padding
 @	DNSKEY	0	0	0	====		; Bad padding
 @	DNSKEY	0	0	0			; Missing item
-@	DNSKEY	\# 5 000000000000			; Too long rdata
-@	DNSKEY	\# 6 0000000000				; Bad rdata length
diff --git a/src/zscanner/test/cases/31_DNSKEY.out b/src/zscanner/test/cases/31_DNSKEY.out
index f72ec07466409e85eb54d7ae681e0e10e1462611..df897ffc6f28833846c093d9e2b77246d6e79c8c 100644
--- a/src/zscanner/test/cases/31_DNSKEY.out
+++ b/src/zscanner/test/cases/31_DNSKEY.out
@@ -64,8 +64,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -92,7 +90,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/32_APL.in b/src/zscanner/test/cases/32_APL.in
index 56f2c232959cebcbe49efb29759f1a209487b9eb..43ffbadc0a28609c1e6986b81355395da1482267 100644
--- a/src/zscanner/test/cases/32_APL.in
+++ b/src/zscanner/test/cases/32_APL.in
@@ -25,6 +25,3 @@ $TTL	1
 @	APL	2::/0			; Bad ipv6 address
 @	APL	2:0::0/x		; Bad prefix length
 @	APL	1:0.0.0.0/		; Missing prefix length
-@	APL	\#			; Missing hex length
-@	APL	\# 4 0001000000		; Too long rdata
-@	APL	\# 5 00000000		; Bad rdata length
diff --git a/src/zscanner/test/cases/32_APL.out b/src/zscanner/test/cases/32_APL.out
index 8e7ae85749c4d21195c2e973e1a532af882619b0..07579c2ff3b0b3b020a3eaa26db0abe6c21b9f90 100644
--- a/src/zscanner/test/cases/32_APL.out
+++ b/src/zscanner/test/cases/32_APL.out
@@ -92,9 +92,3 @@ WARNG=ZSCANNER_EBAD_APL
 ------
 WARNG=ZSCANNER_EBAD_APL
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/33_DS.in b/src/zscanner/test/cases/33_DS.in
index 8a71195b1a90b6ea8560234ea01a2cfc11737269..a5d805c3aa6d139a45b922be0207e83436fbde16 100644
--- a/src/zscanner/test/cases/33_DS.in
+++ b/src/zscanner/test/cases/33_DS.in
@@ -14,7 +14,6 @@ $TTL	1
 @	DS
 @	DS						; Empty rdata
 @	DS	\# 0					; Hex empty rdata
-@	DS	\#					; Missing hex length
 @	DS	65536	0	0	00		; Key tag overflow
 @	DS	0	256	0	00		; Algorithm overflow
 @	DS	0	0	256	00		; Digest type overflow
@@ -22,5 +21,3 @@ $TTL	1
 @	DS	0	0	0	00 0		; Continuous block length must be multiple of 2
 @	DS	0	0	0	XX		; Bad hex character
 @	DS	0	0	0			; Missing item
-@	DS	\# 5 000000000000			; Too long rdata
-@	DS	\# 6 0000000000				; Bad rdata length
diff --git a/src/zscanner/test/cases/33_DS.out b/src/zscanner/test/cases/33_DS.out
index b727d1c1d8bd2b15ee35e7116ecead088fa304d6..803d409cf7b0775a16fdbf0f7d310cc256e27068 100644
--- a/src/zscanner/test/cases/33_DS.out
+++ b/src/zscanner/test/cases/33_DS.out
@@ -46,8 +46,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -62,7 +60,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/34_SSHFP.in b/src/zscanner/test/cases/34_SSHFP.in
index b8b502d988ba41da69b29f0a31d1f2316a3229e5..89c4de3da227905fd5d1d22ba2346ee5892d0634 100644
--- a/src/zscanner/test/cases/34_SSHFP.in
+++ b/src/zscanner/test/cases/34_SSHFP.in
@@ -13,12 +13,9 @@ $TTL	1
 @	SSHFP
 @	SSHFP					; Empty rdata
 @	SSHFP	\# 0				; Hex empty rdata
-@	SSHFP	\#				; Missing hex length
 @	SSHFP	256	0	00		; Algorithm overflow
 @	SSHFP	0	256	00		; Fp type overflow
 @	SSHFP	0	0	0		; Continuous block length must be multiple of 2
 @	SSHFP	0	0	00 0		; Continuous block length must be multiple of 2
 @	SSHFP	0	0	XX		; Bad hex character
 @	SSHFP	0	0			; Missing item
-@	SSHFP	\# 3 00000000			; Too long rdata
-@	SSHFP	\# 4 000000			; Bad rdata length
diff --git a/src/zscanner/test/cases/34_SSHFP.out b/src/zscanner/test/cases/34_SSHFP.out
index 787074b91f0b5bc89b985957380727bd214857bc..4b6c7d6c9c069e1b6ea9afd99a7632e1e4adad61 100644
--- a/src/zscanner/test/cases/34_SSHFP.out
+++ b/src/zscanner/test/cases/34_SSHFP.out
@@ -40,8 +40,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -54,7 +52,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/35_IPSECKEY.in b/src/zscanner/test/cases/35_IPSECKEY.in
index c0e06f8b3e4c01f4bf15956f4275658ecd6d823a..dc56bd71503645c1c19169a152ea95d90182a771 100644
--- a/src/zscanner/test/cases/35_IPSECKEY.in
+++ b/src/zscanner/test/cases/35_IPSECKEY.in
@@ -19,7 +19,6 @@ $TTL	1
 @	IPSECKEY
 @	IPSECKEY							; Empty rdata
 @	IPSECKEY	\# 0						; Hex empty rdata
-@	IPSECKEY	\#						; Missing hex length
 @	IPSECKEY	256	0	0	.			; Precedence overflow
 @	IPSECKEY	0	4	0	.			; Unknown gateway
 @	IPSECKEY	0	0	256	.	AA==		; Algorithm overflow
@@ -28,5 +27,3 @@ $TTL	1
 @	IPSECKEY	0	0	1	.	A		; Continuous block length must be multiple of 4
 @	IPSECKEY	0	0	1	.	=		; Bad padding
 @	IPSECKEY	0	0					; Missing item
-@	IPSECKEY	\# 3 00000000					; Too long rdata
-@	IPSECKEY	\# 4 000000					; Bad rdata length
diff --git a/src/zscanner/test/cases/35_IPSECKEY.out b/src/zscanner/test/cases/35_IPSECKEY.out
index 6865bc72c8b9fe0e47df7cd2248f0a0a2b84a898..8432557a3bcf029678b2ad4ac2a45db5280cbd20 100644
--- a/src/zscanner/test/cases/35_IPSECKEY.out
+++ b/src/zscanner/test/cases/35_IPSECKEY.out
@@ -76,8 +76,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_GATEWAY
@@ -94,7 +92,3 @@ WARNG=ZSCANNER_EBAD_GATEWAY_KEY
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/36_RRSIG.in b/src/zscanner/test/cases/36_RRSIG.in
index 700c152d155cb88e53e104b01537a08c0bde9898..e970fc513ad532a8bb16f66e280903c65fff2327 100644
--- a/src/zscanner/test/cases/36_RRSIG.in
+++ b/src/zscanner/test/cases/36_RRSIG.in
@@ -21,7 +21,6 @@ $TTL	1
 @	RRSIG
 @	RRSIG										; Empty rdata
 @	RRSIG	\# 0									; Hex empty rdata
-@	RRSIG	\#									; Missing hex length
 @	RRSIG	X	0	0	0	0	0	0	.	AA==	; Unknown type
 @	RRSIG	TYPE65536	0	0	0	0	0	0	.	AA==	; Type overflow
 @	RRSIG	A	256	0	0	0	0	0		.	AA==	; Algorithm overflow
@@ -44,5 +43,3 @@ $TTL	1
 @	RRSIG	A	0	0	0	0	0	0	.	===	; Bad padding
 @	RRSIG	A	0	0	0	0	0	0	.	====	; Bad padding
 @	RRSIG	A	0	0	0	0	0	0	.		; Missing item
-@	RRSIG	\# 20 000100000000000000000000000000000000 00 0000			; Too long rdata
-@	RRSIG	\# 21 000100000000000000000000000000000000 00 00			; Bad rdata length
diff --git a/src/zscanner/test/cases/36_RRSIG.out b/src/zscanner/test/cases/36_RRSIG.out
index f6244327ab1054343d3cafd49ef8d1a88142e8f3..15d1ca897a53f1afeb70ab5e0caeaecf87066272 100644
--- a/src/zscanner/test/cases/36_RRSIG.out
+++ b/src/zscanner/test/cases/36_RRSIG.out
@@ -2,85 +2,85 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000000000000000000000000000000000000 00 00
+RDATA=0000000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000102030000000400000005000000060007 010800 09
+RDATA=00010203000000040000000500000006000701080009
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 00 00
+RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000001000000000000000000000000000000 00 00
+RDATA=0000010000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 00
+RDATA=0001000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000FFCEDD7F0000 00 00
+RDATA=000100000000000000000000FFCEDD7F00000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000000000000000000000000000000000000 082030205C222E402A03746C6400 00
+RDATA=000000000000000000000000000000000000082030205C222E402A03746C640000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 666F
+RDATA=00010000000000000000000000000000000000666F
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 666F6F
+RDATA=00010000000000000000000000000000000000666F6F
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 666F6F62
+RDATA=00010000000000000000000000000000000000666F6F62
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 666F6F6261
+RDATA=00010000000000000000000000000000000000666F6F6261
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 00
+RDATA=0001000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 00
+RDATA=0001000000000000000000000000000000000000
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002E
-RDATA=000100000000000000000000000000000000 00 00
+RDATA=0001000000000000000000000000000000000000
 ------
 WARNG=ZSCANNER_EUNSUPPORTED_TYPE
 ------
@@ -88,8 +88,6 @@ WARNG=ZSCANNER_EUNSUPPORTED_TYPE
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EUNSUPPORTED_TYPE
 ------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
@@ -134,7 +132,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/37_NSEC.in b/src/zscanner/test/cases/37_NSEC.in
index e424816d62b632ba0da3889adfb7d4219a88df57..1efa4ea4b52821d7708724531aa0de7ad13f884f 100644
--- a/src/zscanner/test/cases/37_NSEC.in
+++ b/src/zscanner/test/cases/37_NSEC.in
@@ -16,8 +16,5 @@ $TTL	1
 @	NSEC
 @	NSEC						; Empty rdata
 @	NSEC	\# 0					; Hex empty rdata
-@	NSEC	\#					; Missing hex length
 @	NSEC	.	TYPE65536			; Type number overflow
 @	NSEC	.	X				; Unknown type
-@	NSEC	\# 1 0000				; Too long rdata
-@	NSEC	\# 2 00					; Bad rdata length
diff --git a/src/zscanner/test/cases/37_NSEC.out b/src/zscanner/test/cases/37_NSEC.out
index b6e57a4b5e1125fd61216cd3f064219cb17585c9..29ec7493c02b8a84b9ef28c8b615fe55662a39fd 100644
--- a/src/zscanner/test/cases/37_NSEC.out
+++ b/src/zscanner/test/cases/37_NSEC.out
@@ -2,55 +2,55 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 
+RDATA=00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=082030205C222E402A03746C6400 
+RDATA=082030205C222E402A03746C6400
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 000180
+RDATA=00000180
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 FF200000000000000000000000000000000000000000000000000000000000000001
+RDATA=00FF200000000000000000000000000000000000000000000000000000000000000001
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 0001E0
+RDATA=000001E0
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 000180010180020180800180
+RDATA=00000180010180020180800180
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 
+RDATA=00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 
+RDATA=00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=002F
-RDATA=00 
+RDATA=00
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
@@ -58,13 +58,7 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_BITMAP
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/38_DHCID.in b/src/zscanner/test/cases/38_DHCID.in
index 30f5c006e43a581ec96b6fef81281962ea225b3b..4c0642a9bf09646a3bc48438f17793677c91c614 100644
--- a/src/zscanner/test/cases/38_DHCID.in
+++ b/src/zscanner/test/cases/38_DHCID.in
@@ -15,7 +15,6 @@ $TTL	1
 @	DHCID
 @	DHCID			; Empty rdata
 @	DHCID	\# 0		; Hex empty rdata
-@	DHCID	\#		; Missing hex length
 @	DHCID	A		; Continuous block length must be multiple of 4
 @	DHCID	AB		; Continuous block length must be multiple of 4
 @	DHCID	ABC		; Continuous block length must be multiple of 4
@@ -25,5 +24,3 @@ $TTL	1
 @	DHCID	==		; Bad padding
 @	DHCID	===		; Bad padding
 @	DHCID	====		; Bad padding
-@	DHCID	\# 1 0000	; Too long rdata
-@	DHCID	\# 2 00		; Bad rdata length
diff --git a/src/zscanner/test/cases/38_DHCID.out b/src/zscanner/test/cases/38_DHCID.out
index 41774e3ee37da9d4a9af1379877d5c96da438d73..288e863f4adfc50fe0559e9328c536d44e503d74 100644
--- a/src/zscanner/test/cases/38_DHCID.out
+++ b/src/zscanner/test/cases/38_DHCID.out
@@ -52,8 +52,6 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_BASE64_CHAR
 ------
 WARNG=ZSCANNER_EBAD_BASE64_CHAR
@@ -72,7 +70,3 @@ WARNG=ZSCANNER_EBAD_RDATA
 ------
 WARNG=ZSCANNER_EBAD_RDATA
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/39_NSEC3.in b/src/zscanner/test/cases/39_NSEC3.in
index ba8bdcb25e2bddae3f20d51e394e9f916f9458b2..cadbfa9d3a6769edbfae27776f7bf78ae97c493c 100644
--- a/src/zscanner/test/cases/39_NSEC3.in
+++ b/src/zscanner/test/cases/39_NSEC3.in
@@ -23,7 +23,6 @@ $TTL	1
 @	NSEC3
 @	NSEC3								; Empty rdata
 @	NSEC3	\# 0							; Hex empty rdata
-@	NSEC3	\#							; Missing hex length
 @	NSEC3	256	0	0	-	00======		; Algorithm overflow
 @	NSEC3	0	256	0	-	00======		; Flags overflow
 @	NSEC3	0	0	65536	-	00======		; Iterations overflow
@@ -45,5 +44,3 @@ $TTL	1
 @	NSEC3	0	0	0	-	CPNMUOJ1  E8======	; Two base32hex blocks with blank space between them
 @	NSEC3	0	0	0	-	00======	TYPE65536	; Type number overflow
 @	NSEC3	0	0	0	-	00======	X		; Unknown type
-@	NSEC3	\# 7 0000000000010000					; Too long rdata
-@	NSEC3	\# 8 00000000000100					; Bad rdata length
diff --git a/src/zscanner/test/cases/39_NSEC3.out b/src/zscanner/test/cases/39_NSEC3.out
index ef533f58778075183c5f153337fff37d1a7ee55f..35ee74a107aac227462bf3362227000ad7f24089 100644
--- a/src/zscanner/test/cases/39_NSEC3.out
+++ b/src/zscanner/test/cases/39_NSEC3.out
@@ -100,8 +100,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -144,7 +142,3 @@ WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_BITMAP
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/40_NSEC3PARAM.in b/src/zscanner/test/cases/40_NSEC3PARAM.in
index b397591e514ab9fd002c201da3c52902fe5bb309..694e3f7309d574d7febf147684d7b4f392fd4009 100644
--- a/src/zscanner/test/cases/40_NSEC3PARAM.in
+++ b/src/zscanner/test/cases/40_NSEC3PARAM.in
@@ -13,7 +13,6 @@ $TTL	1
 @	NSEC3PARAM
 @	NSEC3PARAM						; Empty rdata
 @	NSEC3PARAM	\# 0					; Hex empty rdata
-@	NSEC3PARAM	\#					; Missing hex length
 @	NSEC3PARAM	256	0	0	00		; Algorithm overflow
 @	NSEC3PARAM	0	256	0	00		; Flags overflow
 @	NSEC3PARAM	0	0	65536	00		; Iterations overflow
@@ -22,5 +21,3 @@ $TTL	1
 @	NSEC3PARAM	0	0	0	00 00		; Hex block must not contain blank spaces
 @	NSEC3PARAM	0	0	0	00 x		; Unexpected item
 @	NSEC3PARAM	0	0	0			; Missing item
-@	NSEC3PARAM	\# 5 000000000000			; Too long rdata
-@	NSEC3PARAM	\# 6 0000000000				; Bad rdata length
diff --git a/src/zscanner/test/cases/40_NSEC3PARAM.out b/src/zscanner/test/cases/40_NSEC3PARAM.out
index 85f9f74f76816115404d169d00d37add1bbf52cf..0fbc44807e73c9beee03fa3fb64c6a7670708d83 100644
--- a/src/zscanner/test/cases/40_NSEC3PARAM.out
+++ b/src/zscanner/test/cases/40_NSEC3PARAM.out
@@ -40,8 +40,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -58,7 +56,3 @@ WARNG=ZSCANNER_EBAD_REST
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/41_TLSA.in b/src/zscanner/test/cases/41_TLSA.in
index d28f426ee73b64eab1acb240c92fc301c4be1c92..927498e537c19d6812d2a6d979725a144f2d34e8 100644
--- a/src/zscanner/test/cases/41_TLSA.in
+++ b/src/zscanner/test/cases/41_TLSA.in
@@ -13,12 +13,9 @@ $TTL	1
 @	TLSA
 @	TLSA						; Empty rdata
 @	TLSA	\# 0					; Hex empty rdata
-@	TLSA	\#					; Missing hex length
 @	TLSA	256	0	0	00		; Algorithm overflow
 @	TLSA	0	256	0	00		; Flags overflow
 @	TLSA	0	0	256	00		; Iterations overflow
 @	TLSA	0	0	0	0		; Hex block length must be multiple of 2
 @	TLSA	0	0	0	0x		; Bad hex char
 @	TLSA	0	0	0			; Missing item
-@	TLSA	\# 4 0000000000				; Too long rdata
-@	TLSA	\# 5 00000000				; Bad rdata length
diff --git a/src/zscanner/test/cases/41_TLSA.out b/src/zscanner/test/cases/41_TLSA.out
index ebe5eaa31653c9daf549eac62b95501fec9facb5..8bbc0d47962dd45f70e54427312b7d79cd6a7374 100644
--- a/src/zscanner/test/cases/41_TLSA.out
+++ b/src/zscanner/test/cases/41_TLSA.out
@@ -40,8 +40,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
 ------
 WARNG=ZSCANNER_ENUMBER8_OVERFLOW
@@ -54,7 +52,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/42_LOC.in b/src/zscanner/test/cases/42_LOC.in
index 11a87c472a84f2b02af38f86fedc05d5d2241162..9eaae1dd6a25dc016606e977879cb37322445c80 100644
--- a/src/zscanner/test/cases/42_LOC.in
+++ b/src/zscanner/test/cases/42_LOC.in
@@ -40,7 +40,6 @@ $TTL	1
 @	LOC
 @	LOC						; Empty rdata
 @	LOC	\# 0					; Hex empty rdata
-@	LOC	\#					; Missing hex length
 @	LOC	91 0 0 N 0 0 0 E 0 0 0 0		; Degree overflow
 @	LOC	0 60 0 N 0 0 0 E 0 0 0 0		; Minute overflow
 @	LOC	0 0 60 0 N 0 0 0 E 0 0 0 0		; Second overflow
@@ -63,5 +62,3 @@ $TTL	1
 @	LOC	1     N 1     x	0			; Bad letter
 @	LOC	1     N 1     E				; Missing altitude
 @	LOC	0 0 0 N 0 0 0 E 0 0 0 0 x		; Unexpected item
-@	LOC	\# 17 00 00 00 00 00000000 00000000 00000000 00	; Too long rdata
-@	LOC	\# 17 00 00 00 00 00000000 00000000 00000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/42_LOC.out b/src/zscanner/test/cases/42_LOC.out
index 9c6580c0c64c5867f46633e37164710d4b2901c2..5418c325869ea10d4c7aeaebf59d49406eabcf2d 100644
--- a/src/zscanner/test/cases/42_LOC.out
+++ b/src/zscanner/test/cases/42_LOC.out
@@ -234,8 +234,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_LOC_DATA
 ------
 WARNG=ZSCANNER_EBAD_LOC_DATA
@@ -248,7 +246,3 @@ WARNG=ZSCANNER_EBAD_LOC_DATA
 ------
 WARNG=ZSCANNER_EBAD_LOC_DATA
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/43_EUI48.in b/src/zscanner/test/cases/43_EUI48.in
index 2da448fba5ebf5529216fcc38bb3e84b4db3f36b..a2abbec5c7f961b3bc7f2754145962685fc29137 100644
--- a/src/zscanner/test/cases/43_EUI48.in
+++ b/src/zscanner/test/cases/43_EUI48.in
@@ -14,12 +14,9 @@ $TTL	1
 @	EUI48
 @	EUI48				; Empty rdata
 @	EUI48	\# 0			; Hex empty rdata
-@	EUI48	\#			; Missing hex length
 @	EUI48	00-00-00-00-00		; Too few hex pairs
 @	EUI48	00-00-00-00-00-00-00	; Too many hex pairs
 @	EUI48	00-00-00-00-00-0	; Missing char in a hex pair
 @	EUI48	00:00-00-00-00-00	; Bad separator
 @	EUI48	00-00-00-x0-00-00	; Bad character
 @	EUI48	00-00-00-00-00-00 x	; Unexpected item
-@	EUI48	\# 7 000000000000 00	; Too long rdata
-@	EUI48	\# 7 000000000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/43_EUI48.out b/src/zscanner/test/cases/43_EUI48.out
index 29f6bdbb84bed5776ab7743535d6b846e8244304..1b06bb3993f0a78151447ef35babe5475f6da5e5 100644
--- a/src/zscanner/test/cases/43_EUI48.out
+++ b/src/zscanner/test/cases/43_EUI48.out
@@ -46,8 +46,6 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_EUI_LENGTH
 ------
 WARNG=ZSCANNER_EBAD_EUI_LENGTH
@@ -60,7 +58,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/44_EUI64.in b/src/zscanner/test/cases/44_EUI64.in
index 1c707570f4ea5a5a9ee93762e796ab43ed021727..10f6346cc762d096c172037f09f93e7dbf630b0c 100644
--- a/src/zscanner/test/cases/44_EUI64.in
+++ b/src/zscanner/test/cases/44_EUI64.in
@@ -14,12 +14,9 @@ $TTL	1
 @	EUI64
 @	EUI64					; Empty rdata
 @	EUI64	\# 0				; Hex empty rdata
-@	EUI64	\#				; Missing hex length
 @	EUI64	00-00-00-00-00-00-00		; Too few hex pairs
 @	EUI64	00-00-00-00-00-00-00-00-00	; Too many hex pairs
 @	EUI64	00-00-00-00-00-00-00-0		; Missing char in a hex pair
 @	EUI64	00:00-00-00-00-00-00-00		; Bad separator
 @	EUI64	00-00-00-x0-00-00-00-00		; Bad character
 @	EUI64	00-00-00-00-00-00-00-00 x	; Unexpected item
-@	EUI64	\# 9 0000000000000000 00	; Too long rdata
-@	EUI64	\# 9 0000000000000000		; Bad rdata length
diff --git a/src/zscanner/test/cases/44_EUI64.out b/src/zscanner/test/cases/44_EUI64.out
index 745ba09bd5da8dbe5f4dbed047d0279fd91b7f27..a25974bd5d45d06944bb919cd11cc822f0fb186d 100644
--- a/src/zscanner/test/cases/44_EUI64.out
+++ b/src/zscanner/test/cases/44_EUI64.out
@@ -46,8 +46,6 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_EBAD_EUI_LENGTH
 ------
 WARNG=ZSCANNER_EBAD_EUI_LENGTH
@@ -60,7 +58,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/46_L32.in b/src/zscanner/test/cases/46_L32.in
index 1340573806b08932de4a276ba64392410caa4fe5..866e953947649d1238637bca14f46a96e43f746a 100644
--- a/src/zscanner/test/cases/46_L32.in
+++ b/src/zscanner/test/cases/46_L32.in
@@ -13,13 +13,9 @@ $TTL	1
 @	L32
 @	L32				; Empty rdata
 @	L32	\# 0			; Hex empty rdata
-@	L32	\#			; Missing hex length
 @	L32	65536	0.0.0.0		; Too big preference
-
 @	L32	0	0.0.0.256	; 8-bit overflow
 @	L32	0	0.0.0		; Short address
 @	L32	0	0.0.0.0.0	; Long address
 @	L32	0	0.0.0.x		; Bad character
 @	L32	0	0.0.0.0 x	; Unexpected item
-@	L32	\# 7 000000000000 00	; Too long rdata
-@	L32	\# 7 000000000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/46_L32.out b/src/zscanner/test/cases/46_L32.out
index 9509b57e9af5e1de54885036af49969c41623b93..a05fc47c6e07779db03afca093d2926ae6b77e59 100644
--- a/src/zscanner/test/cases/46_L32.out
+++ b/src/zscanner/test/cases/46_L32.out
@@ -40,8 +40,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_IPV4
@@ -54,7 +52,3 @@ WARNG=ZSCANNER_EBAD_ADDRESS_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/47_L64.in b/src/zscanner/test/cases/47_L64.in
index 7d1020ad8ef2c471f21682b23ce1448484c451b8..b4aabf37077026d647d2cc18c931eef11a348d0b 100644
--- a/src/zscanner/test/cases/47_L64.in
+++ b/src/zscanner/test/cases/47_L64.in
@@ -14,7 +14,6 @@ $TTL	1
 @	L64
 @	L64					; Empty rdata
 @	L64	\# 0				; Hex empty rdata
-@	L64	\#				; Missing hex length
 @	L64	65536				; Too big preference
 @	L64	0	0000:0000:0000		; Missing label
 @	L64	0	0000:0000:0000:0000:0000	; Too many labels
@@ -22,5 +21,3 @@ $TTL	1
 @	L64	0	0000:0000:0000-0000	; Bad separator
 @	L64	0	0000:0000:0000:x000	; Bad hex character
 @	L64	0	0000:0000:0000:0000 x	; Unexpected item
-@	L64	\# 11 00000000000000000000 00	; Too long rdata
-@	L64	\# 11 00000000000000000000	; Bad rdata length
diff --git a/src/zscanner/test/cases/47_L64.out b/src/zscanner/test/cases/47_L64.out
index 9184048181fb256eefc8e0177c630e67e84dfa53..aaa53c2ae0866b10f16c8ef7b0f4c41787f4c374 100644
--- a/src/zscanner/test/cases/47_L64.out
+++ b/src/zscanner/test/cases/47_L64.out
@@ -46,8 +46,6 @@ WARNG=ZSCANNER_EBAD_NUMBER
 ------
 WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
-WARNG=ZSCANNER_EBAD_NUMBER
-------
 WARNG=ZSCANNER_ENUMBER16_OVERFLOW
 ------
 WARNG=ZSCANNER_EBAD_L64_LENGTH
@@ -62,7 +60,3 @@ WARNG=ZSCANNER_EBAD_HEX_CHAR
 ------
 WARNG=ZSCANNER_EBAD_REST
 ------
-WARNG=ZSCANNER_EBAD_HEX_RDATA
-------
-WARNG=ZSCANNER_EBAD_RDATA_LENGTH
-------
diff --git a/src/zscanner/test/cases/48_LP.out b/src/zscanner/test/cases/48_LP.out
index 79ff530006d45a38aba3e45d5c62169f1da48aa4..2dcf142a3a1ea0bed098f0c3c88b3a39a6f78e69 100644
--- a/src/zscanner/test/cases/48_LP.out
+++ b/src/zscanner/test/cases/48_LP.out
@@ -2,31 +2,31 @@ OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=006B
-RDATA=0001 046D61696C00
+RDATA=0001046D61696C00
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=006B
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=006B
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=006B
-RDATA=0001 00
+RDATA=000100
 ------
 OWNER=00
 CLASS=0001
 RRTTL=00000001
 RTYPE=006B
-RDATA=0001 00
+RDATA=000100
 ------
 WARNG=ZSCANNER_EBAD_NUMBER
 ------
diff --git a/src/zscanner/test/processing.c b/src/zscanner/test/processing.c
index 7d0e75f444f73310be19e61d61ccbca18f314b89..0da42c1fd6fe4b564a76e72eb6b2ae360d319ecf 100644
--- a/src/zscanner/test/processing.c
+++ b/src/zscanner/test/processing.c
@@ -15,76 +15,13 @@
  */
 
 #include <config.h>
-#include "zscanner/scanner_functions.h"
-
 #include <inttypes.h>			// PRIu64
 #include <stdio.h>			// printf
 
-#include "common/errcode.h"		// knot_strerror
-#include "common/descriptor.h"		// knot_rrtype_to_string
+#include "zscanner/test/processing.h"
+#include "zscanner/error.h"		// zscanner_strerror
 #include "zscanner/scanner.h"		// scanner_t
-
-#define ERROR_CODE_TO_STRING(code) [code - ZSCANNER_UNCOVERED_STATE] = #code
-const char *error_names[] = {
-	ERROR_CODE_TO_STRING(ZSCANNER_UNCOVERED_STATE),
-	ERROR_CODE_TO_STRING(ZSCANNER_UNCLOSED_MULTILINE),
-	ERROR_CODE_TO_STRING(ZSCANNER_ELEFT_PARENTHESIS),
-	ERROR_CODE_TO_STRING(ZSCANNER_ERIGHT_PARENTHESIS),
-	ERROR_CODE_TO_STRING(ZSCANNER_EUNSUPPORTED_TYPE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_PREVIOUS_OWNER),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_DNAME_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_OWNER),
-	ERROR_CODE_TO_STRING(ZSCANNER_ELABEL_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EDNAME_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_NUMBER),
-	ERROR_CODE_TO_STRING(ZSCANNER_ENUMBER64_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_ENUMBER32_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_ENUMBER16_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_ENUMBER8_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EFLOAT_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_ERDATA_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EITEM_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_ADDRESS_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_IPV4),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_IPV6),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_GATEWAY),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_GATEWAY_KEY),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_APL),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_RDATA),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_HEX_RDATA),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_HEX_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_BASE64_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_BASE32HEX_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_REST),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TIMESTAMP_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TIMESTAMP_LENGTH),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TIMESTAMP),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_DATE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TIME),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TIME_UNIT),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_BITMAP),
-	ERROR_CODE_TO_STRING(ZSCANNER_ETEXT_OVERFLOW),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TEXT_CHAR),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TEXT),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_DIRECTIVE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_TTL),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_ORIGIN),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_INCLUDE_FILENAME),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_INCLUDE_ORIGIN),
-	ERROR_CODE_TO_STRING(ZSCANNER_EUNPROCESSED_INCLUDE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EUNOPENED_INCLUDE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_RDATA_LENGTH),
-	ERROR_CODE_TO_STRING(ZSCANNER_ECANNOT_TEXT_DATA),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_LOC_DATA),
-	ERROR_CODE_TO_STRING(ZSCANNER_EUNKNOWN_BLOCK),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_ALGORITHM),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_CERT_TYPE),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_EUI_LENGTH),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_L64_LENGTH),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_CHAR_COLON),
-	ERROR_CODE_TO_STRING(ZSCANNER_EBAD_CHAR_DASH),
-};
-#define ERROR_CODE_NAME(code) error_names[code - ZSCANNER_UNCOVERED_STATE]
+#include "zscanner/descriptor.h"	// knot_rrtype_to_string
 
 const char *separator = "------\n";
 
@@ -103,23 +40,18 @@ static void print_wire_dname(const uint8_t *dname, uint32_t dname_length)
 	}
 }
 
-void empty_process(const scanner_t *s)
-{
-	(void)s;
-}
-
 void debug_process_error(const scanner_t *s)
 {
 	if (s->stop == true) {
 		printf("LINE(%03"PRIu64") ERROR(%s) FILE(%s) NEAR(%s)\n",
 		       s->line_counter,
-		       knot_strerror(s->error_code),
+		       zscanner_strerror(s->error_code),
 		       s->file_name,
 		       s->buffer);
 	} else {
 		printf("LINE(%03"PRIu64") WARNING(%s) FILE(%s) NEAR(%s)\n",
 		       s->line_counter,
-		       knot_strerror(s->error_code),
+		       zscanner_strerror(s->error_code),
 		       s->file_name,
 		       s->buffer);
 	}
@@ -128,34 +60,26 @@ void debug_process_error(const scanner_t *s)
 
 void debug_process_record(const scanner_t *s)
 {
-	uint32_t block, block_length, i;
+	uint32_t i;
 
 	char rclass[32];
 	char rtype[32];
 
 	if (knot_rrclass_to_string(s->r_class, rclass, sizeof(rclass)) > 0 &&
 	    knot_rrtype_to_string(s->r_type, rtype, sizeof(rtype)) > 0) {
-		printf("LINE(%03"PRIu64") %s %u %*s ",
+		printf("LINE(%03"PRIu64") %s %6u %*s ",
 		       s->line_counter, rclass, s->r_ttl, 5, rtype);
 	} else {
-		printf("LINE(%03"PRIu64") %u %u %*u ",
+		printf("LINE(%03"PRIu64") %u %6u %*u ",
 		       s->line_counter, s->r_class, s->r_ttl, 5, s->r_type);
 	}
 
 	print_wire_dname(s->r_owner, s->r_owner_length);
 
-	printf("  #%u/%uB:", s->r_data_blocks_count, s->r_data_length);
-
-	for (block = 1; block <= s->r_data_blocks_count; block++) {
-		block_length =
-			s->r_data_blocks[block] - s->r_data_blocks[block - 1];
-		printf(" (%u)", block_length);
+	printf(" \\# %u ", s->r_data_length);
 
-		for (i = s->r_data_blocks[block - 1];
-		     i < s->r_data_blocks[block];
-		     i++) {
-			printf("%02X", (s->r_data)[i]);
-		}
+	for (i = 0; i < s->r_data_length; i++) {
+		printf("%02X", (s->r_data)[i]);
 	}
 	printf("\n");
 	fflush(stdout);
@@ -164,16 +88,16 @@ void debug_process_record(const scanner_t *s)
 void test_process_error(const scanner_t *s)
 {
 	if (s->stop == true) {
-		printf("ERROR=%s\n%s", ERROR_CODE_NAME(s->error_code), separator);
+		printf("ERROR=%s\n%s", zscanner_errorname(s->error_code), separator);
 	} else {
-		printf("WARNG=%s\n%s", ERROR_CODE_NAME(s->error_code), separator);
+		printf("WARNG=%s\n%s", zscanner_errorname(s->error_code), separator);
 	}
 	fflush(stdout);
 }
 
 void test_process_record(const scanner_t *s)
 {
-	uint32_t block, i;
+	uint32_t i;
 
 	printf("OWNER=");
 	for (i = 0; i < s->r_owner_length; i++) {
@@ -184,30 +108,9 @@ void test_process_record(const scanner_t *s)
 	printf("RRTTL=%08X\n", s->r_ttl);
 	printf("RTYPE=%04X\n", s->r_type);
 	printf("RDATA=");
-	for (block = 1; block <= s->r_data_blocks_count; block++) {
-		if (block > 1) {
-			printf(" ");
-		}
-
-		for (i = s->r_data_blocks[block - 1];
-		     i < s->r_data_blocks[block];
-		     i++) {
-			printf("%02X", (s->r_data)[i]);
-		}
+	for (i = 0; i < s->r_data_length; i++) {
+		printf("%02X", (s->r_data)[i]);
 	}
 	printf("\n%s", separator);
 	fflush(stdout);
 }
-
-void dump_rdata(const scanner_t *s)
-{
-	uint32_t block, i;
-
-	for (block = 1; block <= s->r_data_blocks_count; block++) {
-		for (i = s->r_data_blocks[block - 1];
-		     i < s->r_data_blocks[block];
-		     i++) {
-			printf("%c", (s->r_data)[i]);
-		}
-	}
-}
diff --git a/src/zscanner/test/processing.h b/src/zscanner/test/processing.h
index 26db2153254b3bbb8e4a04d9ef0503b862ecc2dd..81235f1d478c8d98485909aed06d0dcae019e56b 100644
--- a/src/zscanner/test/processing.h
+++ b/src/zscanner/test/processing.h
@@ -29,8 +29,6 @@
 
 #include "zscanner/scanner.h"
 
-void empty_process(const scanner_t *scanner);
-
 void debug_process_error(const scanner_t *scanner);
 
 void debug_process_record(const scanner_t *scanner);
@@ -39,8 +37,6 @@ void test_process_error(const scanner_t *scanner);
 
 void test_process_record(const scanner_t *scanner);
 
-void dump_rdata(const scanner_t *scanner);
-
 #endif // _ZSCANNER__TEST_FUNCTIONS_H_
 
 /*! @} */
diff --git a/src/zscanner/test/run_tests.sh.in b/src/zscanner/test/run_tests.sh.in
index fc00c571330cf17f9e40c67d4c6fc9e23447a226..8248539085aef87f5048b60fbcf87f12b015617f 100644
--- a/src/zscanner/test/run_tests.sh.in
+++ b/src/zscanner/test/run_tests.sh.in
@@ -2,13 +2,13 @@
 
 TESTS_DIR="@abs_srcdir@/cases"
 OUTS_DIR="@abs_builddir@/.out"
-TEST_BIN="@builddir@/../zscanner-tool -m 2"
+TEST_BIN="@builddir@/zscanner-tool -m 2"
 
 # Delete temporary output directory at exit.
 trap "chmod -R u+rw ${OUTS_DIR} && rm -rf ${OUTS_DIR}" EXIT
 
 # If an argument -> verbose mode (stores result in /tmp).
-if [ $# -eq 0 ]; then
+if [ $# -ne 0 ]; then
 	RESULT_DIR=`mktemp -d /tmp/zscanner_test.XXXX`
 	echo "ZSCANNER TEST ${RESULT_DIR}"
 fi
@@ -31,15 +31,17 @@ for file in $(find "${TESTS_DIR}" -name "*.in" | sort -n); do
 	# Check for differences.
 	if [ $RET -ne 0 ]; then
 		# If verbose print diff.
-		if [ $# -eq 0 ]; then
+		if [ $# -ne 0 ]; then
 			echo "\n=== ${fileout} DIFF ======================"
 			diff "${OUTS_DIR}/${fileout}" "${TESTS_DIR}/${fileout}"
+		else
+			exit $RET
 		fi
 	fi
 done
 
 # Store test result.
-if [ $# -eq 0 ]; then
+if [ $# -ne 0 ]; then
 	cp -a "${OUTS_DIR}/." "${RESULT_DIR}/"
 	echo "\nFINISHED ${RESULT_DIR}"
 fi
diff --git a/src/zscanner/test/tests.c b/src/zscanner/test/tests.c
index fdd87ccf6d676199f009450c83b46005a696868f..5b0391944f7d27f53c3393c7ec3acc7e78e05f55 100644
--- a/src/zscanner/test/tests.c
+++ b/src/zscanner/test/tests.c
@@ -15,13 +15,13 @@
  */
 
 #include <config.h>
-#include "zscanner/test/tests.h"
-
 #include <inttypes.h>			// PRIu64
+#include <stdlib.h>			// putenv
 #include <stdio.h>			// printf
 #include <time.h>			// mktime
-#include <stdlib.h>			// printf
-#include "../scanner_functions.h"	// date_to_timestamp
+
+#include "zscanner/test/tests.h"
+#include "zscanner/scanner_functions.h"	// date_to_timestamp
 
 int test__date_to_timestamp()
 {
diff --git a/src/zscanner/test/zscanner-tool.c b/src/zscanner/test/zscanner-tool.c
index c6a728d06d76b53d5ec1f4bd056073bdc4c0740f..b1fd1d576c7c4a762364efce92dd99bbf92e59d0 100644
--- a/src/zscanner/test/zscanner-tool.c
+++ b/src/zscanner/test/zscanner-tool.c
@@ -20,12 +20,14 @@
 #include <stdlib.h>			// atoi
 #include <getopt.h>			// getopt
 
-#include "common/errcode.h"		// knot_strerror
+#include "zscanner/error.h"		// knot_strerror
 #include "zscanner/file_loader.h"	// file_loader
 #include "zscanner/test/processing.h"	// processing functions
 #include "zscanner/test/tests.h"	// test functions
 
 #define DEFAULT_MODE	1
+#define DEFAULT_CLASS	1
+#define DEFAULT_TTL	0
 
 void help(void)
 {
@@ -52,10 +54,10 @@ int main(int argc, char *argv[])
 
 	// Command line long options.
 	struct option opts[] = {
-		{"mode",	required_argument, 0, 'm'},
-		{"test",	no_argument,	   0, 't'},
-		{"help",	no_argument,	   0, 'h'},
-		{0, 		0, 		   0, 0}
+		{ "mode",	required_argument,	0,	'm' },
+		{ "test",	no_argument,		0,	't' },
+		{ "help",	no_argument,		0,	'h' },
+		{ 0, 		0, 			0,	0 }
 	};
 
 	// Command line options processing.
@@ -67,7 +69,9 @@ int main(int argc, char *argv[])
 		case 't':
 			test = 1;
 			break;
-		case 'h': // Fall through.
+		case 'h':
+			help();
+			return EXIT_SUCCESS;
 		default:
 			help();
 			return EXIT_FAILURE;
@@ -83,37 +87,37 @@ int main(int argc, char *argv[])
 			return EXIT_FAILURE;
 		}
 
-		zone_file = argv[optind];
-		origin = argv[optind + 1];
+		origin = argv[optind];
+		zone_file = argv[optind + 1];
 
 		// Create appropriate file loader.
 		switch (mode) {
 		case 0:
-			fl = file_loader_create(origin,
-						zone_file,
-						DEFAULT_CLASS,
-						DEFAULT_TTL,
-						&empty_process,
-						&empty_process,
-						NULL);
+			fl = file_loader_create(zone_file,
+			                        origin,
+			                        DEFAULT_CLASS,
+			                        DEFAULT_TTL,
+			                        NULL,
+			                        NULL,
+			                        NULL);
 			break;
 		case 1:
-			fl = file_loader_create(origin,
-						zone_file,
-						DEFAULT_CLASS,
-						DEFAULT_TTL,
-						&debug_process_record,
-						&debug_process_error,
-						NULL);
+			fl = file_loader_create(zone_file,
+			                        origin,
+			                        DEFAULT_CLASS,
+			                        DEFAULT_TTL,
+			                        &debug_process_record,
+			                        &debug_process_error,
+			                        NULL);
 			break;
 		case 2:
-			fl = file_loader_create(origin,
-						zone_file,
-						DEFAULT_CLASS,
-						DEFAULT_TTL,
-						&test_process_record,
-						&test_process_error,
-						NULL);
+			fl = file_loader_create(zone_file,
+			                        origin,
+			                        DEFAULT_CLASS,
+			                        DEFAULT_TTL,
+			                        &test_process_record,
+			                        &test_process_error,
+			                        NULL);
 			break;
 		default:
 			printf("Bad mode number!\n");
@@ -126,7 +130,7 @@ int main(int argc, char *argv[])
 			ret = file_loader_process(fl);
 
 			switch (ret) {
-			case KNOT_EOK:
+			case ZSCANNER_OK:
 				if (mode == DEFAULT_MODE) {
 					printf("Zone file has been processed "
 					       "successfully\n");
@@ -145,7 +149,7 @@ int main(int argc, char *argv[])
 
 			default:
 				if (mode == DEFAULT_MODE) {
-					printf("%s\n", knot_strerror(ret));
+					printf("%s\n", zscanner_strerror(ret));
 				}
 				file_loader_free(fl);
 				return EXIT_FAILURE;
diff --git a/src/tests/zscanner/zscanner_tests.c b/src/zscanner/zscanner.h
similarity index 56%
rename from src/tests/zscanner/zscanner_tests.c
rename to src/zscanner/zscanner.h
index 112c9289a3e7c420ff18d77891c4995b59562382..7d12ed0a9b73701501fa4a0cb6c385eaa2ee8113 100644
--- a/src/tests/zscanner/zscanner_tests.c
+++ b/src/zscanner/zscanner.h
@@ -13,32 +13,23 @@
     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 zscanner.h
+ *
+ * \author Daniel Salzman <daniel.salzman@nic.cz>
+ *
+ * \brief Public interface for zscanner.
+ * @{
+ */
 
-#include <config.h>
-#include "tests/zscanner/zscanner_tests.h"
-
-#include <stdlib.h>
-
-static int zscanner_tests_count(int argc, char *argv[]);
-static int zscanner_tests_run(int argc, char *argv[]);
-
-unit_api zscanner_tests_api = {
-	"Zone scanner",
-	&zscanner_tests_count,
-	&zscanner_tests_run
-};
-
-static int zscanner_tests_count(int argc, char *argv[])
-{
-	return 1;
-}
+#ifndef _ZSCANNER__ZSCANNER_H_
+#define _ZSCANNER__ZSCANNER_H_
 
-static int zscanner_tests_run(int argc, char *argv[])
-{
-	int  ret;
+#include "zscanner/error.h"
+#include "zscanner/scanner.h"
+#include "zscanner/descriptor.h"
+#include "zscanner/file_loader.h"
 
-	ret = system("/bin/sh ../zscanner/test/run_tests.sh test");
-	cmp_ok(ret, "==", 0, "zscanner unittests");
+#endif // _ZSCANNER__ZSCANNER_H_
 
-	return 0;
-}
+/*! @} */
diff --git a/tests/querytcp.c b/tests/querytcp.c
deleted file mode 100644
index d90029885bee0e9f9ee2075e9a7e467dfa77e9c2..0000000000000000000000000000000000000000
--- a/tests/querytcp.c
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
-TCP query version of queryperf
-querytcp.c
-				fujiwara@jprs.co.jp
-				2009.08.12
-				version 0.4
-
-queryperf for tcp query
-
-This program measures DNS server performance of TCP query.
-
-o Running environment:
-	Development environment:
-        Linux
-		FreeBSD
-		MacOS X 10.3.4
-
-o How to make:
-    Linux:   gcc -D_LINUX -Wall -O2 -g -lm -o querytcp querytcp.c
-    FreeBSD: gcc -Wall -O2 -g -lm -o querytcp querytcp.c
-    MacOS X: gcc -Wall -O2 -g -lm -lresolv -o querytcp querytcp.c
-
-o changes
-
-  2010/6/7: Linux compatibility
-  2009/8/12: Remove use of res_mkquery
-*/
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <netdb.h>
-#include <errno.h>
-#include <math.h>
-#include <err.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <arpa/inet.h>
-#include <resolv.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <math.h>
-#ifndef NO_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifdef __APPLE__
-#include <nameser8_compat.h>
-#endif
-
-#ifndef ns_t_soa
-#define	ns_t_soa	T_SOA
-#endif
-#ifndef ns_t_ns
-#define	ns_t_ns		T_NS
-#endif
-#ifndef ns_c_in
-#define	ns_c_in		C_IN
-#endif
-
-#ifdef NOINET6
-#undef AF_INET6
-#endif
-
-#define	Global
-
-#ifndef PACKETSZ
-#define	PACKETSZ	512
-#endif
-
-/* debug.c */
-void hexdump(char *title, unsigned char *memory, int len)
-{
-	printf("[ %s ", title);
-	while (len-- > 0)
-		printf("%02x ", *memory++);
-	printf("]\n");
-}
-
-#define Xmalloc(size)	Xrealloc(NULL, size)
-
-void *Xrealloc(void *p, int size)
-{
-	int sz;
-
-	sz = (size > 0) ? size : -size;
-	if (p == NULL) {
-		p = malloc(sz);
-	} else {
-		p = realloc(p, sz);
-	}
-	if (p == NULL) {
-		char buf[100];
-		snprintf(buf, sizeof buf, "size=%d", size);
-		perror(buf);
-		exit(1);
-	}
-	if (size < 0)
-		memset(p, 0, sz);
-	return p;
-}
-
-/* strlcpy() emulation for Linux. */
-#ifdef _LINUX
-static inline size_t strlcpy(char *destination, const char *source, size_t size)
-{
-    if(strncpy(destination, source, size) == NULL)
-        return 0;
-
-    return size;
-}
-#endif
-
-/*
-  NULL ... returns NULL
- */
-char *Xstrdup(char *p)
-{
-	char *q;
-	int len;
-
-	if (p == NULL)
-		return NULL;
-	len = strlen(p) + 1;
-	q = Xmalloc(len);
-	strlcpy(q, p, len);
-	return q;
-}
-
-
-typedef int64_t timediff_t;
-
-/* packet buffer */
-static struct timeval current;
-static struct timeval start, send_finished;;
-static fd_set fdset0r, fdset0w;
-static int nfds;
-static struct sockaddr_storage remote;
-static int remote_len = 0;
-static int finished = 0;
-static timediff_t Timeout = 10*1000000LL;
-unsigned short counter = 0;
-
-#define	UpdateCurrentTime		gettimeofday(&current, NULL)
-
-#define	RECVBUFSIZ	65537
-#define	SENDBUFSIZ	512
-
-struct dnsheader  {
-  unsigned short id; // 2
-  unsigned char flag1, flag2; // 2
-  unsigned short qdcount, ancount, nscount, arcount; // 8
-};
-
-/*
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-*/
-
-struct queries {
-	struct tcpdns {
-		unsigned short len;
-		union {
-			struct dnsheader h;
-			unsigned char dnsdata[SENDBUFSIZ];
-		} u;
-	} send;
-	unsigned char recvbuf[RECVBUFSIZ];
-	int sendlen;
-	int sent_flag:1;
-	int tcpstate:2;
-	int fd;
-	int rpos;
-	int wpos;
-	int no;
-	struct timeval sent; /* long tv_sec, long tv_usec */
-};
-
-struct queries *Queries;
-
-#define	NQUERY 100
-
-#define	TCP_NONE	0
-#define	TCP_WRITABLE	1
-#define	TCP_READABLE	2
-
-/* input */
-char *ServerName = "127.0.0.1";
-char *ServerPort = "53";
-int family = PF_UNSPEC;
-char *datafile = NULL;
-int TimeLimit = 20;
-int EDNS0 = 0;
-int DNSSEC = 0;
-int recursion = 0;
-FILE *fp = NULL;
-int datafileloop = 0;
-int verbose = 0;
-int nQueries = 120;
-int printrcode = 0;
-char *rcodestr[]= {
-	"NOERROR", "FormatError", "ServerFailure", "NameError",
-	"NotImplemented", "Reused", "RCODE06", "RCODE07",
-	"RCODE08", "RCODE09", "RCODE10", "RCODE11",
-	"RCODE12", "RCODE13", "RCODE14", "RCODE15",
-};
-
-timediff_t timediff(struct timeval *a, struct timeval *b) /* u sec */
-{
-	return (a->tv_sec - b->tv_sec) * 1000000 + (a->tv_usec - b->tv_usec);
-}
-
-#define	TIMEOUTERROR	-10000
-#define	ERROROFFSET	-20000
-#define	ERRZEROREAD	-30000
-
-uint64_t countrcode[16];
-uint64_t response_size_sum = 0;
-uint64_t response_size_sum2 = 0;
-uint64_t countanswers = 0;
-uint64_t countqueries = 0;
-uint64_t countzeroread = 0;
-uint64_t counttimeout = 0;
-uint64_t counterror = 0;
-
-int response_size_min = 0;
-int response_size_max = 0;
-
-
-
-void register_response(struct queries *q, int timeout, char *note)
-{
-	u_char *p;
-	int size;
-	int rcode;
-	int id;
-
-    id = ntohs(q->send.u.h.id);
-	if (note == NULL)
-		note = "";
-	countqueries++;
-	if (timeout >= 0) {
-		p = q->recvbuf;
-		NS_GET16(size, p);
-		response_size_sum += size;
-		response_size_sum2 += size * size;
-		if (response_size_min == 0 || response_size_min > size)
-			response_size_min = size;
-		if (response_size_max == 0 || response_size_max < size)
-			response_size_max = size;
-		rcode = p[3] & 0x0f;
-		countrcode[rcode]++;
-		countanswers++;
-		if (verbose)
-			printf("recv response id=%d rcode=%d size=%d rtt=%d\n", id, rcode, size, timeout);
-	} else if (timeout == ERRZEROREAD) {
-		countzeroread++;
-		if (verbose)
-			printf("recv response id=%d zeroread\n", id);
-	} else if (timeout == TIMEOUTERROR) {
-		counttimeout++;
-		if (verbose)
-			printf("recv timeout id=%d %lld usec\n", id, timediff(&current, &q->sent));
-	} else {
-		counterror++;
-		if (verbose) {
-			printf("recv error id=%d errno=%d at %s (%s)\n", id, ERROROFFSET - timeout, note, strerror(errno));
-		}
-	}
-#ifdef DEBUG
-    printf("%ld.%03ld no=%d fd=%d %d %s\n", q->sent.tv_sec, q->sent.tv_usec/1000, q->no, q->fd, timeout, note);
-	fflush(stdout);
-#endif
-}
-
-void output()
-{
-	double response_size_average, response_size_variance, et;
-
-	et = ((double)timediff(&current, &start))/1000000.0;
-
-	printf("elapsed time: %.3f\n", et);
-	printf("tcp qps: %.3f\n", (double)countanswers/et);
-	printf("sent: %lld\n", countqueries);
-	printf("answer: %lld  %3.1f%%\n", countanswers,
-		 (double)((double)countanswers/(double)countqueries*100.0));
-	printf("error: %lld  %3.1f%%\n", counterror,
-		 (double)((double)counterror/(double)countqueries*100.0));
-	printf("zeroread: %lld  %3.1f%%\n", countzeroread,
-		 (double)((double)countzeroread/(double)countqueries*100.0));
-	printf("timeout: %lld  %3.1f%%\n", counttimeout,
-		 (double)((double)counttimeout/(double)countqueries*100.0));
-	response_size_average = (double)response_size_sum/countanswers;
-	response_size_variance = (double)response_size_sum2 / countanswers
-		- response_size_average * response_size_average;
-	printf("response size:        %d/%.3f/%d/%.3f bytes\n", response_size_min, response_size_average, response_size_max, sqrt(response_size_variance));
-	if (printrcode) {
-		int i;
-		for (i = 0; i < 16; i++) {
-			if (countrcode[i] != 0) {
-				printf("%s %lld %5.1f\n", rcodestr[i], countrcode[i], ((double)countrcode[i])/((double)countanswers)*100.0);
-			}
-		}
-	}
-}
-
-void tcp_close(struct queries *q)
-{
-
-#ifdef DEBUG
-printf("tcp_close no=%d fd=%d\n", q->no, q->fd);
-#endif
-	if (q->fd >= 0) {
-		close(q->fd);
-		FD_CLR(q->fd, &fdset0r);
-		FD_CLR(q->fd, &fdset0w);
-	}
-	q->sent_flag = 0;
-	q->tcpstate = TCP_NONE;
-	q->fd = -1;
-}
-
-void tcp_send(struct queries *q)
-{
-	int len;
-
-	len = send(q->fd, &q->send, q->sendlen, MSG_NOSIGNAL);
-#ifdef DEBUG
-printf("tcp_send no=%d fd=%d %d:%d:%d\n", q->no, q->fd, len, q->wpos, q->sendlen);
-#endif
-	if (len < 0) {
-		if (errno == ENOTCONN) {
-printf("tcp_send no=%d fd=%d ENOTCONN return\n", q->no, q->fd);
-			return;
-		}
-		register_response(q, ERROROFFSET - errno, "tcp_send");
-		tcp_close(q);
-		return;
-	}
-	if (len != q->sendlen) {
-		register_response(q, ERROROFFSET - errno, "tcp_send:sendto");
-		tcp_close(q);
-		return;
-	}
-	FD_CLR(q->fd, &fdset0w);
-	FD_SET(q->fd, &fdset0r);
-}
-
-struct typecodes {
-	char *name;
-	int code;
-} typecodes[] = {
-	{ "A", ns_t_a },
-	{ "NS", ns_t_ns },
-	{ "SOA", ns_t_soa },
-	{ "PTR", ns_t_ptr },
-	{ "HINFO", ns_t_hinfo },
-	{ "MX", ns_t_mx },
-	{ "TXT", ns_t_txt },
-	{ "SIG", ns_t_sig },
-	{ "KEY", ns_t_key },
-	{ "AAAA", ns_t_aaaa },
-	{ "NXT", ns_t_nxt },
-	{ "SRV", ns_t_srv },
-	{ "NAPTR", ns_t_naptr },
-	{ NULL, -1 },
-};
-
-int stringtodname(unsigned char *qname, unsigned char *buff, unsigned char *lim)
-{
-	unsigned char *p, *s, *t;
-	int count, total;
-
-	t = qname;
-	p = buff;
-	total = 0;
-	for ( ;; ) {
-		s = p++;
-		count = 0;
-		if (p >= lim) return -1;
-		while (*t != 0 && *t != '.')
-			if (p < lim) {
-				*p++ = *t++;
-				count++;
-			} else
-				return -1;
-		*s = count;
-		if (count == 0)
-			break;
-		if (count > 63)
-			return -1;
-		total += count + 1;
-		if (*t == '.') t++;
-	}
-	if (total > 250 || !(*t == 0 || (*t == '.' && t[1] == 0)))
-		return -1;
-	return p - buff;
-}
-
-void send_query_error(char *mesg)
-{
-	err(1, "Packet size exceed: %s", mesg);
-}
-
-void send_query(struct queries *q)
-{
-    u_char *p, *lim;
-    char *qname;
-	int qclass;
-	int qtype;
-	int tmp;
-	struct typecodes *t = typecodes;
-	u_char buff[512];
-	static char sep[] = "\n\t ";
-	static int lineno = 0;
-
-	/*
-		SEND E[send_packet_pos]
-	 */
-	if (q->sent_flag) {
-		register_response(q, TIMEOUTERROR, "send_query");
-		tcp_close(q);
-	}
-	if (fp == NULL) {
-		qname = "version.bind";
-		qclass = ns_c_chaos;
-		qtype = ns_t_txt;
-	} else {
-		do {
-            if (fgets((char*)buff, sizeof(char)*512, fp) == NULL) {
-				if (datafileloop == 1) {
-					finished = 1;
-					fclose(fp);
-					fp = NULL;
-					return;
-				}
-				if (datafileloop > 0)
-					datafileloop--;
-				rewind(fp);
-				lineno = 0;
-                if (fgets((char*)buff, sizeof(char)*512, fp) == NULL)
-					err(1, "cannot rewind input file");
-			}
-			lineno++;
-		} while(buff[0] == '#');
-        qname = strtok((char*)buff, sep);
-        p = (u_char*) strtok(NULL, sep);
-		if (p != NULL) {
-			while(t->name != NULL) {
-                if (!strcasecmp(t->name, (char*)p))
-					break;
-				t++;
-			}
-			qtype = t->code;
-		} else {
-			qtype = ns_t_a;
-		}
-		if (qname == NULL || qtype < 0)
-			err(1, "datafile format error at line %d, qname=%s qtype=%d", lineno, qname, qtype);
-		qclass = ns_c_in;
-	}
-	q->send.u.h.id = counter++;
-	q->send.u.h.flag1 = recursion ? 1 : 0; /* Query,OP=0,AA=0,TC=0,RD=0/1 */
-	q->send.u.h.flag2 = 0;
-	q->send.u.h.qdcount = htons(1);
-	q->send.u.h.ancount = 0;
-	q->send.u.h.nscount = 0;
-	q->send.u.h.arcount = 0;
-	p = q->send.u.dnsdata + sizeof(q->send.u.h);
-	lim = p + sizeof(q->send.u.dnsdata);
-    if ((tmp = stringtodname((u_char*) qname, p, lim)) < 0)
-		send_query_error(qname);
-	p += tmp;
-	*(unsigned short *)p = htons(qtype);
-	p += sizeof(unsigned short);
-	*(unsigned short *)p = htons(qclass);
-	p += sizeof(unsigned short);
-	q->sendlen = p - q->send.u.dnsdata;
-	if (EDNS0) {
-#define EDNS0size 11
-		if (q->sendlen + EDNS0size >= sizeof(q->send.u.dnsdata))
-			send_query_error("ENDS0");
-		*p++ = 0; /* . */
-		*(unsigned short *)p = htons(ns_t_opt);
-		p += 2;
-		*(unsigned short *)p = htons(4096);
-		p += 2;
-		*p++ = 0;
-		*p++ = 0;
-		*p++ = (DNSSEC == 0) ? 0 : 0x80; /* eflag: DO bit */
-		*p++ = 0;
-		*p++ = 0;
-		*p++ = 0;
-		q->sendlen += EDNS0size;
-        p = (u_char*) &q->send.u.dnsdata;
-		q->send.u.h.ancount = htons(1);
-	}
-	q->send.len = htons(q->sendlen);
-	q->sendlen += sizeof(q->send.len);
-	q->wpos = 0;
-	q->rpos = 0;
-	q->sent = current;
-	if (verbose > 0) {
-		int id = ntohs(*(unsigned short *)&q->send.u.dnsdata);
-		printf("sending query(%s,%d,%d) id=%d %d bytes to %s\n", qname, qclass, qtype, id, q->sendlen, ServerName);
-        hexdump("sending packet header:", (unsigned char*) &q->send.u.h, 12);
-	}
-	if (q->fd > 0)
-		err(1, "q->fd > 0 but ignored\n");
-
-	q->fd = socket(remote.ss_family, SOCK_STREAM, 0);
-	tmp = fcntl(q->fd, F_GETFL, 0);
-	fcntl(q->fd, F_SETFL, O_NONBLOCK | tmp);
-	int conn_ret = connect(q->fd, (struct sockaddr *)&remote, remote_len);
-	if(conn_ret < 0 && errno != EINPROGRESS) {
-		register_response(q, ERROROFFSET - errno, "send_query:socket+fcntl+connect");
-		tcp_close(q);
-		return;
-	}
-#ifdef DEBUG
-printf("send_query no=%d fd=%d socket|connect\n", q->no, q->fd);
-#endif
-	q->tcpstate = TCP_WRITABLE;
-	FD_SET(q->fd, &fdset0w);
-	FD_CLR(q->fd, &fdset0r);
-	if (nfds <= q->fd) {
-		nfds = q->fd + 1;
-	}
-	q->sent = current;
-	q->sent_flag = 1;
-}
-
-int UpdateQuery()
-{
-	int i;
-	timediff_t t, min = Timeout;
-	struct queries *q;
-	int free = 0;
-
-	if (!finished && TimeLimit > 0) {
-		if ((t = timediff(&current, &start)) > TimeLimit * 1000000LL) {
-			finished = 1;
-			send_finished = current;
-		}
-	}
-	for(i = 0; i < nQueries; i++) {
-		q = &Queries[i];
-		if (q->sent_flag) {
-			if ((t = timediff(&current, &q->sent)) > Timeout) {
-				/* timeouted */
-				register_response(q, TIMEOUTERROR, "UpdateQuery");
-				tcp_close(q);
-			} else
-			if (t < min)
-				min = t;
-		}
-		if (!q->sent_flag) {
-			if (!finished)
-				send_query(q);
-			else
-				free++;
-		}
-	}
-	if (free == nQueries)
-		min = -1; /* finished */
-	return min;
-}
-
-char *skipname(char *p)
-{
-	while(*p > 0 && *p < 0x40) p += *p + 1;
-	if (*p == 0)
-		return p+1;
-	return p+2;
-}
-
-#define Hexdump(A,B,C)
-
-void tcp_receive(struct queries *q)
-{
-	int len, len2;
-	timediff_t tmp;
-	unsigned char *recvp;
-
-/*printf("tcp_receive %s\n", q->nameserverlabel);*/
-
-	len = read(q->fd, q->recvbuf + q->rpos, len2 = RECVBUFSIZ - q->rpos);
-	if (len < 0) {
-		if (errno == EAGAIN)
-			return;
-		register_response(q, ERROROFFSET - errno, "tcp_receive:read");
-		tcp_close(q);
-		return;
-	}
-	if (len == 0) {
-		register_response(q, ERRZEROREAD, "tcp_receive:read");
-		tcp_close(q);
-		return;
-	}
-	q->rpos += len;
-	if (q->rpos < 2)
-		return;
-	len2 = ntohs(*(unsigned short *)(q->recvbuf));
-	if (q->rpos >= len2 + 2) {
-		/* finished */
-		recvp = q->recvbuf + 2;
-		if (memcmp(recvp, q->send.u.dnsdata, 2) == 0) {
-			if ((recvp[2] & 1) == 0 /* RA bit */
-			  || (recvp[3] & 15) != 0 /* RCODE must be 0 */
-			) {
-/*
-				fprintf(stderr, "WRONG AA=%d RCODE=%d\n",
-					((recvp[2]>>2) & 1), recvp[3]&15);
-*/
-			}
-			tmp = timediff(&current, &q->sent);
-			register_response(q, tmp, "tcp_receive");
-			tcp_close(q);
-			return;
-		} else {
-printf("no=%d fd=%d unknown recv %d bytes, len=%d\n", q->no, q->fd, q->rpos, ntohs(*(unsigned short *)(q->recvbuf)));
-			hexdump("", q->recvbuf, len);
-			/*
-			fprintf(stderr, "unknown recv from %s, %d bytes %02x %02x\n", q->nameserverlabel, q->rpos, recvp[0], recvp[1]);
-			*/
-			tcp_close(q);
-		}
-	}
-}
-
-void query()
-{
-	fd_set fdsetr, fdsetw;
-	struct timeval timeout;
-	int min;
-	struct queries *q;
-	int i, n;
-	struct addrinfo hints, *res0;
-	int error;
-
-	Queries = Xmalloc(sizeof(Queries[0]) * nQueries);
-	memset(&remote, 0, sizeof(remote));
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = family;
-	hints.ai_socktype = SOCK_STREAM;
-	printf("resolving: %s:%s\n", ServerName, ServerPort);
-	error = getaddrinfo(ServerName, 0, &hints, &res0);
-	if (error) {
-		errx(1, "%s", gai_strerror(error));
-	}
-
-	/* Update server port. */
-	int port = atoi(ServerPort);
-	if (res0->ai_family == AF_INET6) {
-		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)res0->ai_addr;
-		ipv6->sin6_port = htons(port);
-	} else {
-		struct sockaddr_in *ipv4 = (struct sockaddr_in*)res0->ai_addr;
-		ipv4->sin_port = htons(port);
-	}
-
-	remote_len = res0->ai_addrlen;
-	memcpy(&remote, res0->ai_addr, res0->ai_addrlen);
-	memset(&countrcode, 0, sizeof(countrcode));
-
-	res_init();
-	_res.options ^= ~RES_RECURSE;
-	_res.options |= RES_AAONLY;
-
-	for (i = 0; i < nQueries; i++) {
-		Queries[i].sent_flag = 0;
-		Queries[i].no = i;
-	}
-
-	FD_ZERO(&fdset0r);
-	FD_ZERO(&fdset0w);
-	nfds = 0;
-	UpdateCurrentTime;
-	start = current;
-	finished = 0;
-
-	for (;;) {
-		UpdateCurrentTime;
-		if ((min = UpdateQuery()) < 0)
-			break;
-		timeout.tv_sec = min / 1000000;
-		timeout.tv_usec = min % 1000000;
-		fdsetr = fdset0r;
-		fdsetw = fdset0w;
-		n = select(nfds, &fdsetr, &fdsetw, NULL, &timeout);
-		UpdateCurrentTime;
-		for(i = 0; i < nQueries; i++) {
-			q = &Queries[i];
-			if (q->fd < 0 || !q->sent_flag)
-				continue;
-			if (FD_ISSET(q->fd, &fdsetw)) {
-				tcp_send(q);
-			} else if (FD_ISSET(q->fd, &fdsetr)) {
-				tcp_receive(q);
-			}
-		}
-	}
-}
-
-void usage()
-{
-	fprintf(stderr,
-"querytcp [-d datafile] [-s server_addr] [-p port] [-q num_queries] [-t timeout] [l limit] [-4] [-6] [-h]\n"
-"  -d specifies the input data file (default: stdin)\n"
-"  -s sets the server to query (default: 127.0.0.1)\n"
-"  -p sets the port on which to query the server (default: 53)\n"
-"  -q specifies the maximum number of queries outstanding (default: 120)\n"
-"  -t specifies the timeout for query completion in seconds (default: 10)\n"
-"  -l specifies how a limit for how long to run tests in seconds (no default)\n"
-"  -e enable EDNS0\n"
-"  -D set DO bit\n"
-"  -r set RD bit\n"
-"\n"
-"\n"
-"\n"
-"  -c print the number of packets with each rcode\n"
-"  -v verbose: report the RCODE of each response on stdout\n"
-"  -h print this usage\n"
-);
-	exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-	int ch, i;
-    printf("dnsheader size: %d\n", sizeof(struct dnsheader));
-	while ((ch = getopt(argc, argv, "d:s:p:q:t:l:46eDrvh")) != -1) {
-	switch (ch) {
-	case 'q':
-		nQueries = atoi(optarg);
-		if (nQueries < 1)
-			err(1, "-q requires natural number");
-		break;
-	case 'p':
-		ServerPort = Xstrdup(optarg);
-		break;
-	case 's':
-		ServerName = Xstrdup(optarg);
-		break;
-	case 'd':
-		datafile = Xstrdup(optarg);
-		if ((fp = fopen(datafile, "r")) == NULL)
-			err(1, "cannot open %s", optarg);
-		break;
-	case 't':
-		i = atoi(optarg);
-		if (i < 1)
-			err(1, "-t timeout > 0");
-		Timeout = (int64_t)i * 1000000LL;
-		break;
-	case 'l':
-		TimeLimit = atoi(optarg);
-		break;
-	case '4':
-		family = AF_INET;
-		break;
-	case '6':
-		family = AF_INET6;
-		break;
-	case 'e':
-		EDNS0 = 1;
-		break;
-	case 'D':
-		DNSSEC = 1;
-		break;
-	case 'r':
-		recursion = 1;
-		break;
-	case 'v':
-		verbose = 1;
-		break;
-	case 'c':
-		printrcode = 1;
-		break;
-	case 'h':
-	default:
-		usage();
-	}
-	}
-	argc -= optind;
-	argv += optind;
-
-	query();
-	output();
-
-	return 0;
-}