summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
committerOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
commitc8d5977bb546dae9ed59d81556639c49badd8121 (patch)
tree4c86750db26c1c3502b60f2cd78ca9611cfa01d6 /src
downloadknot-c8d5977bb546dae9ed59d81556639c49badd8121.tar.gz
Imported Upstream version 0.8.0~pre1upstream/0.8.0_pre1
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am336
-rw-r--r--src/Makefile.in2396
-rw-r--r--src/common/WELL1024a.c116
-rw-r--r--src/common/WELL1024a.h32
-rw-r--r--src/common/acl.c185
-rw-r--r--src/common/acl.h138
-rw-r--r--src/common/base32.c539
-rw-r--r--src/common/base32.h121
-rw-r--r--src/common/base32hex.c562
-rw-r--r--src/common/base32hex.h124
-rw-r--r--src/common/crc.c155
-rw-r--r--src/common/crc.h110
-rw-r--r--src/common/dynamic-array.c224
-rw-r--r--src/common/dynamic-array.h156
-rw-r--r--src/common/errors.c74
-rw-r--r--src/common/errors.h80
-rw-r--r--src/common/evqueue.c130
-rw-r--r--src/common/evqueue.h200
-rw-r--r--src/common/evsched.c309
-rw-r--r--src/common/evsched.h240
-rw-r--r--src/common/fdset.c80
-rw-r--r--src/common/fdset.h196
-rw-r--r--src/common/fdset_epoll.c216
-rw-r--r--src/common/fdset_epoll.h133
-rw-r--r--src/common/fdset_kqueue.c251
-rw-r--r--src/common/fdset_kqueue.h133
-rw-r--r--src/common/fdset_poll.c230
-rw-r--r--src/common/fdset_poll.h133
-rw-r--r--src/common/general-tree.c215
-rw-r--r--src/common/general-tree.h73
-rw-r--r--src/common/latency.c197
-rw-r--r--src/common/latency.h115
-rw-r--r--src/common/libtap/README231
-rw-r--r--src/common/libtap/tap.c313
-rw-r--r--src/common/libtap/tap.h101
-rw-r--r--src/common/libtap/tap_unit.h94
-rw-r--r--src/common/lists.c160
-rw-r--r--src/common/lists.h103
-rw-r--r--src/common/modified_tree.h292
-rw-r--r--src/common/print.c57
-rw-r--r--src/common/print.h72
-rw-r--r--src/common/ref.c44
-rw-r--r--src/common/ref.h90
-rw-r--r--src/common/skip-list.c437
-rw-r--r--src/common/skip-list.h215
-rw-r--r--src/common/slab/alloc-common.h61
-rw-r--r--src/common/slab/malloc.c60
-rw-r--r--src/common/slab/malloc.h43
-rw-r--r--src/common/slab/slab.c732
-rw-r--r--src/common/slab/slab.h353
-rw-r--r--src/common/sockaddr.c174
-rw-r--r--src/common/sockaddr.h120
-rw-r--r--src/common/tree.h268
-rw-r--r--src/config.h.in307
-rw-r--r--src/knot/common.h133
-rw-r--r--src/knot/conf/cf-lex.l218
-rw-r--r--src/knot/conf/cf-parse.y646
-rw-r--r--src/knot/conf/conf.c715
-rw-r--r--src/knot/conf/conf.h361
-rw-r--r--src/knot/conf/logconf.c102
-rw-r--r--src/knot/conf/logconf.h45
-rw-r--r--src/knot/ctl/knotc_main.c658
-rw-r--r--src/knot/ctl/process.c126
-rw-r--r--src/knot/ctl/process.h88
-rw-r--r--src/knot/main.c336
-rw-r--r--src/knot/other/debug.h426
-rw-r--r--src/knot/other/error.c46
-rw-r--r--src/knot/other/error.h99
-rw-r--r--src/knot/other/log.c305
-rw-r--r--src/knot/other/log.h208
-rw-r--r--src/knot/server/dthreads.c1006
-rw-r--r--src/knot/server/dthreads.h353
-rw-r--r--src/knot/server/journal.c636
-rw-r--r--src/knot/server/journal.h243
-rw-r--r--src/knot/server/notify.c327
-rw-r--r--src/knot/server/notify.h128
-rw-r--r--src/knot/server/server.c730
-rw-r--r--src/knot/server/server.h211
-rw-r--r--src/knot/server/socket.c192
-rw-r--r--src/knot/server/socket.h120
-rw-r--r--src/knot/server/tcp-handler.c511
-rw-r--r--src/knot/server/tcp-handler.h103
-rw-r--r--src/knot/server/udp-handler.c438
-rw-r--r--src/knot/server/udp-handler.h74
-rw-r--r--src/knot/server/xfr-handler.c1296
-rw-r--r--src/knot/server/xfr-handler.h163
-rw-r--r--src/knot/server/zones.c2352
-rw-r--r--src/knot/server/zones.h264
-rw-r--r--src/knot/stat/gatherer.c77
-rw-r--r--src/knot/stat/gatherer.h110
-rw-r--r--src/knot/stat/stat-common.h46
-rw-r--r--src/knot/stat/stat.c270
-rw-r--r--src/knot/stat/stat.h157
-rw-r--r--src/knot/zone/zone-dump-text.c1083
-rw-r--r--src/knot/zone/zone-dump-text.h46
-rw-r--r--src/knot/zone/zone-dump.c2301
-rw-r--r--src/knot/zone/zone-dump.h94
-rw-r--r--src/knot/zone/zone-load.c1209
-rw-r--r--src/knot/zone/zone-load.h104
-rw-r--r--src/knotc.863
-rw-r--r--src/knotd.835
-rw-r--r--src/libknot/common.h105
-rw-r--r--src/libknot/consts.h108
-rw-r--r--src/libknot/dname.c1070
-rw-r--r--src/libknot/dname.h428
-rw-r--r--src/libknot/edns.c416
-rw-r--r--src/libknot/edns.h273
-rw-r--r--src/libknot/hash/cuckoo-hash-table.c1688
-rw-r--r--src/libknot/hash/cuckoo-hash-table.h333
-rw-r--r--src/libknot/hash/hash-functions.c241
-rw-r--r--src/libknot/hash/hash-functions.h85
-rw-r--r--src/libknot/hash/universal-system.c116
-rw-r--r--src/libknot/hash/universal-system.h109
-rw-r--r--src/libknot/libknot.h48
-rw-r--r--src/libknot/nameserver/name-server.c3663
-rw-r--r--src/libknot/nameserver/name-server.h358
-rw-r--r--src/libknot/nsec3.c265
-rw-r--r--src/libknot/nsec3.h92
-rw-r--r--src/libknot/packet/packet.c1532
-rw-r--r--src/libknot/packet/packet.h538
-rw-r--r--src/libknot/packet/query.c228
-rw-r--r--src/libknot/packet/query.h93
-rw-r--r--src/libknot/packet/response.c1170
-rw-r--r--src/libknot/packet/response.h198
-rw-r--r--src/libknot/rdata.c838
-rw-r--r--src/libknot/rdata.h339
-rw-r--r--src/libknot/rrset.c719
-rw-r--r--src/libknot/rrset.h306
-rw-r--r--src/libknot/tsig-op.c1089
-rw-r--r--src/libknot/tsig-op.h161
-rw-r--r--src/libknot/tsig.c618
-rw-r--r--src/libknot/tsig.h145
-rw-r--r--src/libknot/updates/changesets.c296
-rw-r--r--src/libknot/updates/changesets.h102
-rw-r--r--src/libknot/updates/ddns.c638
-rw-r--r--src/libknot/updates/ddns.h74
-rw-r--r--src/libknot/updates/xfr-in.c3013
-rw-r--r--src/libknot/updates/xfr-in.h184
-rw-r--r--src/libknot/util/debug.c233
-rw-r--r--src/libknot/util/debug.h755
-rw-r--r--src/libknot/util/descriptor.c501
-rw-r--r--src/libknot/util/descriptor.h332
-rw-r--r--src/libknot/util/error.h87
-rw-r--r--src/libknot/util/libknot_error.c53
-rw-r--r--src/libknot/util/tolower.c276
-rw-r--r--src/libknot/util/tolower.h57
-rw-r--r--src/libknot/util/utils.c127
-rw-r--r--src/libknot/util/utils.h196
-rw-r--r--src/libknot/util/wire.h926
-rw-r--r--src/libknot/zone/dname-table.c310
-rw-r--r--src/libknot/zone/dname-table.h168
-rw-r--r--src/libknot/zone/node.c906
-rw-r--r--src/libknot/zone/node.h436
-rw-r--r--src/libknot/zone/zone-contents.c2396
-rw-r--r--src/libknot/zone/zone-contents.h556
-rw-r--r--src/libknot/zone/zone-tree.c470
-rw-r--r--src/libknot/zone/zone-tree.h300
-rw-r--r--src/libknot/zone/zone.c246
-rw-r--r--src/libknot/zone/zone.h157
-rw-r--r--src/libknot/zone/zonedb.c389
-rw-r--r--src/libknot/zone/zonedb.h145
-rw-r--r--src/tests/README10
-rw-r--r--src/tests/common/acl_tests.c111
-rw-r--r--src/tests/common/acl_tests.h25
-rw-r--r--src/tests/common/da_tests.c330
-rw-r--r--src/tests/common/da_tests.h25
-rw-r--r--src/tests/common/events_tests.c192
-rw-r--r--src/tests/common/events_tests.h25
-rw-r--r--src/tests/common/fdset_tests.c177
-rw-r--r--src/tests/common/fdset_tests.h25
-rw-r--r--src/tests/common/skiplist_tests.c198
-rw-r--r--src/tests/common/skiplist_tests.h25
-rw-r--r--src/tests/common/slab_tests.c141
-rw-r--r--src/tests/common/slab_tests.h25
-rw-r--r--src/tests/files/sample_conf59
-rw-r--r--src/tests/knot/conf_tests.c141
-rw-r--r--src/tests/knot/conf_tests.h25
-rw-r--r--src/tests/knot/dthreads_tests.c392
-rw-r--r--src/tests/knot/dthreads_tests.h25
-rw-r--r--src/tests/knot/journal_tests.c184
-rw-r--r--src/tests/knot/journal_tests.h25
-rw-r--r--src/tests/knot/server_tests.c113
-rw-r--r--src/tests/knot/server_tests.h25
-rw-r--r--src/tests/libknot/files/parsed_databin0 -> 71188 bytes
-rw-r--r--src/tests/libknot/files/parsed_data_queriesbin0 -> 1325 bytes
-rw-r--r--src/tests/libknot/files/raw_databin0 -> 72100 bytes
-rw-r--r--src/tests/libknot/files/raw_data_queriesbin0 -> 1387 bytes
-rw-r--r--src/tests/libknot/libknot/cuckoo_tests.c382
-rw-r--r--src/tests/libknot/libknot/cuckoo_tests.h25
-rw-r--r--src/tests/libknot/libknot/dname_table_tests.c393
-rw-r--r--src/tests/libknot/libknot/dname_table_tests.h25
-rw-r--r--src/tests/libknot/libknot/dname_tests.c877
-rw-r--r--src/tests/libknot/libknot/dname_tests.h25
-rw-r--r--src/tests/libknot/libknot/edns_tests.c596
-rw-r--r--src/tests/libknot/libknot/edns_tests.h34
-rw-r--r--src/tests/libknot/libknot/node_tests.c344
-rw-r--r--src/tests/libknot/libknot/node_tests.h25
-rw-r--r--src/tests/libknot/libknot/nsec3_tests.c252
-rw-r--r--src/tests/libknot/libknot/nsec3_tests.h25
-rw-r--r--src/tests/libknot/libknot/packet_tests.c430
-rw-r--r--src/tests/libknot/libknot/packet_tests.h25
-rw-r--r--src/tests/libknot/libknot/query_tests.c160
-rw-r--r--src/tests/libknot/libknot/query_tests.h25
-rw-r--r--src/tests/libknot/libknot/rdata_tests.c954
-rw-r--r--src/tests/libknot/libknot/rdata_tests.h52
-rw-r--r--src/tests/libknot/libknot/response_tests.c450
-rw-r--r--src/tests/libknot/libknot/response_tests.h25
-rw-r--r--src/tests/libknot/libknot/rrset_tests.c888
-rw-r--r--src/tests/libknot/libknot/rrset_tests.h34
-rw-r--r--src/tests/libknot/libknot/zone_tests.c853
-rw-r--r--src/tests/libknot/libknot/zone_tests.h25
-rw-r--r--src/tests/libknot/libknot/zone_tree_tests.c758
-rw-r--r--src/tests/libknot/libknot/zone_tree_tests.h25
-rw-r--r--src/tests/libknot/libknot/zonedb_tests.c44
-rw-r--r--src/tests/libknot/libknot/zonedb_tests.h25
-rw-r--r--src/tests/libknot/realdata/files/parsed_databin0 -> 4851 bytes
-rw-r--r--src/tests/libknot/realdata/files/parsed_data_queriesbin0 -> 1325 bytes
-rw-r--r--src/tests/libknot/realdata/files/raw_databin0 -> 4935 bytes
-rw-r--r--src/tests/libknot/realdata/files/raw_data_queriesbin0 -> 1387 bytes
-rw-r--r--src/tests/libknot/realdata/libknot/dname_tests_realdata.c411
-rw-r--r--src/tests/libknot/realdata/libknot/dname_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot/edns_tests_realdata.c563
-rw-r--r--src/tests/libknot/realdata/libknot/edns_tests_realdata.h35
-rw-r--r--src/tests/libknot/realdata/libknot/node_tests_realdata.c385
-rw-r--r--src/tests/libknot/realdata/libknot/node_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot/packet_tests_realdata.c679
-rw-r--r--src/tests/libknot/realdata/libknot/packet_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot/rdata_tests_realdata.c329
-rw-r--r--src/tests/libknot/realdata/libknot/rdata_tests_realdata.h53
-rw-r--r--src/tests/libknot/realdata/libknot/response_tests_realdata.c173
-rw-r--r--src/tests/libknot/realdata/libknot/response_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot/rrset_tests_realdata.c289
-rw-r--r--src/tests/libknot/realdata/libknot/rrset_tests_realdata.h35
-rw-r--r--src/tests/libknot/realdata/libknot/zone_tests_realdata.c335
-rw-r--r--src/tests/libknot/realdata/libknot/zone_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c44
-rw-r--r--src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h25
-rw-r--r--src/tests/libknot/realdata/libknot_tests_loader_realdata.c1300
-rw-r--r--src/tests/libknot/realdata/libknot_tests_loader_realdata.h179
-rw-r--r--src/tests/libknot/realdata/unittests_libknot_realdata.c93
-rw-r--r--src/tests/libknot/unittests_libknot.c90
-rw-r--r--src/tests/unittests_main.c84
-rw-r--r--src/zcompile/LICENSE30
-rw-r--r--src/zcompile/parser-descriptor.c535
-rw-r--r--src/zcompile/parser-descriptor.h278
-rw-r--r--src/zcompile/parser-util.c2435
-rw-r--r--src/zcompile/parser-util.h357
-rw-r--r--src/zcompile/tests/unittests_zp_main.c62
-rw-r--r--src/zcompile/tests/zcompile_tests.c425
-rw-r--r--src/zcompile/zcompile-error.c52
-rw-r--r--src/zcompile/zcompile-error.h90
-rw-r--r--src/zcompile/zcompile.c639
-rw-r--r--src/zcompile/zcompile.h207
-rw-r--r--src/zcompile/zcompile_main.c116
-rw-r--r--src/zcompile/zlexer.l531
-rw-r--r--src/zcompile/zparser.y1730
256 files changed, 86269 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..3f65566
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,336 @@
+ACLOCAL_AMFLAGS = -I ../m4
+libexec_PROGRAMS = knot-zcompile unittests unittests-zcompile unittests-libknot-realdata unittests-libknot
+sbin_PROGRAMS = knotc knotd
+MANPAGES = knotc.8 knotd.8
+man8_MANS = knotc.8 knotd.8
+EXTRA_DIST = $(man8_MANS)
+
+# $(YACC) will generate header file
+AM_CFLAGS = -Wall -Ilibknot -DLIBEXECDIR='"$(libexecdir)"' -DSYSCONFDIR='"$(sysconfdir)"' -DSBINDIR='"$(sbindir)"'
+AM_YFLAGS = -d
+libknotd_la_YFLAGS = -pcf_ -d
+libknotd_la_LFLAGS = # TODO: reentrant parser, prefix
+
+BUILT_SOURCES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/realdata/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/realdata/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc \
+ zparser.h \
+ zparser.c \
+ zlexer.c \
+ libknotd_la-cf-lex.c \
+ libknotd_la-cf-parse.c \
+ libknotd_la-cf-parse.h
+
+CLEANFILES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/realdata/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/realdata/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc \
+ zparser.h \
+ zparser.c \
+ zlexer.c \
+ libknotd_la-cf-lex.c \
+ libknotd_la-cf-parse.c \
+ libknotd_la-cf-parse.h
+
+knotc_SOURCES = \
+ knot/ctl/knotc_main.c
+
+knot_zcompile_SOURCES = \
+ zcompile/zcompile_main.c \
+ zcompile/zcompile-error.c \
+ zcompile/parser-util.h \
+ zcompile/parser-descriptor.h \
+ zcompile/zparser.y \
+ zcompile/zlexer.l \
+ zcompile/zcompile.c \
+ zcompile/parser-util.c \
+ zcompile/parser-descriptor.c
+
+unittests_SOURCES = \
+ tests/common/acl_tests.c \
+ tests/common/acl_tests.h \
+ tests/common/da_tests.c \
+ tests/common/da_tests.h \
+ tests/common/events_tests.c \
+ tests/common/events_tests.h \
+ tests/common/skiplist_tests.c \
+ tests/common/skiplist_tests.h \
+ tests/common/slab_tests.c \
+ tests/common/slab_tests.h \
+ tests/common/fdset_tests.c \
+ tests/common/fdset_tests.h \
+ tests/knot/conf_tests.c \
+ tests/knot/conf_tests.h \
+ tests/knot/dthreads_tests.c \
+ tests/knot/dthreads_tests.h \
+ tests/knot/journal_tests.c \
+ tests/knot/journal_tests.h \
+ tests/knot/server_tests.c \
+ tests/knot/server_tests.h \
+ tests/unittests_main.c
+
+unittests_libknot_realdata_SOURCES = \
+ tests/libknot/realdata/libknot/dname_tests_realdata.c \
+ tests/libknot/realdata/libknot/response_tests_realdata.c \
+ tests/libknot/realdata/libknot/edns_tests_realdata.c \
+ tests/libknot/realdata/libknot/node_tests_realdata.c \
+ tests/libknot/realdata/libknot/rdata_tests_realdata.c \
+ tests/libknot/realdata/libknot/rrset_tests_realdata.c \
+ tests/libknot/realdata/libknot/zone_tests_realdata.c \
+ tests/libknot/realdata/libknot/zonedb_tests_realdata.c \
+ tests/libknot/realdata/libknot/packet_tests_realdata.h \
+ tests/libknot/realdata/libknot/packet_tests_realdata.c \
+ tests/libknot/realdata/libknot_tests_loader_realdata.h \
+ tests/libknot/realdata/libknot_tests_loader_realdata.c \
+ tests/libknot/realdata/unittests_libknot_realdata.c
+
+unittests_libknot_SOURCES = \
+ tests/libknot/libknot/cuckoo_tests.c \
+ tests/libknot/libknot/cuckoo_tests.h \
+ tests/libknot/libknot/response_tests.h \
+ tests/libknot/libknot/response_tests.c \
+ tests/libknot/libknot/dname_tests.c \
+ tests/libknot/libknot/dname_tests.h \
+ tests/libknot/libknot/dname_table_tests.h \
+ tests/libknot/libknot/dname_table_tests.c \
+ tests/libknot/libknot/nsec3_tests.h \
+ tests/libknot/libknot/nsec3_tests.c \
+ tests/libknot/libknot/packet_tests.h \
+ tests/libknot/libknot/packet_tests.c \
+ tests/libknot/libknot/query_tests.h \
+ tests/libknot/libknot/query_tests.c \
+ tests/libknot/libknot/edns_tests.c \
+ tests/libknot/libknot/edns_tests.h \
+ tests/libknot/libknot/node_tests.c \
+ tests/libknot/libknot/node_tests.h \
+ tests/libknot/libknot/rdata_tests.c \
+ tests/libknot/libknot/rdata_tests.h \
+ tests/libknot/libknot/rrset_tests.c \
+ tests/libknot/libknot/rrset_tests.h \
+ tests/libknot/libknot/zone_tests.c \
+ tests/libknot/libknot/zone_tests.h \
+ tests/libknot/libknot/zone_tree_tests.h\
+ tests/libknot/libknot/zone_tree_tests.c \
+ tests/libknot/libknot/zonedb_tests.c \
+ tests/libknot/libknot/zonedb_tests.h \
+ tests/libknot/unittests_libknot.c
+
+unittests_zcompile_SOURCES = \
+ zcompile/parser-util.h \
+ zcompile/parser-descriptor.h \
+ zcompile/zcompile-error.c \
+ zcompile/zparser.y \
+ zcompile/zlexer.l \
+ zcompile/zcompile.c \
+ zcompile/parser-util.c \
+ zcompile/parser-descriptor.c \
+ zcompile/tests/unittests_zp_main.c
+
+nodist_unittests_SOURCES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc
+
+knotd_SOURCES = \
+ knot/main.c
+
+noinst_LTLIBRARIES = libknot.la libknotd.la libknots.la
+
+libknot_la_SOURCES = \
+ libknot/util/libknot_error.c \
+ libknot/util/utils.c \
+ libknot/util/debug.c \
+ libknot/util/debug.h \
+ libknot/util/utils.h \
+ libknot/util/descriptor.c \
+ libknot/util/tolower.h \
+ libknot/util/tolower.c \
+ libknot/util/descriptor.h \
+ libknot/util/wire.h \
+ libknot/packet/query.c \
+ libknot/packet/response.c \
+ libknot/packet/packet.c \
+ libknot/packet/packet.h \
+ libknot/packet/query.h \
+ libknot/packet/response.h \
+ libknot/zone/zone.c \
+ libknot/zone/zone-contents.c \
+ libknot/zone/zone-tree.c \
+ libknot/zone/zone-tree.h \
+ libknot/zone/node.h \
+ libknot/zone/zone.h \
+ libknot/zone/zone-contents.h \
+ libknot/zone/zonedb.c \
+ libknot/zone/zonedb.h \
+ libknot/zone/node.c \
+ libknot/zone/dname-table.h \
+ libknot/zone/dname-table.c \
+ libknot/hash/hash-functions.c \
+ libknot/hash/cuckoo-hash-table.c \
+ libknot/hash/universal-system.c \
+ libknot/hash/universal-system.h \
+ libknot/hash/cuckoo-hash-table.h \
+ libknot/hash/hash-functions.h \
+ libknot/nameserver/name-server.h \
+ libknot/nameserver/name-server.c \
+ libknot/updates/changesets.h \
+ libknot/updates/changesets.c \
+ libknot/updates/xfr-in.h \
+ libknot/updates/xfr-in.c \
+ libknot/updates/ddns.h \
+ libknot/updates/ddns.c \
+ libknot/edns.c \
+ libknot/rrset.c \
+ libknot/dname.c \
+ libknot/rdata.c \
+ libknot/nsec3.c \
+ libknot/consts.h \
+ libknot/edns.h \
+ libknot/rdata.h \
+ libknot/libknot.h \
+ libknot/dname.h \
+ libknot/rrset.h \
+ libknot/nsec3.h \
+ libknot/tsig.h \
+ libknot/tsig.c \
+ libknot/tsig-op.h \
+ libknot/tsig-op.c
+
+libknots_la_SOURCES = \
+ common/slab/slab.c \
+ common/slab/malloc.c \
+ common/slab/slab.h \
+ common/slab/malloc.h \
+ common/libtap/tap.c \
+ common/libtap/tap.h \
+ common/libtap/tap_unit.h \
+ common/lists.c \
+ common/base32.c \
+ common/lists.h \
+ common/base32.h \
+ common/print.c \
+ common/print.h \
+ common/dynamic-array.c \
+ common/skip-list.c \
+ common/base32hex.c \
+ common/skip-list.h \
+ common/general-tree.h \
+ common/general-tree.c \
+ common/dynamic-array.h \
+ common/tree.h \
+ common/base32hex.h \
+ common/evqueue.h \
+ common/evqueue.c \
+ common/evsched.h \
+ common/evsched.c \
+ common/acl.h \
+ common/acl.c \
+ common/sockaddr.h \
+ common/sockaddr.c \
+ common/crc.h \
+ common/crc.c \
+ common/ref.h \
+ common/ref.c \
+ common/errors.h \
+ common/errors.c \
+ common/WELL1024a.h \
+ common/WELL1024a.c \
+ common/fdset.h \
+ common/fdset.c \
+ common/fdset_poll.h \
+ common/fdset_poll.c \
+ common/fdset_kqueue.h \
+ common/fdset_kqueue.c \
+ common/fdset_epoll.h \
+ common/fdset_epoll.c
+
+libknotd_la_SOURCES = \
+ knot/stat/gatherer.c \
+ knot/stat/stat.c \
+ knot/stat/gatherer.h \
+ knot/stat/stat.h \
+ knot/common.h \
+ knot/other/log.c \
+ knot/other/log.h \
+ knot/other/debug.h \
+ knot/other/error.h \
+ knot/other/error.c \
+ knot/conf/cf-parse.y \
+ knot/conf/cf-lex.l \
+ knot/conf/conf.c \
+ knot/conf/logconf.c \
+ knot/conf/logconf.h \
+ knot/conf/conf.h \
+ knot/ctl/process.c \
+ knot/ctl/process.h \
+ knot/server/dthreads.c \
+ knot/server/journal.c \
+ knot/server/socket.c \
+ knot/server/server.c \
+ knot/server/udp-handler.c \
+ knot/server/tcp-handler.c \
+ knot/server/xfr-handler.c \
+ knot/server/zones.c \
+ knot/server/socket.h \
+ knot/server/udp-handler.h \
+ knot/server/tcp-handler.h \
+ knot/server/xfr-handler.h \
+ knot/server/dthreads.h \
+ knot/server/journal.h \
+ knot/server/zones.h \
+ knot/server/notify.h \
+ knot/server/notify.c \
+ knot/server/zones.h \
+ knot/zone/zone-load.c \
+ knot/zone/zone-load.h \
+ knot/zone/zone-dump.c \
+ knot/zone/zone-dump-text.c \
+ knot/zone/zone-dump-text.h \
+ knot/zone/zone-dump.h \
+ knot/server/server.h
+
+libknotd_la_LIBADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+libknots_la_LIBADD = @LIBOBJS@
+knotd_LDADD = libknotd.la ../libknot/libknot.la libknots.la @LIBOBJS@
+knotc_LDADD = libknotd.la ../libknot/libknot.la libknots.la @LIBOBJS@
+knot_zcompile_LDADD = libknots.la ../libknot/libknot.la libknotd.la @LIBOBJS@
+unittests_LDADD = libknotd.la libknots.la @LIBOBJS@
+unittests_zcompile_LDADD = ../libknot/libknot.la libknots.la libknotd.la @LIBOBJS@
+unittests_libknot_LDADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+unittests_libknot_realdata_LDADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+
+# automake complains on % rules:
+# `%'-style pattern rules are a GNU make extension
+
+tests/libknot/parsed_data.rc: tests/libknot/files/parsed_data
+ ../resource.sh tests/libknot/files/parsed_data >$@
+
+tests/libknot/realdata/parsed_data.rc: tests/libknot/realdata/files/parsed_data
+ ../resource.sh tests/libknot/realdata/files/parsed_data >$@
+
+tests/libknot/parsed_data_queries.rc: tests/libknot/files/parsed_data_queries
+ ../resource.sh tests/libknot/files/parsed_data_queries >$@
+
+tests/libknot/raw_data_queries.rc: tests/libknot/files/raw_data_queries
+ ../resource.sh tests/libknot/files/raw_data_queries >$@
+
+tests/libknot/raw_data.rc: tests/libknot/files/raw_data
+ ../resource.sh tests/libknot/files/raw_data >$@
+
+tests/libknot/realdata/raw_data.rc: tests/libknot/realdata/files/raw_data
+ ../resource.sh tests/libknot/realdata/files/raw_data >$@
+
+tests/sample_conf.rc: tests/files/sample_conf
+ ../resource.sh tests/files/sample_conf >$@
+
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..7811da5
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,2396 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = knot-zcompile$(EXEEXT) unittests$(EXEEXT) \
+ unittests-zcompile$(EXEEXT) \
+ unittests-libknot-realdata$(EXEEXT) unittests-libknot$(EXEEXT)
+sbin_PROGRAMS = knotc$(EXEEXT) knotd$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in libknotd_la-cf-lex.c \
+ libknotd_la-cf-parse.c libknotd_la-cf-parse.h zlexer.c \
+ zparser.c zparser.h
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
+ $(top_srcdir)/m4/ax_ext.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libknot_la_LIBADD =
+am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo \
+ descriptor.lo tolower.lo query.lo response.lo packet.lo \
+ zone.lo zone-contents.lo zone-tree.lo zonedb.lo node.lo \
+ dname-table.lo hash-functions.lo cuckoo-hash-table.lo \
+ universal-system.lo name-server.lo changesets.lo xfr-in.lo \
+ ddns.lo edns.lo rrset.lo dname.lo rdata.lo nsec3.lo tsig.lo \
+ tsig-op.lo
+libknot_la_OBJECTS = $(am_libknot_la_OBJECTS)
+libknotd_la_DEPENDENCIES = ../libknot/libknot.la libknots.la @LIBOBJS@
+am_libknotd_la_OBJECTS = gatherer.lo stat.lo log.lo error.lo \
+ libknotd_la-cf-parse.lo libknotd_la-cf-lex.lo conf.lo \
+ logconf.lo process.lo dthreads.lo journal.lo socket.lo \
+ server.lo udp-handler.lo tcp-handler.lo xfr-handler.lo \
+ zones.lo notify.lo zone-load.lo zone-dump.lo zone-dump-text.lo
+libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS)
+libknots_la_DEPENDENCIES = @LIBOBJS@
+am_libknots_la_OBJECTS = slab.lo malloc.lo tap.lo lists.lo base32.lo \
+ print.lo dynamic-array.lo skip-list.lo base32hex.lo \
+ general-tree.lo evqueue.lo evsched.lo acl.lo sockaddr.lo \
+ crc.lo ref.lo errors.lo WELL1024a.lo fdset.lo fdset_poll.lo \
+ fdset_kqueue.lo fdset_epoll.lo
+libknots_la_OBJECTS = $(am_libknots_la_OBJECTS)
+am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \
+ "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(libexec_PROGRAMS) $(sbin_PROGRAMS)
+am_knot_zcompile_OBJECTS = zcompile_main.$(OBJEXT) \
+ zcompile-error.$(OBJEXT) zparser.$(OBJEXT) zlexer.$(OBJEXT) \
+ zcompile.$(OBJEXT) parser-util.$(OBJEXT) \
+ parser-descriptor.$(OBJEXT)
+knot_zcompile_OBJECTS = $(am_knot_zcompile_OBJECTS)
+knot_zcompile_DEPENDENCIES = libknots.la ../libknot/libknot.la \
+ libknotd.la @LIBOBJS@
+am_knotc_OBJECTS = knotc_main.$(OBJEXT)
+knotc_OBJECTS = $(am_knotc_OBJECTS)
+knotc_DEPENDENCIES = libknotd.la ../libknot/libknot.la libknots.la \
+ @LIBOBJS@ $(am__empty)
+am_knotd_OBJECTS = main.$(OBJEXT)
+knotd_OBJECTS = $(am_knotd_OBJECTS)
+knotd_DEPENDENCIES = libknotd.la ../libknot/libknot.la libknots.la \
+ @LIBOBJS@ $(am__empty)
+am_unittests_OBJECTS = acl_tests.$(OBJEXT) da_tests.$(OBJEXT) \
+ events_tests.$(OBJEXT) skiplist_tests.$(OBJEXT) \
+ slab_tests.$(OBJEXT) fdset_tests.$(OBJEXT) \
+ conf_tests.$(OBJEXT) dthreads_tests.$(OBJEXT) \
+ journal_tests.$(OBJEXT) server_tests.$(OBJEXT) \
+ unittests_main.$(OBJEXT)
+nodist_unittests_OBJECTS =
+unittests_OBJECTS = $(am_unittests_OBJECTS) \
+ $(nodist_unittests_OBJECTS)
+unittests_DEPENDENCIES = libknotd.la libknots.la @LIBOBJS@
+am_unittests_libknot_OBJECTS = cuckoo_tests.$(OBJEXT) \
+ response_tests.$(OBJEXT) dname_tests.$(OBJEXT) \
+ dname_table_tests.$(OBJEXT) nsec3_tests.$(OBJEXT) \
+ packet_tests.$(OBJEXT) query_tests.$(OBJEXT) \
+ edns_tests.$(OBJEXT) node_tests.$(OBJEXT) \
+ rdata_tests.$(OBJEXT) rrset_tests.$(OBJEXT) \
+ zone_tests.$(OBJEXT) zone_tree_tests.$(OBJEXT) \
+ zonedb_tests.$(OBJEXT) unittests_libknot.$(OBJEXT)
+unittests_libknot_OBJECTS = $(am_unittests_libknot_OBJECTS)
+unittests_libknot_DEPENDENCIES = ../libknot/libknot.la libknots.la \
+ @LIBOBJS@ $(am__empty)
+am_unittests_libknot_realdata_OBJECTS = \
+ dname_tests_realdata.$(OBJEXT) \
+ response_tests_realdata.$(OBJEXT) \
+ edns_tests_realdata.$(OBJEXT) node_tests_realdata.$(OBJEXT) \
+ rdata_tests_realdata.$(OBJEXT) rrset_tests_realdata.$(OBJEXT) \
+ zone_tests_realdata.$(OBJEXT) zonedb_tests_realdata.$(OBJEXT) \
+ packet_tests_realdata.$(OBJEXT) \
+ libknot_tests_loader_realdata.$(OBJEXT) \
+ unittests_libknot_realdata.$(OBJEXT)
+unittests_libknot_realdata_OBJECTS = \
+ $(am_unittests_libknot_realdata_OBJECTS)
+unittests_libknot_realdata_DEPENDENCIES = ../libknot/libknot.la \
+ libknots.la @LIBOBJS@
+am_unittests_zcompile_OBJECTS = zcompile-error.$(OBJEXT) \
+ zparser.$(OBJEXT) zlexer.$(OBJEXT) zcompile.$(OBJEXT) \
+ parser-util.$(OBJEXT) parser-descriptor.$(OBJEXT) \
+ unittests_zp_main.$(OBJEXT)
+unittests_zcompile_OBJECTS = $(am_unittests_zcompile_OBJECTS)
+unittests_zcompile_DEPENDENCIES = ../libknot/libknot.la libknots.la \
+ libknotd.la @LIBOBJS@
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
+LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+YLWRAP = $(top_srcdir)/ylwrap
+@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+SOURCES = $(libknot_la_SOURCES) $(libknotd_la_SOURCES) \
+ $(libknots_la_SOURCES) $(knot_zcompile_SOURCES) \
+ $(knotc_SOURCES) $(knotd_SOURCES) $(unittests_SOURCES) \
+ $(nodist_unittests_SOURCES) $(unittests_libknot_SOURCES) \
+ $(unittests_libknot_realdata_SOURCES) \
+ $(unittests_zcompile_SOURCES)
+DIST_SOURCES = $(libknot_la_SOURCES) $(libknotd_la_SOURCES) \
+ $(libknots_la_SOURCES) $(knot_zcompile_SOURCES) \
+ $(knotc_SOURCES) $(knotd_SOURCES) $(unittests_SOURCES) \
+ $(unittests_libknot_SOURCES) \
+ $(unittests_libknot_realdata_SOURCES) \
+ $(unittests_zcompile_SOURCES)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIMD_FLAGS = @SIMD_FLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I ../m4
+MANPAGES = knotc.8 knotd.8
+man8_MANS = knotc.8 knotd.8
+EXTRA_DIST = $(man8_MANS)
+
+# $(YACC) will generate header file
+AM_CFLAGS = -Wall -Ilibknot -DLIBEXECDIR='"$(libexecdir)"' -DSYSCONFDIR='"$(sysconfdir)"' -DSBINDIR='"$(sbindir)"'
+AM_YFLAGS = -d
+libknotd_la_YFLAGS = -pcf_ -d
+libknotd_la_LFLAGS = # TODO: reentrant parser, prefix
+BUILT_SOURCES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/realdata/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/realdata/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc \
+ zparser.h \
+ zparser.c \
+ zlexer.c \
+ libknotd_la-cf-lex.c \
+ libknotd_la-cf-parse.c \
+ libknotd_la-cf-parse.h
+
+CLEANFILES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/realdata/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/realdata/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc \
+ zparser.h \
+ zparser.c \
+ zlexer.c \
+ libknotd_la-cf-lex.c \
+ libknotd_la-cf-parse.c \
+ libknotd_la-cf-parse.h
+
+knotc_SOURCES = \
+ knot/ctl/knotc_main.c
+
+knot_zcompile_SOURCES = \
+ zcompile/zcompile_main.c \
+ zcompile/zcompile-error.c \
+ zcompile/parser-util.h \
+ zcompile/parser-descriptor.h \
+ zcompile/zparser.y \
+ zcompile/zlexer.l \
+ zcompile/zcompile.c \
+ zcompile/parser-util.c \
+ zcompile/parser-descriptor.c
+
+unittests_SOURCES = \
+ tests/common/acl_tests.c \
+ tests/common/acl_tests.h \
+ tests/common/da_tests.c \
+ tests/common/da_tests.h \
+ tests/common/events_tests.c \
+ tests/common/events_tests.h \
+ tests/common/skiplist_tests.c \
+ tests/common/skiplist_tests.h \
+ tests/common/slab_tests.c \
+ tests/common/slab_tests.h \
+ tests/common/fdset_tests.c \
+ tests/common/fdset_tests.h \
+ tests/knot/conf_tests.c \
+ tests/knot/conf_tests.h \
+ tests/knot/dthreads_tests.c \
+ tests/knot/dthreads_tests.h \
+ tests/knot/journal_tests.c \
+ tests/knot/journal_tests.h \
+ tests/knot/server_tests.c \
+ tests/knot/server_tests.h \
+ tests/unittests_main.c
+
+unittests_libknot_realdata_SOURCES = \
+ tests/libknot/realdata/libknot/dname_tests_realdata.c \
+ tests/libknot/realdata/libknot/response_tests_realdata.c \
+ tests/libknot/realdata/libknot/edns_tests_realdata.c \
+ tests/libknot/realdata/libknot/node_tests_realdata.c \
+ tests/libknot/realdata/libknot/rdata_tests_realdata.c \
+ tests/libknot/realdata/libknot/rrset_tests_realdata.c \
+ tests/libknot/realdata/libknot/zone_tests_realdata.c \
+ tests/libknot/realdata/libknot/zonedb_tests_realdata.c \
+ tests/libknot/realdata/libknot/packet_tests_realdata.h \
+ tests/libknot/realdata/libknot/packet_tests_realdata.c \
+ tests/libknot/realdata/libknot_tests_loader_realdata.h \
+ tests/libknot/realdata/libknot_tests_loader_realdata.c \
+ tests/libknot/realdata/unittests_libknot_realdata.c
+
+unittests_libknot_SOURCES = \
+ tests/libknot/libknot/cuckoo_tests.c \
+ tests/libknot/libknot/cuckoo_tests.h \
+ tests/libknot/libknot/response_tests.h \
+ tests/libknot/libknot/response_tests.c \
+ tests/libknot/libknot/dname_tests.c \
+ tests/libknot/libknot/dname_tests.h \
+ tests/libknot/libknot/dname_table_tests.h \
+ tests/libknot/libknot/dname_table_tests.c \
+ tests/libknot/libknot/nsec3_tests.h \
+ tests/libknot/libknot/nsec3_tests.c \
+ tests/libknot/libknot/packet_tests.h \
+ tests/libknot/libknot/packet_tests.c \
+ tests/libknot/libknot/query_tests.h \
+ tests/libknot/libknot/query_tests.c \
+ tests/libknot/libknot/edns_tests.c \
+ tests/libknot/libknot/edns_tests.h \
+ tests/libknot/libknot/node_tests.c \
+ tests/libknot/libknot/node_tests.h \
+ tests/libknot/libknot/rdata_tests.c \
+ tests/libknot/libknot/rdata_tests.h \
+ tests/libknot/libknot/rrset_tests.c \
+ tests/libknot/libknot/rrset_tests.h \
+ tests/libknot/libknot/zone_tests.c \
+ tests/libknot/libknot/zone_tests.h \
+ tests/libknot/libknot/zone_tree_tests.h\
+ tests/libknot/libknot/zone_tree_tests.c \
+ tests/libknot/libknot/zonedb_tests.c \
+ tests/libknot/libknot/zonedb_tests.h \
+ tests/libknot/unittests_libknot.c
+
+unittests_zcompile_SOURCES = \
+ zcompile/parser-util.h \
+ zcompile/parser-descriptor.h \
+ zcompile/zcompile-error.c \
+ zcompile/zparser.y \
+ zcompile/zlexer.l \
+ zcompile/zcompile.c \
+ zcompile/parser-util.c \
+ zcompile/parser-descriptor.c \
+ zcompile/tests/unittests_zp_main.c
+
+nodist_unittests_SOURCES = \
+ tests/libknot/parsed_data.rc \
+ tests/libknot/raw_data_queries.rc \
+ tests/libknot/raw_data.rc \
+ tests/libknot/parsed_data_queries.rc \
+ tests/sample_conf.rc
+
+knotd_SOURCES = \
+ knot/main.c
+
+noinst_LTLIBRARIES = libknot.la libknotd.la libknots.la
+libknot_la_SOURCES = \
+ libknot/util/libknot_error.c \
+ libknot/util/utils.c \
+ libknot/util/debug.c \
+ libknot/util/debug.h \
+ libknot/util/utils.h \
+ libknot/util/descriptor.c \
+ libknot/util/tolower.h \
+ libknot/util/tolower.c \
+ libknot/util/descriptor.h \
+ libknot/util/wire.h \
+ libknot/packet/query.c \
+ libknot/packet/response.c \
+ libknot/packet/packet.c \
+ libknot/packet/packet.h \
+ libknot/packet/query.h \
+ libknot/packet/response.h \
+ libknot/zone/zone.c \
+ libknot/zone/zone-contents.c \
+ libknot/zone/zone-tree.c \
+ libknot/zone/zone-tree.h \
+ libknot/zone/node.h \
+ libknot/zone/zone.h \
+ libknot/zone/zone-contents.h \
+ libknot/zone/zonedb.c \
+ libknot/zone/zonedb.h \
+ libknot/zone/node.c \
+ libknot/zone/dname-table.h \
+ libknot/zone/dname-table.c \
+ libknot/hash/hash-functions.c \
+ libknot/hash/cuckoo-hash-table.c \
+ libknot/hash/universal-system.c \
+ libknot/hash/universal-system.h \
+ libknot/hash/cuckoo-hash-table.h \
+ libknot/hash/hash-functions.h \
+ libknot/nameserver/name-server.h \
+ libknot/nameserver/name-server.c \
+ libknot/updates/changesets.h \
+ libknot/updates/changesets.c \
+ libknot/updates/xfr-in.h \
+ libknot/updates/xfr-in.c \
+ libknot/updates/ddns.h \
+ libknot/updates/ddns.c \
+ libknot/edns.c \
+ libknot/rrset.c \
+ libknot/dname.c \
+ libknot/rdata.c \
+ libknot/nsec3.c \
+ libknot/consts.h \
+ libknot/edns.h \
+ libknot/rdata.h \
+ libknot/libknot.h \
+ libknot/dname.h \
+ libknot/rrset.h \
+ libknot/nsec3.h \
+ libknot/tsig.h \
+ libknot/tsig.c \
+ libknot/tsig-op.h \
+ libknot/tsig-op.c
+
+libknots_la_SOURCES = \
+ common/slab/slab.c \
+ common/slab/malloc.c \
+ common/slab/slab.h \
+ common/slab/malloc.h \
+ common/libtap/tap.c \
+ common/libtap/tap.h \
+ common/libtap/tap_unit.h \
+ common/lists.c \
+ common/base32.c \
+ common/lists.h \
+ common/base32.h \
+ common/print.c \
+ common/print.h \
+ common/dynamic-array.c \
+ common/skip-list.c \
+ common/base32hex.c \
+ common/skip-list.h \
+ common/general-tree.h \
+ common/general-tree.c \
+ common/dynamic-array.h \
+ common/tree.h \
+ common/base32hex.h \
+ common/evqueue.h \
+ common/evqueue.c \
+ common/evsched.h \
+ common/evsched.c \
+ common/acl.h \
+ common/acl.c \
+ common/sockaddr.h \
+ common/sockaddr.c \
+ common/crc.h \
+ common/crc.c \
+ common/ref.h \
+ common/ref.c \
+ common/errors.h \
+ common/errors.c \
+ common/WELL1024a.h \
+ common/WELL1024a.c \
+ common/fdset.h \
+ common/fdset.c \
+ common/fdset_poll.h \
+ common/fdset_poll.c \
+ common/fdset_kqueue.h \
+ common/fdset_kqueue.c \
+ common/fdset_epoll.h \
+ common/fdset_epoll.c
+
+libknotd_la_SOURCES = \
+ knot/stat/gatherer.c \
+ knot/stat/stat.c \
+ knot/stat/gatherer.h \
+ knot/stat/stat.h \
+ knot/common.h \
+ knot/other/log.c \
+ knot/other/log.h \
+ knot/other/debug.h \
+ knot/other/error.h \
+ knot/other/error.c \
+ knot/conf/cf-parse.y \
+ knot/conf/cf-lex.l \
+ knot/conf/conf.c \
+ knot/conf/logconf.c \
+ knot/conf/logconf.h \
+ knot/conf/conf.h \
+ knot/ctl/process.c \
+ knot/ctl/process.h \
+ knot/server/dthreads.c \
+ knot/server/journal.c \
+ knot/server/socket.c \
+ knot/server/server.c \
+ knot/server/udp-handler.c \
+ knot/server/tcp-handler.c \
+ knot/server/xfr-handler.c \
+ knot/server/zones.c \
+ knot/server/socket.h \
+ knot/server/udp-handler.h \
+ knot/server/tcp-handler.h \
+ knot/server/xfr-handler.h \
+ knot/server/dthreads.h \
+ knot/server/journal.h \
+ knot/server/zones.h \
+ knot/server/notify.h \
+ knot/server/notify.c \
+ knot/server/zones.h \
+ knot/zone/zone-load.c \
+ knot/zone/zone-load.h \
+ knot/zone/zone-dump.c \
+ knot/zone/zone-dump-text.c \
+ knot/zone/zone-dump-text.h \
+ knot/zone/zone-dump.h \
+ knot/server/server.h
+
+libknotd_la_LIBADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+libknots_la_LIBADD = @LIBOBJS@
+knotd_LDADD = libknotd.la ../libknot/libknot.la libknots.la @LIBOBJS@
+knotc_LDADD = libknotd.la ../libknot/libknot.la libknots.la @LIBOBJS@
+knot_zcompile_LDADD = libknots.la ../libknot/libknot.la libknotd.la @LIBOBJS@
+unittests_LDADD = libknotd.la libknots.la @LIBOBJS@
+unittests_zcompile_LDADD = ../libknot/libknot.la libknots.la libknotd.la @LIBOBJS@
+unittests_libknot_LDADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+unittests_libknot_realdata_LDADD = ../libknot/libknot.la libknots.la @LIBOBJS@
+all: $(BUILT_SOURCES) config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .l .lo .o .obj .y
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status src/config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libknot.la: $(libknot_la_OBJECTS) $(libknot_la_DEPENDENCIES)
+ $(LINK) $(libknot_la_OBJECTS) $(libknot_la_LIBADD) $(LIBS)
+libknotd_la-cf-parse.h: libknotd_la-cf-parse.c
+ @if test ! -f $@; then \
+ rm -f libknotd_la-cf-parse.c; \
+ $(MAKE) $(AM_MAKEFLAGS) libknotd_la-cf-parse.c; \
+ else :; fi
+libknotd.la: $(libknotd_la_OBJECTS) $(libknotd_la_DEPENDENCIES)
+ $(LINK) $(libknotd_la_OBJECTS) $(libknotd_la_LIBADD) $(LIBS)
+libknots.la: $(libknots_la_OBJECTS) $(libknots_la_DEPENDENCIES)
+ $(LINK) $(libknots_la_OBJECTS) $(libknots_la_LIBADD) $(LIBS)
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+zparser.h: zparser.c
+ @if test ! -f $@; then \
+ rm -f zparser.c; \
+ $(MAKE) $(AM_MAKEFLAGS) zparser.c; \
+ else :; fi
+knot-zcompile$(EXEEXT): $(knot_zcompile_OBJECTS) $(knot_zcompile_DEPENDENCIES)
+ @rm -f knot-zcompile$(EXEEXT)
+ $(LINK) $(knot_zcompile_OBJECTS) $(knot_zcompile_LDADD) $(LIBS)
+knotc$(EXEEXT): $(knotc_OBJECTS) $(knotc_DEPENDENCIES)
+ @rm -f knotc$(EXEEXT)
+ $(LINK) $(knotc_OBJECTS) $(knotc_LDADD) $(LIBS)
+knotd$(EXEEXT): $(knotd_OBJECTS) $(knotd_DEPENDENCIES)
+ @rm -f knotd$(EXEEXT)
+ $(LINK) $(knotd_OBJECTS) $(knotd_LDADD) $(LIBS)
+unittests$(EXEEXT): $(unittests_OBJECTS) $(unittests_DEPENDENCIES)
+ @rm -f unittests$(EXEEXT)
+ $(LINK) $(unittests_OBJECTS) $(unittests_LDADD) $(LIBS)
+unittests-libknot$(EXEEXT): $(unittests_libknot_OBJECTS) $(unittests_libknot_DEPENDENCIES)
+ @rm -f unittests-libknot$(EXEEXT)
+ $(LINK) $(unittests_libknot_OBJECTS) $(unittests_libknot_LDADD) $(LIBS)
+unittests-libknot-realdata$(EXEEXT): $(unittests_libknot_realdata_OBJECTS) $(unittests_libknot_realdata_DEPENDENCIES)
+ @rm -f unittests-libknot-realdata$(EXEEXT)
+ $(LINK) $(unittests_libknot_realdata_OBJECTS) $(unittests_libknot_realdata_LDADD) $(LIBS)
+unittests-zcompile$(EXEEXT): $(unittests_zcompile_OBJECTS) $(unittests_zcompile_DEPENDENCIES)
+ @rm -f unittests-zcompile$(EXEEXT)
+ $(LINK) $(unittests_zcompile_OBJECTS) $(unittests_zcompile_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WELL1024a.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32hex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/changesets.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo-hash-table.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/da_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddns.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname-table.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname_table_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamic-array.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/events_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/evqueue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/evsched.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdset.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdset_epoll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdset_kqueue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdset_poll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdset_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gatherer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general-tree.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-functions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knotc_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libknot_error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libknot_tests_loader_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libknotd_la-cf-lex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libknotd_la-cf-parse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lists.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logconf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name-server.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsec3.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsec3_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser-descriptor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/query.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/query_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rdata.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rdata_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rdata_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ref.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/response_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skip-list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skiplist_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slab.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slab_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockaddr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcp-handler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tolower.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig-op.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp-handler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_libknot.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_libknot_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_zp_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/universal-system.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfr-handler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfr-in.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile-error.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zlexer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-contents.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump-text.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-load.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-tree.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone_tree_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zonedb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zonedb_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zonedb_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zones.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zparser.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libknot_error.lo: libknot/util/libknot_error.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot_error.lo -MD -MP -MF $(DEPDIR)/libknot_error.Tpo -c -o libknot_error.lo `test -f 'libknot/util/libknot_error.c' || echo '$(srcdir)/'`libknot/util/libknot_error.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libknot_error.Tpo $(DEPDIR)/libknot_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/libknot_error.c' object='libknot_error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot_error.lo `test -f 'libknot/util/libknot_error.c' || echo '$(srcdir)/'`libknot/util/libknot_error.c
+
+utils.lo: libknot/util/utils.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils.lo -MD -MP -MF $(DEPDIR)/utils.Tpo -c -o utils.lo `test -f 'libknot/util/utils.c' || echo '$(srcdir)/'`libknot/util/utils.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/utils.Tpo $(DEPDIR)/utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/utils.c' object='utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils.lo `test -f 'libknot/util/utils.c' || echo '$(srcdir)/'`libknot/util/utils.c
+
+debug.lo: libknot/util/debug.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT debug.lo -MD -MP -MF $(DEPDIR)/debug.Tpo -c -o debug.lo `test -f 'libknot/util/debug.c' || echo '$(srcdir)/'`libknot/util/debug.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/debug.Tpo $(DEPDIR)/debug.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/debug.c' object='debug.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o debug.lo `test -f 'libknot/util/debug.c' || echo '$(srcdir)/'`libknot/util/debug.c
+
+descriptor.lo: libknot/util/descriptor.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT descriptor.lo -MD -MP -MF $(DEPDIR)/descriptor.Tpo -c -o descriptor.lo `test -f 'libknot/util/descriptor.c' || echo '$(srcdir)/'`libknot/util/descriptor.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/descriptor.Tpo $(DEPDIR)/descriptor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/descriptor.c' object='descriptor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o descriptor.lo `test -f 'libknot/util/descriptor.c' || echo '$(srcdir)/'`libknot/util/descriptor.c
+
+tolower.lo: libknot/util/tolower.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tolower.lo -MD -MP -MF $(DEPDIR)/tolower.Tpo -c -o tolower.lo `test -f 'libknot/util/tolower.c' || echo '$(srcdir)/'`libknot/util/tolower.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tolower.Tpo $(DEPDIR)/tolower.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/tolower.c' object='tolower.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tolower.lo `test -f 'libknot/util/tolower.c' || echo '$(srcdir)/'`libknot/util/tolower.c
+
+query.lo: libknot/packet/query.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT query.lo -MD -MP -MF $(DEPDIR)/query.Tpo -c -o query.lo `test -f 'libknot/packet/query.c' || echo '$(srcdir)/'`libknot/packet/query.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/query.Tpo $(DEPDIR)/query.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/packet/query.c' object='query.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o query.lo `test -f 'libknot/packet/query.c' || echo '$(srcdir)/'`libknot/packet/query.c
+
+response.lo: libknot/packet/response.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT response.lo -MD -MP -MF $(DEPDIR)/response.Tpo -c -o response.lo `test -f 'libknot/packet/response.c' || echo '$(srcdir)/'`libknot/packet/response.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/response.Tpo $(DEPDIR)/response.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/packet/response.c' object='response.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o response.lo `test -f 'libknot/packet/response.c' || echo '$(srcdir)/'`libknot/packet/response.c
+
+packet.lo: libknot/packet/packet.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet.lo -MD -MP -MF $(DEPDIR)/packet.Tpo -c -o packet.lo `test -f 'libknot/packet/packet.c' || echo '$(srcdir)/'`libknot/packet/packet.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/packet.Tpo $(DEPDIR)/packet.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/packet/packet.c' object='packet.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet.lo `test -f 'libknot/packet/packet.c' || echo '$(srcdir)/'`libknot/packet/packet.c
+
+zone.lo: libknot/zone/zone.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone.lo -MD -MP -MF $(DEPDIR)/zone.Tpo -c -o zone.lo `test -f 'libknot/zone/zone.c' || echo '$(srcdir)/'`libknot/zone/zone.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone.Tpo $(DEPDIR)/zone.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zone.c' object='zone.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone.lo `test -f 'libknot/zone/zone.c' || echo '$(srcdir)/'`libknot/zone/zone.c
+
+zone-contents.lo: libknot/zone/zone-contents.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-contents.lo -MD -MP -MF $(DEPDIR)/zone-contents.Tpo -c -o zone-contents.lo `test -f 'libknot/zone/zone-contents.c' || echo '$(srcdir)/'`libknot/zone/zone-contents.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-contents.Tpo $(DEPDIR)/zone-contents.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zone-contents.c' object='zone-contents.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-contents.lo `test -f 'libknot/zone/zone-contents.c' || echo '$(srcdir)/'`libknot/zone/zone-contents.c
+
+zone-tree.lo: libknot/zone/zone-tree.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-tree.lo -MD -MP -MF $(DEPDIR)/zone-tree.Tpo -c -o zone-tree.lo `test -f 'libknot/zone/zone-tree.c' || echo '$(srcdir)/'`libknot/zone/zone-tree.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-tree.Tpo $(DEPDIR)/zone-tree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zone-tree.c' object='zone-tree.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-tree.lo `test -f 'libknot/zone/zone-tree.c' || echo '$(srcdir)/'`libknot/zone/zone-tree.c
+
+zonedb.lo: libknot/zone/zonedb.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zonedb.lo -MD -MP -MF $(DEPDIR)/zonedb.Tpo -c -o zonedb.lo `test -f 'libknot/zone/zonedb.c' || echo '$(srcdir)/'`libknot/zone/zonedb.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zonedb.Tpo $(DEPDIR)/zonedb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zonedb.c' object='zonedb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zonedb.lo `test -f 'libknot/zone/zonedb.c' || echo '$(srcdir)/'`libknot/zone/zonedb.c
+
+node.lo: libknot/zone/node.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT node.lo -MD -MP -MF $(DEPDIR)/node.Tpo -c -o node.lo `test -f 'libknot/zone/node.c' || echo '$(srcdir)/'`libknot/zone/node.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/node.Tpo $(DEPDIR)/node.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/node.c' object='node.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o node.lo `test -f 'libknot/zone/node.c' || echo '$(srcdir)/'`libknot/zone/node.c
+
+dname-table.lo: libknot/zone/dname-table.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname-table.lo -MD -MP -MF $(DEPDIR)/dname-table.Tpo -c -o dname-table.lo `test -f 'libknot/zone/dname-table.c' || echo '$(srcdir)/'`libknot/zone/dname-table.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname-table.Tpo $(DEPDIR)/dname-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/dname-table.c' object='dname-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname-table.lo `test -f 'libknot/zone/dname-table.c' || echo '$(srcdir)/'`libknot/zone/dname-table.c
+
+hash-functions.lo: libknot/hash/hash-functions.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hash-functions.lo -MD -MP -MF $(DEPDIR)/hash-functions.Tpo -c -o hash-functions.lo `test -f 'libknot/hash/hash-functions.c' || echo '$(srcdir)/'`libknot/hash/hash-functions.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hash-functions.Tpo $(DEPDIR)/hash-functions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/hash/hash-functions.c' object='hash-functions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hash-functions.lo `test -f 'libknot/hash/hash-functions.c' || echo '$(srcdir)/'`libknot/hash/hash-functions.c
+
+cuckoo-hash-table.lo: libknot/hash/cuckoo-hash-table.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cuckoo-hash-table.lo -MD -MP -MF $(DEPDIR)/cuckoo-hash-table.Tpo -c -o cuckoo-hash-table.lo `test -f 'libknot/hash/cuckoo-hash-table.c' || echo '$(srcdir)/'`libknot/hash/cuckoo-hash-table.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/cuckoo-hash-table.Tpo $(DEPDIR)/cuckoo-hash-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/hash/cuckoo-hash-table.c' object='cuckoo-hash-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cuckoo-hash-table.lo `test -f 'libknot/hash/cuckoo-hash-table.c' || echo '$(srcdir)/'`libknot/hash/cuckoo-hash-table.c
+
+universal-system.lo: libknot/hash/universal-system.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT universal-system.lo -MD -MP -MF $(DEPDIR)/universal-system.Tpo -c -o universal-system.lo `test -f 'libknot/hash/universal-system.c' || echo '$(srcdir)/'`libknot/hash/universal-system.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/universal-system.Tpo $(DEPDIR)/universal-system.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/hash/universal-system.c' object='universal-system.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o universal-system.lo `test -f 'libknot/hash/universal-system.c' || echo '$(srcdir)/'`libknot/hash/universal-system.c
+
+name-server.lo: libknot/nameserver/name-server.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT name-server.lo -MD -MP -MF $(DEPDIR)/name-server.Tpo -c -o name-server.lo `test -f 'libknot/nameserver/name-server.c' || echo '$(srcdir)/'`libknot/nameserver/name-server.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/name-server.Tpo $(DEPDIR)/name-server.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/nameserver/name-server.c' object='name-server.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o name-server.lo `test -f 'libknot/nameserver/name-server.c' || echo '$(srcdir)/'`libknot/nameserver/name-server.c
+
+changesets.lo: libknot/updates/changesets.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT changesets.lo -MD -MP -MF $(DEPDIR)/changesets.Tpo -c -o changesets.lo `test -f 'libknot/updates/changesets.c' || echo '$(srcdir)/'`libknot/updates/changesets.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/changesets.Tpo $(DEPDIR)/changesets.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/updates/changesets.c' object='changesets.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o changesets.lo `test -f 'libknot/updates/changesets.c' || echo '$(srcdir)/'`libknot/updates/changesets.c
+
+xfr-in.lo: libknot/updates/xfr-in.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT xfr-in.lo -MD -MP -MF $(DEPDIR)/xfr-in.Tpo -c -o xfr-in.lo `test -f 'libknot/updates/xfr-in.c' || echo '$(srcdir)/'`libknot/updates/xfr-in.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/xfr-in.Tpo $(DEPDIR)/xfr-in.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/updates/xfr-in.c' object='xfr-in.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xfr-in.lo `test -f 'libknot/updates/xfr-in.c' || echo '$(srcdir)/'`libknot/updates/xfr-in.c
+
+ddns.lo: libknot/updates/ddns.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ddns.lo -MD -MP -MF $(DEPDIR)/ddns.Tpo -c -o ddns.lo `test -f 'libknot/updates/ddns.c' || echo '$(srcdir)/'`libknot/updates/ddns.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ddns.Tpo $(DEPDIR)/ddns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/updates/ddns.c' object='ddns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ddns.lo `test -f 'libknot/updates/ddns.c' || echo '$(srcdir)/'`libknot/updates/ddns.c
+
+edns.lo: libknot/edns.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT edns.lo -MD -MP -MF $(DEPDIR)/edns.Tpo -c -o edns.lo `test -f 'libknot/edns.c' || echo '$(srcdir)/'`libknot/edns.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/edns.Tpo $(DEPDIR)/edns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/edns.c' object='edns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o edns.lo `test -f 'libknot/edns.c' || echo '$(srcdir)/'`libknot/edns.c
+
+rrset.lo: libknot/rrset.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrset.lo -MD -MP -MF $(DEPDIR)/rrset.Tpo -c -o rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrset.Tpo $(DEPDIR)/rrset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/rrset.c' object='rrset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c
+
+dname.lo: libknot/dname.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname.lo -MD -MP -MF $(DEPDIR)/dname.Tpo -c -o dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname.Tpo $(DEPDIR)/dname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/dname.c' object='dname.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c
+
+rdata.lo: libknot/rdata.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdata.lo -MD -MP -MF $(DEPDIR)/rdata.Tpo -c -o rdata.lo `test -f 'libknot/rdata.c' || echo '$(srcdir)/'`libknot/rdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rdata.Tpo $(DEPDIR)/rdata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/rdata.c' object='rdata.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdata.lo `test -f 'libknot/rdata.c' || echo '$(srcdir)/'`libknot/rdata.c
+
+nsec3.lo: libknot/nsec3.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nsec3.lo -MD -MP -MF $(DEPDIR)/nsec3.Tpo -c -o nsec3.lo `test -f 'libknot/nsec3.c' || echo '$(srcdir)/'`libknot/nsec3.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nsec3.Tpo $(DEPDIR)/nsec3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/nsec3.c' object='nsec3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nsec3.lo `test -f 'libknot/nsec3.c' || echo '$(srcdir)/'`libknot/nsec3.c
+
+tsig.lo: libknot/tsig.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tsig.lo -MD -MP -MF $(DEPDIR)/tsig.Tpo -c -o tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsig.Tpo $(DEPDIR)/tsig.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/tsig.c' object='tsig.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c
+
+tsig-op.lo: libknot/tsig-op.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tsig-op.lo -MD -MP -MF $(DEPDIR)/tsig-op.Tpo -c -o tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsig-op.Tpo $(DEPDIR)/tsig-op.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/tsig-op.c' object='tsig-op.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c
+
+gatherer.lo: knot/stat/gatherer.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gatherer.lo -MD -MP -MF $(DEPDIR)/gatherer.Tpo -c -o gatherer.lo `test -f 'knot/stat/gatherer.c' || echo '$(srcdir)/'`knot/stat/gatherer.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/gatherer.Tpo $(DEPDIR)/gatherer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/stat/gatherer.c' object='gatherer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gatherer.lo `test -f 'knot/stat/gatherer.c' || echo '$(srcdir)/'`knot/stat/gatherer.c
+
+stat.lo: knot/stat/stat.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT stat.lo -MD -MP -MF $(DEPDIR)/stat.Tpo -c -o stat.lo `test -f 'knot/stat/stat.c' || echo '$(srcdir)/'`knot/stat/stat.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/stat.Tpo $(DEPDIR)/stat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/stat/stat.c' object='stat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stat.lo `test -f 'knot/stat/stat.c' || echo '$(srcdir)/'`knot/stat/stat.c
+
+log.lo: knot/other/log.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT log.lo -MD -MP -MF $(DEPDIR)/log.Tpo -c -o log.lo `test -f 'knot/other/log.c' || echo '$(srcdir)/'`knot/other/log.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/log.Tpo $(DEPDIR)/log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/other/log.c' object='log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o log.lo `test -f 'knot/other/log.c' || echo '$(srcdir)/'`knot/other/log.c
+
+error.lo: knot/other/error.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT error.lo -MD -MP -MF $(DEPDIR)/error.Tpo -c -o error.lo `test -f 'knot/other/error.c' || echo '$(srcdir)/'`knot/other/error.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/error.Tpo $(DEPDIR)/error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/other/error.c' object='error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o error.lo `test -f 'knot/other/error.c' || echo '$(srcdir)/'`knot/other/error.c
+
+conf.lo: knot/conf/conf.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conf.lo -MD -MP -MF $(DEPDIR)/conf.Tpo -c -o conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conf.Tpo $(DEPDIR)/conf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/conf/conf.c' object='conf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c
+
+logconf.lo: knot/conf/logconf.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT logconf.lo -MD -MP -MF $(DEPDIR)/logconf.Tpo -c -o logconf.lo `test -f 'knot/conf/logconf.c' || echo '$(srcdir)/'`knot/conf/logconf.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/logconf.Tpo $(DEPDIR)/logconf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/conf/logconf.c' object='logconf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o logconf.lo `test -f 'knot/conf/logconf.c' || echo '$(srcdir)/'`knot/conf/logconf.c
+
+process.lo: knot/ctl/process.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT process.lo -MD -MP -MF $(DEPDIR)/process.Tpo -c -o process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/process.Tpo $(DEPDIR)/process.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/ctl/process.c' object='process.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c
+
+dthreads.lo: knot/server/dthreads.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dthreads.lo -MD -MP -MF $(DEPDIR)/dthreads.Tpo -c -o dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dthreads.Tpo $(DEPDIR)/dthreads.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/dthreads.c' object='dthreads.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c
+
+journal.lo: knot/server/journal.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT journal.lo -MD -MP -MF $(DEPDIR)/journal.Tpo -c -o journal.lo `test -f 'knot/server/journal.c' || echo '$(srcdir)/'`knot/server/journal.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/journal.Tpo $(DEPDIR)/journal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/journal.c' object='journal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o journal.lo `test -f 'knot/server/journal.c' || echo '$(srcdir)/'`knot/server/journal.c
+
+socket.lo: knot/server/socket.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket.lo -MD -MP -MF $(DEPDIR)/socket.Tpo -c -o socket.lo `test -f 'knot/server/socket.c' || echo '$(srcdir)/'`knot/server/socket.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/socket.Tpo $(DEPDIR)/socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/socket.c' object='socket.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket.lo `test -f 'knot/server/socket.c' || echo '$(srcdir)/'`knot/server/socket.c
+
+server.lo: knot/server/server.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT server.lo -MD -MP -MF $(DEPDIR)/server.Tpo -c -o server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/server.Tpo $(DEPDIR)/server.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/server.c' object='server.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c
+
+udp-handler.lo: knot/server/udp-handler.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT udp-handler.lo -MD -MP -MF $(DEPDIR)/udp-handler.Tpo -c -o udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/udp-handler.Tpo $(DEPDIR)/udp-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/udp-handler.c' object='udp-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c
+
+tcp-handler.lo: knot/server/tcp-handler.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tcp-handler.lo -MD -MP -MF $(DEPDIR)/tcp-handler.Tpo -c -o tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tcp-handler.Tpo $(DEPDIR)/tcp-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/tcp-handler.c' object='tcp-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c
+
+xfr-handler.lo: knot/server/xfr-handler.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT xfr-handler.lo -MD -MP -MF $(DEPDIR)/xfr-handler.Tpo -c -o xfr-handler.lo `test -f 'knot/server/xfr-handler.c' || echo '$(srcdir)/'`knot/server/xfr-handler.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/xfr-handler.Tpo $(DEPDIR)/xfr-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/xfr-handler.c' object='xfr-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xfr-handler.lo `test -f 'knot/server/xfr-handler.c' || echo '$(srcdir)/'`knot/server/xfr-handler.c
+
+zones.lo: knot/server/zones.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zones.lo -MD -MP -MF $(DEPDIR)/zones.Tpo -c -o zones.lo `test -f 'knot/server/zones.c' || echo '$(srcdir)/'`knot/server/zones.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zones.Tpo $(DEPDIR)/zones.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/zones.c' object='zones.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zones.lo `test -f 'knot/server/zones.c' || echo '$(srcdir)/'`knot/server/zones.c
+
+notify.lo: knot/server/notify.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT notify.lo -MD -MP -MF $(DEPDIR)/notify.Tpo -c -o notify.lo `test -f 'knot/server/notify.c' || echo '$(srcdir)/'`knot/server/notify.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/notify.Tpo $(DEPDIR)/notify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/server/notify.c' object='notify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify.lo `test -f 'knot/server/notify.c' || echo '$(srcdir)/'`knot/server/notify.c
+
+zone-load.lo: knot/zone/zone-load.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-load.lo -MD -MP -MF $(DEPDIR)/zone-load.Tpo -c -o zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-load.Tpo $(DEPDIR)/zone-load.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/zone/zone-load.c' object='zone-load.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
+
+zone-dump.lo: knot/zone/zone-dump.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-dump.lo -MD -MP -MF $(DEPDIR)/zone-dump.Tpo -c -o zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-dump.Tpo $(DEPDIR)/zone-dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/zone/zone-dump.c' object='zone-dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c
+
+zone-dump-text.lo: knot/zone/zone-dump-text.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-dump-text.lo -MD -MP -MF $(DEPDIR)/zone-dump-text.Tpo -c -o zone-dump-text.lo `test -f 'knot/zone/zone-dump-text.c' || echo '$(srcdir)/'`knot/zone/zone-dump-text.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-dump-text.Tpo $(DEPDIR)/zone-dump-text.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/zone/zone-dump-text.c' object='zone-dump-text.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-dump-text.lo `test -f 'knot/zone/zone-dump-text.c' || echo '$(srcdir)/'`knot/zone/zone-dump-text.c
+
+slab.lo: common/slab/slab.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT slab.lo -MD -MP -MF $(DEPDIR)/slab.Tpo -c -o slab.lo `test -f 'common/slab/slab.c' || echo '$(srcdir)/'`common/slab/slab.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/slab.Tpo $(DEPDIR)/slab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/slab/slab.c' object='slab.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o slab.lo `test -f 'common/slab/slab.c' || echo '$(srcdir)/'`common/slab/slab.c
+
+malloc.lo: common/slab/malloc.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT malloc.lo -MD -MP -MF $(DEPDIR)/malloc.Tpo -c -o malloc.lo `test -f 'common/slab/malloc.c' || echo '$(srcdir)/'`common/slab/malloc.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/malloc.Tpo $(DEPDIR)/malloc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/slab/malloc.c' object='malloc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o malloc.lo `test -f 'common/slab/malloc.c' || echo '$(srcdir)/'`common/slab/malloc.c
+
+tap.lo: common/libtap/tap.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tap.lo -MD -MP -MF $(DEPDIR)/tap.Tpo -c -o tap.lo `test -f 'common/libtap/tap.c' || echo '$(srcdir)/'`common/libtap/tap.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tap.Tpo $(DEPDIR)/tap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/libtap/tap.c' object='tap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tap.lo `test -f 'common/libtap/tap.c' || echo '$(srcdir)/'`common/libtap/tap.c
+
+lists.lo: common/lists.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lists.lo -MD -MP -MF $(DEPDIR)/lists.Tpo -c -o lists.lo `test -f 'common/lists.c' || echo '$(srcdir)/'`common/lists.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/lists.Tpo $(DEPDIR)/lists.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/lists.c' object='lists.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lists.lo `test -f 'common/lists.c' || echo '$(srcdir)/'`common/lists.c
+
+base32.lo: common/base32.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT base32.lo -MD -MP -MF $(DEPDIR)/base32.Tpo -c -o base32.lo `test -f 'common/base32.c' || echo '$(srcdir)/'`common/base32.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/base32.Tpo $(DEPDIR)/base32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/base32.c' object='base32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o base32.lo `test -f 'common/base32.c' || echo '$(srcdir)/'`common/base32.c
+
+print.lo: common/print.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT print.lo -MD -MP -MF $(DEPDIR)/print.Tpo -c -o print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/print.Tpo $(DEPDIR)/print.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/print.c' object='print.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c
+
+dynamic-array.lo: common/dynamic-array.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dynamic-array.lo -MD -MP -MF $(DEPDIR)/dynamic-array.Tpo -c -o dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dynamic-array.Tpo $(DEPDIR)/dynamic-array.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/dynamic-array.c' object='dynamic-array.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c
+
+skip-list.lo: common/skip-list.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT skip-list.lo -MD -MP -MF $(DEPDIR)/skip-list.Tpo -c -o skip-list.lo `test -f 'common/skip-list.c' || echo '$(srcdir)/'`common/skip-list.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/skip-list.Tpo $(DEPDIR)/skip-list.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/skip-list.c' object='skip-list.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o skip-list.lo `test -f 'common/skip-list.c' || echo '$(srcdir)/'`common/skip-list.c
+
+base32hex.lo: common/base32hex.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT base32hex.lo -MD -MP -MF $(DEPDIR)/base32hex.Tpo -c -o base32hex.lo `test -f 'common/base32hex.c' || echo '$(srcdir)/'`common/base32hex.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/base32hex.Tpo $(DEPDIR)/base32hex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/base32hex.c' object='base32hex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o base32hex.lo `test -f 'common/base32hex.c' || echo '$(srcdir)/'`common/base32hex.c
+
+general-tree.lo: common/general-tree.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT general-tree.lo -MD -MP -MF $(DEPDIR)/general-tree.Tpo -c -o general-tree.lo `test -f 'common/general-tree.c' || echo '$(srcdir)/'`common/general-tree.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/general-tree.Tpo $(DEPDIR)/general-tree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/general-tree.c' object='general-tree.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o general-tree.lo `test -f 'common/general-tree.c' || echo '$(srcdir)/'`common/general-tree.c
+
+evqueue.lo: common/evqueue.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT evqueue.lo -MD -MP -MF $(DEPDIR)/evqueue.Tpo -c -o evqueue.lo `test -f 'common/evqueue.c' || echo '$(srcdir)/'`common/evqueue.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/evqueue.Tpo $(DEPDIR)/evqueue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/evqueue.c' object='evqueue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o evqueue.lo `test -f 'common/evqueue.c' || echo '$(srcdir)/'`common/evqueue.c
+
+evsched.lo: common/evsched.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT evsched.lo -MD -MP -MF $(DEPDIR)/evsched.Tpo -c -o evsched.lo `test -f 'common/evsched.c' || echo '$(srcdir)/'`common/evsched.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/evsched.Tpo $(DEPDIR)/evsched.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/evsched.c' object='evsched.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o evsched.lo `test -f 'common/evsched.c' || echo '$(srcdir)/'`common/evsched.c
+
+acl.lo: common/acl.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acl.lo -MD -MP -MF $(DEPDIR)/acl.Tpo -c -o acl.lo `test -f 'common/acl.c' || echo '$(srcdir)/'`common/acl.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/acl.Tpo $(DEPDIR)/acl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/acl.c' object='acl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acl.lo `test -f 'common/acl.c' || echo '$(srcdir)/'`common/acl.c
+
+sockaddr.lo: common/sockaddr.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sockaddr.lo -MD -MP -MF $(DEPDIR)/sockaddr.Tpo -c -o sockaddr.lo `test -f 'common/sockaddr.c' || echo '$(srcdir)/'`common/sockaddr.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/sockaddr.Tpo $(DEPDIR)/sockaddr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/sockaddr.c' object='sockaddr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sockaddr.lo `test -f 'common/sockaddr.c' || echo '$(srcdir)/'`common/sockaddr.c
+
+crc.lo: common/crc.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT crc.lo -MD -MP -MF $(DEPDIR)/crc.Tpo -c -o crc.lo `test -f 'common/crc.c' || echo '$(srcdir)/'`common/crc.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/crc.Tpo $(DEPDIR)/crc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/crc.c' object='crc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o crc.lo `test -f 'common/crc.c' || echo '$(srcdir)/'`common/crc.c
+
+ref.lo: common/ref.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ref.lo -MD -MP -MF $(DEPDIR)/ref.Tpo -c -o ref.lo `test -f 'common/ref.c' || echo '$(srcdir)/'`common/ref.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ref.Tpo $(DEPDIR)/ref.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/ref.c' object='ref.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ref.lo `test -f 'common/ref.c' || echo '$(srcdir)/'`common/ref.c
+
+errors.lo: common/errors.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT errors.lo -MD -MP -MF $(DEPDIR)/errors.Tpo -c -o errors.lo `test -f 'common/errors.c' || echo '$(srcdir)/'`common/errors.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/errors.Tpo $(DEPDIR)/errors.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/errors.c' object='errors.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o errors.lo `test -f 'common/errors.c' || echo '$(srcdir)/'`common/errors.c
+
+WELL1024a.lo: common/WELL1024a.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT WELL1024a.lo -MD -MP -MF $(DEPDIR)/WELL1024a.Tpo -c -o WELL1024a.lo `test -f 'common/WELL1024a.c' || echo '$(srcdir)/'`common/WELL1024a.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/WELL1024a.Tpo $(DEPDIR)/WELL1024a.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/WELL1024a.c' object='WELL1024a.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o WELL1024a.lo `test -f 'common/WELL1024a.c' || echo '$(srcdir)/'`common/WELL1024a.c
+
+fdset.lo: common/fdset.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset.lo -MD -MP -MF $(DEPDIR)/fdset.Tpo -c -o fdset.lo `test -f 'common/fdset.c' || echo '$(srcdir)/'`common/fdset.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset.Tpo $(DEPDIR)/fdset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/fdset.c' object='fdset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset.lo `test -f 'common/fdset.c' || echo '$(srcdir)/'`common/fdset.c
+
+fdset_poll.lo: common/fdset_poll.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset_poll.lo -MD -MP -MF $(DEPDIR)/fdset_poll.Tpo -c -o fdset_poll.lo `test -f 'common/fdset_poll.c' || echo '$(srcdir)/'`common/fdset_poll.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset_poll.Tpo $(DEPDIR)/fdset_poll.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/fdset_poll.c' object='fdset_poll.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset_poll.lo `test -f 'common/fdset_poll.c' || echo '$(srcdir)/'`common/fdset_poll.c
+
+fdset_kqueue.lo: common/fdset_kqueue.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset_kqueue.lo -MD -MP -MF $(DEPDIR)/fdset_kqueue.Tpo -c -o fdset_kqueue.lo `test -f 'common/fdset_kqueue.c' || echo '$(srcdir)/'`common/fdset_kqueue.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset_kqueue.Tpo $(DEPDIR)/fdset_kqueue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/fdset_kqueue.c' object='fdset_kqueue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset_kqueue.lo `test -f 'common/fdset_kqueue.c' || echo '$(srcdir)/'`common/fdset_kqueue.c
+
+fdset_epoll.lo: common/fdset_epoll.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset_epoll.lo -MD -MP -MF $(DEPDIR)/fdset_epoll.Tpo -c -o fdset_epoll.lo `test -f 'common/fdset_epoll.c' || echo '$(srcdir)/'`common/fdset_epoll.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset_epoll.Tpo $(DEPDIR)/fdset_epoll.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/fdset_epoll.c' object='fdset_epoll.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset_epoll.lo `test -f 'common/fdset_epoll.c' || echo '$(srcdir)/'`common/fdset_epoll.c
+
+zcompile_main.o: zcompile/zcompile_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile_main.o -MD -MP -MF $(DEPDIR)/zcompile_main.Tpo -c -o zcompile_main.o `test -f 'zcompile/zcompile_main.c' || echo '$(srcdir)/'`zcompile/zcompile_main.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile_main.Tpo $(DEPDIR)/zcompile_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile_main.c' object='zcompile_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile_main.o `test -f 'zcompile/zcompile_main.c' || echo '$(srcdir)/'`zcompile/zcompile_main.c
+
+zcompile_main.obj: zcompile/zcompile_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile_main.obj -MD -MP -MF $(DEPDIR)/zcompile_main.Tpo -c -o zcompile_main.obj `if test -f 'zcompile/zcompile_main.c'; then $(CYGPATH_W) 'zcompile/zcompile_main.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile_main.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile_main.Tpo $(DEPDIR)/zcompile_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile_main.c' object='zcompile_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile_main.obj `if test -f 'zcompile/zcompile_main.c'; then $(CYGPATH_W) 'zcompile/zcompile_main.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile_main.c'; fi`
+
+zcompile-error.o: zcompile/zcompile-error.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile-error.o -MD -MP -MF $(DEPDIR)/zcompile-error.Tpo -c -o zcompile-error.o `test -f 'zcompile/zcompile-error.c' || echo '$(srcdir)/'`zcompile/zcompile-error.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile-error.Tpo $(DEPDIR)/zcompile-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile-error.c' object='zcompile-error.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile-error.o `test -f 'zcompile/zcompile-error.c' || echo '$(srcdir)/'`zcompile/zcompile-error.c
+
+zcompile-error.obj: zcompile/zcompile-error.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile-error.obj -MD -MP -MF $(DEPDIR)/zcompile-error.Tpo -c -o zcompile-error.obj `if test -f 'zcompile/zcompile-error.c'; then $(CYGPATH_W) 'zcompile/zcompile-error.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile-error.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile-error.Tpo $(DEPDIR)/zcompile-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile-error.c' object='zcompile-error.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile-error.obj `if test -f 'zcompile/zcompile-error.c'; then $(CYGPATH_W) 'zcompile/zcompile-error.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile-error.c'; fi`
+
+zcompile.o: zcompile/zcompile.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile.o -MD -MP -MF $(DEPDIR)/zcompile.Tpo -c -o zcompile.o `test -f 'zcompile/zcompile.c' || echo '$(srcdir)/'`zcompile/zcompile.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile.Tpo $(DEPDIR)/zcompile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile.c' object='zcompile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile.o `test -f 'zcompile/zcompile.c' || echo '$(srcdir)/'`zcompile/zcompile.c
+
+zcompile.obj: zcompile/zcompile.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zcompile.obj -MD -MP -MF $(DEPDIR)/zcompile.Tpo -c -o zcompile.obj `if test -f 'zcompile/zcompile.c'; then $(CYGPATH_W) 'zcompile/zcompile.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zcompile.Tpo $(DEPDIR)/zcompile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/zcompile.c' object='zcompile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zcompile.obj `if test -f 'zcompile/zcompile.c'; then $(CYGPATH_W) 'zcompile/zcompile.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/zcompile.c'; fi`
+
+parser-util.o: zcompile/parser-util.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser-util.o -MD -MP -MF $(DEPDIR)/parser-util.Tpo -c -o parser-util.o `test -f 'zcompile/parser-util.c' || echo '$(srcdir)/'`zcompile/parser-util.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parser-util.Tpo $(DEPDIR)/parser-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/parser-util.c' object='parser-util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser-util.o `test -f 'zcompile/parser-util.c' || echo '$(srcdir)/'`zcompile/parser-util.c
+
+parser-util.obj: zcompile/parser-util.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser-util.obj -MD -MP -MF $(DEPDIR)/parser-util.Tpo -c -o parser-util.obj `if test -f 'zcompile/parser-util.c'; then $(CYGPATH_W) 'zcompile/parser-util.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/parser-util.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parser-util.Tpo $(DEPDIR)/parser-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/parser-util.c' object='parser-util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser-util.obj `if test -f 'zcompile/parser-util.c'; then $(CYGPATH_W) 'zcompile/parser-util.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/parser-util.c'; fi`
+
+parser-descriptor.o: zcompile/parser-descriptor.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser-descriptor.o -MD -MP -MF $(DEPDIR)/parser-descriptor.Tpo -c -o parser-descriptor.o `test -f 'zcompile/parser-descriptor.c' || echo '$(srcdir)/'`zcompile/parser-descriptor.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parser-descriptor.Tpo $(DEPDIR)/parser-descriptor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/parser-descriptor.c' object='parser-descriptor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser-descriptor.o `test -f 'zcompile/parser-descriptor.c' || echo '$(srcdir)/'`zcompile/parser-descriptor.c
+
+parser-descriptor.obj: zcompile/parser-descriptor.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser-descriptor.obj -MD -MP -MF $(DEPDIR)/parser-descriptor.Tpo -c -o parser-descriptor.obj `if test -f 'zcompile/parser-descriptor.c'; then $(CYGPATH_W) 'zcompile/parser-descriptor.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/parser-descriptor.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parser-descriptor.Tpo $(DEPDIR)/parser-descriptor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/parser-descriptor.c' object='parser-descriptor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser-descriptor.obj `if test -f 'zcompile/parser-descriptor.c'; then $(CYGPATH_W) 'zcompile/parser-descriptor.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/parser-descriptor.c'; fi`
+
+knotc_main.o: knot/ctl/knotc_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotc_main.o -MD -MP -MF $(DEPDIR)/knotc_main.Tpo -c -o knotc_main.o `test -f 'knot/ctl/knotc_main.c' || echo '$(srcdir)/'`knot/ctl/knotc_main.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/knotc_main.Tpo $(DEPDIR)/knotc_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/ctl/knotc_main.c' object='knotc_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotc_main.o `test -f 'knot/ctl/knotc_main.c' || echo '$(srcdir)/'`knot/ctl/knotc_main.c
+
+knotc_main.obj: knot/ctl/knotc_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotc_main.obj -MD -MP -MF $(DEPDIR)/knotc_main.Tpo -c -o knotc_main.obj `if test -f 'knot/ctl/knotc_main.c'; then $(CYGPATH_W) 'knot/ctl/knotc_main.c'; else $(CYGPATH_W) '$(srcdir)/knot/ctl/knotc_main.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/knotc_main.Tpo $(DEPDIR)/knotc_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/ctl/knotc_main.c' object='knotc_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotc_main.obj `if test -f 'knot/ctl/knotc_main.c'; then $(CYGPATH_W) 'knot/ctl/knotc_main.c'; else $(CYGPATH_W) '$(srcdir)/knot/ctl/knotc_main.c'; fi`
+
+main.o: knot/main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT main.o -MD -MP -MF $(DEPDIR)/main.Tpo -c -o main.o `test -f 'knot/main.c' || echo '$(srcdir)/'`knot/main.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/main.Tpo $(DEPDIR)/main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/main.c' object='main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o main.o `test -f 'knot/main.c' || echo '$(srcdir)/'`knot/main.c
+
+main.obj: knot/main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT main.obj -MD -MP -MF $(DEPDIR)/main.Tpo -c -o main.obj `if test -f 'knot/main.c'; then $(CYGPATH_W) 'knot/main.c'; else $(CYGPATH_W) '$(srcdir)/knot/main.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/main.Tpo $(DEPDIR)/main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/main.c' object='main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o main.obj `if test -f 'knot/main.c'; then $(CYGPATH_W) 'knot/main.c'; else $(CYGPATH_W) '$(srcdir)/knot/main.c'; fi`
+
+acl_tests.o: tests/common/acl_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acl_tests.o -MD -MP -MF $(DEPDIR)/acl_tests.Tpo -c -o acl_tests.o `test -f 'tests/common/acl_tests.c' || echo '$(srcdir)/'`tests/common/acl_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/acl_tests.Tpo $(DEPDIR)/acl_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/acl_tests.c' object='acl_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acl_tests.o `test -f 'tests/common/acl_tests.c' || echo '$(srcdir)/'`tests/common/acl_tests.c
+
+acl_tests.obj: tests/common/acl_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acl_tests.obj -MD -MP -MF $(DEPDIR)/acl_tests.Tpo -c -o acl_tests.obj `if test -f 'tests/common/acl_tests.c'; then $(CYGPATH_W) 'tests/common/acl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/acl_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/acl_tests.Tpo $(DEPDIR)/acl_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/acl_tests.c' object='acl_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acl_tests.obj `if test -f 'tests/common/acl_tests.c'; then $(CYGPATH_W) 'tests/common/acl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/acl_tests.c'; fi`
+
+da_tests.o: tests/common/da_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.o -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c
+
+da_tests.obj: tests/common/da_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.obj -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi`
+
+events_tests.o: tests/common/events_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT events_tests.o -MD -MP -MF $(DEPDIR)/events_tests.Tpo -c -o events_tests.o `test -f 'tests/common/events_tests.c' || echo '$(srcdir)/'`tests/common/events_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/events_tests.Tpo $(DEPDIR)/events_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/events_tests.c' object='events_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o events_tests.o `test -f 'tests/common/events_tests.c' || echo '$(srcdir)/'`tests/common/events_tests.c
+
+events_tests.obj: tests/common/events_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT events_tests.obj -MD -MP -MF $(DEPDIR)/events_tests.Tpo -c -o events_tests.obj `if test -f 'tests/common/events_tests.c'; then $(CYGPATH_W) 'tests/common/events_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/events_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/events_tests.Tpo $(DEPDIR)/events_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/events_tests.c' object='events_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o events_tests.obj `if test -f 'tests/common/events_tests.c'; then $(CYGPATH_W) 'tests/common/events_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/events_tests.c'; fi`
+
+skiplist_tests.o: tests/common/skiplist_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT skiplist_tests.o -MD -MP -MF $(DEPDIR)/skiplist_tests.Tpo -c -o skiplist_tests.o `test -f 'tests/common/skiplist_tests.c' || echo '$(srcdir)/'`tests/common/skiplist_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/skiplist_tests.Tpo $(DEPDIR)/skiplist_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/skiplist_tests.c' object='skiplist_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o skiplist_tests.o `test -f 'tests/common/skiplist_tests.c' || echo '$(srcdir)/'`tests/common/skiplist_tests.c
+
+skiplist_tests.obj: tests/common/skiplist_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT skiplist_tests.obj -MD -MP -MF $(DEPDIR)/skiplist_tests.Tpo -c -o skiplist_tests.obj `if test -f 'tests/common/skiplist_tests.c'; then $(CYGPATH_W) 'tests/common/skiplist_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/skiplist_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/skiplist_tests.Tpo $(DEPDIR)/skiplist_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/skiplist_tests.c' object='skiplist_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o skiplist_tests.obj `if test -f 'tests/common/skiplist_tests.c'; then $(CYGPATH_W) 'tests/common/skiplist_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/skiplist_tests.c'; fi`
+
+slab_tests.o: tests/common/slab_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT slab_tests.o -MD -MP -MF $(DEPDIR)/slab_tests.Tpo -c -o slab_tests.o `test -f 'tests/common/slab_tests.c' || echo '$(srcdir)/'`tests/common/slab_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/slab_tests.Tpo $(DEPDIR)/slab_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/slab_tests.c' object='slab_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o slab_tests.o `test -f 'tests/common/slab_tests.c' || echo '$(srcdir)/'`tests/common/slab_tests.c
+
+slab_tests.obj: tests/common/slab_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT slab_tests.obj -MD -MP -MF $(DEPDIR)/slab_tests.Tpo -c -o slab_tests.obj `if test -f 'tests/common/slab_tests.c'; then $(CYGPATH_W) 'tests/common/slab_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/slab_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/slab_tests.Tpo $(DEPDIR)/slab_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/slab_tests.c' object='slab_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o slab_tests.obj `if test -f 'tests/common/slab_tests.c'; then $(CYGPATH_W) 'tests/common/slab_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/slab_tests.c'; fi`
+
+fdset_tests.o: tests/common/fdset_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset_tests.o -MD -MP -MF $(DEPDIR)/fdset_tests.Tpo -c -o fdset_tests.o `test -f 'tests/common/fdset_tests.c' || echo '$(srcdir)/'`tests/common/fdset_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset_tests.Tpo $(DEPDIR)/fdset_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/fdset_tests.c' object='fdset_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset_tests.o `test -f 'tests/common/fdset_tests.c' || echo '$(srcdir)/'`tests/common/fdset_tests.c
+
+fdset_tests.obj: tests/common/fdset_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fdset_tests.obj -MD -MP -MF $(DEPDIR)/fdset_tests.Tpo -c -o fdset_tests.obj `if test -f 'tests/common/fdset_tests.c'; then $(CYGPATH_W) 'tests/common/fdset_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/fdset_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/fdset_tests.Tpo $(DEPDIR)/fdset_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/fdset_tests.c' object='fdset_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fdset_tests.obj `if test -f 'tests/common/fdset_tests.c'; then $(CYGPATH_W) 'tests/common/fdset_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/fdset_tests.c'; fi`
+
+conf_tests.o: tests/knot/conf_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conf_tests.o -MD -MP -MF $(DEPDIR)/conf_tests.Tpo -c -o conf_tests.o `test -f 'tests/knot/conf_tests.c' || echo '$(srcdir)/'`tests/knot/conf_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conf_tests.Tpo $(DEPDIR)/conf_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/conf_tests.c' object='conf_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o conf_tests.o `test -f 'tests/knot/conf_tests.c' || echo '$(srcdir)/'`tests/knot/conf_tests.c
+
+conf_tests.obj: tests/knot/conf_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conf_tests.obj -MD -MP -MF $(DEPDIR)/conf_tests.Tpo -c -o conf_tests.obj `if test -f 'tests/knot/conf_tests.c'; then $(CYGPATH_W) 'tests/knot/conf_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/conf_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conf_tests.Tpo $(DEPDIR)/conf_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/conf_tests.c' object='conf_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o conf_tests.obj `if test -f 'tests/knot/conf_tests.c'; then $(CYGPATH_W) 'tests/knot/conf_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/conf_tests.c'; fi`
+
+dthreads_tests.o: tests/knot/dthreads_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dthreads_tests.o -MD -MP -MF $(DEPDIR)/dthreads_tests.Tpo -c -o dthreads_tests.o `test -f 'tests/knot/dthreads_tests.c' || echo '$(srcdir)/'`tests/knot/dthreads_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dthreads_tests.Tpo $(DEPDIR)/dthreads_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/dthreads_tests.c' object='dthreads_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dthreads_tests.o `test -f 'tests/knot/dthreads_tests.c' || echo '$(srcdir)/'`tests/knot/dthreads_tests.c
+
+dthreads_tests.obj: tests/knot/dthreads_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dthreads_tests.obj -MD -MP -MF $(DEPDIR)/dthreads_tests.Tpo -c -o dthreads_tests.obj `if test -f 'tests/knot/dthreads_tests.c'; then $(CYGPATH_W) 'tests/knot/dthreads_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/dthreads_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dthreads_tests.Tpo $(DEPDIR)/dthreads_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/dthreads_tests.c' object='dthreads_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dthreads_tests.obj `if test -f 'tests/knot/dthreads_tests.c'; then $(CYGPATH_W) 'tests/knot/dthreads_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/dthreads_tests.c'; fi`
+
+journal_tests.o: tests/knot/journal_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT journal_tests.o -MD -MP -MF $(DEPDIR)/journal_tests.Tpo -c -o journal_tests.o `test -f 'tests/knot/journal_tests.c' || echo '$(srcdir)/'`tests/knot/journal_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/journal_tests.Tpo $(DEPDIR)/journal_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/journal_tests.c' object='journal_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o journal_tests.o `test -f 'tests/knot/journal_tests.c' || echo '$(srcdir)/'`tests/knot/journal_tests.c
+
+journal_tests.obj: tests/knot/journal_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT journal_tests.obj -MD -MP -MF $(DEPDIR)/journal_tests.Tpo -c -o journal_tests.obj `if test -f 'tests/knot/journal_tests.c'; then $(CYGPATH_W) 'tests/knot/journal_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/journal_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/journal_tests.Tpo $(DEPDIR)/journal_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/journal_tests.c' object='journal_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o journal_tests.obj `if test -f 'tests/knot/journal_tests.c'; then $(CYGPATH_W) 'tests/knot/journal_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/journal_tests.c'; fi`
+
+server_tests.o: tests/knot/server_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT server_tests.o -MD -MP -MF $(DEPDIR)/server_tests.Tpo -c -o server_tests.o `test -f 'tests/knot/server_tests.c' || echo '$(srcdir)/'`tests/knot/server_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/server_tests.Tpo $(DEPDIR)/server_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/server_tests.c' object='server_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o server_tests.o `test -f 'tests/knot/server_tests.c' || echo '$(srcdir)/'`tests/knot/server_tests.c
+
+server_tests.obj: tests/knot/server_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT server_tests.obj -MD -MP -MF $(DEPDIR)/server_tests.Tpo -c -o server_tests.obj `if test -f 'tests/knot/server_tests.c'; then $(CYGPATH_W) 'tests/knot/server_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/server_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/server_tests.Tpo $(DEPDIR)/server_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/knot/server_tests.c' object='server_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o server_tests.obj `if test -f 'tests/knot/server_tests.c'; then $(CYGPATH_W) 'tests/knot/server_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/knot/server_tests.c'; fi`
+
+unittests_main.o: tests/unittests_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_main.o -MD -MP -MF $(DEPDIR)/unittests_main.Tpo -c -o unittests_main.o `test -f 'tests/unittests_main.c' || echo '$(srcdir)/'`tests/unittests_main.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_main.Tpo $(DEPDIR)/unittests_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/unittests_main.c' object='unittests_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_main.o `test -f 'tests/unittests_main.c' || echo '$(srcdir)/'`tests/unittests_main.c
+
+unittests_main.obj: tests/unittests_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_main.obj -MD -MP -MF $(DEPDIR)/unittests_main.Tpo -c -o unittests_main.obj `if test -f 'tests/unittests_main.c'; then $(CYGPATH_W) 'tests/unittests_main.c'; else $(CYGPATH_W) '$(srcdir)/tests/unittests_main.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_main.Tpo $(DEPDIR)/unittests_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/unittests_main.c' object='unittests_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_main.obj `if test -f 'tests/unittests_main.c'; then $(CYGPATH_W) 'tests/unittests_main.c'; else $(CYGPATH_W) '$(srcdir)/tests/unittests_main.c'; fi`
+
+cuckoo_tests.o: tests/libknot/libknot/cuckoo_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cuckoo_tests.o -MD -MP -MF $(DEPDIR)/cuckoo_tests.Tpo -c -o cuckoo_tests.o `test -f 'tests/libknot/libknot/cuckoo_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/cuckoo_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/cuckoo_tests.Tpo $(DEPDIR)/cuckoo_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/cuckoo_tests.c' object='cuckoo_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cuckoo_tests.o `test -f 'tests/libknot/libknot/cuckoo_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/cuckoo_tests.c
+
+cuckoo_tests.obj: tests/libknot/libknot/cuckoo_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cuckoo_tests.obj -MD -MP -MF $(DEPDIR)/cuckoo_tests.Tpo -c -o cuckoo_tests.obj `if test -f 'tests/libknot/libknot/cuckoo_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/cuckoo_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/cuckoo_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/cuckoo_tests.Tpo $(DEPDIR)/cuckoo_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/cuckoo_tests.c' object='cuckoo_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cuckoo_tests.obj `if test -f 'tests/libknot/libknot/cuckoo_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/cuckoo_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/cuckoo_tests.c'; fi`
+
+response_tests.o: tests/libknot/libknot/response_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT response_tests.o -MD -MP -MF $(DEPDIR)/response_tests.Tpo -c -o response_tests.o `test -f 'tests/libknot/libknot/response_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/response_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/response_tests.Tpo $(DEPDIR)/response_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/response_tests.c' object='response_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o response_tests.o `test -f 'tests/libknot/libknot/response_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/response_tests.c
+
+response_tests.obj: tests/libknot/libknot/response_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT response_tests.obj -MD -MP -MF $(DEPDIR)/response_tests.Tpo -c -o response_tests.obj `if test -f 'tests/libknot/libknot/response_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/response_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/response_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/response_tests.Tpo $(DEPDIR)/response_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/response_tests.c' object='response_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o response_tests.obj `if test -f 'tests/libknot/libknot/response_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/response_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/response_tests.c'; fi`
+
+dname_tests.o: tests/libknot/libknot/dname_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_tests.o -MD -MP -MF $(DEPDIR)/dname_tests.Tpo -c -o dname_tests.o `test -f 'tests/libknot/libknot/dname_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/dname_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_tests.Tpo $(DEPDIR)/dname_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/dname_tests.c' object='dname_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_tests.o `test -f 'tests/libknot/libknot/dname_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/dname_tests.c
+
+dname_tests.obj: tests/libknot/libknot/dname_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_tests.obj -MD -MP -MF $(DEPDIR)/dname_tests.Tpo -c -o dname_tests.obj `if test -f 'tests/libknot/libknot/dname_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/dname_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/dname_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_tests.Tpo $(DEPDIR)/dname_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/dname_tests.c' object='dname_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_tests.obj `if test -f 'tests/libknot/libknot/dname_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/dname_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/dname_tests.c'; fi`
+
+dname_table_tests.o: tests/libknot/libknot/dname_table_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_table_tests.o -MD -MP -MF $(DEPDIR)/dname_table_tests.Tpo -c -o dname_table_tests.o `test -f 'tests/libknot/libknot/dname_table_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/dname_table_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_table_tests.Tpo $(DEPDIR)/dname_table_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/dname_table_tests.c' object='dname_table_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_table_tests.o `test -f 'tests/libknot/libknot/dname_table_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/dname_table_tests.c
+
+dname_table_tests.obj: tests/libknot/libknot/dname_table_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_table_tests.obj -MD -MP -MF $(DEPDIR)/dname_table_tests.Tpo -c -o dname_table_tests.obj `if test -f 'tests/libknot/libknot/dname_table_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/dname_table_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/dname_table_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_table_tests.Tpo $(DEPDIR)/dname_table_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/dname_table_tests.c' object='dname_table_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_table_tests.obj `if test -f 'tests/libknot/libknot/dname_table_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/dname_table_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/dname_table_tests.c'; fi`
+
+nsec3_tests.o: tests/libknot/libknot/nsec3_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nsec3_tests.o -MD -MP -MF $(DEPDIR)/nsec3_tests.Tpo -c -o nsec3_tests.o `test -f 'tests/libknot/libknot/nsec3_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/nsec3_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nsec3_tests.Tpo $(DEPDIR)/nsec3_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/nsec3_tests.c' object='nsec3_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nsec3_tests.o `test -f 'tests/libknot/libknot/nsec3_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/nsec3_tests.c
+
+nsec3_tests.obj: tests/libknot/libknot/nsec3_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nsec3_tests.obj -MD -MP -MF $(DEPDIR)/nsec3_tests.Tpo -c -o nsec3_tests.obj `if test -f 'tests/libknot/libknot/nsec3_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/nsec3_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/nsec3_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nsec3_tests.Tpo $(DEPDIR)/nsec3_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/nsec3_tests.c' object='nsec3_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nsec3_tests.obj `if test -f 'tests/libknot/libknot/nsec3_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/nsec3_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/nsec3_tests.c'; fi`
+
+packet_tests.o: tests/libknot/libknot/packet_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet_tests.o -MD -MP -MF $(DEPDIR)/packet_tests.Tpo -c -o packet_tests.o `test -f 'tests/libknot/libknot/packet_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/packet_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/packet_tests.Tpo $(DEPDIR)/packet_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/packet_tests.c' object='packet_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet_tests.o `test -f 'tests/libknot/libknot/packet_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/packet_tests.c
+
+packet_tests.obj: tests/libknot/libknot/packet_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet_tests.obj -MD -MP -MF $(DEPDIR)/packet_tests.Tpo -c -o packet_tests.obj `if test -f 'tests/libknot/libknot/packet_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/packet_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/packet_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/packet_tests.Tpo $(DEPDIR)/packet_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/packet_tests.c' object='packet_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet_tests.obj `if test -f 'tests/libknot/libknot/packet_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/packet_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/packet_tests.c'; fi`
+
+query_tests.o: tests/libknot/libknot/query_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT query_tests.o -MD -MP -MF $(DEPDIR)/query_tests.Tpo -c -o query_tests.o `test -f 'tests/libknot/libknot/query_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/query_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/query_tests.Tpo $(DEPDIR)/query_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/query_tests.c' object='query_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o query_tests.o `test -f 'tests/libknot/libknot/query_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/query_tests.c
+
+query_tests.obj: tests/libknot/libknot/query_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT query_tests.obj -MD -MP -MF $(DEPDIR)/query_tests.Tpo -c -o query_tests.obj `if test -f 'tests/libknot/libknot/query_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/query_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/query_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/query_tests.Tpo $(DEPDIR)/query_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/query_tests.c' object='query_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o query_tests.obj `if test -f 'tests/libknot/libknot/query_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/query_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/query_tests.c'; fi`
+
+edns_tests.o: tests/libknot/libknot/edns_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT edns_tests.o -MD -MP -MF $(DEPDIR)/edns_tests.Tpo -c -o edns_tests.o `test -f 'tests/libknot/libknot/edns_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/edns_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/edns_tests.Tpo $(DEPDIR)/edns_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/edns_tests.c' object='edns_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o edns_tests.o `test -f 'tests/libknot/libknot/edns_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/edns_tests.c
+
+edns_tests.obj: tests/libknot/libknot/edns_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT edns_tests.obj -MD -MP -MF $(DEPDIR)/edns_tests.Tpo -c -o edns_tests.obj `if test -f 'tests/libknot/libknot/edns_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/edns_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/edns_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/edns_tests.Tpo $(DEPDIR)/edns_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/edns_tests.c' object='edns_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o edns_tests.obj `if test -f 'tests/libknot/libknot/edns_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/edns_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/edns_tests.c'; fi`
+
+node_tests.o: tests/libknot/libknot/node_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT node_tests.o -MD -MP -MF $(DEPDIR)/node_tests.Tpo -c -o node_tests.o `test -f 'tests/libknot/libknot/node_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/node_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/node_tests.Tpo $(DEPDIR)/node_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/node_tests.c' object='node_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o node_tests.o `test -f 'tests/libknot/libknot/node_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/node_tests.c
+
+node_tests.obj: tests/libknot/libknot/node_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT node_tests.obj -MD -MP -MF $(DEPDIR)/node_tests.Tpo -c -o node_tests.obj `if test -f 'tests/libknot/libknot/node_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/node_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/node_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/node_tests.Tpo $(DEPDIR)/node_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/node_tests.c' object='node_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o node_tests.obj `if test -f 'tests/libknot/libknot/node_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/node_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/node_tests.c'; fi`
+
+rdata_tests.o: tests/libknot/libknot/rdata_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdata_tests.o -MD -MP -MF $(DEPDIR)/rdata_tests.Tpo -c -o rdata_tests.o `test -f 'tests/libknot/libknot/rdata_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/rdata_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rdata_tests.Tpo $(DEPDIR)/rdata_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/rdata_tests.c' object='rdata_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdata_tests.o `test -f 'tests/libknot/libknot/rdata_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/rdata_tests.c
+
+rdata_tests.obj: tests/libknot/libknot/rdata_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdata_tests.obj -MD -MP -MF $(DEPDIR)/rdata_tests.Tpo -c -o rdata_tests.obj `if test -f 'tests/libknot/libknot/rdata_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/rdata_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/rdata_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rdata_tests.Tpo $(DEPDIR)/rdata_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/rdata_tests.c' object='rdata_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdata_tests.obj `if test -f 'tests/libknot/libknot/rdata_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/rdata_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/rdata_tests.c'; fi`
+
+rrset_tests.o: tests/libknot/libknot/rrset_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrset_tests.o -MD -MP -MF $(DEPDIR)/rrset_tests.Tpo -c -o rrset_tests.o `test -f 'tests/libknot/libknot/rrset_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/rrset_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrset_tests.Tpo $(DEPDIR)/rrset_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/rrset_tests.c' object='rrset_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset_tests.o `test -f 'tests/libknot/libknot/rrset_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/rrset_tests.c
+
+rrset_tests.obj: tests/libknot/libknot/rrset_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrset_tests.obj -MD -MP -MF $(DEPDIR)/rrset_tests.Tpo -c -o rrset_tests.obj `if test -f 'tests/libknot/libknot/rrset_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/rrset_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/rrset_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrset_tests.Tpo $(DEPDIR)/rrset_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/rrset_tests.c' object='rrset_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset_tests.obj `if test -f 'tests/libknot/libknot/rrset_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/rrset_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/rrset_tests.c'; fi`
+
+zone_tests.o: tests/libknot/libknot/zone_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tests.o -MD -MP -MF $(DEPDIR)/zone_tests.Tpo -c -o zone_tests.o `test -f 'tests/libknot/libknot/zone_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zone_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tests.Tpo $(DEPDIR)/zone_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zone_tests.c' object='zone_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tests.o `test -f 'tests/libknot/libknot/zone_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zone_tests.c
+
+zone_tests.obj: tests/libknot/libknot/zone_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tests.obj -MD -MP -MF $(DEPDIR)/zone_tests.Tpo -c -o zone_tests.obj `if test -f 'tests/libknot/libknot/zone_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zone_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zone_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tests.Tpo $(DEPDIR)/zone_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zone_tests.c' object='zone_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tests.obj `if test -f 'tests/libknot/libknot/zone_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zone_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zone_tests.c'; fi`
+
+zone_tree_tests.o: tests/libknot/libknot/zone_tree_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tree_tests.o -MD -MP -MF $(DEPDIR)/zone_tree_tests.Tpo -c -o zone_tree_tests.o `test -f 'tests/libknot/libknot/zone_tree_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zone_tree_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tree_tests.Tpo $(DEPDIR)/zone_tree_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zone_tree_tests.c' object='zone_tree_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tree_tests.o `test -f 'tests/libknot/libknot/zone_tree_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zone_tree_tests.c
+
+zone_tree_tests.obj: tests/libknot/libknot/zone_tree_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tree_tests.obj -MD -MP -MF $(DEPDIR)/zone_tree_tests.Tpo -c -o zone_tree_tests.obj `if test -f 'tests/libknot/libknot/zone_tree_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zone_tree_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zone_tree_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tree_tests.Tpo $(DEPDIR)/zone_tree_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zone_tree_tests.c' object='zone_tree_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tree_tests.obj `if test -f 'tests/libknot/libknot/zone_tree_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zone_tree_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zone_tree_tests.c'; fi`
+
+zonedb_tests.o: tests/libknot/libknot/zonedb_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zonedb_tests.o -MD -MP -MF $(DEPDIR)/zonedb_tests.Tpo -c -o zonedb_tests.o `test -f 'tests/libknot/libknot/zonedb_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zonedb_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zonedb_tests.Tpo $(DEPDIR)/zonedb_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zonedb_tests.c' object='zonedb_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zonedb_tests.o `test -f 'tests/libknot/libknot/zonedb_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zonedb_tests.c
+
+zonedb_tests.obj: tests/libknot/libknot/zonedb_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zonedb_tests.obj -MD -MP -MF $(DEPDIR)/zonedb_tests.Tpo -c -o zonedb_tests.obj `if test -f 'tests/libknot/libknot/zonedb_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zonedb_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zonedb_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zonedb_tests.Tpo $(DEPDIR)/zonedb_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/zonedb_tests.c' object='zonedb_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zonedb_tests.obj `if test -f 'tests/libknot/libknot/zonedb_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/zonedb_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/zonedb_tests.c'; fi`
+
+unittests_libknot.o: tests/libknot/unittests_libknot.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_libknot.o -MD -MP -MF $(DEPDIR)/unittests_libknot.Tpo -c -o unittests_libknot.o `test -f 'tests/libknot/unittests_libknot.c' || echo '$(srcdir)/'`tests/libknot/unittests_libknot.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_libknot.Tpo $(DEPDIR)/unittests_libknot.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/unittests_libknot.c' object='unittests_libknot.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_libknot.o `test -f 'tests/libknot/unittests_libknot.c' || echo '$(srcdir)/'`tests/libknot/unittests_libknot.c
+
+unittests_libknot.obj: tests/libknot/unittests_libknot.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_libknot.obj -MD -MP -MF $(DEPDIR)/unittests_libknot.Tpo -c -o unittests_libknot.obj `if test -f 'tests/libknot/unittests_libknot.c'; then $(CYGPATH_W) 'tests/libknot/unittests_libknot.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/unittests_libknot.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_libknot.Tpo $(DEPDIR)/unittests_libknot.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/unittests_libknot.c' object='unittests_libknot.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_libknot.obj `if test -f 'tests/libknot/unittests_libknot.c'; then $(CYGPATH_W) 'tests/libknot/unittests_libknot.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/unittests_libknot.c'; fi`
+
+dname_tests_realdata.o: tests/libknot/realdata/libknot/dname_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_tests_realdata.o -MD -MP -MF $(DEPDIR)/dname_tests_realdata.Tpo -c -o dname_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/dname_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/dname_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_tests_realdata.Tpo $(DEPDIR)/dname_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/dname_tests_realdata.c' object='dname_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/dname_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/dname_tests_realdata.c
+
+dname_tests_realdata.obj: tests/libknot/realdata/libknot/dname_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dname_tests_realdata.obj -MD -MP -MF $(DEPDIR)/dname_tests_realdata.Tpo -c -o dname_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/dname_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/dname_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/dname_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dname_tests_realdata.Tpo $(DEPDIR)/dname_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/dname_tests_realdata.c' object='dname_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/dname_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/dname_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/dname_tests_realdata.c'; fi`
+
+response_tests_realdata.o: tests/libknot/realdata/libknot/response_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT response_tests_realdata.o -MD -MP -MF $(DEPDIR)/response_tests_realdata.Tpo -c -o response_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/response_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/response_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/response_tests_realdata.Tpo $(DEPDIR)/response_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/response_tests_realdata.c' object='response_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o response_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/response_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/response_tests_realdata.c
+
+response_tests_realdata.obj: tests/libknot/realdata/libknot/response_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT response_tests_realdata.obj -MD -MP -MF $(DEPDIR)/response_tests_realdata.Tpo -c -o response_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/response_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/response_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/response_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/response_tests_realdata.Tpo $(DEPDIR)/response_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/response_tests_realdata.c' object='response_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o response_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/response_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/response_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/response_tests_realdata.c'; fi`
+
+edns_tests_realdata.o: tests/libknot/realdata/libknot/edns_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT edns_tests_realdata.o -MD -MP -MF $(DEPDIR)/edns_tests_realdata.Tpo -c -o edns_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/edns_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/edns_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/edns_tests_realdata.Tpo $(DEPDIR)/edns_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/edns_tests_realdata.c' object='edns_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o edns_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/edns_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/edns_tests_realdata.c
+
+edns_tests_realdata.obj: tests/libknot/realdata/libknot/edns_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT edns_tests_realdata.obj -MD -MP -MF $(DEPDIR)/edns_tests_realdata.Tpo -c -o edns_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/edns_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/edns_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/edns_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/edns_tests_realdata.Tpo $(DEPDIR)/edns_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/edns_tests_realdata.c' object='edns_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o edns_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/edns_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/edns_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/edns_tests_realdata.c'; fi`
+
+node_tests_realdata.o: tests/libknot/realdata/libknot/node_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT node_tests_realdata.o -MD -MP -MF $(DEPDIR)/node_tests_realdata.Tpo -c -o node_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/node_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/node_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/node_tests_realdata.Tpo $(DEPDIR)/node_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/node_tests_realdata.c' object='node_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o node_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/node_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/node_tests_realdata.c
+
+node_tests_realdata.obj: tests/libknot/realdata/libknot/node_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT node_tests_realdata.obj -MD -MP -MF $(DEPDIR)/node_tests_realdata.Tpo -c -o node_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/node_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/node_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/node_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/node_tests_realdata.Tpo $(DEPDIR)/node_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/node_tests_realdata.c' object='node_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o node_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/node_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/node_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/node_tests_realdata.c'; fi`
+
+rdata_tests_realdata.o: tests/libknot/realdata/libknot/rdata_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdata_tests_realdata.o -MD -MP -MF $(DEPDIR)/rdata_tests_realdata.Tpo -c -o rdata_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/rdata_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/rdata_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rdata_tests_realdata.Tpo $(DEPDIR)/rdata_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/rdata_tests_realdata.c' object='rdata_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdata_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/rdata_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/rdata_tests_realdata.c
+
+rdata_tests_realdata.obj: tests/libknot/realdata/libknot/rdata_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdata_tests_realdata.obj -MD -MP -MF $(DEPDIR)/rdata_tests_realdata.Tpo -c -o rdata_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/rdata_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/rdata_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/rdata_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rdata_tests_realdata.Tpo $(DEPDIR)/rdata_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/rdata_tests_realdata.c' object='rdata_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdata_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/rdata_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/rdata_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/rdata_tests_realdata.c'; fi`
+
+rrset_tests_realdata.o: tests/libknot/realdata/libknot/rrset_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrset_tests_realdata.o -MD -MP -MF $(DEPDIR)/rrset_tests_realdata.Tpo -c -o rrset_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/rrset_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/rrset_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrset_tests_realdata.Tpo $(DEPDIR)/rrset_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/rrset_tests_realdata.c' object='rrset_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/rrset_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/rrset_tests_realdata.c
+
+rrset_tests_realdata.obj: tests/libknot/realdata/libknot/rrset_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rrset_tests_realdata.obj -MD -MP -MF $(DEPDIR)/rrset_tests_realdata.Tpo -c -o rrset_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/rrset_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/rrset_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/rrset_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/rrset_tests_realdata.Tpo $(DEPDIR)/rrset_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/rrset_tests_realdata.c' object='rrset_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/rrset_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/rrset_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/rrset_tests_realdata.c'; fi`
+
+zone_tests_realdata.o: tests/libknot/realdata/libknot/zone_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tests_realdata.o -MD -MP -MF $(DEPDIR)/zone_tests_realdata.Tpo -c -o zone_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/zone_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/zone_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tests_realdata.Tpo $(DEPDIR)/zone_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/zone_tests_realdata.c' object='zone_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/zone_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/zone_tests_realdata.c
+
+zone_tests_realdata.obj: tests/libknot/realdata/libknot/zone_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tests_realdata.obj -MD -MP -MF $(DEPDIR)/zone_tests_realdata.Tpo -c -o zone_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/zone_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/zone_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/zone_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tests_realdata.Tpo $(DEPDIR)/zone_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/zone_tests_realdata.c' object='zone_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/zone_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/zone_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/zone_tests_realdata.c'; fi`
+
+zonedb_tests_realdata.o: tests/libknot/realdata/libknot/zonedb_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zonedb_tests_realdata.o -MD -MP -MF $(DEPDIR)/zonedb_tests_realdata.Tpo -c -o zonedb_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/zonedb_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zonedb_tests_realdata.Tpo $(DEPDIR)/zonedb_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/zonedb_tests_realdata.c' object='zonedb_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zonedb_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/zonedb_tests_realdata.c
+
+zonedb_tests_realdata.obj: tests/libknot/realdata/libknot/zonedb_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zonedb_tests_realdata.obj -MD -MP -MF $(DEPDIR)/zonedb_tests_realdata.Tpo -c -o zonedb_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zonedb_tests_realdata.Tpo $(DEPDIR)/zonedb_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/zonedb_tests_realdata.c' object='zonedb_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zonedb_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/zonedb_tests_realdata.c'; fi`
+
+packet_tests_realdata.o: tests/libknot/realdata/libknot/packet_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet_tests_realdata.o -MD -MP -MF $(DEPDIR)/packet_tests_realdata.Tpo -c -o packet_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/packet_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/packet_tests_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/packet_tests_realdata.Tpo $(DEPDIR)/packet_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/packet_tests_realdata.c' object='packet_tests_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet_tests_realdata.o `test -f 'tests/libknot/realdata/libknot/packet_tests_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot/packet_tests_realdata.c
+
+packet_tests_realdata.obj: tests/libknot/realdata/libknot/packet_tests_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet_tests_realdata.obj -MD -MP -MF $(DEPDIR)/packet_tests_realdata.Tpo -c -o packet_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/packet_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/packet_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/packet_tests_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/packet_tests_realdata.Tpo $(DEPDIR)/packet_tests_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot/packet_tests_realdata.c' object='packet_tests_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet_tests_realdata.obj `if test -f 'tests/libknot/realdata/libknot/packet_tests_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot/packet_tests_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot/packet_tests_realdata.c'; fi`
+
+libknot_tests_loader_realdata.o: tests/libknot/realdata/libknot_tests_loader_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot_tests_loader_realdata.o -MD -MP -MF $(DEPDIR)/libknot_tests_loader_realdata.Tpo -c -o libknot_tests_loader_realdata.o `test -f 'tests/libknot/realdata/libknot_tests_loader_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot_tests_loader_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libknot_tests_loader_realdata.Tpo $(DEPDIR)/libknot_tests_loader_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot_tests_loader_realdata.c' object='libknot_tests_loader_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot_tests_loader_realdata.o `test -f 'tests/libknot/realdata/libknot_tests_loader_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/libknot_tests_loader_realdata.c
+
+libknot_tests_loader_realdata.obj: tests/libknot/realdata/libknot_tests_loader_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot_tests_loader_realdata.obj -MD -MP -MF $(DEPDIR)/libknot_tests_loader_realdata.Tpo -c -o libknot_tests_loader_realdata.obj `if test -f 'tests/libknot/realdata/libknot_tests_loader_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot_tests_loader_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot_tests_loader_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libknot_tests_loader_realdata.Tpo $(DEPDIR)/libknot_tests_loader_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/libknot_tests_loader_realdata.c' object='libknot_tests_loader_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot_tests_loader_realdata.obj `if test -f 'tests/libknot/realdata/libknot_tests_loader_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/libknot_tests_loader_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/libknot_tests_loader_realdata.c'; fi`
+
+unittests_libknot_realdata.o: tests/libknot/realdata/unittests_libknot_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_libknot_realdata.o -MD -MP -MF $(DEPDIR)/unittests_libknot_realdata.Tpo -c -o unittests_libknot_realdata.o `test -f 'tests/libknot/realdata/unittests_libknot_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/unittests_libknot_realdata.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_libknot_realdata.Tpo $(DEPDIR)/unittests_libknot_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/unittests_libknot_realdata.c' object='unittests_libknot_realdata.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_libknot_realdata.o `test -f 'tests/libknot/realdata/unittests_libknot_realdata.c' || echo '$(srcdir)/'`tests/libknot/realdata/unittests_libknot_realdata.c
+
+unittests_libknot_realdata.obj: tests/libknot/realdata/unittests_libknot_realdata.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_libknot_realdata.obj -MD -MP -MF $(DEPDIR)/unittests_libknot_realdata.Tpo -c -o unittests_libknot_realdata.obj `if test -f 'tests/libknot/realdata/unittests_libknot_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/unittests_libknot_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/unittests_libknot_realdata.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_libknot_realdata.Tpo $(DEPDIR)/unittests_libknot_realdata.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/realdata/unittests_libknot_realdata.c' object='unittests_libknot_realdata.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_libknot_realdata.obj `if test -f 'tests/libknot/realdata/unittests_libknot_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/unittests_libknot_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/unittests_libknot_realdata.c'; fi`
+
+unittests_zp_main.o: zcompile/tests/unittests_zp_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_zp_main.o -MD -MP -MF $(DEPDIR)/unittests_zp_main.Tpo -c -o unittests_zp_main.o `test -f 'zcompile/tests/unittests_zp_main.c' || echo '$(srcdir)/'`zcompile/tests/unittests_zp_main.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_zp_main.Tpo $(DEPDIR)/unittests_zp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/tests/unittests_zp_main.c' object='unittests_zp_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_zp_main.o `test -f 'zcompile/tests/unittests_zp_main.c' || echo '$(srcdir)/'`zcompile/tests/unittests_zp_main.c
+
+unittests_zp_main.obj: zcompile/tests/unittests_zp_main.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_zp_main.obj -MD -MP -MF $(DEPDIR)/unittests_zp_main.Tpo -c -o unittests_zp_main.obj `if test -f 'zcompile/tests/unittests_zp_main.c'; then $(CYGPATH_W) 'zcompile/tests/unittests_zp_main.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/tests/unittests_zp_main.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_zp_main.Tpo $(DEPDIR)/unittests_zp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='zcompile/tests/unittests_zp_main.c' object='unittests_zp_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_zp_main.obj `if test -f 'zcompile/tests/unittests_zp_main.c'; then $(CYGPATH_W) 'zcompile/tests/unittests_zp_main.c'; else $(CYGPATH_W) '$(srcdir)/zcompile/tests/unittests_zp_main.c'; fi`
+
+.l.c:
+ $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+libknotd_la-cf-lex.c: knot/conf/cf-lex.l
+ \
+ $(am__skiplex) \
+ $(SHELL) $(YLWRAP) `test -f 'knot/conf/cf-lex.l' || echo '$(srcdir)/'`knot/conf/cf-lex.l $(LEX_OUTPUT_ROOT).c libknotd_la-cf-lex.c -- $(LEX) $(LFLAGS) $(libknotd_la_LFLAGS)
+
+zlexer.c: zcompile/zlexer.l
+ \
+ $(am__skiplex) \
+ $(SHELL) $(YLWRAP) `test -f 'zcompile/zlexer.l' || echo '$(srcdir)/'`zcompile/zlexer.l $(LEX_OUTPUT_ROOT).c zlexer.c -- $(LEX) $(LFLAGS) $(AM_LFLAGS)
+
+.y.c:
+ $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+
+libknotd_la-cf-parse.c: knot/conf/cf-parse.y
+ \
+ $(am__skipyacc) \
+ $(SHELL) $(YLWRAP) `test -f 'knot/conf/cf-parse.y' || echo '$(srcdir)/'`knot/conf/cf-parse.y y.tab.c libknotd_la-cf-parse.c y.tab.h libknotd_la-cf-parse.h y.output libknotd_la-cf-parse.output -- $(YACC) $(YFLAGS) $(libknotd_la_YFLAGS)
+
+zparser.c: zcompile/zparser.y
+ \
+ $(am__skipyacc) \
+ $(SHELL) $(YLWRAP) `test -f 'zcompile/zparser.y' || echo '$(srcdir)/'`zcompile/zparser.y y.tab.c zparser.c y.tab.h zparser.h y.output zparser.output -- $(YACC) $(YFLAGS) $(AM_YFLAGS)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) config.h
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f libknotd_la-cf-lex.c
+ -rm -f libknotd_la-cf-parse.c
+ -rm -f libknotd_la-cf-parse.h
+ -rm -f zlexer.c
+ -rm -f zparser.c
+ -rm -f zparser.h
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ clean-noinstLTLIBRARIES clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS uninstall-man \
+ uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool clean-noinstLTLIBRARIES \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-hdr distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libexecPROGRAMS install-man install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-libexecPROGRAMS uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+
+# automake complains on % rules:
+# `%'-style pattern rules are a GNU make extension
+
+tests/libknot/parsed_data.rc: tests/libknot/files/parsed_data
+ ../resource.sh tests/libknot/files/parsed_data >$@
+
+tests/libknot/realdata/parsed_data.rc: tests/libknot/realdata/files/parsed_data
+ ../resource.sh tests/libknot/realdata/files/parsed_data >$@
+
+tests/libknot/parsed_data_queries.rc: tests/libknot/files/parsed_data_queries
+ ../resource.sh tests/libknot/files/parsed_data_queries >$@
+
+tests/libknot/raw_data_queries.rc: tests/libknot/files/raw_data_queries
+ ../resource.sh tests/libknot/files/raw_data_queries >$@
+
+tests/libknot/raw_data.rc: tests/libknot/files/raw_data
+ ../resource.sh tests/libknot/files/raw_data >$@
+
+tests/libknot/realdata/raw_data.rc: tests/libknot/realdata/files/raw_data
+ ../resource.sh tests/libknot/realdata/files/raw_data >$@
+
+tests/sample_conf.rc: tests/files/sample_conf
+ ../resource.sh tests/files/sample_conf >$@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/common/WELL1024a.c b/src/common/WELL1024a.c
new file mode 100644
index 0000000..dddf75e
--- /dev/null
+++ b/src/common/WELL1024a.c
@@ -0,0 +1,116 @@
+/* ***************************************************************************** */
+/* Copyright: Francois Panneton and Pierre L'Ecuyer, University of Montreal */
+/* Makoto Matsumoto, Hiroshima University */
+/* Notice: This code can be used freely for personal, academic, */
+/* or non-commercial purposes. For commercial purposes, */
+/* please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca */
+/* ***************************************************************************** */
+
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+
+#include "WELL1024a.h"
+
+#define W 32
+#define M1 3
+#define M2 24
+#define M3 10
+
+#define MAT0POS(t,v) (v^(v>>t))
+#define MAT0NEG(t,v) (v^(v<<(-(t))))
+#define Identity(v) (v)
+
+#define V0(s) (s)->state[(s)->i ]
+#define VM1(s) (s)->state[((s)->i+M1) & 0x0000001fU]
+#define VM2(s) (s)->state[((s)->i+M2) & 0x0000001fU]
+#define VM3(s) (s)->state[((s)->i+M3) & 0x0000001fU]
+#define VRm1(s) (s)->state[((s)->i+31) & 0x0000001fU]
+#define newV0(s) (s)->state[((s)->i+31) & 0x0000001fU]
+#define newV1(s) (s)->state[(s)->i ]
+
+#define FACT 2.32830643653869628906e-10
+
+rngstate_t* InitWELLRNG1024a (unsigned *init) {
+
+ rngstate_t *s = malloc(sizeof(rngstate_t));
+ if (s == 0) {
+ return 0;
+ }
+
+ s->i = 0;
+ for (int j = 0; j < WELL1024_WIDTH; j++)
+ s->state[j] = init[j];
+ return s;
+}
+
+double WELLRNG1024a (rngstate_t* s) {
+ unsigned z0 = VRm1(s);
+ unsigned z1 = Identity(V0(s)) ^ MAT0POS (8, VM1(s));
+ unsigned z2 = MAT0NEG (-19, VM2(s)) ^ MAT0NEG(-14,VM3(s));
+ newV1(s) = z1 ^ z2;
+ newV0(s) = MAT0NEG (-11,z0) ^ MAT0NEG(-7,z1) ^ MAT0NEG(-13,z2) ;
+ s->i = (s->i + 31) & 0x0000001fU;
+ return ((double) s->state[s->i] * FACT);
+}
+
+/*! \brief TLS unique key for each thread seed. */
+static pthread_key_t tls_prng_key;
+static pthread_once_t tls_prng_once = PTHREAD_ONCE_INIT;
+
+static void tls_prng_deinit(void *ptr)
+{
+ free(ptr);
+}
+
+static void tls_prng_deinit_main()
+{
+ tls_prng_deinit(pthread_getspecific(tls_prng_key));
+}
+
+static void tls_prng_init()
+{
+ (void) pthread_key_create(&tls_prng_key, tls_prng_deinit);
+ atexit(tls_prng_deinit_main); // Main thread cleanup
+}
+
+double tls_rand()
+{
+ /* Setup PRNG state for current thread. */
+ (void)pthread_once(&tls_prng_once, tls_prng_init);
+
+ /* Create PRNG state if not exists. */
+ rngstate_t* s = pthread_getspecific(tls_prng_key);
+ if (!s) {
+ /* Initialize seed from system PRNG generator. */
+ unsigned init[WELL1024_WIDTH];
+ FILE *fp = fopen("/dev/urandom", "r");
+ for (unsigned i = 0; i < WELL1024_WIDTH; ++i) {
+ int rc = fread(&init[i], sizeof(unsigned), 1, fp);
+ rc = rc;
+ }
+ fclose(fp);
+
+ /* Initialize PRNG state. */
+ s = InitWELLRNG1024a(init);
+ (void)pthread_setspecific(tls_prng_key, s);
+ }
+
+ return WELLRNG1024a(s);
+}
+
+void tls_seed_set(unsigned init[WELL1024_WIDTH])
+{
+ /* Initialize new PRNG state if not exists. */
+ rngstate_t* s = pthread_getspecific(tls_prng_key);
+ if (!s) {
+ s = InitWELLRNG1024a(init);
+ (void)pthread_setspecific(tls_prng_key, s);
+ } else {
+ /* Reset PRNG state if exists. */
+ memcpy(s->state, init, sizeof(unsigned) * WELL1024_WIDTH);
+ s->i = 0;
+ }
+}
diff --git a/src/common/WELL1024a.h b/src/common/WELL1024a.h
new file mode 100644
index 0000000..04bf1a1
--- /dev/null
+++ b/src/common/WELL1024a.h
@@ -0,0 +1,32 @@
+/* ***************************************************************************** */
+/* Copyright: Francois Panneton and Pierre L'Ecuyer, University of Montreal */
+/* Makoto Matsumoto, Hiroshima University */
+/* Notice: This code can be used freely for personal, academic, */
+/* or non-commercial purposes. For commercial purposes, */
+/* please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca */
+/* ***************************************************************************** */
+
+#define WELL1024_WIDTH 32 /* 128 bytes */
+
+typedef struct {
+ unsigned i;
+ unsigned state[WELL1024_WIDTH];
+} rngstate_t;
+
+rngstate_t* InitWELLRNG1024a (unsigned *init);
+double WELLRNG1024a (rngstate_t* s);
+
+/*!
+ * \brief Get pseudorandom number from PRNG initialized in thread-local storage.
+ *
+ * No need for initialization, TLS will take care of it.
+ *
+ * \retval Pseudorandom number.
+ */
+double tls_rand();
+
+/*!
+ * \brief Set PRNG seed in thread-local storage to requested value.
+ *
+ */
+void tls_seed_set(unsigned init[WELL1024_WIDTH]);
diff --git a/src/common/acl.c b/src/common/acl.c
new file mode 100644
index 0000000..e73c4dd
--- /dev/null
+++ b/src/common/acl.c
@@ -0,0 +1,185 @@
+/* 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 <string.h>
+#include <stdlib.h>
+
+#include "common/acl.h"
+
+static int acl_compare(void *k1, void *k2)
+{
+ sockaddr_t* a1 = (sockaddr_t *)k1;
+ sockaddr_t* a2 = (sockaddr_t *)k2;
+
+ /* Check different length, IPv4 goes first. */
+ int ldiff = a1->len - a2->len;
+ if (ldiff != 0) {
+ return ldiff < 0 ? -1 : 1;
+ }
+
+ /* Compare integers if IPv4. */
+ if (a1->len == sizeof(struct sockaddr_in)) {
+
+ /* Allow if k1 == INADDR_ANY. */
+ if (a1->addr4.sin_addr.s_addr == 0) {
+ return 0;
+ }
+
+ /* Compare address. */
+ ldiff = a1->addr4.sin_addr.s_addr - a2->addr4.sin_addr.s_addr;
+ if (ldiff != 0) {
+ return ldiff < 0 ? -1 : 1;
+ }
+
+ /* Port = 0 means any port match. */
+ if (a1->addr4.sin_port == 0) {
+ return 0;
+ }
+
+ /* Compare ports on address match. */
+ ldiff = ntohs(a1->addr4.sin_port) - ntohs(a2->addr4.sin_port);
+ if (ldiff != 0) {
+ return ldiff < 0 ? -1 : 1;
+ }
+ return 0;
+ }
+
+ /* IPv6 matching. */
+#ifndef DISABLE_IPV6
+ if (a1->len == sizeof(struct sockaddr_in6)) {
+
+ /* Compare address. */
+ /*! \todo Maybe use memcmp()? */
+ ldiff = 0;
+ const unsigned int *a6 = (const unsigned int *)&a1->addr6.sin6_addr;
+ const unsigned int *b6 = (const unsigned int *)&a2->addr6.sin6_addr;
+ for (int i = 0; i < (sizeof(struct in6_addr)/ sizeof(int)) ; ++i) {
+ ldiff = a6[i] - b6[i];
+ if (ldiff < 0) {
+ return -1;
+ }
+ if (ldiff > 0) {
+ return 1;
+ }
+ }
+
+ /* Port = 0 means any port match. */
+ if (a1->addr6.sin6_port == 0) {
+ return 0;
+ }
+
+ /* Compare ports on address match. */
+ ldiff = ntohs(a1->addr6.sin6_port) - ntohs(a2->addr6.sin6_port);
+ if (ldiff != 0) {
+ return ldiff < 0 ? -1 : 1;
+ }
+ return 0;
+ }
+#endif
+
+ return 0;
+}
+
+acl_t *acl_new(acl_rule_t default_rule, const char *name)
+{
+ /* Trailing '\0' for NULL name. */
+ size_t name_len = 1;
+ if (name) {
+ name_len += strlen(name);
+ } else {
+ name = "";
+ }
+
+ /* Allocate memory for ACL. */
+ acl_t* acl = malloc(sizeof(acl_t) + name_len);
+ if (!acl) {
+ return 0;
+ }
+
+ /* Initialize skip list. */
+ acl->rules = skip_create_list(acl_compare);
+ if (!acl->rules) {
+ free(acl);
+ return 0;
+ }
+
+ /* Initialize. */
+ memcpy(&acl->name, name, name_len);
+ acl->default_rule = default_rule;
+ return acl;
+}
+
+void acl_delete(acl_t **acl)
+{
+ if ((acl == NULL) || (*acl == NULL)) {
+ return;
+ }
+
+ /* Truncate rules. */
+ if (acl_truncate(*acl) != ACL_ACCEPT) {
+ return;
+ }
+
+ /* Free ACL. */
+ free(*acl);
+ *acl = 0;
+}
+
+int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule)
+{
+ if (!acl || !addr || rule < 0) {
+ return ACL_ERROR;
+ }
+
+ /* Insert into skip list. */
+ sockaddr_t *key = malloc(sizeof(sockaddr_t));
+ memcpy(key, addr, sizeof(sockaddr_t));
+
+ skip_insert(acl->rules, key, (void*)((ssize_t)rule + 1), 0);
+
+ return ACL_ACCEPT;
+}
+
+int acl_match(acl_t *acl, sockaddr_t* addr)
+{
+ if (!acl || !addr) {
+ return ACL_ERROR;
+ }
+
+ /* Return default rule if not found.
+ * Conversion to the same length integer is made,
+ * but we can be sure, that the value range is within <-1,1>.
+ */
+ ssize_t val = ((ssize_t)skip_find(acl->rules, addr)) - 1;
+ if (val < 0) {
+ return acl->default_rule;
+ }
+
+ /* Return stored rule if found. */
+ return (int)val;
+}
+
+int acl_truncate(acl_t *acl)
+{
+ if (acl == NULL) {
+ return ACL_ERROR;
+ }
+
+ /* Destroy all rules. */
+ skip_destroy_list(&acl->rules, free, 0);
+
+ return ACL_ACCEPT;
+}
diff --git a/src/common/acl.h b/src/common/acl.h
new file mode 100644
index 0000000..c79db7f
--- /dev/null
+++ b/src/common/acl.h
@@ -0,0 +1,138 @@
+/* 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 acl.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Access control lists.
+ *
+ * An access control list is a named structure
+ * for efficient IP address and port matching.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_ACL_H_
+#define _KNOTD_ACL_H_
+
+#include "common/skip-list.h"
+#include "common/sockaddr.h"
+
+/*! \brief ACL rules types. */
+typedef enum acl_rule_t {
+ ACL_ERROR = -1,
+ ACL_DENY = 0,
+ ACL_ACCEPT = 1
+} acl_rule_t;
+
+/*! \brief ACL structure. */
+typedef struct acl_t {
+ acl_rule_t default_rule;
+ skip_list_t *rules;
+ const char name[];
+} acl_t;
+
+/*!
+ * \brief Create a new ACL.
+ *
+ * \param default_rule Default rule for address matching.
+ * \param name ACL symbolic name (or NULL).
+ *
+ * \retval New ACL instance when successful.
+ * \retval NULL on errors.
+ */
+acl_t *acl_new(acl_rule_t default_rule, const char *name);
+
+/*!
+ * \brief Delete ACL structure.
+ *
+ * \param acl Pointer to ACL instance.
+ */
+void acl_delete(acl_t **acl);
+
+/*!
+ * \brief Create new ACL rule.
+ *
+ * \todo Support address subnets.
+ *
+ * \param acl Pointer to ACL instance.
+ * \param addr IP address (will be duplicated).
+ * \param rule Rule.
+ *
+ * \retval ACL_ACCEPT if successful.
+ * \retval ACP_ERROR on error.
+ */
+int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule);
+
+/*!
+ * \brief Match address against ACL.
+ *
+ * \param acl Pointer to ACL instance.
+ * \param addr IP address.
+ *
+ * \retval ACL_ACCEPT if the address is accepted.
+ * \retval ACL_DENY if the address is not accepted.
+ * \retval ACP_ERROR on error.
+ */
+int acl_match(acl_t *acl, sockaddr_t* addr);
+
+/*!
+ * \brief Truncate ACL.
+ *
+ * All but the default rule will be dropped.
+ *
+ * \param acl Pointer to ACL instance.
+ *
+ * \retval ACL_ACCEPT if successful.
+ * \retval ACP_ERROR on error.
+ */
+int acl_truncate(acl_t *acl);
+
+/*!
+ * \brief Return ACL name.
+ *
+ * \param acl Pointer to ACL instance.
+ *
+ * \retval ACL name.
+ */
+static inline const char* acl_name(acl_t *acl) {
+ if (!acl) {
+ return 0;
+ }
+
+ return acl->name;
+}
+
+/*!
+ * \brief Return ACL rules.
+ *
+ * \param acl Pointer to ACL instance.
+ *
+ * \retval ACL rules skip-list.
+ */
+static inline skip_list_t* acl_rules(acl_t *acl) {
+ if (!acl) {
+ return 0;
+ }
+
+ return acl->rules;
+}
+
+#endif /* _KNOTD_ACL_H_ */
+
+/*! @} */
diff --git a/src/common/base32.c b/src/common/base32.c
new file mode 100644
index 0000000..43b86c1
--- /dev/null
+++ b/src/common/base32.c
@@ -0,0 +1,539 @@
+/* base32.c -- Encode binary data using printable characters.
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2010 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Adapted from base64.{h,c} by Ondřej Surý. base64.{h,c} was written
+ * by Simon Josefsson. Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking. Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base32_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ * FAIL: input was not valid base32
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base32_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ * FAIL: input too long
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+/* Get prototype. */
+#include "base32.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char to_uchar(char ch)
+{
+ return ch;
+}
+
+/* Base32 encode IN array of size INLEN into OUT array of size OUTLEN.
+ If OUTLEN is less than BASE32_LENGTH(INLEN), write as many bytes as
+ possible. If OUTLEN is larger than BASE32_LENGTH(INLEN), also zero
+ terminate the output buffer. */
+void base32_encode(const char *in, size_t inlen, char *out, size_t outlen)
+{
+ static const char b32str[32] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+ while (inlen && outlen) {
+ *out++ = b32str[(to_uchar(in[0]) >> 3) & 0x1f];
+ if (!--outlen) {
+ break;
+ }
+ *out++ = b32str[((to_uchar(in[0]) << 2)
+ + (--inlen ? to_uchar(in[1]) >> 6 : 0))
+ & 0x1f];
+ if (!--outlen) {
+ break;
+ }
+ *out++ =(inlen
+ ? b32str[(to_uchar(in[1]) >> 1) & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[1]) << 4)
+ + (--inlen ? to_uchar(in[2]) >> 4 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[2]) << 1)
+ + (--inlen ? to_uchar(in[3]) >> 7 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[(to_uchar(in[3]) >> 2) & 0x1f]
+ : '=');
+ if (!--outlen)
+ {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[3]) << 3)
+ + (--inlen ? to_uchar(in[4]) >> 5 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = inlen ? b32str[to_uchar(in[4]) & 0x1f] : '=';
+ if (!--outlen) {
+ break;
+ }
+ if (inlen) {
+ inlen--;
+ }
+ if (inlen) {
+ in += 5;
+ }
+ }
+
+ if (outlen) {
+ *out = '\0';
+ }
+}
+
+/* Allocate a buffer and store zero terminated base32 encoded data
+ from array IN of size INLEN, returning BASE32_LENGTH(INLEN), i.e.,
+ the length of the encoded data, excluding the terminating zero. On
+ return, the OUT variable will hold a pointer to newly allocated
+ memory that must be deallocated by the caller. If output string
+ length would overflow, 0 is returned and OUT is set to NULL. If
+ memory allocation failed, OUT is set to NULL, and the return value
+ indicates length of the requested memory block, i.e.,
+ BASE32_LENGTH(inlen) + 1. */
+size_t base32_encode_alloc(const char *in, size_t inlen, char **out)
+{
+ size_t outlen = 1 + BASE32_LENGTH (inlen);
+
+ /* Check for overflow in outlen computation.
+ *
+ * If there is no overflow, outlen >= inlen.
+ *
+ * If the operation (inlen + 2) overflows then it yields at most +1, so
+ * outlen is 0.
+ *
+ * If the multiplication overflows, we lose at least half of the
+ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+ * (inlen > 4).
+ */
+ if (inlen > outlen)
+ {
+ *out = NULL;
+ return 0;
+ }
+
+ *out = malloc(outlen);
+ if (!*out) {
+ return outlen;
+ }
+
+ base32_encode(in, inlen, *out, outlen);
+
+ return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+ (think EBCDIC). However, it does assume that the characters in the
+ Base32 alphabet (A-Z2-7) are encoded in 0..255. POSIX
+ 1003.1-2001 require that char and unsigned char are 8-bit
+ quantities, though, taking care of that problem. But this may be a
+ potential problem on non-POSIX C99 platforms.
+
+ IBM C V6 for AIX mishandles "#define B32(x) ...'x'...", so use "_"
+ as the formal parameter rather than "x". */
+#define B32(_) \
+ ((_) == 'A' ? 0 \
+ : (_) == 'B' ? 1 \
+ : (_) == 'C' ? 2 \
+ : (_) == 'D' ? 3 \
+ : (_) == 'E' ? 4 \
+ : (_) == 'F' ? 5 \
+ : (_) == 'G' ? 6 \
+ : (_) == 'H' ? 7 \
+ : (_) == 'I' ? 8 \
+ : (_) == 'J' ? 9 \
+ : (_) == 'K' ? 10 \
+ : (_) == 'L' ? 11 \
+ : (_) == 'M' ? 12 \
+ : (_) == 'N' ? 13 \
+ : (_) == 'O' ? 14 \
+ : (_) == 'P' ? 15 \
+ : (_) == 'Q' ? 16 \
+ : (_) == 'R' ? 17 \
+ : (_) == 'S' ? 18 \
+ : (_) == 'T' ? 19 \
+ : (_) == 'U' ? 20 \
+ : (_) == 'V' ? 21 \
+ : (_) == 'W' ? 22 \
+ : (_) == 'X' ? 23 \
+ : (_) == 'Y' ? 24 \
+ : (_) == 'Z' ? 25 \
+ : (_) == '2' ? 26 \
+ : (_) == '3' ? 27 \
+ : (_) == '4' ? 28 \
+ : (_) == '5' ? 29 \
+ : (_) == '6' ? 30 \
+ : (_) == '7' ? 31 \
+ : -1)
+
+static const signed char b32[0x100] = {
+ B32 (0), B32 (1), B32 (2), B32 (3),
+ B32 (4), B32 (5), B32 (6), B32 (7),
+ B32 (8), B32 (9), B32 (10), B32 (11),
+ B32 (12), B32 (13), B32 (14), B32 (15),
+ B32 (16), B32 (17), B32 (18), B32 (19),
+ B32 (20), B32 (21), B32 (22), B32 (23),
+ B32 (24), B32 (25), B32 (26), B32 (27),
+ B32 (28), B32 (29), B32 (30), B32 (31),
+ B32 (32), B32 (33), B32 (34), B32 (35),
+ B32 (36), B32 (37), B32 (38), B32 (39),
+ B32 (40), B32 (41), B32 (42), B32 (43),
+ B32 (44), B32 (45), B32 (46), B32 (47),
+ B32 (48), B32 (49), B32 (50), B32 (51),
+ B32 (52), B32 (53), B32 (54), B32 (55),
+ B32 (56), B32 (57), B32 (58), B32 (59),
+ B32 (60), B32 (61), B32 (62), B32 (63),
+ B32 (64), B32 (65), B32 (66), B32 (67),
+ B32 (68), B32 (69), B32 (70), B32 (71),
+ B32 (72), B32 (73), B32 (74), B32 (75),
+ B32 (76), B32 (77), B32 (78), B32 (79),
+ B32 (80), B32 (81), B32 (82), B32 (83),
+ B32 (84), B32 (85), B32 (86), B32 (87),
+ B32 (88), B32 (89), B32 (90), B32 (91),
+ B32 (92), B32 (93), B32 (94), B32 (95),
+ B32 (96), B32 (97), B32 (98), B32 (99),
+ B32 (100), B32 (101), B32 (102), B32 (103),
+ B32 (104), B32 (105), B32 (106), B32 (107),
+ B32 (108), B32 (109), B32 (110), B32 (111),
+ B32 (112), B32 (113), B32 (114), B32 (115),
+ B32 (116), B32 (117), B32 (118), B32 (119),
+ B32 (120), B32 (121), B32 (122), B32 (123),
+ B32 (124), B32 (125), B32 (126), B32 (127),
+ B32 (128), B32 (129), B32 (130), B32 (131),
+ B32 (132), B32 (133), B32 (134), B32 (135),
+ B32 (136), B32 (137), B32 (138), B32 (139),
+ B32 (140), B32 (141), B32 (142), B32 (143),
+ B32 (144), B32 (145), B32 (146), B32 (147),
+ B32 (148), B32 (149), B32 (150), B32 (151),
+ B32 (152), B32 (153), B32 (154), B32 (155),
+ B32 (156), B32 (157), B32 (158), B32 (159),
+ B32 (160), B32 (161), B32 (162), B32 (163),
+ B32 (164), B32 (165), B32 (166), B32 (167),
+ B32 (168), B32 (169), B32 (170), B32 (171),
+ B32 (172), B32 (173), B32 (174), B32 (175),
+ B32 (176), B32 (177), B32 (178), B32 (179),
+ B32 (180), B32 (181), B32 (182), B32 (183),
+ B32 (184), B32 (185), B32 (186), B32 (187),
+ B32 (188), B32 (189), B32 (190), B32 (191),
+ B32 (192), B32 (193), B32 (194), B32 (195),
+ B32 (196), B32 (197), B32 (198), B32 (199),
+ B32 (200), B32 (201), B32 (202), B32 (203),
+ B32 (204), B32 (205), B32 (206), B32 (207),
+ B32 (208), B32 (209), B32 (210), B32 (211),
+ B32 (212), B32 (213), B32 (214), B32 (215),
+ B32 (216), B32 (217), B32 (218), B32 (219),
+ B32 (220), B32 (221), B32 (222), B32 (223),
+ B32 (224), B32 (225), B32 (226), B32 (227),
+ B32 (228), B32 (229), B32 (230), B32 (231),
+ B32 (232), B32 (233), B32 (234), B32 (235),
+ B32 (236), B32 (237), B32 (238), B32 (239),
+ B32 (240), B32 (241), B32 (242), B32 (243),
+ B32 (244), B32 (245), B32 (246), B32 (247),
+ B32 (248), B32 (249), B32 (250), B32 (251),
+ B32 (252), B32 (253), B32 (254), B32 (255)
+};
+
+#if UCHAR_MAX == 255
+#define uchar_in_range(c) true
+#else
+#define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base32 alphabet, and
+ false otherwise. Note that '=' is padding and not considered to be
+ part of the alphabet. */
+bool isbase32(char ch)
+{
+ return uchar_in_range(to_uchar(ch)) && 0 <= b32[to_uchar(ch)];
+}
+
+/* Decode base32 encoded input array IN of length INLEN to output
+ array OUT that can hold *OUTLEN bytes. Return true if decoding was
+ successful, i.e. if the input was valid base32 data, false
+ otherwise. If *OUTLEN is too small, as many bytes as possible will
+ be written to OUT. On return, *OUTLEN holds the length of decoded
+ bytes in OUT. Note that as soon as any non-alphabet characters are
+ encountered, decoding is stopped and false is returned. This means
+ that, when applicable, you must remove any line terminators that is
+ part of the data stream before calling this function. */
+bool base32_decode(const char *in, size_t inlen, char *out, size_t *outlen)
+{
+ size_t outleft = *outlen;
+
+ while (inlen >= 2) {
+ if (!isbase32(in[0]) || !isbase32(in[1])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[0])] << 3)
+ | (b32[to_uchar(in[1])] >> 2));
+ outleft--;
+ }
+
+ if (inlen == 2) {
+ break;
+ }
+
+ if (in[2] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[3] != '=') ||
+ (in[4] != '=') ||
+ (in[5] != '=') ||
+ (in[6] != '=') ||
+ (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32(in[2]) || !isbase32(in[3])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[1])] << 6)
+ | ((b32[to_uchar(in[2])] << 1) & 0x3E)
+ | (b32[to_uchar(in[3])] >> 4));
+ outleft--;
+ }
+
+ if (inlen == 4) {
+ break;
+ }
+
+ if (in[4] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[5] != '=') ||
+ (in[6] != '=') ||
+ (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32 (in[3]) || !isbase32(in[4])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[3])] << 4)
+ | (b32[to_uchar(in[4])] >> 1));
+ outleft--;
+ }
+
+ if (inlen == 5) {
+ break;
+ }
+
+ if (in[5] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[6] != '=')
+ || (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32 (in[5])
+ || !isbase32 (in[6])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[4])]
+ << 7)
+ | (b32[to_uchar(in[5])] << 2)
+ | (b32[to_uchar(in[6])]
+ >> 3));
+ outleft--;
+ }
+
+ if (inlen == 7) {
+ break;
+ }
+
+ if (in[7] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+ } else {
+ if (!isbase32 (in[7])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ =
+ ((b32[to_uchar(in[6])]
+ << 5) | (b32[
+ to_uchar(in[7])]));
+ outleft--;
+ }
+ }
+ }
+ }
+ }
+
+ in += 8;
+ inlen -= 8;
+ }
+
+ *outlen -= outleft;
+
+ if (inlen != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base32 encoded
+ data stored in IN of size INLEN to the *OUT buffer. On return, the
+ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
+ if the caller is not interested in the decoded length. *OUT may be
+ NULL to indicate an out of memory error, in which case *OUTLEN
+ contains the size of the memory block needed. The function returns
+ true on successful decoding and memory allocation errors. (Use the
+ *OUT and *OUTLEN parameters to differentiate between successful
+ decoding and memory error.) The function returns false if the
+ input was invalid, in which case *OUT is NULL and *OUTLEN is
+ undefined. */
+bool base32_decode_alloc(const char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ /* This may allocate a few bytes too much, depending on input,
+ but it's not worth the extra CPU time to compute the exact amount.
+ The exact amount is 5 * inlen / 8, minus 1 if the input ends
+ with "=" and minus another 1 if the input ends with "==", etc.
+ Dividing before multiplying avoids the possibility of overflow. */
+ size_t needlen = 5 * (inlen / 8) + 4;
+
+ *out = malloc(needlen);
+ if (!*out) {
+ return true;
+ }
+
+ if (!base32_decode(in, inlen, *out, &needlen)) {
+ free (*out);
+ *out = NULL;
+ return false;
+ }
+
+ if (outlen) {
+ *outlen = needlen;
+ }
+
+ return true;
+}
+
+#ifdef MAIN
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include "base32.h"
+
+int main(int argc, char **argv) {
+ int i = 1;
+ size_t inlen, outlen, argvlen;
+ char *out;
+ char *in;
+ bool ok;
+
+ while (argc > 1) {
+ argv++; argc--;
+ argvlen = strlen(*argv);
+
+ outlen = base32_encode_alloc(*argv, argvlen, &out);
+
+ if (out == NULL && outlen == 0 && inlen != 0) {
+ fprintf(stderr, "ERROR(encode): input too long: %zd\n",
+ outlen);
+ return 1;
+ }
+
+ if (out == NULL) {
+ fprintf(stderr, "ERROR(encode): memory allocation error"
+ "\n");
+ return 1;
+ }
+
+ ok = base32_decode_alloc(out, outlen, &in, &inlen);
+
+ if (!ok) {
+ fprintf(stderr, "ERROR(decode): input was not valid "
+ "base32: `%s'\n", out);
+ return 1;
+ }
+
+ if (in == NULL) {
+ fprintf(stderr, "ERROR(decode): memory allocation "
+ "error\n");
+ }
+
+ if ((inlen != argvlen) ||
+ strcmp(*argv, in) != 0) {
+ fprintf(stderr, "ERROR(encode/decode): input `%s' and "
+ "output `%s'\n", *argv, in);
+ return 1;
+ }
+ printf("INPUT: `%s'\nENCODE: `%s'\nDECODE: `%s'\n", *argv, out,
+ in);
+ }
+}
+
+#endif
diff --git a/src/common/base32.h b/src/common/base32.h
new file mode 100644
index 0000000..45df9fa
--- /dev/null
+++ b/src/common/base32.h
@@ -0,0 +1,121 @@
+/* base32.h -- Encode binary data using printable characters.
+ Copyright (C) 2004, 2005, 2006, 2010 Free Software Foundation, Inc.
+ Written by Ondřej Surý & Simon Josefsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _BASE32_H_
+#define _BASE32_H_
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get bool. */
+#include <stdbool.h>
+
+/*!
+ * \brief Counts the size of the Base32-encoded output for given input length.
+ *
+ * \note This uses that the expression (n+(k-1))/k means the smallest
+ * integer >= n/k, i.e., the ceiling of n/k.
+ */
+#define BASE32_LENGTH(inlen) ((((inlen) + 4) / 5) * 8)
+
+/*!
+ * \brief Checks if the given character belongs to the Base32 alphabet.
+ *
+ * \param ch Character to check.
+ *
+ * \retval true if \a ch belongs to the Base32 alphabet.
+ * \retval false otherwise.
+ */
+extern bool isbase32(char ch);
+
+/*!
+ * \brief Encodes the given character array using Base32 encoding.
+ *
+ * If \a outlen is less than BASE32_LENGTH(\a inlen), the function writes as
+ * many bytes as possible to the output buffer. If \a outlen is more than
+ * BASE32_LENGTH(\a inlen), the output will be zero-terminated.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ * \param outlen Size of the output buffer.
+ */
+extern void base32_encode(const char *in, size_t inlen, char *out,
+ size_t outlen);
+
+/*!
+ * \brief Encodes the given character array using Base32 encoding and allocates
+ * space for the output.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ *
+ * \return Size of the allocated output buffer (0 if failed).
+ */
+extern size_t base32_encode_alloc(const char *in, size_t inlen, char **out);
+
+/*!
+ * \brief Decodes the given character array in Base32 encoding.
+ *
+ * If \a *outlen is too small, as many bytes as possible will be written to
+ * \a out. On return, \a *outlen holds the length of decoded bytes in \a out.
+ *
+ * \note As soon as any non-alphabet characters are encountered, decoding is
+ * stopped and false is returned. This means that, when applicable, you
+ * must remove any line terminators that is part of the data stream before
+ * calling this function.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ * \param outlen Size of the output buffer.
+ *
+ * \retval true if decoding was successful, i.e. if the input was valid base32
+ * data.
+ * \retval false otherwise.
+ */
+extern bool base32_decode(const char *in, size_t inlen, char *out,
+ size_t *outlen);
+
+/*!
+ * \brief Allocate an output buffer and decode the base32 encoded data to it.
+ *
+ * On return, the size of the decoded data is stored in \a *outlen. \a outlen
+ * may be NULL, if the caller is not interested in the decoded length. \a *out
+ * may be NULL to indicate an out of memory error, in which case \a *outlen
+ * contains the size of the memory block needed.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer. \a *out may be NULL to indicate an out of memory
+ * error in which case \a *outlen contains the size of the memory
+ * block needed
+ * \param outlen Size of the output buffer. May be NULL, if the caller is not
+ * interested in the decoded length
+ *
+ * \retval true on successful decoding and memory allocation errors. (Use the
+ * \a *out and \a *outlen parameters to differentiate between
+ * successful decoding and memory error.)
+ * \retval false if the input was invalid, in which case \a *out is NULL and
+ * \a *outlen is undefined.
+ */
+extern bool base32_decode_alloc(const char *in, size_t inlen, char **out,
+ size_t *outlen);
+
+#endif /* _BASE32_H_ */
diff --git a/src/common/base32hex.c b/src/common/base32hex.c
new file mode 100644
index 0000000..cd2d2ce
--- /dev/null
+++ b/src/common/base32hex.c
@@ -0,0 +1,562 @@
+/* base32hex.c -- Encode binary data using printable characters.
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2010 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Adapted from base32.{h,c}. base32.{h,c} was adapted from
+ * base64.{h,c} by Ondřej Surý. base64.{h,c} was written by Simon
+ * Josefsson. Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking. Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base32hex_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ * FAIL: input was not valid base32hex
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base32hex_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ * FAIL: input too long
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+/* Get prototype. */
+#include "base32hex.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char to_uchar(char ch)
+{
+ return ch;
+}
+
+/* Base32hex encode IN array of size INLEN into OUT array of size OUTLEN.
+ If OUTLEN is less than BASE32HEX_LENGTH(INLEN), write as many bytes as
+ possible. If OUTLEN is larger than BASE32HEX_LENGTH(INLEN), also zero
+ terminate the output buffer. */
+void base32hex_encode(const char *in, size_t inlen, char *out, size_t outlen)
+{
+ static const char b32str[32] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+
+ while (inlen && outlen) {
+ *out++ = b32str[(to_uchar(in[0]) >> 3) & 0x1f];
+ if (!--outlen) {
+ break;
+ }
+ *out++ = b32str[((to_uchar(in[0]) << 2)
+ + (--inlen ? to_uchar(in[1]) >> 6 : 0))
+ & 0x1f];
+ if (!--outlen) {
+ break;
+ }
+ *out++ =(inlen
+ ? b32str[(to_uchar(in[1]) >> 1) & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[1]) << 4)
+ + (--inlen ? to_uchar(in[2]) >> 4 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[2]) << 1)
+ + (--inlen ? to_uchar(in[3]) >> 7 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[(to_uchar(in[3]) >> 2) & 0x1f]
+ : '=');
+ if (!--outlen)
+ {
+ break;
+ }
+ *out++ = (inlen
+ ? b32str[((to_uchar(in[3]) << 3)
+ + (--inlen ? to_uchar(in[4]) >> 5 : 0))
+ & 0x1f]
+ : '=');
+ if (!--outlen) {
+ break;
+ }
+ *out++ = inlen ? b32str[to_uchar(in[4]) & 0x1f] : '=';
+ if (!--outlen) {
+ break;
+ }
+ if (inlen) {
+ inlen--;
+ }
+ if (inlen) {
+ in += 5;
+ }
+ }
+
+ if (outlen) {
+ *out = '\0';
+ }
+}
+
+/* Allocate a buffer and store zero terminated base32hex encoded data
+ from array IN of size INLEN, returning BASE32HEX_LENGTH(INLEN), i.e.,
+ the length of the encoded data, excluding the terminating zero. On
+ return, the OUT variable will hold a pointer to newly allocated
+ memory that must be deallocated by the caller. If output string
+ length would overflow, 0 is returned and OUT is set to NULL. If
+ memory allocation failed, OUT is set to NULL, and the return value
+ indicates length of the requested memory block, i.e.,
+ BASE32HEX_LENGTH(inlen) + 1. */
+size_t base32hex_encode_alloc(const char *in, size_t inlen, char **out)
+{
+ size_t outlen = 1 + BASE32HEX_LENGTH (inlen);
+
+ /* Check for overflow in outlen computation.
+ *
+ * If there is no overflow, outlen >= inlen.
+ *
+ * If the operation (inlen + 2) overflows then it yields at most +1, so
+ * outlen is 0.
+ *
+ * If the multiplication overflows, we lose at least half of the
+ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+ * (inlen > 4).
+ */
+ if (inlen > outlen)
+ {
+ *out = NULL;
+ return 0;
+ }
+
+ *out = malloc(outlen);
+ if (!*out) {
+ return outlen;
+ }
+
+ base32hex_encode(in, inlen, *out, outlen);
+
+ return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+ (think EBCDIC). However, it does assume that the characters in the
+ Base32hex alphabet (A-Z2-7) are encoded in 0..255. POSIX
+ 1003.1-2001 require that char and unsigned char are 8-bit
+ quantities, though, taking care of that problem. But this may be a
+ potential problem on non-POSIX C99 platforms.
+
+ IBM C V6 for AIX mishandles "#define B32(x) ...'x'...", so use "_"
+ as the formal parameter rather than "x". */
+#define B32(_) \
+ ((_) == '0' ? 0 \
+ : (_) == '1' ? 1 \
+ : (_) == '2' ? 2 \
+ : (_) == '3' ? 3 \
+ : (_) == '4' ? 4 \
+ : (_) == '5' ? 5 \
+ : (_) == '6' ? 6 \
+ : (_) == '7' ? 7 \
+ : (_) == '8' ? 8 \
+ : (_) == '9' ? 9 \
+ : (_) == 'A' ? 10 \
+ : (_) == 'B' ? 11 \
+ : (_) == 'C' ? 12 \
+ : (_) == 'D' ? 13 \
+ : (_) == 'E' ? 14 \
+ : (_) == 'F' ? 15 \
+ : (_) == 'G' ? 16 \
+ : (_) == 'H' ? 17 \
+ : (_) == 'I' ? 18 \
+ : (_) == 'J' ? 19 \
+ : (_) == 'K' ? 20 \
+ : (_) == 'L' ? 21 \
+ : (_) == 'M' ? 22 \
+ : (_) == 'N' ? 23 \
+ : (_) == 'O' ? 24 \
+ : (_) == 'P' ? 25 \
+ : (_) == 'Q' ? 26 \
+ : (_) == 'R' ? 27 \
+ : (_) == 'S' ? 28 \
+ : (_) == 'T' ? 29 \
+ : (_) == 'U' ? 30 \
+ : (_) == 'V' ? 31 \
+ : (_) == 'a' ? 10 \
+ : (_) == 'b' ? 11 \
+ : (_) == 'c' ? 12 \
+ : (_) == 'd' ? 13 \
+ : (_) == 'e' ? 14 \
+ : (_) == 'f' ? 15 \
+ : (_) == 'g' ? 16 \
+ : (_) == 'h' ? 17 \
+ : (_) == 'i' ? 18 \
+ : (_) == 'j' ? 19 \
+ : (_) == 'k' ? 20 \
+ : (_) == 'l' ? 21 \
+ : (_) == 'm' ? 22 \
+ : (_) == 'n' ? 23 \
+ : (_) == 'o' ? 24 \
+ : (_) == 'p' ? 25 \
+ : (_) == 'q' ? 26 \
+ : (_) == 'r' ? 27 \
+ : (_) == 's' ? 28 \
+ : (_) == 't' ? 29 \
+ : (_) == 'u' ? 30 \
+ : (_) == 'v' ? 31 \
+ : -1)
+
+static const signed char b32[0x100] = {
+ B32 (0), B32 (1), B32 (2), B32 (3),
+ B32 (4), B32 (5), B32 (6), B32 (7),
+ B32 (8), B32 (9), B32 (10), B32 (11),
+ B32 (12), B32 (13), B32 (14), B32 (15),
+ B32 (16), B32 (17), B32 (18), B32 (19),
+ B32 (20), B32 (21), B32 (22), B32 (23),
+ B32 (24), B32 (25), B32 (26), B32 (27),
+ B32 (28), B32 (29), B32 (30), B32 (31),
+ B32 (32), B32 (33), B32 (34), B32 (35),
+ B32 (36), B32 (37), B32 (38), B32 (39),
+ B32 (40), B32 (41), B32 (42), B32 (43),
+ B32 (44), B32 (45), B32 (46), B32 (47),
+ B32 (48), B32 (49), B32 (50), B32 (51),
+ B32 (52), B32 (53), B32 (54), B32 (55),
+ B32 (56), B32 (57), B32 (58), B32 (59),
+ B32 (60), B32 (61), B32 (62), B32 (63),
+ B32 (64), B32 (65), B32 (66), B32 (67),
+ B32 (68), B32 (69), B32 (70), B32 (71),
+ B32 (72), B32 (73), B32 (74), B32 (75),
+ B32 (76), B32 (77), B32 (78), B32 (79),
+ B32 (80), B32 (81), B32 (82), B32 (83),
+ B32 (84), B32 (85), B32 (86), B32 (87),
+ B32 (88), B32 (89), B32 (90), B32 (91),
+ B32 (92), B32 (93), B32 (94), B32 (95),
+ B32 (96), B32 (97), B32 (98), B32 (99),
+ B32 (100), B32 (101), B32 (102), B32 (103),
+ B32 (104), B32 (105), B32 (106), B32 (107),
+ B32 (108), B32 (109), B32 (110), B32 (111),
+ B32 (112), B32 (113), B32 (114), B32 (115),
+ B32 (116), B32 (117), B32 (118), B32 (119),
+ B32 (120), B32 (121), B32 (122), B32 (123),
+ B32 (124), B32 (125), B32 (126), B32 (127),
+ B32 (128), B32 (129), B32 (130), B32 (131),
+ B32 (132), B32 (133), B32 (134), B32 (135),
+ B32 (136), B32 (137), B32 (138), B32 (139),
+ B32 (140), B32 (141), B32 (142), B32 (143),
+ B32 (144), B32 (145), B32 (146), B32 (147),
+ B32 (148), B32 (149), B32 (150), B32 (151),
+ B32 (152), B32 (153), B32 (154), B32 (155),
+ B32 (156), B32 (157), B32 (158), B32 (159),
+ B32 (160), B32 (161), B32 (162), B32 (163),
+ B32 (164), B32 (165), B32 (166), B32 (167),
+ B32 (168), B32 (169), B32 (170), B32 (171),
+ B32 (172), B32 (173), B32 (174), B32 (175),
+ B32 (176), B32 (177), B32 (178), B32 (179),
+ B32 (180), B32 (181), B32 (182), B32 (183),
+ B32 (184), B32 (185), B32 (186), B32 (187),
+ B32 (188), B32 (189), B32 (190), B32 (191),
+ B32 (192), B32 (193), B32 (194), B32 (195),
+ B32 (196), B32 (197), B32 (198), B32 (199),
+ B32 (200), B32 (201), B32 (202), B32 (203),
+ B32 (204), B32 (205), B32 (206), B32 (207),
+ B32 (208), B32 (209), B32 (210), B32 (211),
+ B32 (212), B32 (213), B32 (214), B32 (215),
+ B32 (216), B32 (217), B32 (218), B32 (219),
+ B32 (220), B32 (221), B32 (222), B32 (223),
+ B32 (224), B32 (225), B32 (226), B32 (227),
+ B32 (228), B32 (229), B32 (230), B32 (231),
+ B32 (232), B32 (233), B32 (234), B32 (235),
+ B32 (236), B32 (237), B32 (238), B32 (239),
+ B32 (240), B32 (241), B32 (242), B32 (243),
+ B32 (244), B32 (245), B32 (246), B32 (247),
+ B32 (248), B32 (249), B32 (250), B32 (251),
+ B32 (252), B32 (253), B32 (254), B32 (255)
+};
+
+#if UCHAR_MAX == 255
+#define uchar_in_range(c) true
+#else
+#define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base32hex alphabet, and
+ false otherwise. Note that '=' is padding and not considered to be
+ part of the alphabet. */
+bool isbase32hex(char ch)
+{
+ return uchar_in_range(to_uchar(ch)) && 0 <= b32[to_uchar(ch)];
+}
+
+/* Decode base32hex encoded input array IN of length INLEN to output
+ array OUT that can hold *OUTLEN bytes. Return true if decoding was
+ successful, i.e. if the input was valid base32hex data, false
+ otherwise. If *OUTLEN is too small, as many bytes as possible will
+ be written to OUT. On return, *OUTLEN holds the length of decoded
+ bytes in OUT. Note that as soon as any non-alphabet characters are
+ encountered, decoding is stopped and false is returned. This means
+ that, when applicable, you must remove any line terminators that is
+ part of the data stream before calling this function. */
+bool base32hex_decode(const char *in, size_t inlen, char *out, size_t *outlen)
+{
+ size_t outleft = *outlen;
+
+ while (inlen >= 2) {
+ if (!isbase32hex(in[0]) || !isbase32hex(in[1])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[0])] << 3)
+ | (b32[to_uchar(in[1])] >> 2));
+ outleft--;
+ }
+
+ if (inlen == 2) {
+ break;
+ }
+
+ if (in[2] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[3] != '=') ||
+ (in[4] != '=') ||
+ (in[5] != '=') ||
+ (in[6] != '=') ||
+ (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32hex(in[2]) || !isbase32hex(in[3])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[1])] << 6)
+ | ((b32[to_uchar(in[2])] << 1) & 0x3E)
+ | (b32[to_uchar(in[3])] >> 4));
+ outleft--;
+ }
+
+ if (inlen == 4) {
+ break;
+ }
+
+ if (in[4] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[5] != '=') ||
+ (in[6] != '=') ||
+ (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32hex (in[3]) || !isbase32hex(in[4])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[3])] << 4)
+ | (b32[to_uchar(in[4])] >> 1));
+ outleft--;
+ }
+
+ if (inlen == 5) {
+ break;
+ }
+
+ if (in[5] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+
+ if ((in[6] != '=')
+ || (in[7] != '=')) {
+ break;
+ }
+ } else {
+ if (!isbase32hex (in[5])
+ || !isbase32hex (in[6])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ = ((b32[to_uchar(in[4])]
+ << 7)
+ | (b32[to_uchar(in[5])] << 2)
+ | (b32[to_uchar(in[6])]
+ >> 3));
+ outleft--;
+ }
+
+ if (inlen == 7) {
+ break;
+ }
+
+ if (in[7] == '=') {
+ if (inlen != 8) {
+ break;
+ }
+ } else {
+ if (!isbase32hex (in[7])) {
+ break;
+ }
+
+ if (outleft) {
+ *out++ =
+ ((b32[to_uchar(in[6])]
+ << 5) | (b32[
+ to_uchar(in[7])]));
+ outleft--;
+ }
+ }
+ }
+ }
+ }
+
+ in += 8;
+ inlen -= 8;
+ }
+
+ *outlen -= outleft;
+
+ if (inlen != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base32hex encoded
+ data stored in IN of size INLEN to the *OUT buffer. On return, the
+ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
+ if the caller is not interested in the decoded length. *OUT may be
+ NULL to indicate an out of memory error, in which case *OUTLEN
+ contains the size of the memory block needed. The function returns
+ true on successful decoding and memory allocation errors. (Use the
+ *OUT and *OUTLEN parameters to differentiate between successful
+ decoding and memory error.) The function returns false if the
+ input was invalid, in which case *OUT is NULL and *OUTLEN is
+ undefined. */
+bool base32hex_decode_alloc(const char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ /* This may allocate a few bytes too much, depending on input,
+ but it's not worth the extra CPU time to compute the exact amount.
+ The exact amount is 5 * inlen / 8, minus 1 if the input ends
+ with "=" and minus another 1 if the input ends with "==", etc.
+ Dividing before multiplying avoids the possibility of overflow. */
+ size_t needlen = 5 * (inlen / 8) + 4;
+
+ *out = malloc(needlen);
+ if (!*out) {
+ return true;
+ }
+
+ if (!base32hex_decode(in, inlen, *out, &needlen)) {
+ free (*out);
+ *out = NULL;
+ return false;
+ }
+
+ if (outlen) {
+ *outlen = needlen;
+ }
+
+ return true;
+}
+
+#ifdef MAIN
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include "base32hex.h"
+
+int main(int argc, char **argv) {
+ int i = 1;
+ size_t inlen, outlen, argvlen;
+ char *out;
+ char *in;
+ bool ok;
+
+ while (argc > 1) {
+ argv++; argc--;
+ argvlen = strlen(*argv);
+
+ outlen = base32hex_encode_alloc(*argv, argvlen, &out);
+
+ if (out == NULL && outlen == 0 && inlen != 0) {
+ fprintf(stderr, "ERROR(encode): input too long: %zd\n",
+ outlen);
+ return 1;
+ }
+
+ if (out == NULL) {
+ fprintf(stderr, "ERROR(encode): memory allocation error"
+ "\n");
+ return 1;
+ }
+
+ ok = base32hex_decode_alloc(out, outlen, &in, &inlen);
+
+ if (!ok) {
+ fprintf(stderr, "ERROR(decode): input was not valid "
+ "base32hex: `%s'\n", out);
+ return 1;
+ }
+
+ if (in == NULL) {
+ fprintf(stderr, "ERROR(decode): memory allocation "
+ "error\n");
+ }
+
+ if ((inlen != argvlen) ||
+ strcmp(*argv, in) != 0) {
+ fprintf(stderr, "ERROR(encode/decode): input `%s' and "
+ "output `%s'\n", *argv, in);
+ return 1;
+ }
+ printf("INPUT: `%s'\nENCODE: `%s'\nDECODE: `%s'\n", *argv, out,
+ in);
+ }
+}
+
+#endif
diff --git a/src/common/base32hex.h b/src/common/base32hex.h
new file mode 100644
index 0000000..9ac4fa8
--- /dev/null
+++ b/src/common/base32hex.h
@@ -0,0 +1,124 @@
+/* base32hex.h -- Encode binary data using printable characters.
+ Copyright (C) 2004, 2005, 2006, 2010 Free Software Foundation, Inc.
+ Written by Ondřej Surý & Simon Josefsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _BASE32HEX_H_
+#define _BASE32HEX_H_
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get bool. */
+#include <stdbool.h>
+
+/*!
+ * \brief Counts the size of the Base32Hex-encoded output for given input
+ * length.
+ *
+ * \note This uses that the expression (n+(k-1))/k means the smallest
+ * integer >= n/k, i.e., the ceiling of n/k.
+ */
+#define BASE32HEX_LENGTH(inlen) ((((inlen) + 4) / 5) * 8)
+
+/*!
+ * \brief Checks if the given character belongs to the Base32Hex alphabet.
+ *
+ * \param ch Character to check.
+ *
+ * \retval true if \a ch belongs to the Base32Hex alphabet.
+ * \retval false otherwise.
+ */
+extern bool isbase32hex(char ch);
+
+/*!
+ * \brief Encodes the given character array using Base32 encoding with extended
+ * hex alphabet.
+ *
+ * If \a outlen is less than BASE32HEX_LENGTH(\a inlen), the function writes as
+ * many bytes as possible to the output buffer. If \a outlen is more than
+ * BASE32HEX_LENGTH(\a inlen), the output will be zero-terminated.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ * \param outlen Size of the output buffer.
+ */
+extern void base32hex_encode(const char *in, size_t inlen, char *out,
+ size_t outlen);
+
+/*!
+ * \brief Encodes the given character array using Base32 encoding with extended
+ * hex alphabet and allocates space for the output.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ *
+ * \return Size of the allocated output buffer (0 if failed).
+ */
+extern size_t base32hex_encode_alloc(const char *in, size_t inlen, char **out);
+
+/*!
+ * \brief Decodes the given character array in Base32 encoding with extended
+ * hex alphabet.
+ *
+ * If \a *outlen is too small, as many bytes as possible will be written to
+ * \a out. On return, \a *outlen holds the length of decoded bytes in \a out.
+ *
+ * \note As soon as any non-alphabet characters are encountered, decoding is
+ * stopped and false is returned. This means that, when applicable, you
+ * must remove any line terminators that is part of the data stream before
+ * calling this function.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer.
+ * \param outlen Size of the output buffer.
+ *
+ * \retval true if decoding was successful, i.e. if the input was valid
+ * base32hex data.
+ * \retval false otherwise.
+ */
+extern bool base32hex_decode(const char *in, size_t inlen, char *out,
+ size_t *outlen);
+
+/*!
+ * \brief Allocate an output buffer and decode the base32hex encoded data to it.
+ *
+ * On return, the size of the decoded data is stored in \a *outlen. \a outlen
+ * may be NULL, if the caller is not interested in the decoded length. \a *out
+ * may be NULL to indicate an out of memory error, in which case \a *outlen
+ * contains the size of the memory block needed.
+ *
+ * \param in Input array of characters.
+ * \param inlen Length of the input array.
+ * \param out Output buffer. \a *out may be NULL to indicate an out of memory
+ * error in which case \a *outlen contains the size of the memory
+ * block needed
+ * \param outlen Size of the output buffer. May be NULL, if the caller is not
+ * interested in the decoded length
+ *
+ * \retval true on successful decoding and memory allocation errors. (Use the
+ * \a *out and \a *outlen parameters to differentiate between
+ * successful decoding and memory error.)
+ * \retval false if the input was invalid, in which case \a *out is NULL and
+ * \a *outlen is undefined.
+ */
+extern bool base32hex_decode_alloc(const char *in, size_t inlen, char **out,
+ size_t *outlen);
+
+#endif /* _BASE32HEX_H_ */
diff --git a/src/common/crc.c b/src/common/crc.c
new file mode 100644
index 0000000..33bf903
--- /dev/null
+++ b/src/common/crc.c
@@ -0,0 +1,155 @@
+/*
+ Copyright (c) 2006-2011, Thomas Pircher <tehpeh@gmx.net>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * \file crc.c
+ * Functions and types for CRC checks.
+ *
+ * Generated on Fri May 6 11:25:47 2011,
+ * by pycrc v0.7.7, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 32
+ * Poly = 0x04c11db7
+ * XorIn = 0xffffffff
+ * ReflectIn = True
+ * XorOut = 0xffffffff
+ * ReflectOut = True
+ * Algorithm = table-driven
+ *****************************************************************************/
+#include "crc.h"
+#include <stdint.h>
+#include <stdlib.h>
+
+/**
+ * Static table used for the table_driven implementation.
+ *****************************************************************************/
+static const crc_t crc_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/**
+ * Reflect all bits of a \a data word of \a data_len bytes.
+ *
+ * \param data The data word to be reflected.
+ * \param data_len The width of \a data expressed in number of bits.
+ * \return The reflected data.
+ *****************************************************************************/
+crc_t crc_reflect(crc_t data, size_t data_len)
+{
+ unsigned int i;
+ crc_t ret;
+
+ ret = data & 0x01;
+ for (i = 1; i < data_len; i++) {
+ data >>= 1;
+ ret = (ret << 1) | (data & 0x01);
+ }
+ return ret;
+}
+
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len)
+{
+ unsigned int tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = (crc ^ *data) & 0xff;
+ crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff;
+
+ data++;
+ }
+ return crc & 0xffffffff;
+}
+
+
+
diff --git a/src/common/crc.h b/src/common/crc.h
new file mode 100644
index 0000000..41971a9
--- /dev/null
+++ b/src/common/crc.h
@@ -0,0 +1,110 @@
+/*
+ Copyright (c) 2006-2011, Thomas Pircher <tehpeh@gmx.net>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+/**
+ * \file crc.h
+ * Functions and types for CRC checks.
+ *
+ * Generated on Fri May 6 11:25:43 2011,
+ * by pycrc v0.7.7, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 32
+ * Poly = 0x04c11db7
+ * XorIn = 0xffffffff
+ * ReflectIn = True
+ * XorOut = 0xffffffff
+ * ReflectOut = True
+ * Algorithm = table-driven
+ *****************************************************************************/
+#ifndef __CRC_H__
+#define __CRC_H__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * The definition of the used algorithm.
+ *****************************************************************************/
+#define CRC_ALGO_TABLE_DRIVEN 1
+
+
+/**
+ * The type of the CRC values.
+ *
+ * This type must be big enough to contain at least 32 bits.
+ *****************************************************************************/
+typedef uint32_t crc_t;
+
+
+/**
+ * Reflect all bits of a \a data word of \a data_len bytes.
+ *
+ * \param data The data word to be reflected.
+ * \param data_len The width of \a data expressed in number of bits.
+ * \return The reflected data.
+ *****************************************************************************/
+crc_t crc_reflect(crc_t data, size_t data_len);
+
+
+/**
+ * Calculate the initial crc value.
+ *
+ * \return The initial crc value.
+ *****************************************************************************/
+static inline crc_t crc_init(void)
+{
+ return 0xffffffff;
+}
+
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len);
+
+
+/**
+ * Calculate the final crc value.
+ *
+ * \param crc The current crc value.
+ * \return The final crc value.
+ *****************************************************************************/
+static inline crc_t crc_finalize(crc_t crc)
+{
+ return crc ^ 0xffffffff;
+}
+
+
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
+
+#endif /* __CRC_H__ */
diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c
new file mode 100644
index 0000000..1e2efac
--- /dev/null
+++ b/src/common/dynamic-array.c
@@ -0,0 +1,224 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <pthread.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include <urcu.h>
+
+//#include "common.h"
+#include "common/dynamic-array.h"
+
+#ifndef ERR_ALLOC_FAILED
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d\n", \
+ __FILE__, __LINE__)
+#endif
+
+//#define DA_DEBUG
+
+#ifndef dbg_da
+#ifdef DA_DEBUG
+#define dbg_da(msg...) fprintf(stderr, msg)
+#else
+#define dbg_da(msg...)
+#endif
+#endif
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+
+enum da_resize_type {
+ DA_LARGER, DA_SMALLER
+};
+
+typedef enum da_resize_type da_resize_type_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \retval 1 On success.
+ * \retval -1 On failure.
+ */
+static int da_resize(da_array_t *array, da_resize_type_t type)
+{
+ dbg_da("da_resize(): array pointer: %p, items pointer: %p\n", array,
+ array->items);
+
+ unsigned new_size = ((type == DA_LARGER)
+ ? (array->allocated *= 2)
+ : (array->allocated /= 2));
+
+ void *new_items = malloc(new_size * array->item_size);
+ if (new_items == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ dbg_da("Place for new items: %p\n", new_items);
+
+ // copy the contents from the old array to the new
+ memcpy(new_items, array->items, array->count * array->item_size);
+
+ // do RCU update
+ void *old_items = rcu_xchg_pointer(&array->items, new_items);
+ array->allocated = new_size;
+
+ dbg_da("Old items pointer: %p\n", old_items);
+
+ // wait for readers to finish
+ synchronize_rcu();
+ // deallocate the old array
+ dbg_da("RCU synchronized, deallocating old items array at address %p."
+ "\n", old_items);
+ free(old_items);
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+da_array_t *da_create(unsigned count, size_t item_size)
+{
+ da_array_t *a = (da_array_t *)malloc(sizeof(da_array_t));
+ if (a == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ da_initialize(a, count, item_size);
+ return a;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int da_initialize(da_array_t *array, unsigned count, size_t item_size)
+{
+ assert(array != NULL);
+ pthread_mutex_init(&array->mtx, NULL);
+ pthread_mutex_lock(&array->mtx);
+
+ array->items = malloc(count * item_size);
+ if (array->items == NULL) {
+ array->allocated = 0;
+ array->count = 0;
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ array->allocated = count;
+ array->count = 0;
+ array->item_size = item_size;
+ memset(array->items, 0, count * item_size);
+
+ pthread_mutex_unlock(&array->mtx);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int da_reserve(da_array_t *array, unsigned count)
+{
+ pthread_mutex_lock(&array->mtx);
+ unsigned res = 0;
+
+ assert(array->allocated >= array->count);
+ if ((array->allocated - array->count) >= count) {
+ dbg_da("Enough place in the array, no resize needed.\n");
+ res = 0;
+ } else {
+ dbg_da("Resizing array.\n");
+ res = da_resize(array, DA_LARGER);
+ }
+ pthread_mutex_unlock(&array->mtx);
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int da_occupy(da_array_t *array, unsigned count)
+{
+ pthread_mutex_lock(&array->mtx);
+ unsigned res = 0;
+ assert(array->allocated >= array->count);
+
+ if ((array->allocated - array->count) < count) {
+ dbg_da("Not enough place to occupy.\n");
+ res = -1;
+ } else {
+ array->count += count;
+ }
+
+ pthread_mutex_unlock(&array->mtx);
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned da_try_reserve(const da_array_t *array, unsigned count)
+{
+ assert(array->allocated >= array->count);
+ if ((array->allocated - array->count) >= count) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void da_release(da_array_t *array, unsigned count)
+{
+ pthread_mutex_lock(&array->mtx);
+
+ assert(array->allocated >= array->count);
+ assert(array->count >= count);
+ dbg_da("Decreasing count of items in array.\n");
+ array->count -= count;
+
+ pthread_mutex_unlock(&array->mtx);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void da_destroy(da_array_t *array)
+{
+ pthread_mutex_lock(&array->mtx);
+ void *old_items = rcu_dereference(array->items);
+ rcu_set_pointer(&array->items, NULL);
+ pthread_mutex_unlock(&array->mtx);
+
+ synchronize_rcu();
+ free(old_items);
+ pthread_mutex_destroy(&array->mtx);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *da_get_items(const da_array_t *array)
+{
+ return array->items;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned da_get_count(const da_array_t *array)
+{
+ return array->count;
+}
diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h
new file mode 100644
index 0000000..77a5d13
--- /dev/null
+++ b/src/common/dynamic-array.h
@@ -0,0 +1,156 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file dynamic-array.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Safe dynamic array implementation.
+ *
+ * \todo Somehow check if the array is initialized and do not use otherwise.
+ * Maybe some magic, or so.
+ * \todo This structure is too slow because of the mutex.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_DYNAMIC_ARRAY_H_
+#define _KNOTD_COMMON_DYNAMIC_ARRAY_H_
+
+#include <string.h>
+#include <pthread.h>
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Dynamic array structure.
+ *
+ * Before using the dynamic array, it must be initialized using da_initialize().
+ * When getting individual items always use da_get_items() to obtain pointer to
+ * the actual array.
+ *
+ * Items in the array cannot be dereferenced (it uses void * for storing the
+ * the items). It is needed to type-cast the item array (obtained by calling
+ * da_get_items()) to a proper type before dereferencing.
+ *
+ * When adding items, first reserve enough space for them by callling
+ * da_reserve() and subsequently tell the array about the inserted items by
+ * calling da_occupy(). When removing, the array must be told about the fact
+ * by calling da_release().
+ *
+ * For getting the actual number of items in array use da_get_count().
+ *
+ * When the array is no more needed, the da_destroy() function must be called
+ * before deallocating the structure.
+ */
+struct da_array {
+ /*! \brief The actual array. The items can't be dereferenced directly.*/
+ void *items;
+
+ /*!
+ * \brief Size of the stored items in bytes (used in counting of space
+ * needed.
+ */
+ size_t item_size;
+
+ /*!
+ * \brief Size of allocated space in number of items that can be stored.
+ */
+ unsigned allocated;
+
+ /*! \brief Number of items actually stored in the array. */
+ unsigned count;
+
+ /*! \brief Mutex. */
+ pthread_mutex_t mtx;
+};
+
+typedef struct da_array da_array_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates and initializes the dynamic array.
+ *
+ * Initialization comprises of allocating place for \a count items of size
+ * \a item_size and setting the items to zeros.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not successful.
+ */
+da_array_t *da_create(unsigned count, size_t item_size);
+
+/*!
+ * \brief Initializes the dynamic array.
+ *
+ * Initialization comprises of allocating place for \a count items of size
+ * \a item_size and setting the items to zeros.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if not successful.
+ */
+int da_initialize(da_array_t *array, unsigned count, size_t item_size);
+
+/*!
+ * \brief Reserves space for \a count more items.
+ *
+ * \retval 0 if successful and resizing was not necessary.
+ * \retval 1 if successful and the array was enlarged.
+ * \retval -1 if not successful - resizing was needed but could not be done.
+ */
+int da_reserve(da_array_t *array, unsigned count);
+
+/*!
+ * \brief Increases the number of items in array by \a count.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If not successful (not enough allocated space, i.e. must run
+ * da_reserve()).
+ */
+int da_occupy(da_array_t *array, unsigned count);
+
+/*!
+ * \brief Tries to reserve space for \a count more items.
+ *
+ * \retval 0 if successful and resizing is not necessary.
+ * \retval 1 if successful but the array will need to be resized.
+ */
+unsigned da_try_reserve(const da_array_t *array, unsigned count);
+
+/*!
+ * \brief Releases space taken by \a count items.
+ */
+void da_release(da_array_t *array, unsigned count);
+
+/*!
+ * \brief Poperly deallocates the array.
+ */
+void da_destroy(da_array_t *array);
+
+/*!
+ * \brief Returns the array of items as a void *.
+ */
+void *da_get_items(const da_array_t *array);
+
+/*!
+ * \brief Returns count of items in the array.
+ */
+unsigned da_get_count(const da_array_t *array);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOTD_COMMON_DYNAMIC_ARRAY_H_ */
+
+/*! @} */
diff --git a/src/common/errors.c b/src/common/errors.c
new file mode 100644
index 0000000..f1e650d
--- /dev/null
+++ b/src/common/errors.c
@@ -0,0 +1,74 @@
+/* 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common/errors.h"
+
+/*!
+ * \brief Looks up the given id in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param id ID to look up.
+ *
+ * \return Item in the lookup table with the given id or NULL if no such is
+ * present.
+ */
+static const error_table_t *error_lookup_by_id(const error_table_t *table,
+ int id)
+{
+ while (table->name != 0) {
+ if (table->id == id) {
+ return table;
+ }
+ table++;
+ }
+
+ return 0;
+}
+
+const char *error_to_str(const error_table_t *table, int code)
+{
+ const error_table_t *msg = error_lookup_by_id(table, code);
+ if (msg != 0) {
+ return msg->name;
+ } else {
+ return "Unknown error.";
+ }
+}
+
+int _map_errno(int fallback_value, int arg0, ...)
+{
+ /* Iterate all variable-length arguments. */
+ va_list ap;
+ va_start(ap, arg0);
+
+ /* KNOTD_ERROR serves as a sentinel. */
+ for (int c = arg0; c != 0; c = va_arg(ap, int)) {
+
+ /* Error code matches with mapped. */
+ if (c == errno) {
+ /* Return negative value of the code. */
+ return -abs(c);
+ }
+ }
+ va_end(ap);
+
+ /* Fallback error code. */
+ return fallback_value;
+}
diff --git a/src/common/errors.h b/src/common/errors.h
new file mode 100644
index 0000000..a2773ac
--- /dev/null
+++ b/src/common/errors.h
@@ -0,0 +1,80 @@
+/* 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 errors.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Error codes and function for getting error message.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_ERROR_H_
+#define _KNOTD_COMMON_ERROR_H_
+
+#include <errno.h>
+
+/*! \brief Error lookup table. */
+typedef struct error_table_t {
+ int id;
+ const char *name;
+} error_table_t;
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param table Table of error messages to use.
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+const char *error_to_str(const error_table_t *table, int code);
+
+/*!
+ * \brief Safe errno mapper that automatically appends sentinel value.
+ *
+ * \see _map_errno()
+ *
+ * \param fallback_value Fallback error value (used if the code could not be
+ * mapped.
+ * \param err POSIX errno.
+ * \param ... List of handled codes.
+ *
+ * \return Mapped error code.
+ */
+#define map_errno(fallback_value, err...) _map_errno(fallback_value, err, 0)
+
+/*!
+ * \brief Returns a mapped POSIX errcode.
+ *
+ * \warning Last error must be 0, it serves as a sentinel value.
+ * Use map_errno() instead.
+ *
+ * \param fallback_value Fallback error value (used if the code could not be
+ * mapped.
+ * \param arg0 First mandatory argument.
+ * \param ... List of handled codes.
+ *
+ * \return Mapped error code.
+ */
+int _map_errno(int fallback_value, int arg0, ...);
+
+#endif /* _KNOTD_COMMON_ERROR_H_ */
+
+/*! @} */
diff --git a/src/common/evqueue.c b/src/common/evqueue.c
new file mode 100644
index 0000000..ca3027f
--- /dev/null
+++ b/src/common/evqueue.c
@@ -0,0 +1,130 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common/evqueue.h"
+
+/*! \brief Singleton application-wide event queue. */
+evqueue_t *s_evqueue = 0;
+
+evqueue_t *evqueue_new()
+{
+ evqueue_t* q = malloc(sizeof(evqueue_t));
+
+ /* Initialize fds. */
+ if (pipe(q->fds) < 0) {
+ free(q);
+ q = 0;
+ }
+
+ return q;
+}
+
+void evqueue_free(evqueue_t **q)
+{
+ /* Check. */
+ if (!q) {
+ return;
+ }
+
+ /* Invalidate pointer to queue. */
+ evqueue_t *eq = *q;
+ *q = 0;
+
+ /* Deinitialize. */
+ close(eq->fds[EVQUEUE_READFD]);
+ close(eq->fds[EVQUEUE_WRITEFD]);
+ free(eq);
+}
+
+int evqueue_poll(evqueue_t *q, const struct timespec *ts,
+ const sigset_t *sigmask)
+{
+ /* Check. */
+ if (!q) {
+ return -1;
+ }
+
+ /* Prepare fd set. */
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(q->fds[EVQUEUE_READFD], &rfds);
+
+ /* Wait for events. */
+ int ret = pselect(q->fds[EVQUEUE_READFD] + 1, &rfds, 0, 0, ts, sigmask);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return ret;
+}
+
+int evqueue_read(evqueue_t *q, void *dst, size_t len)
+{
+ if (!q || !dst || len == 0) {
+ return -1;
+ }
+
+ return read(q->fds[EVQUEUE_READFD], dst, len);
+}
+
+int evqueue_write(evqueue_t *q, const void *src, size_t len)
+{
+ if (!q || !src || len == 0) {
+ return -1;
+ }
+
+ return write(q->fds[EVQUEUE_WRITEFD], src, len);
+}
+
+int evqueue_get(evqueue_t *q, event_t *ev)
+{
+ /* Check. */
+ if (!q || !ev) {
+ return -1;
+ }
+
+ /* Read data. */
+ int ret = evqueue_read(q, ev, sizeof(event_t));
+ if (ret != sizeof(event_t)) {
+ return -1;
+ }
+
+ /* Set parent. */
+ ev->parent = q;
+
+ return 0;
+}
+
+int evqueue_add(evqueue_t *q, const event_t *ev)
+{
+ /* Check. */
+ if (!q || !ev) {
+ return -1;
+ }
+
+ /* Write data. */
+ int ret = evqueue_write(q, ev, sizeof(event_t));
+ if (ret != sizeof(event_t)) {
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/src/common/evqueue.h b/src/common/evqueue.h
new file mode 100644
index 0000000..ffb3860
--- /dev/null
+++ b/src/common/evqueue.h
@@ -0,0 +1,200 @@
+/* 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 evqueue.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Event queue.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_EVQUEUE_H_
+#define _KNOTD_COMMON_EVQUEUE_H_
+
+#include <pthread.h>
+#include <signal.h> // sigset_t
+#include <time.h>
+#include <sys/time.h>
+
+//#include "knot/common.h"
+#include "common/lists.h"
+
+struct event_t;
+
+/*!
+ * \brief Event callback.
+ *
+ * Pointer to whole event structure is passed to the callback.
+ * Callback should return 0 on success and negative integer on error.
+ *
+ * Example callback:
+ * \code
+ * int print_callback(event_t *t) {
+ * return printf("Callback: %s\n", t->data);
+ * }
+ * \endcode
+ */
+typedef int (*event_cb_t)(struct event_t *);
+
+/*!
+ * \brief Event structure.
+ */
+typedef struct event_t {
+ node n; /*!< Node for event queue. */
+ int type; /*!< Event type. */
+ struct timeval tv; /*!< Event scheduled time. */
+ void *data; /*!< Usable data ptr. */
+ event_cb_t cb; /*!< Event callback. */
+ void *parent; /*!< Pointer to parent (evqueue, scheduler...) */
+} event_t;
+
+/*!
+ * \brief Event queue constants.
+ */
+enum {
+ EVQUEUE_READFD = 0,
+ EVQUEUE_WRITEFD = 1
+};
+
+/*!
+ * \brief Event queue structure.
+ */
+typedef struct {
+ int fds[2]; /*!< Read and Write fds. */
+} evqueue_t;
+
+/*!
+ * \brief Create new event queue.
+ *
+ * Event queue is thread-safe and POSIX signal-safe.
+ * It uses piped fds for queueing and pselect(2) to
+ * wait for events.
+ *
+ * \retval New instance on success.
+ * \retval NULL on error.
+ */
+evqueue_t *evqueue_new();
+
+/*!
+ * \brief Deinitialize and free event queue.
+ *
+ * \param q Pointer to queue instance.
+ * \note *q is set to 0.
+ */
+void evqueue_free(evqueue_t **q);
+
+/*!
+ * \brief Poll for new events.
+ *
+ * Unblocked signals during polling are specified
+ * in a sigmask.
+ *
+ * \param q Event queue.
+ * \param ts Timeout (or NULL for infinite).
+ * \param sigmask Bitmask of signals to receive (or NULL).
+ *
+ * \retval Number of polled events on success.
+ * \retval -1 On error or signal interrupt.
+ */
+int evqueue_poll(evqueue_t *q, const struct timespec *ts, const sigset_t *sigmask);
+
+/*!
+ * \brief Return evqueue pollable fd.
+ *
+ * \param q Event queue.
+ *
+ * \retval File descriptor available for polling.
+ * \retval -1 On error.
+ */
+static inline int evqueue_pollfd(evqueue_t *q) {
+ return q->fds[EVQUEUE_READFD];
+}
+
+/*!
+ * \brief Read data from event queue.
+ *
+ * This function is useful for sending custom
+ * events or other data types through the event queue.
+ *
+ * \param q Event queue.
+ * \param dst Destination buffer.
+ * \param len Number of bytes to read.
+ *
+ * \retval Number of read bytes on success.
+ * \retval -1 on error, \see read(2).
+ */
+int evqueue_read(evqueue_t *q, void *dst, size_t len);
+
+/*!
+ * \brief Write data to event queue.
+ *
+ * This function is useful for sending custom
+ * events or other data types through the event queue.
+ *
+ * \param q Event queue.
+ * \param src Source buffer.
+ * \param len Number of bytes to write.
+ *
+ * \retval Number of written bytes on success.
+ * \retval -1 on error, \see write(2).
+ */
+int evqueue_write(evqueue_t *q, const void *src, size_t len);
+
+/*!
+ * \brief Read event from event queue.
+ *
+ * \param q Event queue.
+ * \param ev Event structure for writing.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int evqueue_get(evqueue_t *q, event_t *ev);
+
+/*!
+ * \brief Add event to queue.
+ *
+ * \param q Event queue.
+ * \param ev Event structure to read.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int evqueue_add(evqueue_t *q, const event_t *ev);
+
+/* Singleton event queue pointer. */
+extern evqueue_t *s_evqueue;
+
+/*!
+ * \brief Event queue singleton.
+ */
+static inline evqueue_t *evqueue() {
+ return s_evqueue;
+}
+
+/*!
+ * \brief Set event queue singleton.
+ */
+static inline void evqueue_set(evqueue_t *q) {
+ s_evqueue = q;
+}
+
+#endif /* _KNOTD_COMMON_EVQUEUE_H_ */
+
+/*! @} */
diff --git a/src/common/evsched.c b/src/common/evsched.c
new file mode 100644
index 0000000..4e56028
--- /dev/null
+++ b/src/common/evsched.c
@@ -0,0 +1,309 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common/evsched.h"
+
+/*!
+ * \brief Set event timer to T (now) + dt miliseconds.
+ */
+static void evsched_settimer(event_t *e, uint32_t dt)
+{
+ if (!e) {
+ return;
+ }
+
+ /* Get absolute time T. */
+ gettimeofday(&e->tv, 0);
+
+ /* Add number of seconds. */
+ e->tv.tv_sec += dt / 1000;
+
+ /* Add the number of microseconds. */
+ e->tv.tv_usec += (dt % 1000) * 1000;
+
+ /* Check for overflow. */
+ while (e->tv.tv_usec > 999999) {
+ e->tv.tv_sec += 1;
+ e->tv.tv_usec -= 1 * 1000 * 1000;
+ }
+}
+
+/*! \brief Singleton application-wide event scheduler. */
+evsched_t *s_evsched = 0;
+
+evsched_t *evsched_new()
+{
+ evsched_t *s = malloc(sizeof(evsched_t));
+ if (!s) {
+ return 0;
+ }
+
+ /* Initialize event calendar. */
+ pthread_mutex_init(&s->rl, 0);
+ pthread_mutex_init(&s->mx, 0);
+ pthread_cond_init(&s->notify, 0);
+ pthread_mutex_init(&s->cache.lock, 0);
+ slab_cache_init(&s->cache.alloc, sizeof(event_t));
+ init_list(&s->calendar);
+ return s;
+}
+
+void evsched_delete(evsched_t **s)
+{
+ if (!s) {
+ return;
+ }
+ if (!*s) {
+ return;
+ }
+
+ /* Deinitialize event calendar. */
+ pthread_mutex_destroy(&(*s)->rl);
+ pthread_mutex_destroy(&(*s)->mx);
+ pthread_cond_destroy(&(*s)->notify);
+ node *n = 0, *nxt = 0;
+ WALK_LIST_DELSAFE(n, nxt, (*s)->calendar) {
+ evsched_event_free((*s), (event_t*)n);
+ }
+
+ /* Free allocator. */
+ slab_cache_destroy(&(*s)->cache.alloc);
+ pthread_mutex_destroy(&(*s)->cache.lock);
+
+ /* Free scheduler. */
+ free(*s);
+ *s = 0;
+}
+
+event_t *evsched_event_new(evsched_t *s, int type)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Allocate. */
+ pthread_mutex_lock(&s->cache.lock);
+ event_t *e = slab_cache_alloc(&s->cache.alloc);
+ pthread_mutex_unlock(&s->cache.lock);
+
+ /* Initialize. */
+ memset(e, 0, sizeof(event_t));
+ e->type = type;
+ return e;
+}
+
+void evsched_event_free(evsched_t *s, event_t *ev)
+{
+ if (!s || !ev) {
+ return;
+ }
+
+ pthread_mutex_lock(&s->cache.lock);
+ slab_free(ev);
+ pthread_mutex_unlock(&s->cache.lock);
+}
+
+event_t* evsched_next(evsched_t *s)
+{
+ /* Check. */
+ if (!s) {
+ return 0;
+ }
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ while(1) {
+
+ /* Check event queue. */
+ if (!EMPTY_LIST(s->calendar)) {
+
+ /* Get current time. */
+ struct timeval dt;
+ gettimeofday(&dt, 0);
+
+ /* Get next event. */
+ event_t *next_ev = HEAD(s->calendar);
+
+ /* Immediately return. */
+ if (timercmp(&dt, &next_ev->tv, >=)) {
+ rem_node(&next_ev->n);
+ pthread_mutex_unlock(&s->mx);
+ pthread_mutex_lock(&s->rl);
+ s->current = next_ev;
+ return next_ev;
+ }
+
+ /* Wait for next event or interrupt. Unlock calendar. */
+ struct timespec ts;
+ ts.tv_sec = next_ev->tv.tv_sec;
+ ts.tv_nsec = next_ev->tv.tv_usec * 1000L;
+ pthread_cond_timedwait(&s->notify, &s->mx, &ts);
+ } else {
+ /* Block until an event is scheduled. Unlock calendar.*/
+ pthread_cond_wait(&s->notify, &s->mx);
+ }
+ }
+
+ /* Unlock calendar, this shouldn't happen. */
+ pthread_mutex_unlock(&s->mx);
+ return 0;
+
+}
+
+int evsched_event_finished(evsched_t *s)
+{
+ if (!s) {
+ return -1;
+ }
+
+ /* Mark as finished. */
+ if (s->current) {
+ s->current = 0;
+ pthread_mutex_unlock(&s->rl);
+ return 0;
+ }
+
+ /* Finished event is not current. */
+ return -1;
+}
+
+int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt)
+{
+ if (!s || !ev || dt < 0) {
+ return -1;
+ }
+
+ /* Update event timer. */
+ evsched_settimer(ev, dt);
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ /* Schedule event. */
+ node *n = 0, *prev = 0;
+ if (!EMPTY_LIST(s->calendar)) {
+ WALK_LIST(n, s->calendar) {
+ event_t* cur = (event_t *)n;
+ if (timercmp(&cur->tv, &ev->tv, <)) {
+ prev = n;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* Append to list. */
+ ev->parent = s;
+ if (prev) {
+ insert_node(&ev->n, prev);
+ } else {
+ add_head(&s->calendar, &ev->n);
+ }
+
+
+ /* Unlock calendar. */
+ pthread_cond_signal(&s->notify);
+ pthread_mutex_unlock(&s->mx);
+
+ return 0;
+}
+
+event_t* evsched_schedule_cb(evsched_t *s, event_cb_t cb, void *data, uint32_t dt)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Create event. */
+ event_t *e = evsched_event_new(s, EVSCHED_CB);
+ if (!e) {
+ return 0;
+ }
+ e->cb = cb;
+ e->data = data;
+
+ /* Schedule. */
+ if (evsched_schedule(s, e, dt) != 0) {
+ evsched_event_free(s, e);
+ e = 0;
+ }
+
+ return e;
+}
+
+event_t* evsched_schedule_term(evsched_t *s, uint32_t dt)
+{
+ if (!s) {
+ return 0;
+ }
+
+ /* Create event. */
+ event_t *e = evsched_event_new(s, EVSCHED_TERM);
+ if (!e) {
+ return 0;
+ }
+
+ /* Schedule. */
+ if (evsched_schedule(s, e, dt) != 0) {
+ evsched_event_free(s, e);
+ e = 0;
+ }
+
+ return e;
+}
+
+int evsched_cancel(evsched_t *s, event_t *ev)
+{
+ if (!s || !ev) {
+ return -1;
+ }
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
+ /* Make sure not running. */
+ pthread_mutex_lock(&s->rl);
+
+ /* Find in list. */
+ event_t *n = 0;
+ int found = 0;
+ WALK_LIST(n, s->calendar) {
+ if (n == ev) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* Remove from list. */
+ if (found) {
+ rem_node(&ev->n);
+ }
+
+ /* Enable running events. */
+ pthread_mutex_unlock(&s->rl);
+
+ /* Unlock calendar. */
+ pthread_cond_signal(&s->notify);
+ pthread_mutex_unlock(&s->mx);
+
+ return 0;
+}
+
diff --git a/src/common/evsched.h b/src/common/evsched.h
new file mode 100644
index 0000000..2a682e1
--- /dev/null
+++ b/src/common/evsched.h
@@ -0,0 +1,240 @@
+/* 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 evsched.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Event scheduler.
+ *
+ * Scheduler works with the same event_t type as event queue.
+ * It is also thread-safe so the scheduler can run in a separate thread
+ * while events can be enqueued from different threads.
+ *
+ * Guideline is, that the scheduler run loop should exit with
+ * a special event type EVSCHED_TERM.
+ *
+ * Example usage:
+ * \code
+ * evsched_t *s = evsched_new();
+ *
+ * // Schedule myfunc() after 1000ms
+ * evsched_schedule_cb(s, myfunc, data, 1000)
+ *
+ * // Schedule termination event after 1500ms
+ * evsched_schedule_term(s, 1500);
+ *
+ * // Event scheduler main loop
+ * while (1) {
+ * // Wait for next scheduled event
+ * event_t *ev = evsched_next();
+ *
+ * // Break on termination event
+ * if (ev->type == EVSCHED_TERM) {
+ * evsched_event_free(s, ev);
+ * break;
+ * }
+ *
+ * // Execute and discard event
+ * if (ev->cb) {
+ * ev->cb(ev);
+ * }
+ * evsched_event_free(s, ev); // Free executed event
+ * }
+ *
+ * // Delete event scheduler
+ * evsched_delete(s);
+ * \endcode
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_EVSCHED_H_
+#define _KNOTD_COMMON_EVSCHED_H_
+
+#include <pthread.h>
+#include "common/slab/slab.h"
+#include "common/lists.h"
+#include "common/evqueue.h"
+
+/*!
+ * \brief Scheduler event types.
+ */
+typedef enum evsched_ev_t {
+ EVSCHED_NOOP = 0, /*!< No-op action, skip. */
+ EVSCHED_CB, /*!< Callback action. */
+ EVSCHED_TERM /*!< Terminal action, stop event scheduler. */
+} evsched_ev_t;
+
+/*!
+ * \brief Event scheduler structure.
+ *
+ * Keeps list of scheduled events. Events are executed in their scheduled
+ * time and kept in an ordered list (queue).
+ * Scheduler is terminated with a special EVSCHED_TERM event type.
+ */
+typedef struct {
+ pthread_mutex_t rl; /*!< Event running lock. */
+ event_t *current; /*!< Current running event. */
+ pthread_mutex_t mx; /*!< Event queue locking. */
+ pthread_cond_t notify; /*!< Event queue notification. */
+ list calendar; /*!< Event calendar. */
+ struct {
+ slab_cache_t alloc; /*!< Events SLAB cache. */
+ pthread_mutex_t lock; /*!< Events cache spin lock. */
+ } cache;
+} evsched_t;
+
+/*!
+ * \brief Create new event scheduler instance.
+ *
+ * \retval New instance on success.
+ * \retval NULL on error.
+ */
+evsched_t *evsched_new();
+
+/*!
+ * \brief Deinitialize and free event scheduler instance.
+ *
+ * \param s Pointer to event scheduler instance.
+ * \note *sched is set to 0.
+ */
+void evsched_delete(evsched_t **s);
+
+/*!
+ * \brief Create an empty event.
+ *
+ * \param s Pointer to event scheduler instance.
+ * \param type Event type.
+ * \retval New instance on success.
+ * \retval NULL on error.
+ */
+event_t *evsched_event_new(evsched_t *s, int type);
+
+/*!
+ * \brief Dispose event instance.
+ *
+ * \param s Pointer to event scheduler instance.
+ * \param ev Event instance.
+ */
+void evsched_event_free(evsched_t *s, event_t *ev);
+
+/*!
+ * \brief Fetch next-event.
+ *
+ * Scheduler may block until a next event is available.
+ * Send scheduler an EVSCHED_NOOP or EVSCHED_TERM event to unblock it.
+ *
+ * \warning Returned event must be marked as finished, or deadlock occurs.
+ *
+ * \param s Event scheduler.
+ *
+ * \retval Scheduled event.
+ * \retval NULL on error.
+ */
+event_t* evsched_next(evsched_t *s);
+
+/*!
+ * \brief Mark running event as finished.
+ *
+ * Need to call this after each event returned by evsched_next() is finished.
+ *
+ * \param s Event scheduler.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int evsched_event_finished(evsched_t *s);
+
+/*!
+ * \brief Schedule an event.
+ *
+ * \param s Event scheduler.
+ * \param ev Prepared event.
+ * \param dt Time difference in milliseconds from now (dt is relative).
+ *
+ * \retval 0 on success.
+ * \retval <0 on error.
+ */
+int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt);
+
+/*!
+ * \brief Schedule callback event.
+ *
+ * Execute callback after dt miliseconds has passed.
+ *
+ * \param s Event scheduler.
+ * \param cb Callback handler.
+ * \param data Data for callback.
+ * \param dt Time difference in milliseconds from now (dt is relative).
+ *
+ * \retval Event instance on success.
+ * \retval NULL on error.
+ */
+event_t* evsched_schedule_cb(evsched_t *s, event_cb_t cb, void *data, uint32_t dt);
+
+/*!
+ * \brief Schedule termination event.
+ *
+ * Special action for scheduler termination.
+ *
+ * \param s Event scheduler.
+ * \param dt Time difference in milliseconds from now (dt is relative).
+ *
+ * \retval Event instance on success.
+ * \retval NULL on error.
+ */
+event_t* evsched_schedule_term(evsched_t *s, uint32_t dt);
+
+/*!
+ * \brief Cancel a scheduled event.
+ *
+ * \warning May block until current running event is finished (as it cannot
+ * interrupt running event).
+ *
+ * \warning Never cancel event in it's callback. As it never finishes,
+ * it deadlocks.
+ *
+ * \param s Event scheduler.
+ * \param ev Scheduled event.
+ *
+ * \retval 0 on success.
+ * \retval <0 on error.
+ */
+int evsched_cancel(evsched_t *s, event_t *ev);
+
+/* Singleton event scheduler pointer. */
+extern evsched_t *s_evsched;
+
+/*!
+ * \brief Event scheduler singleton.
+ */
+static inline evsched_t *evsched() {
+ return s_evsched;
+}
+
+/*!
+ * \brief Set event scheduler singleton.
+ */
+static inline void evsched_set(evsched_t *s) {
+ s_evsched = s;
+}
+
+
+#endif /* _KNOTD_COMMON_EVSCHED_H_ */
+
+/*! @} */
diff --git a/src/common/fdset.c b/src/common/fdset.c
new file mode 100644
index 0000000..8c070ad
--- /dev/null
+++ b/src/common/fdset.c
@@ -0,0 +1,80 @@
+/* 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 _GNU_SOURCE
+#define _GNU_SOURCE /* Required for RTLD_DEFAULT. */
+#endif
+
+#include <dlfcn.h>
+#include <string.h>
+#include <stdio.h>
+#include "common/fdset.h"
+#include <config.h>
+
+struct fdset_backend_t _fdset_backend = {
+};
+
+/*! \brief Set backend implementation. */
+static void fdset_set_backend(struct fdset_backend_t *backend) {
+ memcpy(&_fdset_backend, backend, sizeof(struct fdset_backend_t));
+}
+
+/* Linux epoll API. */
+#ifdef HAVE_EPOLL_WAIT
+ #include "common/fdset_epoll.h"
+#endif /* HAVE_EPOLL_WAIT */
+
+/* BSD kqueue API */
+#ifdef HAVE_KQUEUE
+ #include "common/fdset_kqueue.h"
+#endif /* HAVE_KQUEUE */
+
+/* POSIX poll API */
+#ifdef HAVE_POLL
+ #include "common/fdset_poll.h"
+#endif /* HAVE_POLL */
+
+/*! \brief Bootstrap polling subsystem (it is called automatically). */
+void __attribute__ ((constructor)) fdset_init()
+{
+ /* Preference: epoll */
+#ifdef HAVE_EPOLL_WAIT
+ if (dlsym(RTLD_DEFAULT, "epoll_wait") != 0) {
+ fdset_set_backend(&FDSET_EPOLL);
+ return;
+ }
+#endif
+
+ /* Preference: kqueue */
+#ifdef HAVE_KQUEUE
+ if (dlsym(RTLD_DEFAULT, "kqueue") != 0) {
+ fdset_set_backend(&FDSET_KQUEUE);
+ return;
+ }
+#endif
+
+ /* Fallback: poll */
+#ifdef HAVE_POLL
+ if (dlsym(RTLD_DEFAULT, "poll") != 0) {
+ fdset_set_backend(&FDSET_POLL);
+ return;
+ }
+#endif
+
+ /* This shouldn't happen. */
+ fprintf(stderr, "fdset: fatal error - no valid fdset backend found\n");
+ return;
+}
diff --git a/src/common/fdset.h b/src/common/fdset.h
new file mode 100644
index 0000000..10196bf
--- /dev/null
+++ b/src/common/fdset.h
@@ -0,0 +1,196 @@
+/* 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 fdset.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Wrapper for native I/O multiplexing.
+ *
+ * Selects best implementation according to config.
+ * - select()
+ * - poll() \todo
+ * - epoll()
+ * - kqueue()
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_FDSET_H_
+#define _KNOTD_FDSET_H_
+
+#include <stddef.h>
+
+/*! \brief Opaque pointer to implementation-specific fdset data. */
+typedef struct fdset_t fdset_t;
+
+/*! \brief Unified event types. */
+enum fdset_event_t {
+ OS_EV_READ = 1 << 0, /*!< Readable event. */
+ OS_EV_WRITE = 1 << 1, /*!< Writeable event. */
+ OS_EV_ERROR = 1 << 2 /*!< Error event. */
+};
+
+/*! \brief File descriptor set iterator. */
+typedef struct fdset_it_t {
+ int fd; /*!< Current file descriptor. */
+ int events; /*!< Returned events. */
+ size_t pos; /* Internal usage. */
+} fdset_it_t;
+
+/*!
+ * \brief File descriptor set implementation backend.
+ * \notice Functions documentation following.
+ * \internal
+ */
+struct fdset_backend_t
+{
+ fdset_t* (*fdset_new)();
+ int (*fdset_destroy)(fdset_t*);
+ int (*fdset_add)(fdset_t*, int, int);
+ int (*fdset_remove)(fdset_t*, int);
+ int (*fdset_wait)(fdset_t*);
+ int (*fdset_begin)(fdset_t*, fdset_it_t*);
+ int (*fdset_end)(fdset_t*, fdset_it_t*);
+ int (*fdset_next)(fdset_t*, fdset_it_t*);
+ const char* (*fdset_method)();
+};
+
+/*!
+ * \brief Selected backend.
+ * \internal
+ */
+extern struct fdset_backend_t _fdset_backend;
+
+/*!
+ * \brief Create new fdset.
+ *
+ * FDSET implementation depends on system.
+ *
+ * \retval Pointer to initialized FDSET structure if successful.
+ * \retval NULL on error.
+ */
+static inline fdset_t *fdset_new() {
+ return _fdset_backend.fdset_new();
+}
+
+/*!
+ * \brief Destroy FDSET.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on error.
+ */
+static inline int fdset_destroy(fdset_t * fdset) {
+ return _fdset_backend.fdset_destroy(fdset);
+}
+
+/*!
+ * \brief Add file descriptor to watched set.
+ *
+ * \param fdset Target set.
+ * \param fd Added file descriptor.
+ * \param events Mask of watched events.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+static inline int fdset_add(fdset_t *fdset, int fd, int events) {
+ return _fdset_backend.fdset_add(fdset, fd, events);
+}
+
+
+/*!
+ * \brief Remove file descriptor from watched set.
+ *
+ * \param fdset Target set.
+ * \param fd File descriptor to be removed.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+static inline int fdset_remove(fdset_t *fdset, int fd) {
+ return _fdset_backend.fdset_remove(fdset, fd);
+}
+
+/*!
+ * \brief Poll set for new events.
+ *
+ * \param fdset Target set.
+ *
+ * \retval Number of events if successful.
+ * \retval -1 on errors.
+ *
+ * \todo Timeout.
+ */
+static inline int fdset_wait(fdset_t *fdset) {
+ return _fdset_backend.fdset_wait(fdset);
+}
+
+/*!
+ * \brief Set event iterator to the beginning of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+static inline int fdset_begin(fdset_t *fdset, fdset_it_t *it) {
+ return _fdset_backend.fdset_begin(fdset, it);
+}
+
+/*!
+ * \brief Set event iterator to the end of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+static inline int fdset_end(fdset_t *fdset, fdset_it_t *it) {
+ return _fdset_backend.fdset_end(fdset, it);
+}
+
+/*!
+ * \brief Set event iterator to the next event.
+ *
+ * Event iterator fd will be set to -1 if next event doesn't exist.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+static inline int fdset_next(fdset_t *fdset, fdset_it_t *it) {
+ return _fdset_backend.fdset_next(fdset, it);
+}
+
+/*!
+ * \brief Returned name of underlying poll method.
+ *
+ * \retval Name if successful.
+ * \retval NULL if no method was loaded (shouldn't happen).
+ */
+static inline const char* fdset_method() {
+ return _fdset_backend.fdset_method();
+}
+
+#endif /* _KNOTD_FDSET_H_ */
+
+/*! @} */
diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c
new file mode 100644
index 0000000..cb2e3e1
--- /dev/null
+++ b/src/common/fdset_epoll.c
@@ -0,0 +1,216 @@
+/* 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>
+
+#ifdef HAVE_EPOLL_WAIT
+
+#include <sys/epoll.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fdset_epoll.h"
+
+#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */
+#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */
+
+struct fdset_t {
+ int epfd;
+ struct epoll_event *events;
+ size_t nfds;
+ size_t reserved;
+ size_t polled;
+};
+
+fdset_t *fdset_epoll_new()
+{
+ fdset_t *set = malloc(sizeof(fdset_t));
+ if (!set) {
+ return 0;
+ }
+
+ /* Blank memory. */
+ memset(set, 0, sizeof(fdset_t));
+
+ /* Create epoll fd. */
+ set->epfd = epoll_create(OS_FDS_CHUNKSIZE);
+
+ return set;
+}
+
+int fdset_epoll_destroy(fdset_t * fdset)
+{
+ if(!fdset) {
+ return -1;
+ }
+
+ /* Teardown epoll. */
+ close(fdset->epfd);
+
+ /* OK if NULL. */
+ free(fdset->events);
+ free(fdset);
+ return 0;
+}
+
+int fdset_epoll_add(fdset_t *fdset, int fd, int events)
+{
+ if (!fdset || fd < 0 || events <= 0) {
+ return -1;
+ }
+
+ /* Realloc needed. */
+ if (fdset->nfds == fdset->reserved) {
+ const size_t chunk = OS_FDS_CHUNKSIZE;
+ const size_t nsize = (fdset->reserved + chunk) *
+ sizeof(struct epoll_event);
+ struct epoll_event *events_n = malloc(nsize);
+ if (!events_n) {
+ return -1;
+ }
+
+ /* Clear and copy old fdset data. */
+ memset(events_n, 0, nsize);
+ memcpy(events_n, fdset->events,
+ fdset->nfds * sizeof(struct epoll_event));
+ free(fdset->events);
+ fdset->events = events_n;
+ fdset->reserved += chunk;
+ }
+
+ /* Add to epoll set. */
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(struct epoll_event));
+ ev.events = EPOLLIN; /*! \todo MAP events. */
+ ev.data.fd = fd;
+ if (epoll_ctl(fdset->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ return -1;
+ }
+
+ ++fdset->nfds;
+ return 0;
+}
+
+int fdset_epoll_remove(fdset_t *fdset, int fd)
+{
+ if (!fdset || fd < 0) {
+ return -1;
+ }
+
+ /* Attempt to remove from set. */
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(struct epoll_event));
+ if (epoll_ctl(fdset->epfd, EPOLL_CTL_DEL, fd, &ev) < 0) {
+ return -1;
+ }
+
+ /* Overwrite current item. */
+ --fdset->nfds;
+
+ /*! \todo Return memory if overallocated (nfds is far lower than reserved). */
+ return 0;
+}
+
+int fdset_epoll_wait(fdset_t *fdset)
+{
+ if (!fdset || fdset->nfds < 1 || !fdset->events) {
+ return -1;
+ }
+
+ /* Poll new events. */
+ fdset->polled = 0;
+ int nfds = epoll_wait(fdset->epfd, fdset->events, fdset->nfds, -1);
+
+ /* Check. */
+ if (nfds < 0) {
+ return -1;
+ }
+
+ /* Events array is ordered from 0 to nfds. */
+ fdset->polled = nfds;
+ return nfds;
+}
+
+int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it) {
+ return -1;
+ }
+
+ /* Find first. */
+ it->pos = 0;
+ return fdset_next(fdset, it);
+}
+
+int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check for polled events. */
+ if (fdset->polled < 1) {
+ it->fd = -1;
+ it->pos = 0;
+ return -1;
+ }
+
+ /* No end found, ends on the beginning. */
+ size_t nid = fdset->polled - 1;
+ it->fd = fdset->events[nid].data.fd;
+ it->pos = nid;
+ it->events = 0; /*! \todo Map events. */
+ return -1;
+}
+
+int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check boundaries. */
+ if (it->pos >= fdset->polled) {
+ return -1;
+ }
+
+ /* Select next. */
+ size_t nid = it->pos++;
+ it->fd = fdset->events[nid].data.fd;
+ it->events = 0; /*! \todo Map events. */
+ return 0;
+}
+
+const char* fdset_epoll_method()
+{
+ return "epoll";
+}
+
+/* Package APIs. */
+struct fdset_backend_t FDSET_EPOLL = {
+ .fdset_new = fdset_epoll_new,
+ .fdset_destroy = fdset_epoll_destroy,
+ .fdset_add = fdset_epoll_add,
+ .fdset_remove = fdset_epoll_remove,
+ .fdset_wait = fdset_epoll_wait,
+ .fdset_begin = fdset_epoll_begin,
+ .fdset_end = fdset_epoll_end,
+ .fdset_next = fdset_epoll_next,
+ .fdset_method = fdset_epoll_method
+};
+
+#endif
diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h
new file mode 100644
index 0000000..551394d
--- /dev/null
+++ b/src/common/fdset_epoll.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 fdset_epoll.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Wrapper for epoll I/O multiplexing.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_FDSET_EPOLL_H_
+#define _KNOTD_FDSET_EPOLL_H_
+
+#include "fdset.h"
+
+/*!
+ * \brief Create new fdset.
+ *
+ * Linux epoll() backend.
+ *
+ * \retval Pointer to initialized FDSET structure if successful.
+ * \retval NULL on error.
+ */
+fdset_t *fdset_epoll_new();
+
+/*!
+ * \brief Destroy FDSET.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on error.
+ */
+int fdset_epoll_destroy(fdset_t * fdset);
+
+/*!
+ * \brief Add file descriptor to watched set.
+ *
+ * \param fdset Target set.
+ * \param fd Added file descriptor.
+ * \param events Mask of watched events.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_epoll_add(fdset_t *fdset, int fd, int events);
+
+/*!
+ * \brief Remove file descriptor from watched set.
+ *
+ * \param fdset Target set.
+ * \param fd File descriptor to be removed.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_epoll_remove(fdset_t *fdset, int fd);
+
+/*!
+ * \brief Poll set for new events.
+ *
+ * \param fdset Target set.
+ *
+ * \retval Number of events if successful.
+ * \retval -1 on errors.
+ *
+ * \todo Timeout.
+ */
+int fdset_epoll_wait(fdset_t *fdset);
+
+/*!
+ * \brief Set event iterator to the beginning of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the end of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the next event.
+ *
+ * Event iterator fd will be set to -1 if next event doesn't exist.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Returned name of epoll method.
+ *
+ * \retval Name if successful.
+ * \retval NULL if no method was loaded (shouldn't happen).
+ */
+const char* fdset_epoll_method();
+
+/*! \brief Exported API. */
+extern struct fdset_backend_t FDSET_EPOLL;
+
+#endif /* _KNOTD_FDSET_EPOLL_H_ */
+
+/*! @} */
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
new file mode 100644
index 0000000..c7199ae
--- /dev/null
+++ b/src/common/fdset_kqueue.c
@@ -0,0 +1,251 @@
+/* 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>
+
+#ifdef HAVE_KQUEUE
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include "fdset_kqueue.h"
+
+#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */
+#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */
+
+struct fdset_t {
+ int kq;
+ struct kevent *events;
+ struct kevent *revents;
+ size_t nfds;
+ size_t reserved;
+ size_t polled;
+};
+
+fdset_t *fdset_kqueue_new()
+{
+ fdset_t *set = malloc(sizeof(fdset_t));
+ if (!set) {
+ return 0;
+ }
+
+ /* Blank memory. */
+ memset(set, 0, sizeof(fdset_t));
+
+ /* Create kqueue fd. */
+ set->kq = kqueue();
+ if (set->kq < 0) {
+ free(set);
+ set = 0;
+ }
+
+ return set;
+}
+
+int fdset_kqueue_destroy(fdset_t * fdset)
+{
+ if(!fdset) {
+ return -1;
+ }
+
+ /* Teardown kqueue. */
+ close(fdset->kq);
+
+ /* OK if NULL. */
+ free(fdset->revents);
+ free(fdset->events);
+ free(fdset);
+ return 0;
+}
+
+int fdset_kqueue_realloc(void **old, size_t oldsize, size_t nsize)
+{
+ void *nmem = malloc(nsize);
+ if (!nmem) {
+ return -1;
+ }
+
+ /* Clear and copy old fdset data. */
+ memset(nmem, 0, nsize);
+ if (oldsize > 0) {
+ memcpy(nmem, *old, oldsize);
+ free(*old);
+ }
+
+ *old = nmem;
+ return 0;
+}
+
+int fdset_kqueue_add(fdset_t *fdset, int fd, int events)
+{
+ if (!fdset || fd < 0 || events <= 0) {
+ return -1;
+ }
+
+ /* Realloc needed. */
+ if (fdset->nfds == fdset->reserved) {
+ const size_t chunk = OS_FDS_CHUNKSIZE;
+ const size_t nsize = (fdset->reserved + chunk) *
+ sizeof(struct kevent);
+ const size_t oldsize = fdset->nfds * sizeof(struct kevent);
+
+ if (fdset_kqueue_realloc(&fdset->events, oldsize, nsize) < 0) {
+ return -1;
+ }
+
+ if (fdset_kqueue_realloc(&fdset->revents, oldsize, nsize) < 0) {
+ return -1;
+ }
+
+ }
+
+ /* Add to kqueue set. */
+ int evfilt = EVFILT_READ; /*! \todo Map events. */
+ EV_SET(&fdset->events[fdset->nfds], fd, evfilt,
+ EV_ADD|EV_ENABLE, 0, 0, 0);
+
+ ++fdset->nfds;
+ return 0;
+}
+
+int fdset_kqueue_remove(fdset_t *fdset, int fd)
+{
+ if (!fdset || fd < 0) {
+ return -1;
+ }
+
+ /* Find in set. */
+ int pos = -1;
+ for (int i = 0; i < fdset->nfds; ++i) {
+ if (fdset->events[i].ident == fd) {
+ pos = i;
+ break;
+ }
+ }
+
+ if (pos < 0) {
+ return -1;
+ }
+
+ /* Remove filters. */
+ EV_SET(&fdset->events[pos], fd, EVFILT_READ,
+ EV_DISABLE|EV_DELETE, 0, 0, 0);
+
+ /* Attempt to remove from set. */
+ size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent);
+ memmove(fdset->events + pos, fdset->events + (pos + 1), remaining);
+
+ /* Overwrite current item. */
+ --fdset->nfds;
+
+ /*! \todo Return memory if overallocated (nfds is far lower than reserved). */
+ return 0;
+}
+
+int fdset_kqueue_wait(fdset_t *fdset)
+{
+ if (!fdset || fdset->nfds < 1 || !fdset->events) {
+ return -1;
+ }
+
+ /* Poll new events. */
+ fdset->polled = 0;
+ int nfds = kevent(fdset->kq, fdset->events, fdset->nfds,
+ fdset->revents, fdset->nfds, 0);
+
+ /* Check. */
+ if (nfds < 0) {
+ return -1;
+ }
+
+ /* Events array is ordered from 0 to nfds. */
+ fdset->polled = nfds;
+ return nfds;
+}
+
+int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it) {
+ return -1;
+ }
+
+ /* Find first. */
+ it->pos = 0;
+ return fdset_next(fdset, it);
+}
+
+int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check for polled events. */
+ if (fdset->polled < 1) {
+ it->fd = -1;
+ it->pos = 0;
+ return -1;
+ }
+
+ /* No end found, ends on the beginning. */
+ size_t nid = fdset->polled - 1;
+ it->fd = fdset->revents[nid].ident;
+ it->pos = nid;
+ it->events = 0; /*! \todo Map events. */
+ return -1;
+}
+
+int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check boundaries. */
+ if (it->pos >= fdset->polled) {
+ return -1;
+ }
+
+ /* Select next. */
+ size_t nid = it->pos++;
+ it->fd = fdset->revents[nid].ident;
+ it->events = 0; /*! \todo Map events. */
+ return 0;
+}
+
+const char* fdset_kqueue_method()
+{
+ return "kqueue";
+}
+
+/* Package APIs. */
+struct fdset_backend_t FDSET_KQUEUE = {
+ .fdset_new = fdset_kqueue_new,
+ .fdset_destroy = fdset_kqueue_destroy,
+ .fdset_add = fdset_kqueue_add,
+ .fdset_remove = fdset_kqueue_remove,
+ .fdset_wait = fdset_kqueue_wait,
+ .fdset_begin = fdset_kqueue_begin,
+ .fdset_end = fdset_kqueue_end,
+ .fdset_next = fdset_kqueue_next,
+ .fdset_method = fdset_kqueue_method
+};
+
+#endif
diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h
new file mode 100644
index 0000000..f64482f
--- /dev/null
+++ b/src/common/fdset_kqueue.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 fdset_kqueue.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Wrapper for kqueue I/O multiplexing.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_FDSET_KQUEUE_H_
+#define _KNOTD_FDSET_KQUEUE_H_
+
+#include "fdset.h"
+
+/*!
+ * \brief Create new fdset.
+ *
+ * BSD kqueue() backend.
+ *
+ * \retval Pointer to initialized FDSET structure if successful.
+ * \retval NULL on error.
+ */
+fdset_t *fdset_kqueue_new();
+
+/*!
+ * \brief Destroy FDSET.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on error.
+ */
+int fdset_kqueue_destroy(fdset_t * fdset);
+
+/*!
+ * \brief Add file descriptor to watched set.
+ *
+ * \param fdset Target set.
+ * \param fd Added file descriptor.
+ * \param events Mask of watched events.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_kqueue_add(fdset_t *fdset, int fd, int events);
+
+/*!
+ * \brief Remove file descriptor from watched set.
+ *
+ * \param fdset Target set.
+ * \param fd File descriptor to be removed.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_kqueue_remove(fdset_t *fdset, int fd);
+
+/*!
+ * \brief Poll set for new events.
+ *
+ * \param fdset Target set.
+ *
+ * \retval Number of events if successful.
+ * \retval -1 on errors.
+ *
+ * \todo Timeout.
+ */
+int fdset_kqueue_wait(fdset_t *fdset);
+
+/*!
+ * \brief Set event iterator to the beginning of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the end of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the next event.
+ *
+ * Event iterator fd will be set to -1 if next event doesn't exist.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Returned name of kqueue method.
+ *
+ * \retval Name if successful.
+ * \retval NULL if no method was loaded (shouldn't happen).
+ */
+const char* fdset_kqueue_method();
+
+/*! \brief Exported API. */
+extern struct fdset_backend_t FDSET_KQUEUE;
+
+#endif /* _KNOTD_FDSET_KQUEUE_H_ */
+
+/*! @} */
diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c
new file mode 100644
index 0000000..8682eaf
--- /dev/null
+++ b/src/common/fdset_poll.c
@@ -0,0 +1,230 @@
+/* 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>
+
+#ifdef HAVE_POLL
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <stddef.h>
+
+#include "common/fdset_poll.h"
+
+#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */
+#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */
+
+struct fdset_t {
+ struct pollfd *fds;
+ nfds_t nfds;
+ size_t reserved;
+ size_t polled;
+ size_t begin;
+};
+
+fdset_t *fdset_poll_new()
+{
+ fdset_t *set = malloc(sizeof(fdset_t));
+ if (!set) {
+ return 0;
+ }
+
+ /* Blank memory. */
+ memset(set, 0, sizeof(fdset_t));
+ return set;
+}
+
+int fdset_poll_destroy(fdset_t * fdset)
+{
+ if(!fdset) {
+ return -1;
+ }
+
+ /*! \todo No teardown required I guess. */
+
+ /* OK if NULL. */
+ free(fdset->fds);
+ free(fdset);
+ return 0;
+}
+
+int fdset_poll_add(fdset_t *fdset, int fd, int events)
+{
+ if (!fdset || fd < 0 || events <= 0) {
+ return -1;
+ }
+
+ /* Realloc needed. */
+ if (fdset->nfds == fdset->reserved) {
+ const size_t chunk = OS_FDS_CHUNKSIZE;
+ const size_t nsize = sizeof(struct pollfd) * (fdset->reserved + chunk);
+ struct pollfd *fds_n = malloc(nsize);
+ if (!fds_n) {
+ return -1;
+ }
+
+ /* Clear and copy old fdset data. */
+ memset(fds_n, 0, nsize);
+ memcpy(fds_n, fdset->fds, fdset->nfds * sizeof(struct pollfd));
+ free(fdset->fds);
+ fdset->fds = fds_n;
+ fdset->reserved += chunk;
+ }
+
+ /* Append. */
+ int nid = fdset->nfds++;
+ fdset->fds[nid].fd = fd;
+ fdset->fds[nid].events = POLLIN; /*! \todo Map events to POLL events. */
+ return 0;
+}
+
+int fdset_poll_remove(fdset_t *fdset, int fd)
+{
+ if (!fdset || fd < 0) {
+ return -1;
+ }
+
+ /* Find file descriptor. */
+ unsigned found = 0;
+ size_t pos = 0;
+ for (size_t i = 0; i < fdset->nfds; ++i) {
+ if (fdset->fds[i].fd == fd) {
+ found = 1;
+ pos = i;
+ break;
+ }
+ }
+
+ /* Check. */
+ if (!found) {
+ return -1;
+ }
+
+ /* Overwrite current item. */
+ size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct pollfd);
+ memmove(fdset->fds + pos, fdset->fds + (pos + 1), remaining);
+ --fdset->nfds;
+
+ /*! \todo Return memory if overallocated (nfds is far lower than reserved). */
+ /*! \todo Maybe >64 free chunks is excess? */
+ return 0;
+}
+
+int fdset_poll_wait(fdset_t *fdset)
+{
+ if (!fdset || fdset->nfds < 1 || !fdset->fds) {
+ return -1;
+ }
+
+ /* Initialize pointers. */
+ fdset->polled = 0;
+ fdset->begin = 0;
+
+ /* Poll for events. */
+ int ret = poll(fdset->fds, fdset->nfds, -1);
+ if (ret < 0) {
+ return -1;
+ }
+
+ /* Set pointers for iterating. */
+ fdset->polled = ret;
+ fdset->begin = 0;
+ return ret;
+}
+
+int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it) {
+ return -1;
+ }
+
+ /* Find first. */
+ it->pos = 0;
+ return fdset_next(fdset, it);
+}
+
+int fdset_poll_end(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check for polled events. */
+ if (fdset->polled < 1) {
+ it->fd = -1;
+ it->pos = 0;
+ return -1;
+ }
+
+ /* Trace last matching item from the end. */
+ struct pollfd* pfd = fdset->fds + fdset->nfds - 1;
+ while (pfd != fdset->fds) {
+ if (pfd->events & pfd->revents) {
+ it->fd = pfd->fd;
+ it->pos = pfd - fdset->fds;
+ return 0;
+ }
+ }
+
+ /* No end found, ends on the beginning. */
+ it->fd = -1;
+ it->pos = 0;
+ return -1;
+}
+
+int fdset_poll_next(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Find next with matching flags. */
+ for (; it->pos < fdset->nfds; ++it->pos) {
+ struct pollfd* pfd = fdset->fds + it->pos;
+ if (pfd->events & pfd->revents) {
+ it->fd = pfd->fd;
+ it->events = pfd->revents; /*! \todo MAP events. */
+ ++it->pos; /* Next will start after current. */
+ return 0;
+ }
+ }
+
+ /* No matching event found. */
+ it->fd = -1;
+ it->pos = 0;
+ return -1;
+}
+
+const char* fdset_poll_method()
+{
+ return "poll";
+}
+
+/* Package APIs. */
+struct fdset_backend_t FDSET_POLL = {
+ .fdset_new = fdset_poll_new,
+ .fdset_destroy = fdset_poll_destroy,
+ .fdset_add = fdset_poll_add,
+ .fdset_remove = fdset_poll_remove,
+ .fdset_wait = fdset_poll_wait,
+ .fdset_begin = fdset_poll_begin,
+ .fdset_end = fdset_poll_end,
+ .fdset_next = fdset_poll_next,
+ .fdset_method = fdset_poll_method
+};
+
+#endif
diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h
new file mode 100644
index 0000000..d72b5bb
--- /dev/null
+++ b/src/common/fdset_poll.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 fdset_poll.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Wrapper for poll I/O multiplexing.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_FDSET_POLL_H_
+#define _KNOTD_FDSET_POLL_H_
+
+#include "fdset.h"
+
+/*!
+ * \brief Create new fdset.
+ *
+ * POSIX poll() backend.
+ *
+ * \retval Pointer to initialized FDSET structure if successful.
+ * \retval NULL on error.
+ */
+fdset_t *fdset_poll_new();
+
+/*!
+ * \brief Destroy FDSET.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on error.
+ */
+int fdset_poll_destroy(fdset_t * fdset);
+
+/*!
+ * \brief Add file descriptor to watched set.
+ *
+ * \param fdset Target set.
+ * \param fd Added file descriptor.
+ * \param events Mask of watched events.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_poll_add(fdset_t *fdset, int fd, int events);
+
+/*!
+ * \brief Remove file descriptor from watched set.
+ *
+ * \param fdset Target set.
+ * \param fd File descriptor to be removed.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_poll_remove(fdset_t *fdset, int fd);
+
+/*!
+ * \brief Poll set for new events.
+ *
+ * \param fdset Target set.
+ *
+ * \retval Number of events if successful.
+ * \retval -1 on errors.
+ *
+ * \todo Timeout.
+ */
+int fdset_poll_wait(fdset_t *fdset);
+
+/*!
+ * \brief Set event iterator to the beginning of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the end of last polled events.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_poll_end(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Set event iterator to the next event.
+ *
+ * Event iterator fd will be set to -1 if next event doesn't exist.
+ *
+ * \param fdset Target set.
+ * \param it Event iterator.
+ *
+ * \retval 0 if successful.
+ * \retval -1 on errors.
+ */
+int fdset_poll_next(fdset_t *fdset, fdset_it_t *it);
+
+/*!
+ * \brief Returned name of poll method.
+ *
+ * \retval Name if successful.
+ * \retval NULL if no method was loaded (shouldn't happen).
+ */
+const char* fdset_poll_method();
+
+/*! \brief Exported API. */
+extern struct fdset_backend_t FDSET_POLL;
+
+#endif /* _KNOTD_FDSET_POLL_H_ */
+
+/*! @} */
diff --git a/src/common/general-tree.c b/src/common/general-tree.c
new file mode 100644
index 0000000..202b31a
--- /dev/null
+++ b/src/common/general-tree.c
@@ -0,0 +1,215 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "common/general-tree.h"
+#include "common/errors.h"
+
+MOD_TREE_DEFINE(general_tree_node, avl);
+
+static void gen_rem_func(struct general_tree_node *n1)
+{
+ free(n1);
+}
+
+general_tree_t *gen_tree_new(int (*comp_func)(void *, void *))
+{
+ general_tree_t *ret = malloc(sizeof(general_tree_t));
+ if (ret == NULL) {
+ return NULL;
+ }
+ ret->tree = malloc(sizeof(general_avl_tree_t));
+ if (ret->tree == NULL) {
+ free(ret);
+ return NULL;
+ }
+ MOD_TREE_INIT(ret->tree, comp_func);
+ return ret;
+}
+
+int gen_tree_add(general_tree_t *tree,
+ void *node, int (*mrg_func)(void **n1, void **n2))
+{
+ struct general_tree_node *tree_node =
+ malloc(sizeof(struct general_tree_node));
+ if (tree_node == NULL) {
+ return -1;
+ }
+ memset(tree_node, 0, sizeof(struct general_tree_node));
+ tree_node->data = node;
+ int merged = 0;
+ MOD_TREE_INSERT(tree->tree, general_tree_node, avl,
+ tree_node, mrg_func, &merged);
+ if (merged) {
+ free(tree_node);
+ }
+ return merged;
+}
+
+void gen_tree_remove(general_tree_t *tree,
+ void *what)
+{
+ struct general_tree_node tree_node;
+ tree_node.data = what;
+ MOD_TREE_REMOVE(tree->tree, general_tree_node, avl, &tree_node,
+ gen_rem_func);
+}
+
+void *gen_tree_find(general_tree_t *tree,
+ void *what)
+{
+ struct general_tree_node tree_node;
+ tree_node.data = what;
+ struct general_tree_node *found_node =
+ MOD_TREE_FIND(tree->tree, general_tree_node, avl, &tree_node);
+ if (found_node) {
+ return found_node->data;
+ } else {
+ return NULL;
+ }
+}
+
+int gen_tree_find_less_or_equal(general_tree_t *tree,
+ void *what,
+ void **found)
+{
+ if (tree == NULL || tree->tree == NULL) {
+ return -1;
+ }
+
+ /* Check if tree is empty. */
+ if (tree->tree->th_root == NULL) {
+ *found = NULL;
+ return 0;
+ }
+
+ struct general_tree_node *f = NULL, *prev = NULL;
+ struct general_tree_node tree_node;
+ tree_node.data = what;
+ int exact_match =
+ MOD_TREE_FIND_LESS_EQUAL(tree->tree, general_tree_node, avl,
+ &tree_node, &f, &prev);
+ if (exact_match < 0) {
+ *found = NULL;
+ exact_match = 0;
+ } else if (exact_match == 0) {
+ assert(prev != NULL);
+ *found = prev->data;
+ } else {
+ assert(f != NULL);
+ *found = f->data;
+ }
+// *found = (exact_match > 0) ? f->data : prev->data;
+ return exact_match;
+}
+
+void gen_tree_apply_inorder(general_tree_t *tree,
+ void (*app_func)
+ (void *node, void *data), void *data)
+{
+ MOD_TREE_FORWARD_APPLY(tree->tree, general_tree_node, avl,
+ app_func, data);
+}
+
+void gen_tree_destroy(general_tree_t **tree,
+ void (*dest_func)(void *node, void *data), void *data)
+{
+// gen_tree_apply_inorder(*tree, print_node, NULL);
+ MOD_TREE_DESTROY((*tree)->tree, general_tree_node, avl, dest_func,
+ gen_rem_func, data);
+ free((*tree)->tree);
+ free(*tree);
+ *tree = NULL;
+}
+
+void gen_tree_clear(general_tree_t *tree)
+{
+ MOD_TREE_DESTROY(tree->tree, general_tree_node, avl, NULL,
+ gen_rem_func, NULL);
+}
+
+//static void add_node_to_tree(void *n, void *data)
+//{
+// general_tree_t *tree = (general_tree_t *)data;
+// gen_tree_add(tree, n, NULL);
+//}
+
+static int gen_tree_copy_node(const struct general_tree_node *from,
+ struct general_tree_node **to)
+{
+ if (from == NULL) {
+ return 0;
+ }
+
+ *to = malloc(sizeof(struct general_tree_node));
+ if (*to == NULL) {
+ return -1;
+ }
+ memset(*to, 0, sizeof(struct general_tree_node));
+
+ (*to)->data = from->data;
+ (*to)->avl.avl_height = from->avl.avl_height;
+
+ int ret = gen_tree_copy_node(from->avl.avl_left,
+ &(*to)->avl.avl_left);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = gen_tree_copy_node(from->avl.avl_right,
+ &(*to)->avl.avl_right);
+ if (ret != 0) {
+ /*! \todo Partially cleaunp tree! */
+ (*to)->avl.avl_left = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+general_tree_t *gen_tree_shallow_copy(general_tree_t *tree)
+{
+ general_tree_t *new_tree = malloc(sizeof(general_tree_t));
+ if (new_tree == NULL) {
+ return NULL;
+ }
+ new_tree->tree = malloc(sizeof(general_avl_tree_t));
+ if (new_tree->tree == NULL) {
+ free(new_tree);
+ return NULL;
+ }
+
+ MOD_TREE_INIT(new_tree->tree, tree->tree->th_cmp);
+ assert(new_tree->tree->th_cmp == tree->tree->th_cmp);
+
+// gen_tree_apply_inorder(tree, add_node_to_tree, new_tree);
+
+ if (gen_tree_copy_node(tree->tree->th_root,
+ &new_tree->tree->th_root) != 0) {
+ return NULL;
+ }
+
+ /* CLEANUP */
+// gen_tree_apply_inorder(tree, print_node, NULL);
+// printf("--------------------------\n");
+// gen_tree_apply_inorder(new_tree, print_node, NULL);
+
+ return new_tree;
+}
+
diff --git a/src/common/general-tree.h b/src/common/general-tree.h
new file mode 100644
index 0000000..552638a
--- /dev/null
+++ b/src/common/general-tree.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_COMMON_GENERAL_TREE_H_
+#define _KNOTD_COMMON_GENERAL_TREE_H_
+
+#include "common/modified_tree.h"
+
+typedef MOD_TREE_HEAD(tree, general_tree_node) general_avl_tree_t;
+
+/* Define tree with void * nodes */
+struct general_tree_node {
+ MOD_TREE_ENTRY(general_tree_node) avl;
+// int (*cmp_func)(void *n1,
+// void *n2);
+// int (*mrg_func)(void **n1,
+// void **n2);
+// void (*app_func)(void *n,
+// void *data);
+ void *data;
+};
+
+struct general_tree {
+// int (*cmp_func)(void *n1,
+// void *n2);
+// int (*mrg_func)(void **n1,
+// void **n2);
+ general_avl_tree_t *tree;
+};
+
+typedef struct general_tree general_tree_t;
+
+general_tree_t *gen_tree_new(int (*cmp_func)(void *p1, void *p2));
+
+int gen_tree_add(general_tree_t *tree,
+ void *node,
+ int (*mrg_func)(void **n1, void **n2));
+
+void *gen_tree_find(general_tree_t *tree,
+ void *what);
+
+void gen_tree_remove(general_tree_t *tree,
+ void *what);
+
+void gen_tree_apply_inorder(general_tree_t *tree,
+ void (*app_func)(void *node, void *data),
+ void *data);
+
+void gen_tree_destroy(general_tree_t **tree,
+ void (*dest_func)(void *node, void *data), void *data);
+
+void gen_tree_clear(general_tree_t *tree);
+
+int gen_tree_find_less_or_equal(general_tree_t *tree,
+ void *what,
+ void **found);
+
+general_tree_t *gen_tree_shallow_copy(general_tree_t *tree);
+
+#endif // _KNOTD_COMMON_GENERAL_TREE_H_
diff --git a/src/common/latency.c b/src/common/latency.c
new file mode 100644
index 0000000..a563f58
--- /dev/null
+++ b/src/common/latency.c
@@ -0,0 +1,197 @@
+/* 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/>.
+ */
+
+#ifdef PROF_LATENCY
+
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+
+
+/*! \brief Profiler statistics. */
+typedef struct pstat_t {
+ double M; /*!< \brief Mean value. */
+ unsigned long min, max; /*!< \brief Minimum, maximum. */
+ unsigned long total; /*!< \brief Total profiled time. */
+ unsigned long count; /*!< \brief Number of profiled calls. */
+} pstat_t;
+
+/*! \brief Function call profile. */
+typedef struct profile_t {
+ const char* call;
+ pstat_t stat;
+} profile_t;
+
+/*! \brief Observed function call codes. */
+enum pcall_code_t {
+ PF_RECVFROM = 0,
+ PF_SENDTO,
+ PF_PTHREAD_MUTEX_LOCK,
+ PF_PTHREAD_MUTEX_UNLOCK,
+ PF_CALL_SIZE
+} pcall_code_t;
+
+/*! \brief Table of observed function calls. */
+static profile_t table[] = {
+ { "recvfrom", {0} },
+ { "sendto", {0} },
+ { "pthread_mutex_lock", {0} },
+ { "pthread_mutex_unlock", {0} },
+ { "NULL", {0} }
+};
+
+
+/*! \brief Add value to statistics. */
+static inline void add_stat(pstat_t *stat, unsigned long val) {
+
+ if (val < stat->min) {
+ stat->min = val;
+ }
+ if (val > stat->max) {
+ stat->max = val;
+ }
+
+ stat->total += val;
+
+ double Mprev = stat->M, M = stat->M;
+ M += (val - M)/((double)stat->count + 1);
+ stat->M = M;
+ //S += (val - M)*(x[i] - Mprev);
+
+ ++stat->count;
+}
+
+/*! \brief Call profiler table initialization (automatically called on load). */
+void __attribute__ ((constructor)) profiler_init()
+{
+ for (int i = 0; i < PF_CALL_SIZE; ++i) {
+ pstat_t* stat = &table[i].stat;
+ stat->M = 0;
+ stat->max = 0;
+ stat->min = (unsigned long)~0;
+ stat->total = 0;
+ stat->count = 0;
+ }
+}
+
+/*! \brief Call profiler table evaluation (automatically called on exit). */
+void __attribute__ ((destructor)) profiler_deinit()
+{
+
+ /* Get resource usage. */
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) < 0) {
+ memset(&usage, 0, sizeof(struct rusage));
+ }
+
+ fprintf(stderr, "\nStatistics:");
+ fprintf(stderr, "\n==================\n");
+
+ fprintf(stderr, "User time: %.03lf ms\nSystem time: %.03lf ms\n",
+ usage.ru_utime.tv_sec * (double) 1000.0
+ + usage.ru_utime.tv_usec / (double)1000.0,
+ usage.ru_stime.tv_sec * (double) 1000.0
+ + usage.ru_stime.tv_usec / (double)1000.0);
+ fprintf(stderr, "Voluntary context switches: %lu\nInvoluntary context switches: %lu\n",
+ usage.ru_nvcsw,
+ usage.ru_nivcsw);
+ fprintf(stderr, "==================\n");
+ fprintf(stderr, "\n");
+
+ /* Callers statistics. */
+ for (int i = 0; i < PF_CALL_SIZE; ++i) {
+ pstat_t* stat = &table[i].stat;
+ fprintf(stderr, "%s: M=%lf min=%lu,max=%lu (total=%lu, %lu times) (usec)\n",
+ table[i].call, stat->M, stat->min, stat->max, stat->total,
+ stat->count);
+ }
+
+}
+
+ssize_t pf_recvfrom(int socket, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen,
+ const char* caller, const char* file, int line)
+{
+ unsigned long elapsed = 0;
+ int ret = 0;
+ perf_begin();
+ ret = recvfrom(socket, buf, len, flags, from, fromlen);
+ perf_end(elapsed);
+
+ /* Discard wakeup delays, count statistics otherwise. */
+ if (elapsed < 200000) {
+ add_stat(&table[PF_RECVFROM].stat, elapsed);
+ }
+ return ret;
+}
+
+ssize_t pf_sendto(int socket, const void *buf, size_t len, int flags,
+ const struct sockaddr *to, socklen_t tolen,
+ const char* caller, const char* file, int line)
+{
+ unsigned long elapsed = 0;
+ int ret = 0;
+ perf_begin();
+ ret = sendto(socket, buf, len, flags, to, tolen);
+ perf_end(elapsed);
+
+ /* Discard wakeup delays, count statistics otherwise. */
+ if (elapsed < 200000) {
+ add_stat(&table[PF_SENDTO].stat, elapsed);
+ }
+ return ret;
+}
+
+/* Pthreads */
+int pf_pthread_mutex_lock(pthread_mutex_t *mutex,
+ const char* caller, const char* file, int line)
+{
+ unsigned long elapsed = 0;
+ int ret = 0;
+ perf_begin();
+ ret = pthread_mutex_lock(mutex);
+ perf_end(elapsed);
+
+ /* Discard wakeup delays, count statistics otherwise. */
+ if (elapsed < 200000) {
+ add_stat(&table[PF_PTHREAD_MUTEX_LOCK].stat, elapsed);
+ }
+
+ return ret;
+}
+
+int pf_pthread_mutex_unlock(pthread_mutex_t *mutex,
+ const char* caller, const char* file, int line)
+{
+ unsigned long elapsed = 0;
+ int ret = 0;
+ perf_begin();
+ ret = pthread_mutex_unlock(mutex);
+ perf_end(elapsed);
+
+ /* Discard wakeup delays, count statistics otherwise. */
+ if (elapsed < 200000) {
+ add_stat(&table[PF_PTHREAD_MUTEX_UNLOCK].stat, elapsed);
+ }
+
+ return ret;
+}
+
+#endif // PROF_LATENCY
diff --git a/src/common/latency.h b/src/common/latency.h
new file mode 100644
index 0000000..d965c56
--- /dev/null
+++ b/src/common/latency.h
@@ -0,0 +1,115 @@
+/* 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 latency.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Utilities for latency profiling.
+ *
+ * Selected calls latency profiler is enabled with PROF_LATENCY define.
+ * You can roughly profile own code with perf_begin() and perf_end() macros.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_LATENCY_H_
+#define _KNOTD_COMMON_LATENCY_H_
+
+/* Optional. */
+#ifdef PROF_LATENCY
+
+/* Do not include from latency.c */
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+/* Profiler tools */
+
+/*! \brief Time profile begin macro. */
+#define perf_begin() \
+do { \
+ struct timeval __begin; \
+ gettimeofday(&__begin, 0)
+
+/*! \brief Time profile end macro
+ * \param d Will contain the number of microseconds passed from perf_begin().
+ */
+#define perf_end(d) \
+ struct timeval __end; \
+ gettimeofday(&__end, 0); \
+ unsigned long __us = (__end.tv_sec - __begin.tv_sec) * 1000L * 1000L; \
+ __us += (__end.tv_usec - __begin.tv_usec); \
+ (d) = __us; \
+} while(0)
+
+/* Prototypes. */
+
+/*! \brief Profiled recvfrom(). */
+ssize_t pf_recvfrom(int socket, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen,
+ const char* caller, const char* file, int line);
+
+/*! \brief Profiled sendto(). */
+ssize_t pf_sendto(int socket, const void *buf, size_t len, int flags,
+ const struct sockaddr *to, socklen_t tolen,
+ const char* caller, const char* file, int line);
+
+/*! \brief Profiled pthread_mutex_lock(). */
+int pf_pthread_mutex_lock(pthread_mutex_t *mutex,
+ const char* caller, const char* file, int line);
+
+/*! \brief Profiled pthread_mutex_unlock(). */
+int pf_pthread_mutex_unlock(pthread_mutex_t *mutex,
+ const char* caller, const char* file, int line);
+
+/*
+ * Sockets.
+ */
+
+/*! \brief Rerouted recvfrom(). */
+#define recvfrom(s, buf, len, flags, from, fromlen) \
+ pf_recvfrom((s), (buf), (len), (flags), (from), (fromlen), \
+ __FUNCTION__, __FILE__, __LINE__)
+
+/*! \brief Rerouted sendto(). */
+#define sendto(s, buf, len, flags, to, tolen) \
+ pf_sendto((s), (buf), (len), (flags), (to), (tolen), \
+ __FUNCTION__, __FILE__, __LINE__)
+
+/*
+ * Pthreads.
+ */
+
+/*! \brief Rerouted pthread_mutex_lock(). */
+#define pthread_mutex_lock(m) \
+ pf_pthread_mutex_lock(m, __FUNCTION__, __FILE__, __LINE__)
+
+/*! \brief Rerouted pthread_mutex_unlock(). */
+#define pthread_mutex_unlock(m) \
+ pf_pthread_mutex_unlock(m, __FUNCTION__, __FILE__, __LINE__)
+
+#else // PROF_LATENCY
+
+/* Profiler tools */
+#define perf_begin()
+#define perf_end(d)
+
+#endif // PROF_LATENCY
+#endif // _KNOTD_COMMON_LATENCY_H_
+
+/*! @} */
diff --git a/src/common/libtap/README b/src/common/libtap/README
new file mode 100644
index 0000000..d57b81d
--- /dev/null
+++ b/src/common/libtap/README
@@ -0,0 +1,231 @@
+NAME
+====
+
+libtap - Write tests in C
+
+SYNOPSIS
+========
+
+ #include <tap.h>
+
+ int foo () {return 3;}
+ char *bar () {return "fnord";}
+
+ int main () {
+ plan(5);
+ ok(foo() == 3);
+ is(bar(), "eek");
+ ok(foo() <= 8732, "foo <= %d", 8732);
+ like(bar(), "f(yes|no)r*[a-f]$", "is like");
+ cmp_ok(foo(), ">=", 10, "foo is greater than ten");
+ return exit_status();
+ }
+
+results in:
+
+ 1..5
+ ok 1
+ not ok 2
+ # Failed test at synopsis.c line 9.
+ # got: 'fnord'
+ # expected: 'eek'
+ ok 3 - foo <= 8732
+ ok 4 - is like
+ not ok 5 - foo is greater than ten
+ # Failed test 'foo is greater than ten'
+ # at synopsis.c line 12.
+ # 3
+ # >=
+ # 10
+ # Looks like you failed 2 tests of 5 run.
+
+DESCRIPTION
+===========
+
+tap is an easy to read and easy to write way of creating tests for your
+software. This library creates functions that can be used to generate it for
+your C programs. It is mostly based on the Test::More Perl module.
+
+FUNCTIONS
+=========
+
+- plan(tests)
+- plan(NO_PLAN)
+
+ Use this to start a series of tests. When you know how many tests there
+ will be, you can put a number as a number of tests you expect to run. If
+ you do not know how many tests there will be, you can use plan(NO_PLAN)
+ or not call this function. When you pass it a number of tests to run, a
+ message similar to the following will appear in the output:
+
+ 1..5
+
+- ok(test)
+- ok(test, fmt, ...)
+
+ Specify a test. the test can be any statement returning a true or false
+ value. You may optionally pass a format string describing the test.
+
+ ok(r = reader_new("Of Mice and Men"), "create a new reader");
+ ok(reader_go_to_page(r, 55), "can turn the page");
+ ok(r->page == 55, "page turned to the right one");
+
+ Should print out:
+
+ ok 1 - create a new reader
+ ok 2 - can turn the page
+ ok 3 - page turned to the right one
+
+ On failure, a diagnostic message will be printed out.
+
+ not ok 3 - page turned to the right one
+ # Failed test 'page turned to the right one'
+ # at reader.c line 13.
+
+- is(got, expected)
+- is(got, expected, fmt, ...)
+- isnt(got, expected)
+- isnt(got, expected, fmt, ...)
+
+ Tests that the string you got is what you expected. with isnt, it is the
+ reverse.
+
+ is("this", "that", "this is that");
+
+ prints:
+
+ not ok 1 - this is that
+ # Failed test 'this is that'
+ # at is.c line 6.
+ # got: 'this'
+ # expected: 'that'
+
+- cmp_ok(a, op, b)
+- cmp_ok(a, op, b, fmt, ...)
+
+ Compares two ints with any binary operator that doesn't require an lvalue.
+ This is nice to use since it provides a better error message than an
+ equivalent ok.
+
+ cmp_ok(420, ">", 666);
+
+ prints:
+
+ not ok 1
+ # Failed test at cmpok.c line 5.
+ # 420
+ # >
+ # 666
+
+- like(got, expected)
+- like(got, expected, fmt, ...)
+- unlike(got, expected)
+- unlike(got, expected, fmt, ...)
+
+ Tests that the string you got matches the expected extended POSIX regex.
+ unlike is the reverse. These macros are the equivalent of a skip on
+ Windows.
+
+ like("stranger", "^s.(r).*\\1$", "matches the regex");
+
+ prints:
+
+ ok 1 - matches the regex
+
+- pass()
+- pass(fmt, ...)
+- fail()
+- fail(fmt, ...)
+
+ Speciy that a test succeeded or failed. Use these when the statement is
+ longer than you can fit into the argument given to an ok() test.
+
+- dies_ok(code)
+- dies_ok(code, fmt, ...)
+- lives_ok(code)
+- lives_ok(code, fmt, ...)
+
+ Tests whether the given code causes your program to exit. The code gets
+ passed to a macro that will test it in a forked process. If the code
+ succeeds it will be executed in the parent process. You can test things
+ like passing a function a null pointer and make sure it doesnt
+ dereference it and crash.
+
+ dies_ok({abort();}, "abort does close your program");
+ dies_ok({int x = 0/0;}, "divide by zero crash");
+ lives ok({pow(3.0, 5.0)}, "nothing wrong with taking 3**5");
+
+ On Windows, these macros are the equivalent of a skip.
+
+- exit_status()
+
+ Summarizes the tests that occurred. If there was no plan, it will print
+ out the number of tests as.
+
+ 1..5
+
+ It will also print a diagnostic message about how many
+ failures there were.
+
+ # Looks like you failed 2 tests of 3 run.
+
+ If all planned tests were successful, it will return 0. If any test fails,
+ it will return the number of failed tests (including ones that were
+ missing). If they all passed, but there were missing tests, it will return
+ 255.
+
+- note(fmt, ...)
+- diag(fmt, ...)
+
+ print out a message to the tap output. note prints to stdout and diag
+ prints to stderr. Each line is preceeded by a "# " so that you know its a
+ diagnostic message.
+
+ note("This is\na note\nto describe\nsomething.");
+
+ prints:
+
+ # This is
+ # a note
+ # to describe
+ # something
+
+ ok() and these functions return ints so you can use them like:
+
+ ok(1) && note("yo!");
+ ok(0) || diag("I have no idea what just happened");
+
+- skip(test, n)
+- skip(test, n, fmt, ...)
+- endskip
+
+ Skip a series of n tests if test is true. You may give a reason why you are
+ skipping them or not. The (possibly) skipped tests must occur between the
+ skip and endskip macros.
+
+ skip(TRUE, 2);
+ ok(1);
+ ok(0);
+ endskip;
+
+ prints:
+
+ ok 1 # skip
+ ok 2 # skip
+
+- todo()
+- todo(fmt, ...)
+- endtodo
+
+ Specifies a series of tests that you expect to fail because they are not
+ yet implemented.
+
+ todo()
+ ok(0);
+ endtodo;
+
+ prints:
+
+ not ok 1 # TODO
+ # Failed (TODO) test at todo.c line 7
+
diff --git a/src/common/libtap/tap.c b/src/common/libtap/tap.c
new file mode 100644
index 0000000..61e0528
--- /dev/null
+++ b/src/common/libtap/tap.c
@@ -0,0 +1,313 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+//#include "common.h"
+#include "tap.h"
+
+static int expected_tests = NO_PLAN;
+static int failed_tests;
+static int current_test;
+static char *todo_mesg;
+
+void
+plan (int tests) {
+ expected_tests = tests;
+ if (tests != NO_PLAN)
+ printf("1..%d\n", tests);
+}
+
+static char *
+vstrdupf (const char *fmt, va_list args) {
+ char *str;
+ int size;
+ va_list args2;
+ va_copy(args2, args);
+ if (!fmt)
+ fmt = "";
+ size = vsnprintf(NULL, 0, fmt, args2) + 2;
+ str = malloc(size);
+ vsprintf(str, fmt, args);
+ va_end(args2);
+ return str;
+}
+
+int
+vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args)
+{
+ char *name = vstrdupf(fmt, args);
+ printf("%sok %d", test ? "" : "not ", ++current_test);
+ if (*name)
+ printf(" - %s", name);
+ if (todo_mesg) {
+ printf(" # TODO");
+ if (*todo_mesg)
+ printf(" %s", todo_mesg);
+ }
+ printf("\n");
+ if (!test) {
+ if (*name)
+ diag(" Failed%s test '%s'\n at %s line %d.",
+ todo_mesg ? " (TODO)" : "", name, file, line);
+ else
+ diag(" Failed%s test at %s line %d.",
+ todo_mesg ? " (TODO)" : "", file, line);
+ if (!todo_mesg)
+ failed_tests++;
+ }
+ free(name);
+ return test;
+}
+
+int
+ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ return test;
+}
+
+static int
+mystrcmp (const char *a, const char *b) {
+ return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
+}
+
+#define eq(a, b) (!mystrcmp(a, b))
+#define ne(a, b) (mystrcmp(a, b))
+
+int
+is_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = eq(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: '%s'", expected);
+ }
+ return test;
+}
+
+int
+isnt_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = ne(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: anything else");
+ }
+ return test;
+}
+
+int
+cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
+ const char *fmt, ...)
+{
+ int test = eq(op, "||") ? a || b
+ : eq(op, "&&") ? a && b
+ : eq(op, "|") ? a | b
+ : eq(op, "^") ? a ^ b
+ : eq(op, "&") ? a & b
+ : eq(op, "==") ? a == b
+ : eq(op, "!=") ? a != b
+ : eq(op, "<") ? a < b
+ : eq(op, ">") ? a > b
+ : eq(op, "<=") ? a <= b
+ : eq(op, ">=") ? a >= b
+ : eq(op, "<<") ? a << b
+ : eq(op, ">>") ? a >> b
+ : eq(op, "+") ? a + b
+ : eq(op, "-") ? a - b
+ : eq(op, "*") ? a * b
+ : eq(op, "/") ? a / b
+ : eq(op, "%") ? a % b
+ : diag("unrecognized operator '%s'", op);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" %d", a);
+ diag(" %s", op);
+ diag(" %d", b);
+ }
+ return test;
+}
+
+static void
+vdiag_to_fh (FILE *fh, const char *fmt, va_list args) {
+ char *mesg, *line;
+ int i;
+ if (!fmt)
+ return;
+ mesg = vstrdupf(fmt, args);
+ line = mesg;
+ for (i = 0; *line; i++) {
+ char c = mesg[i];
+ if (!c || c == '\n') {
+ mesg[i] = '\0';
+ fprintf(fh, "# %s\n", line);
+ if (!c) break;
+ mesg[i] = c;
+ line = &mesg[i+1];
+ }
+ }
+ free(mesg);
+ return;
+}
+
+int
+diag (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stderr, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+note (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stdout, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+exit_status () {
+ int retval = 0;
+ if (expected_tests == NO_PLAN) {
+ printf("1..%d\n", current_test);
+ }
+ else if (current_test != expected_tests) {
+ diag("Looks like you planned %d test%s but ran %d.",
+ expected_tests, expected_tests > 1 ? "s" : "", current_test);
+ retval = 255;
+ }
+ if (failed_tests) {
+ diag("Looks like you failed %d test%s of %d run.",
+ failed_tests, failed_tests > 1 ? "s" : "", current_test);
+ if (expected_tests == NO_PLAN)
+ retval = failed_tests;
+ else
+ retval = expected_tests - current_test + failed_tests;
+ }
+ return retval;
+}
+
+void
+skippy (int n, const char *fmt, ...) {
+ char *why;
+ va_list args;
+ va_start(args, fmt);
+ why = vstrdupf(fmt, args);
+ va_end(args);
+ while (n --> 0) {
+ printf("ok %d ", ++current_test);
+ note("skip %s\n", why);
+ }
+ free(why);
+}
+
+void
+ctodo (int ignore, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ todo_mesg = vstrdupf(fmt, args);
+ va_end(args);
+}
+
+void
+cendtodo () {
+ free(todo_mesg);
+ todo_mesg = NULL;
+}
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <regex.h>
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* Create a shared memory int to keep track of whether a piece of code executed
+dies. to be used in the dies_ok and lives_ok macros */
+int
+tap_test_died (int status) {
+ static int *test_died = NULL;
+ int prev;
+ if (!test_died) {
+ test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ *test_died = 0;
+ }
+ prev = *test_died;
+ *test_died = status;
+ return prev;
+}
+
+int
+like_at_loc (int for_match, const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...)
+{
+ int test;
+ regex_t re;
+ int err = regcomp(&re, expected, REG_EXTENDED);
+ if (err) {
+ char errbuf[256];
+ regerror(err, &re, errbuf, sizeof errbuf);
+ fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
+ expected, errbuf, file, line);
+ exit(255);
+ }
+ err = regexec(&re, got, 0, NULL, 0);
+ regfree(&re);
+ test = for_match ? !err : err;
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ if (for_match) {
+ diag(" '%s'", got);
+ diag(" doesn't match: '%s'", expected);
+ }
+ else {
+ diag(" '%s'", got);
+ diag(" matches: '%s'", expected);
+ }
+ }
+ return test;
+}
+#endif
+
diff --git a/src/common/libtap/tap.h b/src/common/libtap/tap.h
new file mode 100644
index 0000000..2e89b90
--- /dev/null
+++ b/src/common/libtap/tap.h
@@ -0,0 +1,101 @@
+/* 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 __TAP_H__
+#define __TAP_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define NO_PLAN -1
+#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define pass(...) ok(1, ## __VA_ARGS__)
+#define fail(...) ok(0, ## __VA_ARGS__)
+#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+
+int vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args);
+void plan (int tests);
+int ok_at_loc (const char *file, int line, int test, const char *fmt,
+ ...);
+int diag (const char *fmt, ...);
+int note (const char *fmt, ...);
+int exit_status (void);
+void skippy (int n, const char *fmt, ...);
+void ctodo (int ignore, const char *fmt, ...);
+void cendtodo (void);
+int is_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int isnt_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
+ int b, const char *fmt, ...);
+
+#ifdef _WIN32
+#define like(...) skippy(1, "like is not implemented on MSWin32")
+#define unlike(...) like()
+#else
+#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL)
+#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
+int like_at_loc (int for_match, const char *file, int line,
+ const char *got, const char *expected,
+ const char *fmt, ...);
+#endif
+
+#define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;}
+#define endskip } while (0)
+
+#define todo(...) ctodo(0, ## __VA_ARGS__, NULL)
+#define endtodo cendtodo()
+
+#define dies_ok(code, ...) dies_ok_common(code, 1, ## __VA_ARGS__)
+#define lives_ok(code, ...) dies_ok_common(code, 0, ## __VA_ARGS__)
+
+#ifdef _WIN32
+#define dies_ok_common(...) \
+ skippy(1, "Death detection is not supported on MSWin32")
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int tap_test_died (int status);
+#define dies_ok_common(code, for_death, ...) \
+ do { \
+ tap_test_died(1); \
+ int cpid = fork(); \
+ switch (cpid) { \
+ case -1: \
+ perror("fork error"); \
+ exit(EXIT_FAILURE); \
+ case 0: /* child */ \
+ close(1); close(2); \
+ code \
+ tap_test_died(0); \
+ exit(EXIT_SUCCESS); \
+ } \
+ if (waitpid(cpid, NULL, 0) < 0) { \
+ perror("waitpid error"); \
+ exit(EXIT_FAILURE); \
+ } \
+ int it_died = tap_test_died(0); \
+ if (!it_died) {code} \
+ ok(for_death ? it_died : !it_died, ## __VA_ARGS__); \
+ } while (0)
+#endif
+#endif
diff --git a/src/common/libtap/tap_unit.h b/src/common/libtap/tap_unit.h
new file mode 100644
index 0000000..c248fde
--- /dev/null
+++ b/src/common/libtap/tap_unit.h
@@ -0,0 +1,94 @@
+/* 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 tap_unit.h
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief libtap test unit.
+ *
+ * Contains description of a single test unit API.
+ *
+ * Export unit_api in each module header file,
+ * and set function pointer to according test routines.
+ *
+ * <b>Example code for myunit.h</b>
+ * \code
+ * #ifndef MYUNIT_TEST_H
+ * #define MYUNIT_TEST_H
+ *
+ * // Export unittest symbol
+ * unit_api mymodule;
+ *
+ * #endif // MYUNIT_TEST_H
+ * \endcode
+ *
+ * <b>Example code for myunit.c</b>
+ * \code
+ * #include "myunit.h"
+ *
+ * // Function to return unit test count
+ * int myunit_count(int argc, char *argv[]) {
+ * return 1; // Number of tests in this unit
+ * }
+ *
+ * // Function to perform tests
+ * int myunit_run(int argc, char *argv[]) {
+ * // 1. test
+ * ok(1 == 1, "test OK");
+ * return 0;
+ * }
+ *
+ * // Declare module API
+ * unit_api mymodule = {
+ * "My module",
+ * &myunit_count,
+ * &myunit_run
+ * };
+ * \endcode
+ *
+ * To incorporate test, add it to unit tests main().
+ *
+ * See https://github.com/zorgnax/libtap for libtap API reference.
+ *
+ * \addtogroup tests
+ * @{
+ */
+
+#ifndef _TAP_UNIT_H_
+#define _TAP_UNIT_H_
+
+#include "common/libtap/tap.h"
+
+/*! \brief Pointer to function for unit_api. */
+typedef int(unitapi_f)(int, char*[]);
+
+
+/*!
+ * \brief Basic Unit APIs.
+ *
+ * Each unit should have one global variable with
+ * an initialized instance of unit_api.
+ */
+typedef struct {
+ const char *name; /*!< Test unit name. */
+ unitapi_f *count; /*!< Function to calculate number of tests. */
+ unitapi_f *run; /*!< Function to run unit tests. */
+} unit_api;
+
+#endif // _TAP_UNIT_H_
+
+/*! @} */
+
diff --git a/src/common/lists.c b/src/common/lists.c
new file mode 100644
index 0000000..9a93733
--- /dev/null
+++ b/src/common/lists.c
@@ -0,0 +1,160 @@
+/*
+ * BIRD Library -- Linked Lists
+ *
+ * (c) 1998 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Linked lists
+ *
+ * The BIRD library provides a set of functions for operating on linked
+ * lists. The lists are internally represented as standard doubly linked
+ * lists with synthetic head and tail which makes all the basic operations
+ * run in constant time and contain no extra end-of-list checks. Each list
+ * is described by a &list structure, nodes can have any format as long
+ * as they start with a &node structure. If you want your nodes to belong
+ * to multiple lists at once, you can embed multiple &node structures in them
+ * and use the SKIP_BACK() macro to calculate a pointer to the start of the
+ * structure from a &node pointer, but beware of obscurity.
+ *
+ * There also exist safe linked lists (&slist, &snode and all functions
+ * being prefixed with |s_|) which support asynchronous walking very
+ * similar to that used in the &fib structure.
+ */
+
+#define _BIRD_LISTS_C_
+
+#include <stdlib.h>
+#include <string.h>
+#include "common/lists.h"
+
+/**
+ * add_tail - append a node to a list
+ * @l: linked list
+ * @n: list node
+ *
+ * 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)
+{
+ node *z = l->tail;
+
+ n->next = (node *) &l->null;
+ n->prev = z;
+ z->next = n;
+ l->tail = n;
+}
+
+/**
+ * add_head - prepend a node to a list
+ * @l: linked list
+ * @n: list node
+ *
+ * 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)
+{
+ node *z = l->head;
+
+ n->next = z;
+ n->prev = (node *) &l->head;
+ z->prev = n;
+ l->head = n;
+}
+
+/**
+ * insert_node - insert a node to a list
+ * @n: a new list node
+ * @after: a node of a list
+ *
+ * Inserts a node @n to a linked list after an already inserted
+ * node @after.
+ */
+LIST_INLINE void
+insert_node(node *n, node *after)
+{
+ node *z = after->next;
+
+ n->next = z;
+ n->prev = after;
+ after->next = n;
+ z->prev = n;
+}
+
+/**
+ * rem_node - remove a node from a list
+ * @n: node to be removed
+ *
+ * Removes a node @n from the list it's linked in.
+ */
+LIST_INLINE void
+rem_node(node *n)
+{
+ node *z = n->prev;
+ node *x = n->next;
+
+ z->next = x;
+ x->prev = z;
+ n->prev = 0;
+ n->next = 0;
+}
+
+/**
+ * init_list - create an empty list
+ * @l: list
+ *
+ * init_list() takes a &list structure and initializes its
+ * fields, so that it represents an empty list.
+ */
+LIST_INLINE void
+init_list(list *l)
+{
+ l->head = (node *) &l->null;
+ l->null = NULL;
+ l->tail = (node *) &l->head;
+}
+
+/**
+ * add_tail_list - concatenate two lists
+ * @to: destination list
+ * @l: source list
+ *
+ * This function appends all elements of the list @l to
+ * the list @to in constant time.
+ */
+LIST_INLINE void
+add_tail_list(list *to, list *l)
+{
+ node *p = to->tail;
+ node *q = l->head;
+
+ p->next = q;
+ q->prev = p;
+ q = l->tail;
+ q->next = (node *) &to->null;
+ to->tail = q;
+}
+
+/**
+ * list_dup - duplicate list
+ * @to: destination list
+ * @l: source list
+ *
+ * This function duplicates all elements of the list @l to
+ * the list @to in linear time.
+ *
+ * This function only works with a homogenous item size.
+ */
+void list_dup(list *dst, list *src, size_t itemsz)
+{
+ node *n = 0;
+ WALK_LIST(n, *src) {
+ node *i = malloc(itemsz);
+ memcpy(i, n, itemsz);
+ add_tail(dst, i);
+ }
+}
diff --git a/src/common/lists.h b/src/common/lists.h
new file mode 100644
index 0000000..972ea49
--- /dev/null
+++ b/src/common/lists.h
@@ -0,0 +1,103 @@
+/* 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/>.
+ */
+/*
+ * BIRD Library -- Linked Lists
+ *
+ * (c) 1998 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LISTS_H_
+#define _BIRD_LISTS_H_
+
+/*
+ * I admit the list structure is very tricky and also somewhat awkward,
+ * but it's both efficient and easy to manipulate once one understands the
+ * basic trick: The list head always contains two synthetic nodes which are
+ * always present in the list: the head and the tail. But as the `next'
+ * entry of the tail and the `prev' entry of the head are both NULL, the
+ * nodes can overlap each other:
+ *
+ * head head_node.next
+ * null head_node.prev tail_node.next
+ * tail tail_node.prev
+ */
+
+#include <string.h> // size_t
+
+typedef struct node {
+ struct node *next, *prev;
+} node;
+
+typedef struct list { /* In fact two overlayed nodes */
+ struct node *head, *null, *tail;
+} list;
+
+#define NODE (node *)
+#define HEAD(list) ((void *)((list).head))
+#define TAIL(list) ((void *)((list).tail))
+#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; \
+ n=(void *)((NODE (n))->next))
+#define WALK_LIST_DELSAFE(n,nxt,list) \
+ for(n=HEAD(list); (nxt=(void *)((NODE (n))->next)); n=(void *) nxt)
+/* WALK_LIST_FIRST supposes that called code removes each processed node */
+#define WALK_LIST_FIRST(n,list) \
+ while(n=HEAD(list), (NODE (n))->next)
+#define WALK_LIST_BACKWARDS(n,list) for(n=TAIL(list);(NODE (n))->prev; \
+ n=(void *)((NODE (n))->prev))
+#define WALK_LIST_BACKWARDS_DELSAFE(n,prv,list) \
+ for(n=TAIL(list); prv=(void *)((NODE (n))->prev); n=(void *) prv)
+
+#define EMPTY_LIST(list) (!(list).head->next)
+
+/*! \brief Free every node in the list. */
+#define WALK_LIST_FREE(list) \
+ do { \
+ node *n=0,*nxt=0; \
+ WALK_LIST_DELSAFE(n,nxt,list) { \
+ free(n); \
+ } \
+ } 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);
+
+/*!
+ * \brief List item for string lists.
+ */
+typedef struct strnode_t {
+ node n;
+ char *str;
+} strnode_t;
+
+/*! \todo This is broken atm.
+#ifndef _BIRD_LISTS_C_
+#define LIST_INLINE extern inline
+#include "knot/lib/lists.c"
+#undef LIST_INLINE
+#else
+#define LIST_INLINE
+#endif
+*/
+#define LIST_INLINE
+
+#endif
diff --git a/src/common/modified_tree.h b/src/common/modified_tree.h
new file mode 100644
index 0000000..4c3e325
--- /dev/null
+++ b/src/common/modified_tree.h
@@ -0,0 +1,292 @@
+/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */
+
+/* Copyright (c) 2005 Ian Piumarta
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the 'Software'), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
+ */
+
+/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and
+ * Evgenii M. Landis, 'An algorithm for the organization of information',
+ * Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron
+ * J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)].
+ *
+ * An AVL tree is headed by pointers to the root node and to a function defining
+ * the ordering relation between nodes. Each node contains an arbitrary payload
+ * plus three fields per tree entry: the depth of the subtree for which it forms
+ * the root and two pointers to child nodes (singly-linked for minimum space, at
+ * the expense of direct access to the parent node given a pointer to one of the
+ * children). The tree is rebalanced after every insertion or removal. The
+ * tree may be traversed in two directions: forward (in-order left-to-right) and
+ * reverse (in-order, right-to-left).
+ *
+ * Because of the recursive nature of many of the operations on trees it is
+ * necessary to define a number of helper functions for each type of tree node.
+ * The macro TREE_DEFINE(node_tag, entry_name) defines these functions with
+ * unique names according to the node_tag. This macro should be invoked,
+ * thereby defining the necessary functions, once per node tag in the program.
+ *
+ * For details on the use of these macros, see the tree(3) manual page.
+ */
+
+#ifndef _modified_tree_h
+#define _modified_tree_h
+
+
+#define MOD_TREE_DELTA_MAX 1
+
+#define MOD_TREE_ENTRY(type) \
+ struct { \
+ struct type *avl_left; \
+ struct type *avl_right; \
+ int avl_height; \
+ }
+
+#define MOD_TREE_HEAD(name, type) \
+ struct name { \
+ struct type *th_root; \
+ int (*th_cmp)(void *lhs, void *rhs); \
+ }
+
+#define MOD_TREE_INITIALIZER(cmp) { 0, cmp}
+
+#define MOD_TREE_DELTA(self, field) \
+ (( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \
+ - (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0))
+
+/* Recursion prevents the following from being defined as macros. */
+
+#define MOD_TREE_DEFINE(node, field) \
+ \
+ struct node *MOD_TREE_BALANCE_##node##_##field(struct node *); \
+ \
+ struct node *MOD_TREE_ROTL_##node##_##field(struct node *self) \
+ { \
+ struct node *r= self->field.avl_right; \
+ self->field.avl_right= r->field.avl_left; \
+ r->field.avl_left= MOD_TREE_BALANCE_##node##_##field(self); \
+ return MOD_TREE_BALANCE_##node##_##field(r); \
+ } \
+ \
+ struct node *MOD_TREE_ROTR_##node##_##field(struct node *self) \
+ { \
+ struct node *l= self->field.avl_left; \
+ self->field.avl_left= l->field.avl_right; \
+ l->field.avl_right= MOD_TREE_BALANCE_##node##_##field(self); \
+ return MOD_TREE_BALANCE_##node##_##field(l); \
+ } \
+ \
+ struct node *MOD_TREE_BALANCE_##node##_##field(struct node *self) \
+ { \
+ int delta= MOD_TREE_DELTA(self, field); \
+ \
+ if (delta < -MOD_TREE_DELTA_MAX) \
+ { \
+ if (MOD_TREE_DELTA(self->field.avl_right, field) > 0) \
+ self->field.avl_right= MOD_TREE_ROTR_##node##_##field(self->field.avl_right); \
+ return MOD_TREE_ROTL_##node##_##field(self); \
+ } \
+ else if (delta > MOD_TREE_DELTA_MAX) \
+ { \
+ if (MOD_TREE_DELTA(self->field.avl_left, field) < 0) \
+ self->field.avl_left= MOD_TREE_ROTL_##node##_##field(self->field.avl_left); \
+ return MOD_TREE_ROTR_##node##_##field(self); \
+ } \
+ self->field.avl_height= 0; \
+ if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_left->field.avl_height; \
+ if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_right->field.avl_height; \
+ self->field.avl_height += 1; \
+ return self; \
+ } \
+ \
+ struct node *MOD_TREE_INSERT_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(void *lhs, void *rhs), int (*merge)(void **lhs, void **rhs), int *merged)\
+ { \
+ if (!self) { \
+ *merged = 0; \
+ return elm; } \
+ int cmp = compare(elm->data, self->data); \
+ if (cmp < 0) \
+ self->field.avl_left= MOD_TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare, merge, merged); \
+ else if (cmp > 0) \
+ self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \
+ else if (merge) { \
+ merge(&(elm->data), &(self->data)); \
+ *merged = 1; } \
+ else \
+ self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \
+ return MOD_TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *MOD_TREE_FIND_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(void *lhs, void *rhs)) \
+ { \
+ if (!compare) \
+ return 0; \
+ if (!self) \
+ return 0; \
+ if (compare(elm->data, self->data) == 0) \
+ return self; \
+ if (compare(elm->data, self->data) < 0) \
+ return MOD_TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ return MOD_TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \
+ } \
+ \
+ int MOD_TREE_FIND_LESS_EQUAL_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(void *lhs, void *rhs), struct node **found, struct node **prev) \
+ { \
+ if (!self) \
+ return 0; \
+ if (compare(elm->data, self->data) == 0) { \
+ *found = self; \
+ return 1; \
+ } \
+ if (compare(elm->data, self->data) < 0) { \
+ int ret = MOD_TREE_FIND_LESS_EQUAL_##node##_##field(self->field.avl_left, elm, compare, found, prev); \
+ if (ret == 0 && *prev == NULL) { \
+ *prev = self; \
+ ret = -1; \
+ } \
+ return ret; \
+ } else { \
+ *found = self; \
+ *prev = self; \
+ return MOD_TREE_FIND_LESS_EQUAL_##node##_##field(self->field.avl_right, elm, compare, found, prev); \
+ } \
+ } \
+ \
+ struct node *MOD_TREE_MOVE_RIGHT_##node##_##field(struct node *self, struct node *rhs) \
+ { \
+ if (!self) \
+ return rhs; \
+ self->field.avl_right= MOD_TREE_MOVE_RIGHT_##node##_##field(self->field.avl_right, rhs); \
+ return MOD_TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *MOD_TREE_REMOVE_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(void *lhs, void *rhs), void (*del)(struct node *lhs)) \
+ { \
+ if (!self) return 0; \
+ \
+ if (compare(elm->data, self->data) == 0) \
+ { \
+ struct node *tmp= MOD_TREE_MOVE_RIGHT_##node##_##field(self->field.avl_left, self->field.avl_right); \
+ self->field.avl_left= 0; \
+ self->field.avl_right= 0; \
+ del(self); \
+ return tmp; \
+ } \
+ if (compare(elm->data, self->data) < 0) \
+ self->field.avl_left= MOD_TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare, del); \
+ else \
+ self->field.avl_right= MOD_TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare, del); \
+ return MOD_TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ void MOD_TREE_FORWARD_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(void *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ MOD_TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self->data, data); \
+ MOD_TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ } \
+ } \
+ \
+ void MOD_TREE_REVERSE_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ MOD_TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ MOD_TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ } \
+ } \
+ \
+ void MOD_TREE_POST_ORDER_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ MOD_TREE_POST_ORDER_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ MOD_TREE_POST_ORDER_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ } \
+ } \
+ \
+void MOD_TREE_DESTROY_ALL_##node##_##field \
+ (struct node *self, void (*function)(void *node, void *data), void(*dest)(struct node *node), void *data) \
+{ \
+ if (self) \
+ { \
+ MOD_TREE_DESTROY_ALL_##node##_##field(self->field.avl_left, function, dest, data); \
+ MOD_TREE_DESTROY_ALL_##node##_##field(self->field.avl_right, function, dest, data); \
+ if (function != NULL) \
+ function(self->data, data); \
+ dest(self); \
+ } \
+} \
+ \
+ void MOD_TREE_REVERSE_APPLY_POST_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ MOD_TREE_REVERSE_APPLY_POST_ALL_##node##_##field(self->field.avl_right, function, data); \
+ MOD_TREE_REVERSE_APPLY_POST_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self, data); \
+ } \
+}
+
+#define MOD_TREE_INSERT(head, node, field, elm, merge, merged) \
+ ((head)->th_root= MOD_TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp, merge, merged))
+
+#define MOD_TREE_FIND(head, node, field, elm) \
+ (MOD_TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define MOD_TREE_FIND_LESS_EQUAL(head, node, field, elm, found, prev) \
+ (MOD_TREE_FIND_LESS_EQUAL_##node##_##field((head)->th_root, (elm), (head)->th_cmp, found, prev))
+
+#define MOD_TREE_REMOVE(head, node, field, elm, rem) \
+ ((head)->th_root= MOD_TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp, (rem)))
+
+#define MOD_TREE_DEPTH(head, field) \
+ ((head)->th_root->field.avl_height)
+
+#define MOD_TREE_FORWARD_APPLY(head, node, field, function, data) \
+ MOD_TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define MOD_TREE_REVERSE_APPLY(head, node, field, function, data) \
+ MOD_TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define MOD_TREE_POST_ORDER_APPLY(head, node, field, function, data) \
+ MOD_TREE_POST_ORDER_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define MOD_TREE_DESTROY(head, node, field, function, dest, data) \
+ MOD_TREE_DESTROY_ALL_##node##_##field((head)->th_root, function, dest, data)
+
+#define MOD_TREE_REVERSE_APPLY_POST(head, node, field, function, data) \
+ MOD_TREE_REVERSE_APPLY_POST_ALL_##node##_##field((head)->th_root, function, data)
+
+#define MOD_TREE_INIT(head, cmp) do { \
+ (head)->th_root= 0; \
+ (head)->th_cmp= (cmp); \
+ } while (0)
+
+
+#endif /* __MOD_TREE_h */
diff --git a/src/common/print.c b/src/common/print.c
new file mode 100644
index 0000000..9764568
--- /dev/null
+++ b/src/common/print.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include "print.h"
+
+void hex_printf(const char *data, int length, printf_t print_handler)
+{
+ int ptr = 0;
+ for (; ptr < length; ptr++) {
+ print_handler("0x%02x ", (unsigned char)*(data + ptr));
+ }
+ print_handler("\n");
+}
+
+void hex_print(const char *data, int length)
+{
+ hex_printf(data, length, &printf);
+}
+
+void bit_printf(const char *data, int length, printf_t print_handler)
+{
+ unsigned char mask = 0x01;
+ int ptr = 0;
+ int bit = 0;
+ for (; ptr < length; ptr++) {
+ for (bit = 7; bit >= 0; bit--) {
+ if ((mask << bit) & (unsigned char)*(data + ptr)) {
+ print_handler("1");
+ } else {
+ print_handler("0");
+ }
+ }
+ print_handler(" ");
+ }
+ print_handler("\n");
+}
+
+void bit_print(const char *data, int length)
+{
+ bit_printf(data, length, &printf);
+}
diff --git a/src/common/print.h b/src/common/print.h
new file mode 100644
index 0000000..482f55e
--- /dev/null
+++ b/src/common/print.h
@@ -0,0 +1,72 @@
+/* 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 print.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Custom printing functions.
+ *
+ * Downloaded hex_print, bit_print from http://www.digitalpeer.com/id/print
+ * Updated with generic printf handler.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_PRINT_H_
+#define _KNOTD_COMMON_PRINT_H_
+
+typedef int (*printf_t)(const char *fmt, ...);
+
+/*!
+ * \brief Prints the given data as hexadecimal characters.
+ *
+ * \param data Data to print.
+ * \param length Size of the \a data array.
+ */
+void hex_print(const char *data, int length);
+
+/*!
+ * \brief Prints the given data as hexadecimal characters using the given
+ * handler.
+ *
+ * \param data Data to print.
+ * \param length Size of the \a data array.
+ * \param print_handler Handler for printing.
+ */
+void hex_printf(const char *data, int length, printf_t print_handler);
+
+/*!
+ * \brief Prints the given data as a bitmap.
+ *
+ * \param data Data to print.
+ * \param length Size of the \a data array.
+ */
+void bit_print(const char *data, int length);
+
+/*!
+ * \brief Prints the given data as a bitmap using the given handler.
+ *
+ * \param data Data to print.
+ * \param length Size of the \a data array.
+ * \param print_handler Handler for printing.
+ */
+void bit_printf(const char *data, int length, printf_t print_handler);
+
+#endif /* _KNOTD_COMMON_PRINT_H_ */
+
+/*! @} */
diff --git a/src/common/ref.c b/src/common/ref.c
new file mode 100644
index 0000000..3b9c033
--- /dev/null
+++ b/src/common/ref.c
@@ -0,0 +1,44 @@
+/* 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 <stdio.h>
+
+#include "ref.h"
+
+void ref_init(ref_t *p, ref_destructor_t dtor)
+{
+ if (p) {
+ p->count = 0;
+ p->dtor = dtor;
+ }
+}
+
+void ref_retain(ref_t *p)
+{
+ if (p) {
+ __sync_add_and_fetch(&p->count, 1);
+ }
+}
+
+void ref_release(ref_t *p)
+{
+ if (p) {
+ int rc = __sync_sub_and_fetch(&p->count, 1);
+ if (rc == 0 && p->dtor) {
+ p->dtor(p);
+ }
+ }
+}
diff --git a/src/common/ref.h b/src/common/ref.h
new file mode 100644
index 0000000..13a7037
--- /dev/null
+++ b/src/common/ref.h
@@ -0,0 +1,90 @@
+/* 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 ref.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Atomic reference counting structures.
+ *
+ * Reference counting allows implicit sharing of objects
+ * between threads with custom destructor functions.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_REF_H_
+#define _KNOTD_REF_H_
+
+#include <stddef.h>
+
+struct ref_t;
+
+/*! \brief Prototype for object destructor callback. */
+typedef void (*ref_destructor_t)(struct ref_t * p);
+
+/*!
+ * \brief Structure for reference counting.
+ *
+ * Size equals to two sizes of pointer size.
+ * Structure may be embedded to the structures which
+ * we want to use for reference counting.
+ *
+ * \code
+ * struct mystruct {
+ * ref_t ref;
+ * int mydata;
+ * char *mystr;
+ * }
+ * \endcode
+ */
+typedef struct ref_t {
+ size_t count; /*! \brief Reference counter. */
+ ref_destructor_t dtor; /*! \brief Object destructor function. */
+} ref_t;
+
+/*!
+ * \brief Initialize reference counter.
+ *
+ * Set reference counter to 0 and initialize destructor callback.
+ *
+ * \param p Reference-counted object.
+ * \param dtor Destructor function.
+ */
+void ref_init(ref_t *p, ref_destructor_t dtor);
+
+/*!
+ * \brief Mark object as used by the caller.
+ *
+ * Reference counter will be incremented.
+ *
+ * \param p Reference-counted object.
+ */
+void ref_retain(ref_t *p);
+
+/*!
+ * \brief Marks object as unused by the caller.
+ *
+ * Reference counter will be decremented.
+ *
+ * \param p Reference-counted object.
+ */
+void ref_release(ref_t *p);
+
+#endif /* _KNOTD_REF_H_ */
+
+/*! @} */
diff --git a/src/common/skip-list.c b/src/common/skip-list.c
new file mode 100644
index 0000000..79e9429
--- /dev/null
+++ b/src/common/skip-list.c
@@ -0,0 +1,437 @@
+/* Copyright (c) 2010 the authors listed at the following URL, and/or
+the authors of referenced articles or incorporated external code:
+http://en.literateprograms.org/Skip_list_(C)?action=history&offset=20080313195128
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Retrieved from: http://en.literateprograms.org/Skip_list_(C)?oldid=12811
+*/
+
+/*
+ * Modifications by Lubos Slovak, 2010-2011
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+
+//#include "common.h"
+#include "common/skip-list.h"
+
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d\n", \
+ __FILE__, __LINE__)
+
+/*----------------------------------------------------------------------------*/
+
+static const float P = 0.5;
+
+/*!
+ * \brief Maximum level of a node, i.e. maximum number of skip list levels.
+ */
+static const int MAX_LEVEL = 6;
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Generates random real number between 0 and 1.
+ */
+static float frand()
+{
+ unsigned seed = (unsigned)time(0);
+ return (float) rand_r(&seed) / RAND_MAX;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns random level between 0 and MAX_LEVEL.
+ */
+static int skip_random_level()
+{
+ static int first = 1;
+ int lvl = 0;
+
+ if (first) {
+ first = 0;
+ }
+
+ while (frand() < P && lvl < MAX_LEVEL) {
+ lvl++;
+ }
+
+ return lvl;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a new skip list node with the given key and value.
+ *
+ * \param level Level of the skip list node.
+ * \param key Key of the new node.
+ * \param value Value to be stored in the node.
+ *
+ * \return Pointer to the newly created node or NULL if not successful.
+ */
+static skip_node_t *skip_make_node(int level, void *key, void *value)
+{
+ skip_node_t *sn = (skip_node_t *)malloc(sizeof(skip_node_t));
+ if (sn == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ sn->forward = (skip_node_t **)calloc(level + 1, sizeof(skip_node_t *));
+ if (sn->forward == NULL) {
+ ERR_ALLOC_FAILED;
+ free(sn);
+ return NULL;
+ }
+ sn->key = key;
+ sn->value = value;
+ return sn;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Properly deallocates the given skip list node, and optionally destroys
+ * its key and value.
+ *
+ * \param node Skip list node to be deleted.
+ * \param destroy_key Function for properly destroying the key. If set tu NULL,
+ * the object at which the key points will not be destroyed.
+ * \param destroy_value Function for properly destroying the key. If set tu
+ * NULL, the object at which the value points will not be
+ * destroyed.
+ */
+static void skip_delete_node(skip_node_t **node, void (*destroy_key)(void *),
+ void (*destroy_value)(void *))
+{
+ if (destroy_key != NULL) {
+ destroy_key((*node)->key);
+ }
+ if (destroy_value != NULL) {
+ destroy_value((*node)->value);
+ }
+
+ free((*node)->forward);
+ free(*node);
+
+ node = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+skip_list_t *skip_create_list(int (*compare_keys)(void *, void *))
+{
+ assert(compare_keys != NULL);
+
+ skip_list_t *ss = (skip_list_t *)malloc(sizeof(skip_list_t));
+ if (ss == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ ss->head = skip_make_node(MAX_LEVEL, NULL, NULL);
+ if (ss->head == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ss);
+ return NULL;
+ }
+
+ ss->level = 0;
+ ss->compare_keys = compare_keys;
+
+ return ss;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void skip_destroy_list(skip_list_t **list, void (*destroy_key)(void *),
+ void (*destroy_value)(void *))
+{
+ assert((*list) != NULL);
+ assert((*list)->head != NULL);
+
+ skip_node_t *x;
+
+ while ((*list)->head->forward[0] != NULL) {
+ x = (*list)->head->forward[0];
+ for (int i = 0; i <= (*list)->level; i++) {
+ if ((*list)->head->forward[i] != x) {
+ break;
+ }
+ (*list)->head->forward[i] = x->forward[i];
+ }
+
+ // delete the item
+ skip_delete_node(&x, destroy_key, destroy_value);
+
+ while ((*list)->level > 0
+ && (*list)->head->forward[(*list)->level] == NULL) {
+ (*list)->level--;
+ }
+ }
+
+ // free the head
+ skip_delete_node(&(*list)->head, NULL, NULL);
+
+ free(*list);
+ *list = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *skip_find(const skip_list_t *list, void *key)
+{
+ assert(list != NULL);
+ assert(list->head != NULL);
+ assert(list->compare_keys != NULL);
+
+ int i;
+ skip_node_t *x = list->head;
+ for (i = list->level; i >= 0; i--) {
+ while (x->forward[i] != NULL
+ && list->compare_keys(x->forward[i]->key, key) == -1) {
+ x = x->forward[i];
+ }
+ }
+ x = x->forward[0];
+
+ if (x != NULL && list->compare_keys(x->key, key) == 0) {
+ return x->value;
+ }
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *skip_find_less_or_equal(const skip_list_t *list, void *key)
+{
+ assert(list != NULL);
+ assert(list->head != NULL);
+ assert(list->compare_keys != NULL);
+
+ int i;
+ skip_node_t *x = list->head;
+ for (i = list->level; i >= 0; i--) {
+ while (x->forward[i] != NULL
+ && list->compare_keys(x->forward[i]->key, key) <= 0) {
+ x = x->forward[i];
+ }
+ }
+
+ return x->value;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int skip_insert(skip_list_t *list, void *key, void *value,
+ int (*merge_values)(void **, void **))
+{
+ assert(list != NULL);
+ assert(list->head != NULL);
+ assert(list->compare_keys != NULL);
+
+ int i;
+ skip_node_t *x = list->head;
+ skip_node_t *update[MAX_LEVEL + 1];
+ memset(update, 0, MAX_LEVEL + 1);
+
+ for (i = list->level; i >= 0; i--) {
+ while (x->forward[i] != NULL
+ && list->compare_keys(x->forward[i]->key, key) == -1) {
+ x = x->forward[i];
+ }
+ update[i] = x;
+ }
+ x = x->forward[0];
+
+ if (x == NULL || list->compare_keys(x->key, key) != 0) {
+ int lvl = skip_random_level();
+
+ if (lvl > list->level) {
+ for (i = list->level + 1; i <= lvl; i++) {
+ update[i] = list->head;
+ }
+ list->level = lvl;
+ }
+
+ x = skip_make_node(lvl, key, value);
+ if (x == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ for (i = 0; i <= lvl; i++) {
+ x->forward[i] = update[i]->forward[i];
+ update[i]->forward[i] = x;
+ }
+
+ return 0;
+ } else { // already in the list
+ if (merge_values != NULL) { // if merge function provided, merge
+ return (merge_values(&x->value, &value) == 0) ? 2 : -2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int skip_remove(skip_list_t *list, void *key, void (*destroy_key)(void *),
+ void (*destroy_value)(void *))
+{
+ assert(list != NULL);
+ assert(list->head != NULL);
+ assert(list->compare_keys != NULL);
+
+ int i;
+ skip_node_t *x = list->head;
+ skip_node_t *update[MAX_LEVEL + 1];
+ memset(update, 0, MAX_LEVEL + 1);
+
+ for (i = list->level; i >= 0; i--) {
+ while (x->forward[i] != NULL
+ && list->compare_keys(x->forward[i]->key, key) == -1) {
+ x = x->forward[i];
+ }
+ update[i] = x;
+ }
+ x = x->forward[0];
+
+ if (x != NULL && list->compare_keys(x->key, key) == 0) {
+ for (i = 0; i <= list->level; i++) {
+ if (update[i]->forward[i] != x) {
+ break;
+ }
+ update[i]->forward[i] = x->forward[i];
+ }
+
+ // delete the item
+ skip_delete_node(&x, destroy_key, destroy_value);
+
+ while (list->level > 0
+ && list->head->forward[list->level] == NULL) {
+ list->level--;
+ }
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int skip_is_empty(const skip_list_t *list)
+{
+ if (!list) {
+ return 1;
+ }
+
+ return (list->head->forward[0] == NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const skip_node_t *skip_first(const skip_list_t *list)
+{
+ if (!list) {
+ return NULL;
+ }
+
+ return list->head->forward[0];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const skip_node_t *skip_next(const skip_node_t *node)
+{
+ return node->forward[0];
+}
+
+/*----------------------------------------------------------------------------*/
+
+void skip_print_list(const skip_list_t *list,
+ void (*print_item)(void *, void *))
+{
+ assert(list != NULL);
+ assert(list->head != NULL);
+ assert(list->compare_keys != NULL);
+ assert(print_item != NULL);
+
+ skip_node_t *x = list->head->forward[0];
+ while (x != NULL) {
+ print_item(x->key, x->value);
+ x = x->forward[0];
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+skip_list_t *skip_copy_list(const skip_list_t *list)
+{
+ skip_list_t *ss = (skip_list_t *)malloc(sizeof(skip_list_t));
+ if (ss == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ ss->head = skip_make_node(list->level, NULL, NULL);
+ if (ss->head == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ss);
+ return NULL;
+ }
+
+ ss->level = list->level;
+ ss->compare_keys = list->compare_keys;
+
+ skip_node_t *x = list->head->forward[0];
+ skip_node_t *prev = list->head;
+ skip_node_t *new_prev = ss->head;
+ while (x != NULL) {
+ //print_item(x->key, x->value);
+
+ // create new node
+ skip_node_t *n = skip_make_node(list->level, x->key, x->value);
+ if (n == NULL) {
+ skip_destroy_list(&ss, NULL, NULL);
+ return NULL;
+ }
+ // set forward pointers from the previous node
+ for (int i = 0; i <= list->level; ++i) {
+ if (prev->forward[i] == x) {
+ new_prev->forward[i] = n;
+ }
+ }
+
+ prev = x;
+ x = x->forward[0];
+ new_prev = n;
+ }
+
+ return ss;
+}
diff --git a/src/common/skip-list.h b/src/common/skip-list.h
new file mode 100644
index 0000000..784f366
--- /dev/null
+++ b/src/common/skip-list.h
@@ -0,0 +1,215 @@
+/*!
+ * \file skip-list.h
+ *
+ * \author Copyright (c) 2010 the authors listed at the following URL, and/or
+ * the authors of referenced articles or incorporated external code:
+ * http://en.literateprograms.org/Skip_list_(C)?action=history&offset=20080313195128
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Generic skip-list implementation.
+ *
+ * Original retrieved from http://en.literateprograms.org/Skip_list_(C)?oldid=12811
+ * Modifications by Lubos Slovak, 2010
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+/* Copyright (c) 2010 the authors listed at the following URL, and/or
+the authors of referenced articles or incorporated external code:
+http://en.literateprograms.org/Skip_list_(C)?action=history&offset=20080313195128
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Retrieved from: http://en.literateprograms.org/Skip_list_(C)?oldid=12811
+*/
+
+#ifndef _KNOTD_COMMON_SKIP_LIST_H_
+#define _KNOTD_COMMON_SKIP_LIST_H_
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Skip list node.
+ */
+struct skip_node {
+ void *key; /*!< Key of the node. Used for ordering. */
+
+ void *value; /*!< Value stored in the node. */
+
+ /*! \brief Pointers to next item on various levels. */
+ struct skip_node **forward;
+};
+
+typedef struct skip_node skip_node_t;
+
+/*!
+ * \brief Skip list.
+ *
+ * \todo Implement quasi-randomization.
+ */
+struct skip_list {
+ /*! \brief Head of the list (with no actual key and value stored). */
+ skip_node_t *head;
+
+ /*! \brief Actual maximum level of the list. */
+ int level;
+
+ /*! \brief Function for comparing two skip list item's keys. */
+ int (*compare_keys)(void *, void *);
+};
+
+typedef struct skip_list skip_list_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a new generic skip list.
+ *
+ * \param compare_keys Function for comparing the keys.
+ * \todo What should compare_keys exactly return?
+ *
+ * \return Pointer to the newly created skip list if successful. NULL otherwise.
+ */
+skip_list_t *skip_create_list(int (*compare_keys)(void *, void *));
+
+/*!
+ * \brief Properly destroys the list, possibly also with the keys and values.
+ *
+ * \param list Skip list to be destroyed.
+ * \param destroy_key Function for properly destroying the key. If set tu NULL,
+ * the object at which the key points will not be destroyed.
+ * \param destroy_value Function for properly destroying the key. If set tu
+ * NULL, the object at which the value points will not be
+ * destroyed.
+ */
+void skip_destroy_list(skip_list_t **list, void (*destroy_key)(void *),
+ void (*destroy_value)(void *));
+
+/*!
+ * \brief Inserts a new key-value pair into the skip list.
+ *
+ * The \a merge_values function should merge the second value to the first
+ * value. It must not delete the first value. The second value also should not
+ * be deleted (as this is concern of the caller).
+ *
+ * \param list The skip list to insert to.
+ * \param key Key of the item to be inserted. Used for ordering.
+ * \param value Value of the item to be inserted.
+ * \param merge_values Function for merging the saved values. Optional. If set
+ * to NULL, the skip list will not merge values when
+ * attempting to insert item with key already present in the
+ * list.
+ * \todo What are the function parameters and what should it
+ * return as integer?
+ *
+ * \retval 0 If successful and the key was not yet present in the list.
+ * \retval 1 If the key was already present and the new value was ignored
+ * (because no merging function was provided).
+ * \retval 2 If successful, the key was already present and the values were
+ * merged.
+ * \retval -1 If an error occured and the key was not present in the list.
+ * \retval -2 If the key is already present in the list and merging was
+ * unsuccessful.
+ */
+int skip_insert(skip_list_t *list, void *key, void *value,
+ int (*merge_values)(void **, void **));
+
+/*!
+ * \brief Removes an item with the given key from the list and optionally
+ * deletes the item's key and value.
+ *
+ * \param list Skip list to delete from.
+ * \param key Key of the item to be deleted.
+ * \param destroy_key Function for properly destroying the key. If set tu NULL,
+ * the object at which the key points will not be destroyed.
+ * \param destroy_value Function for properly destroying the key. If set tu
+ * NULL, the object at which the value points will not be
+ * destroyed.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If the item was not present in the list.
+ */
+int skip_remove(skip_list_t *list, void *key, void (*destroy_key)(void *),
+ void (*destroy_value)(void *));
+
+/*!
+ * \brief Tries to find item with the given key in the list.
+ *
+ * \param list Skip list to search in.
+ * \param key Key of the item to be found.
+ *
+ * \return Value stored in the item with key \a key, or NULL if the key was not
+ * found.
+ */
+void *skip_find(const skip_list_t *list, void *key);
+
+/*!
+ * \brief Returns item with largest key smaller or equal than \a key.
+ *
+ * \param list Skip list to search in.
+ * \param key Key of the item to be found.
+ *
+ * \return Value stored in the item with largest key smaller or equal than \a
+ * key, or NULL if the key was not found.
+ */
+void *skip_find_less_or_equal(const skip_list_t *list, void *key);
+
+/*!
+ * \brief Checks if the skip list is empty.
+ *
+ * \param list Skip list to check.
+ *
+ * \retval 1 if empty.
+ * \retval 0 if non-empty.
+ */
+int skip_is_empty(const skip_list_t *list);
+
+/*!
+ * \brief Returns the first item in the skip list.
+ */
+const skip_node_t *skip_first(const skip_list_t *list);
+
+/*!
+ * \brief Returns the next item in the skip list.
+ */
+const skip_node_t *skip_next(const skip_node_t *node);
+
+/*!
+ * \brief Prints the whole list using the given print function.
+ *
+ * \param list Skip list to be printed.
+ * \param print_item Function for printing the key-value pair.
+ */
+void skip_print_list(const skip_list_t *list,
+ void (*print_item)(void *, void *));
+
+/*!
+ * \brief Copies the skip list.
+ *
+ * \param list Skip list to be copied.
+ *
+ * \return Copy of \a list.
+ *
+ * \todo Test!!!
+ */
+skip_list_t *skip_copy_list(const skip_list_t *list);
+
+#endif /* _KNOTD_COMMON_SKIP_LIST_H_ */
+
+/*! @} */
diff --git a/src/common/slab/alloc-common.h b/src/common/slab/alloc-common.h
new file mode 100644
index 0000000..32878ab
--- /dev/null
+++ b/src/common/slab/alloc-common.h
@@ -0,0 +1,61 @@
+/* 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 alloc-common.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Common macros for alloc.
+ *
+ * \addtogroup alloc
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_ALLOC_COMMON_H_
+#define _KNOTD_COMMON_ALLOC_COMMON_H_
+
+#include <stdio.h>
+
+//#define MEM_DEBUG
+//#define MEM_NOSLAB
+//#define MEM_POISON
+#define MEM_SLAB_CAP 5 // Cap slab_cache empty slab count (undefined = inf)
+#define MEM_COLORING // Slab cache coloring
+//#define MEM_SLAB_DEPOT // Use slab depot for slab caching (not thread-safe)
+
+/* Eliminate compiler warning with unused parameters. */
+#ifndef UNUSED
+#define UNUSED(param) (void)(param)
+#endif
+
+/* Optimisation macros. */
+#ifndef likely
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+#ifdef MEM_DEBUG
+#define dbg_mem(msg...) fprintf(stderr, msg)
+#else
+#define dbg_mem(msg...)
+#endif
+
+
+#endif /* _KNOTD_COMMON_ALLOC_COMMON_H_ */
+
+/*! @} */
diff --git a/src/common/slab/malloc.c b/src/common/slab/malloc.c
new file mode 100644
index 0000000..ec5a68d
--- /dev/null
+++ b/src/common/slab/malloc.c
@@ -0,0 +1,60 @@
+/* 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>
+/*
+ * Skip unit if not debugging memory.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#include "common/slab/alloc-common.h"
+
+#ifdef MEM_DEBUG
+/*
+ * ((destructor)) attribute executes this function after main().
+ * \see http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
+ */
+void __attribute__ ((destructor)) usage_dump()
+#else
+void usage_dump()
+#endif
+{
+ /* Get resource usage. */
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) < 0) {
+ memset(&usage, 0, sizeof(struct rusage));
+ }
+
+ fprintf(stderr, "\nMemory statistics:");
+ fprintf(stderr, "\n==================\n");
+
+ fprintf(stderr, "User time: %.03lf ms\nSystem time: %.03lf ms\n",
+ usage.ru_utime.tv_sec * (double) 1000.0
+ + usage.ru_utime.tv_usec / (double)1000.0,
+ usage.ru_stime.tv_sec * (double) 1000.0
+ + usage.ru_stime.tv_usec / (double)1000.0);
+ fprintf(stderr, "Major page faults: %lu (required I/O)\nMinor page faults: %lu\n",
+ usage.ru_majflt, usage.ru_minflt);
+ fprintf(stderr, "Number of swaps: %lu\n",
+ usage.ru_nswap);
+ fprintf(stderr, "Voluntary context switches: %lu\nInvoluntary context switches: %lu\n",
+ usage.ru_nvcsw,
+ usage.ru_nivcsw);
+ fprintf(stderr, "==================\n");
+}
diff --git a/src/common/slab/malloc.h b/src/common/slab/malloc.h
new file mode 100644
index 0000000..8ca9f58
--- /dev/null
+++ b/src/common/slab/malloc.h
@@ -0,0 +1,43 @@
+/* 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 malloc.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Memory allocation related functions.
+ *
+ * \addtogroup alloc
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_MALLOC_H_
+#define _KNOTD_COMMON_MALLOC_H_
+
+#include <stdlib.h>
+
+/*! \brief Print usage statistics.
+ *
+ * \note This function has destructor attribute set if MEM_DEBUG is enabled.
+ *
+ * \warning Not all printed statistics are available on every OS,
+ * consult manual page for getrusage(2).
+ */
+void usage_dump();
+
+#endif // _KNOTD_COMMON_MALLOC_H_
+
+/*! @} */
diff --git a/src/common/slab/slab.c b/src/common/slab/slab.c
new file mode 100644
index 0000000..ccdf7ca
--- /dev/null
+++ b/src/common/slab/slab.c
@@ -0,0 +1,732 @@
+/* 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "common/slab/alloc-common.h"
+#include "common/slab/slab.h"
+
+/*
+ * Magic constants.
+ */
+#define SLAB_MAGIC 0x51 /*!< "Sl" magic byte (slab type). */
+#define LOBJ_MAGIC 0x0B /*!< "Ob" magic byte (object type). */
+#define POISON_DWORD 0xdeadbeef /*!< Memory boundary guard magic. */
+#define SLAB_MINCOLOR 64 /*!< Minimum space reserved for cache coloring. */
+#define SLAB_HEADER sizeof(slab_t) /*!< Slab header size. */
+#define ALIGN_PTRSZ __attribute__ ((__aligned__(sizeof(void*))))
+
+/*! \brief Fast cache id lookup table.
+ *
+ * Provides O(1) lookup.
+ * Filled with interesting values from default
+ * or on-demand.
+ */
+unsigned ALIGN_PTRSZ SLAB_CACHE_LUT[SLAB_SIZE] = {
+ [24] = SLAB_GP_COUNT + 1,
+ [800] = SLAB_GP_COUNT + 2
+};
+
+/*! \brief Find the next highest power of 2. */
+static inline unsigned get_next_pow2(unsigned v)
+{
+ // Next highest power of 2
+ --v;
+ v |= v >> 1; v |= v >> 2;
+ v |= v >> 4; v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+
+ return v;
+}
+
+/*! \brief Return binary logarithm of a number, which is a power of 2. */
+static inline unsigned fastlog2(unsigned v)
+{
+ // Works if we know the size is a power of 2
+ register unsigned int r = (v & 0xAAAAAAAA) != 0;
+ r |= ((v & 0xFFFF0000) != 0) << 4;
+ r |= ((v & 0xFF00FF00) != 0) << 3;
+ r |= ((v & 0xF0F0F0F0) != 0) << 2;
+ r |= ((v & 0xCCCCCCCC) != 0) << 1;
+ return r;
+}
+
+/*!
+ * \brief Fast hashing function.
+ *
+ * Finds the next highest power of 2 and returns binary logarithm.
+ * Values are stored in LUT cache for future access.
+ */
+static unsigned slab_cache_id(unsigned size)
+{
+ // Assert cache id of the smallest bufsize is 0
+ if(size <= SLAB_MIN_BUFLEN) {
+ return 0;
+ }
+
+ // Check LUT
+ unsigned id = 0;
+ if ((id = SLAB_CACHE_LUT[size])) {
+ return id;
+ } else {
+
+ // Compute binary logarithm
+ // Next highest power of 2
+ id = fastlog2(get_next_pow2(size));
+
+ // Shift cacheid of SLAB_MIN_BUFLEN to 0
+ id -= SLAB_EXP_OFFSET;
+
+ // Store
+ SLAB_CACHE_LUT[size] = id;
+ }
+
+ return id;
+}
+
+/*
+ * Slab run-time constants.
+ */
+
+size_t SLAB_MASK = 0; /*!< \brief Slab address mask (for computing offsets). */
+static unsigned SLAB_LOGSIZE = 0; /*!< \brief Binary logarithm of slab size. */
+
+/*!
+ * Depot is a caching sub-allocator of slabs.
+ * It mitigates performance impact of sequentially allocating and freeing
+ * from a slab with just a few slab items by caching N slabs before returning
+ * them to the system.
+ *
+ * \todo With wider use, locking or RCU will be necessary.
+ */
+#ifdef MEM_SLAB_DEPOT
+static slab_depot_t _depot_g; /*! \brief Global slab depot. */
+#endif // MEM_SLAB_DEPOT
+
+/*!
+ * \brief Allocate a slab of given bufsize from depot.
+ *
+ * \retval Reserved memory for slab on success.
+ * \retval NULL on errors.
+ */
+static void* slab_depot_alloc(size_t bufsize)
+{
+ void *page = 0;
+#ifdef MEM_SLAB_DEPOT
+ if (_depot_g.available) {
+ for (int i = _depot_g.available - 1; i > -1 ; --i) {
+ if(_depot_g.cache[i]->bufsize == bufsize) {
+ page = _depot_g.cache[i];
+ _depot_g.cache[i] = _depot_g.cache[--_depot_g.available];
+ return page;
+ }
+ }
+ page = _depot_g.cache[--_depot_g.available];
+ } else {
+ if(posix_memalign(&page, SLAB_SIZE, SLAB_SIZE) == 0) {
+ ((slab_t*)page)->bufsize = 0;
+ } else {
+ page = 0;
+ }
+
+ }
+#else // MEM_SLAB_DEPOT
+ if(posix_memalign(&page, SLAB_SIZE, SLAB_SIZE) == 0) {
+ ((slab_t*)page)->bufsize = 0;
+ } else {
+ page = 0;
+ }
+#endif // MEM_SLAB_DEPOT
+
+ return page;
+}
+
+/*!
+ * \brief Return a slab to the depot.
+ *
+ * \note If the depot is full, slab gets immediately freed.
+ */
+static inline void slab_depot_free(void* slab)
+{
+#ifdef MEM_SLAB_DEPOT
+ if (_depot_g.available < SLAB_DEPOT_SIZE) {
+ _depot_g.cache[_depot_g.available++] = slab;
+ } else {
+ free(slab);
+ }
+#else // MEM_SLAB_DEPOT
+ free(slab);
+#endif // MEM_SLAB_DEPOT
+}
+
+/*! \brief Initialize slab depot. */
+static void slab_depot_init()
+{
+#ifdef MEM_SLAB_DEPOT
+ _depot_g.available = 0;
+#endif // MEM_SLAB_DEPOT
+}
+
+/*! \brief Destroy slab depot. */
+static void slab_depot_destroy()
+{
+#ifdef MEM_SLAB_DEPOT
+ while(_depot_g.available) {
+ free(_depot_g.cache[--_depot_g.available]);
+ }
+#endif // MEM_SLAB_DEPOT
+}
+
+/*
+ * Initializers.
+ */
+
+/*! \brief Initializes slab subsystem (it is called automatically). */
+void __attribute__ ((constructor)) slab_init()
+{
+ // Fetch page size
+ SLAB_LOGSIZE = fastlog2(SLAB_SIZE);
+
+ // Compute slab page mask
+ SLAB_MASK = 0;
+ for (int i = 0; i < SLAB_LOGSIZE; ++i) {
+ SLAB_MASK |= 1 << i;
+ }
+ SLAB_MASK = ~SLAB_MASK;
+
+ // Initialize depot
+ slab_depot_init();
+}
+
+/*! \brief Deinitializes slab subsystem (it is called automatically). */
+void __attribute__ ((destructor)) slab_deinit()
+{
+ // Deinitialize global allocator
+ if (SLAB_LOGSIZE) {
+ slab_depot_destroy();
+ SLAB_LOGSIZE = SLAB_MASK = 0;
+ }
+}
+
+/*
+ * Cache helper functions.
+ */
+
+/* \notice Not used right now.
+static void slab_dump(slab_t* slab) {
+
+ printf("%s: buffers (bufsize=%zuB, %u/%u free): \n",
+ __func__, slab->cache->bufsize, slab->bufs_free,
+ slab->bufs_count);
+
+ void** buf = slab->head;
+ int i = 0, n = 0;
+ while(buf != 0) {
+ size_t diff = (size_t)((char*)buf - (char*)slab->base);
+ printf("-> %lu", diff / slab->cache->bufsize);
+ buf = (void**)(*buf);
+ if (++i == 10) {
+ printf("\n");
+ i = 0;
+ }
+ ++n;
+ }
+
+ printf("\n");
+}
+*/
+
+/*!
+ * \brief Free all slabs from a slab cache.
+ * \return Number of freed slabs.
+ */
+static inline int slab_cache_free_slabs(slab_t* slab)
+{
+ int count = 0;
+ while (slab) {
+ slab_t* next = slab->next;
+ slab_destroy(&slab);
+ ++count;
+ slab = next;
+
+ }
+ return count;
+}
+
+/*
+ * Slab helper functions.
+ */
+
+/*! \brief Return number of slabs in a linked list. */
+static inline unsigned slab_list_walk(slab_t* slab)
+{
+ unsigned count = 0;
+ while(slab) {
+ slab = slab->next;
+ ++count;
+ }
+ return count;
+}
+
+/*! \brief Remove slab from a linked list. */
+static void slab_list_remove(slab_t* slab)
+{
+ // Disconnect from list
+ if (slab->prev) {
+ slab->prev->next = slab->next;
+ }
+ if(slab->next) {
+ slab->next->prev = slab->prev;
+ }
+
+ // Disconnect from cache
+ slab_cache_t* cache = slab->cache;
+ {
+ if (cache->slabs_free == slab) {
+ cache->slabs_free = slab->next;
+ } else if (cache->slabs_full == slab) {
+ cache->slabs_full = slab->next;
+ }
+ }
+}
+
+/*! \brief Insert slab into a linked list. */
+static void slab_list_insert(slab_t** list, slab_t* item)
+{
+ // If list exists, push to the top
+ item->prev = 0;
+ item->next = *list;
+ if(*list) {
+ (*list)->prev = item;
+ }
+ *list = item;
+}
+
+/*! \brief Move slab from one linked list to another. */
+static inline void slab_list_move(slab_t** target, slab_t* slab)
+{
+ slab_list_remove(slab);
+ slab_list_insert(target, slab);
+}
+
+/*
+ * API functions.
+ */
+
+slab_t* slab_create(slab_cache_t* cache)
+{
+ const size_t size = SLAB_SIZE;
+
+ slab_t* slab = slab_depot_alloc(cache->bufsize);
+
+ if (unlikely(slab < 0)) {
+ dbg_mem("%s: failed to allocate aligned memory block\n",
+ __func__);
+ return 0;
+ }
+
+ /* Initialize slab. */
+ slab->magic = SLAB_MAGIC;
+ slab->cache = cache;
+ slab_list_insert(&cache->slabs_free, slab);
+#ifdef MEM_SLAB_CAP
+ ++cache->empty;
+#endif
+
+ /* Already initialized? */
+ if (slab->bufsize == cache->bufsize) {
+ return slab;
+ } else {
+ slab->bufsize = cache->bufsize;
+ }
+
+ /* Ensure the item size can hold at least a size of ptr. */
+ size_t item_size = slab->bufsize;
+ if (unlikely(item_size < SLAB_MIN_BUFLEN)) {
+ item_size = SLAB_MIN_BUFLEN;
+ }
+
+ /* Ensure at least some space for coloring */
+ size_t data_size = size - sizeof(slab_t);
+#ifdef MEM_COLORING
+ size_t free_space = data_size % item_size;
+ if (unlikely(free_space < SLAB_MINCOLOR)) {
+ free_space = SLAB_MINCOLOR;
+ }
+
+
+ /// unsigned short color = __sync_fetch_and_add(&cache->color, 1);
+ unsigned short color = (cache->color += sizeof(void*));
+ color = color % free_space;
+#else
+ const unsigned short color = 0;
+#endif
+
+ /* Calculate useable data size */
+ data_size -= color;
+ slab->bufs_count = data_size / item_size;
+ slab->bufs_free = slab->bufs_count;
+
+ // Save first item as next free
+ slab->base = (char*)slab + sizeof(slab_t) + color;
+ slab->head = (void**)slab->base;
+
+ // Create freelist, skip last member, which is set to NULL
+ char* item = (char*)slab->head;
+ for(unsigned i = 0; i < slab->bufs_count - 1; ++i) {
+ *((void**)item) = item + item_size;
+ item += item_size;
+ }
+
+ // Set last buf to NULL (tail)
+ *((void**)item) = (void*)0;
+
+ // Ensure the last item has a NULL next
+ dbg_mem("%s: created slab (%p, %p) (%zu B)\n",
+ __func__, slab, slab + size, size);
+ return slab;
+}
+
+void slab_destroy(slab_t** slab)
+{
+ /* Disconnect from the list */
+ slab_list_remove(*slab);
+
+ /* Free slab */
+ slab_depot_free(*slab);
+
+ /* Invalidate pointer. */
+ dbg_mem("%s: deleted slab %p\n", __func__, *slab);
+ *slab = 0;
+}
+
+void* slab_alloc(slab_t* slab)
+{
+ // Fetch first free item
+ void **item = 0;
+ {
+ if((item = slab->head)) {
+ slab->head = (void**)*item;
+ --slab->bufs_free;
+ } else {
+ // No more free items
+ return 0;
+ }
+ }
+
+#ifdef MEM_DEBUG
+ // Increment statistics
+ __sync_add_and_fetch(&slab->cache->stat_allocs, 1);
+#endif
+
+ // Move to full?
+ if (unlikely(slab->bufs_free == 0)) {
+ slab_list_move(&slab->cache->slabs_full, slab);
+ } else {
+#ifdef MEM_SLAB_CAP
+ // Mark not empty?
+ if (unlikely(slab->bufs_free == slab->bufs_count - 1)) {
+ --slab->cache->empty;
+ }
+#endif
+ }
+
+ return item;
+}
+
+void slab_free(void* ptr)
+{
+ // Null pointer check
+ if (unlikely(!ptr)) {
+ return;
+ }
+
+ // Get slab start address
+ slab_t* slab = slab_from_ptr(ptr);
+ assert(slab);
+
+ // Check if it exists in directory
+ if (slab->magic == SLAB_MAGIC) {
+
+ // Return buf to slab
+ *((void**)ptr) = (void*)slab->head;
+ slab->head = (void**)ptr;
+ ++slab->bufs_free;
+
+#ifdef MEM_DEBUG
+ // Increment statistics
+ __sync_add_and_fetch(&slab->cache->stat_frees, 1);
+#endif
+
+ // Return to partial
+ if(unlikely(slab->bufs_free == 1)) {
+ slab_list_move(&slab->cache->slabs_free, slab);
+ } else {
+#ifdef MEM_SLAB_CAP
+ // Recycle if empty
+ if(unlikely(slab_isempty(slab))) {
+ if(slab->cache->empty == MEM_SLAB_CAP) {
+ slab_destroy(&slab);
+ } else {
+ ++slab->cache->empty;
+ }
+ }
+#endif
+ }
+
+ } else {
+
+ // Pointer is not a slab
+ // Presuming it's a large block
+ slab_obj_t* bs = (slab_obj_t*)ptr - 1;
+
+#ifdef MEM_POISON
+ // Remove memory barrier
+ mprotect(ptr + bs->size, sizeof(int), PROT_READ|PROT_WRITE);
+#endif
+
+ // Unmap
+ dbg_mem("%s: unmapping large block of %zu bytes at %p\n",
+ __func__, bs->size, ptr);
+ free(bs);
+ }
+}
+
+int slab_cache_init(slab_cache_t* cache, size_t bufsize)
+{
+ if (unlikely(!bufsize)) {
+ return -1;
+ }
+
+ cache->empty = 0;
+ cache->bufsize = bufsize;
+ cache->slabs_free = cache->slabs_full = 0;
+ cache->color = 0;
+
+ /* Initialize stats */
+ cache->stat_allocs = cache->stat_frees = 0;
+
+ dbg_mem("%s: created cache of size %zu\n",
+ __func__, bufsize);
+
+ return 0;
+}
+
+void slab_cache_destroy(slab_cache_t* cache) {
+
+ // Free slabs
+ unsigned free_s = slab_cache_free_slabs(cache->slabs_free);
+ unsigned full_s = slab_cache_free_slabs(cache->slabs_full);
+#ifndef MEM_DEBUG
+ UNUSED(free_s);
+ UNUSED(full_s);
+#else
+ dbg_mem("%s: %u empty/partial, %u full caches\n",
+ __func__, free_s, full_s);
+#endif
+
+ // Invalidate cache
+ cache->bufsize = 0;
+ cache->slabs_free = cache->slabs_full = 0;
+}
+
+void* slab_cache_alloc(slab_cache_t* cache)
+{
+ slab_t* slab = cache->slabs_free;
+ if(!cache->slabs_free) {
+ slab = slab_create(cache);
+ if (slab == NULL) {
+ return NULL;
+ }
+ }
+
+
+ return slab_alloc(slab);
+}
+
+int slab_cache_reap(slab_cache_t* cache)
+{
+ // For now, just free empty slabs
+ slab_t* slab = cache->slabs_free;
+ int count = 0;
+ while (slab) {
+ slab_t* next = slab->next;
+ if (slab_isempty(slab)) {
+ slab_destroy(&slab);
+ ++count;
+ }
+ slab = next;
+
+ }
+
+ cache->empty = 0;
+ return count;
+}
+
+int slab_alloc_init(slab_alloc_t* alloc)
+{
+ // Invalidate
+ memset(alloc, 0, sizeof(slab_alloc_t));
+
+ // Initialize descriptors cache
+ slab_cache_init(&alloc->descriptors, sizeof(slab_cache_t));
+
+ return 0;
+}
+
+void slab_alloc_destroy(slab_alloc_t* alloc)
+{
+ // Destroy all caches
+ for (unsigned i = 0; i < SLAB_CACHE_COUNT; ++i) {
+ if (alloc->caches[i] != 0) {
+ slab_cache_destroy(alloc->caches[i]);
+ }
+ }
+
+ // Destroy cache for descriptors
+ slab_cache_destroy(&alloc->descriptors);
+}
+
+void* slab_alloc_alloc(slab_alloc_t* alloc, size_t size)
+{
+ // Invalid size check
+ if (unlikely(!size)) {
+ return 0;
+ }
+
+#ifdef MEM_POISON
+ // Reserve memory for poison
+ size += sizeof(int);
+#endif
+ // Directly map large block
+ if (unlikely(size > SLAB_SIZE/2)) {
+
+ // Map block
+ size += sizeof(slab_obj_t);
+ slab_obj_t* p = 0;
+ p = malloc(size);
+
+ dbg_mem("%s: mapping large block of %zu bytes at %p\n",
+ __func__, size, p + 1);
+
+ /* Initialize. */
+ p->magic = LOBJ_MAGIC;
+ p->size = size - sizeof(slab_obj_t);
+
+#ifdef MEM_POISON
+ // Reduce real size
+ p->size -= sizeof(int);
+
+ // Memory barrier
+ int* pb = (int*)((char*)p + size - sizeof(int));
+ *pb = POISON_DWORD;
+ mprotect(pb, sizeof(int), PROT_NONE);
+#endif
+
+ return p + 1;
+ }
+
+ // Get cache id from size
+ unsigned cache_id = slab_cache_id(size);
+
+ // Check if associated cache exists
+ if (unlikely(alloc->caches[cache_id] == 0)) {
+
+ // Assert minimum cache size
+ if (unlikely(size < SLAB_MIN_BUFLEN)) {
+ size = SLAB_MIN_BUFLEN;
+ }
+
+ // Calculate cache bufsize
+ size_t bufsize = size;
+ if (cache_id < SLAB_GP_COUNT) {
+ bufsize = get_next_pow2(size);
+ }
+
+ // Create cache
+ dbg_mem("%s: creating cache of %zuB (req. %zuB) (id=%u)\n",
+ __func__, bufsize, size, cache_id);
+
+ slab_cache_t* cache = slab_cache_alloc(&alloc->descriptors);
+ slab_cache_init(cache, bufsize);
+ alloc->caches[cache_id] = cache;
+ }
+
+ // Allocate from cache
+ void* mem = slab_cache_alloc(alloc->caches[cache_id]);
+
+#ifdef MEM_POISON
+ // Memory barrier
+ /*! \todo Broken, need to store the barrier byte size. */
+ //int* pb = (int*)((char*)mem + size - sizeof(int));
+ //mprotect(pb, sizeof(int), PROT_NONE);
+#endif
+ return mem;
+}
+
+void *slab_alloc_realloc(slab_alloc_t* alloc, void *ptr, size_t size)
+{
+ // realloc(0) equals to free(ptr)
+ if (!size) {
+ slab_free(ptr);
+ return 0;
+ }
+
+ // Allocate new buf
+ void *nptr = slab_alloc_alloc(alloc, size);
+ assert(nptr);
+
+ // Copy memory if present
+ if (ptr) {
+ slab_t* slab = slab_from_ptr(ptr);
+ memcpy(nptr, ptr, slab->cache->bufsize);
+
+ // Free old buf
+ slab_free(ptr);
+ }
+
+ return nptr;
+}
+
+void slab_alloc_stats(slab_alloc_t* alloc)
+{
+#ifdef MEM_DEBUG
+ printf("Cache usage:\n");
+ for (int i = 0; i < SLAB_CACHE_COUNT; ++i) {
+
+ if (!alloc->caches[i])
+ continue;
+
+ slab_cache_t* cache = alloc->caches[i];
+ unsigned free_s = slab_list_walk(cache->slabs_free);
+ unsigned full_s = slab_list_walk(cache->slabs_full);
+ printf("%4zu: allocs=%lu frees=%lu "
+ "(%u empty+partial, %u full)\n",
+ cache->bufsize, cache->stat_allocs,
+ cache->stat_frees, free_s, full_s);
+ }
+#else
+ printf("Cache usage: not available, enable MEM_DEBUG and recompile.\n");
+#endif
+}
+
diff --git a/src/common/slab/slab.h b/src/common/slab/slab.h
new file mode 100644
index 0000000..d64188e
--- /dev/null
+++ b/src/common/slab/slab.h
@@ -0,0 +1,353 @@
+/* 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 slab.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief SLAB allocator.
+ *
+ * SLAB cache works with either custom SLAB sizes and
+ * Next-Highest-Power-Of-2 sizes.
+ *
+ * Slab size is a multiple of PAGE_SIZE and uses
+ * system allocator for larger blocks.
+ *
+ * Allocated SLABs are PAGE_SIZE aligned for a fast O(1)
+ * address-from-item lookup. This results in nearly none memory
+ * overhead for a very small blocks (<64B), but it requires the
+ * underlying allocator to be effective in allocating page size aligned memory
+ * as well. The major disadvantage is that each Slab must be aligned to it's
+ * size as opposed to boundary tags.
+ *
+ * Slab implements simple coloring mechanism to improve
+ * cache line utilisation.
+ *
+ * \ref SLAB_SIZE is a fixed size of a slab. As a rule of thumb, the slab is
+ * effective when the maximum allocated block size is below 1/4 of a SLAB_SIZE.
+ * f.e. 16kB SLAB is most effective up to 4kB block size.
+ *
+ * \ref MEM_POISON flag enables checking read/writes after the allocated memory
+ * and segmentation fault. This poses a significant time and space overhead.
+ * Enable only when debugging.
+ *
+ * \ref MEM_SLAB_CAP defines a maximum limit of a number of empty slabs that a cache
+ * can keep at a time. This results in a slight performance regression,
+ * but actively recycles unuse memory.
+ *
+ * \ref MEM_DEPOT_COUNT defines how many recycled slabs will be cached for a later
+ * use instead of returning them immediately to the OS. This significantly
+ * reduces a number of syscalls in some cases.
+ * f.e. 16 means 16 * SLAB_SIZE cache, for 16kB slabs = 256kB cache
+ *
+ * \ref MEM_COLORING enables simple cache coloring. This is generally a useful
+ * feature since all slabs are page size aligned and
+ * (depending on architecture) this slightly improves performance
+ * and cacheline usage at the cost of a minimum of 64 bytes per slab of
+ * overhead. Undefine MEM_COLORING in common.h to disable coloring.
+ *
+ * Optimal usage for a specific behavior (similar allocation sizes):
+ * \code
+ * slab_cache_t cache;
+ * slab_cache_init(&cache, N); // Initialize, N means cache chunk size
+ * ...
+ * void* mem = slab_cache_alloc(&cache); // Allocate N bytes
+ * ...
+ * slab_free(mem); // Recycle memory
+ * ...
+ * slab_cache_destroy(&cache); // Deinitialize cache
+ * \endcode
+ *
+ *
+ * \todo Allocate slab headers elsewhere and use just first sizeof(void*) bytes
+ * in each slab as a pointer to slab header. This could improve the
+ * performance.
+ *
+ * \note Slab allocation is not thread safe for performance reasons.
+ *
+ * \addtogroup alloc
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_SLAB_H_
+#define _KNOTD_COMMON_SLAB_H_
+
+#include <pthread.h>
+#include <stdint.h>
+
+/* Constants. */
+#define SLAB_SIZE (4096*4) //!< Slab size (16K blocks)
+#define SLAB_MIN_BUFLEN 8 //!< Minimal allocation block size is 8B.
+#define SLAB_EXP_OFFSET 3 //!< Minimal allocation size is 8B = 2^3, exp is 3.
+#define SLAB_GP_COUNT 10 //!< General-purpose caches count.
+#define SLAB_US_COUNT 10 //!< User-specified caches count.
+#define SLAB_DEPOT_SIZE 16 //!< N slabs cached = N*SLAB_SIZE kB cap
+#define SLAB_CACHE_COUNT (SLAB_GP_COUNT + SLAB_US_COUNT) //!< Slab cache count.
+struct slab_cache_t;
+
+/* Macros. */
+
+/*! \brief Return slab base address from pointer. */
+#define slab_from_ptr(p) ((void*)((size_t)(p) & SLAB_MASK))
+
+/*! \brief Return true if slab is empty. */
+#define slab_isempty(s) ((s)->bufs_free == (s)->bufs_count)
+
+/*!
+ * \brief Slab descriptor.
+ *
+ * Slab is a block of memory used for an allocation of
+ * smaller objects (bufs) later on.
+ * Each slab is currently aligned to page size to easily
+ * determine slab address from buf pointer.
+ *
+ * \warning Do not use slab_t directly as it cannot grow, see slab_cache_t.
+ */
+typedef struct slab_t {
+ char magic; /*!< Identifies memory block type. */
+ unsigned short bufsize; /*!< Slab bufsize. */
+ struct slab_cache_t *cache; /*!< Owner cache. */
+ struct slab_t *prev, *next; /*!< Neighbours in slab lists. */
+ unsigned bufs_count; /*!< Number of bufs in slab. */
+ unsigned bufs_free; /*!< Number of available bufs. */
+ void **head; /*!< Pointer to first available buf. */
+ char* base; /*!< Base address for bufs. */
+} slab_t;
+
+/*!
+ * \brief Slab depot.
+ *
+ * To mitigate slab initialization costs, depot keeps a finite number of
+ * stacked slabs before returning them to the system.
+ */
+typedef struct slab_depot_t {
+ size_t available; /*!< Number of available pages. */
+ slab_t* cache[SLAB_DEPOT_SIZE]; /*!< Stack of free slabs. */
+} slab_depot_t;
+
+/*!
+ * \brief Large object descriptor.
+ *
+ * Large object differs from slab with magic byte and
+ * contains object size.
+ *
+ * Magic needs to be first to overlap with slab_t magic byte.
+ */
+typedef struct slab_obj_t {
+ char magic; /*!< Identifies memory block type. */
+ size_t size; /*!< Object size. */
+} slab_obj_t;
+
+/*!
+ * \brief Slab cache descriptor.
+ *
+ * Slab cache is a list of 0..n slabs with the same buf size.
+ * It is responsible for slab state keeping.
+ *
+ * Once a slab is created, it is moved to free list.
+ * When it is full, it is moved to full list.
+ * Once a buf from full slab is freed, the slab is moved to
+ * free list again (there may be some hysteresis for mitigating
+ * a sequential alloc/free).
+ *
+ * Allocation of new slabs is on-demand, empty slabs are reused if possible.
+ *
+ * \note Slab implementation is different from Bonwick (Usenix 2001)
+ * http://www.usenix.org/event/usenix01/bonwick.html
+ * as it doesn't feature empty and partial list.
+ * This is due to fact, that user space allocator rarely
+ * needs to count free slabs. There is no way the OS could
+ * notify the application, that the memory is scarce.
+ * A slight performance increased is measured in benchmark.
+ *
+ * \note Statistics are only available if MEM_DEBUG is enabled.
+ */
+typedef struct slab_cache_t {
+ unsigned short color; /*!< Current cache color. */
+ unsigned short empty; /*!< Number of empty slabs. */
+ size_t bufsize; /*!< Cache object (buf) size. */
+ slab_t *slabs_free; /*!< List of free slabs. */
+ slab_t *slabs_full; /*!< List of full slabs. */
+
+ /* Statistics. */
+ unsigned long stat_allocs; /*!< Allocation count. */
+ unsigned long stat_frees; /*!< Free count. */
+} slab_cache_t;
+
+/*!
+ * \brief Slab allocator descriptor.
+ *
+ * \note For a number of slab caches, consult SLAB_GP_COUNT
+ * and a number of specific records in SLAB_CACHE_LUT lookup table.
+ *
+ * \warning It is currently not advised to use this general purpose allocator,
+ * as it usually doesn't yield an expected performance for higher
+ * bookkeeping costs and it also depends on the allocation behavior
+ * as well. Look for slab_cache for a specialized use in most cases.
+ */
+typedef struct slab_alloc_t {
+ slab_cache_t descriptors; /*!< Slab cache for cache descriptors. */
+ slab_cache_t* caches[SLAB_CACHE_COUNT]; /*!< Number of slab caches. */
+} slab_alloc_t;
+
+/*!
+ * \brief Create a slab of predefined size.
+ *
+ * At the moment, slabs are equal to page size and page size aligned.
+ * This enables quick and efficient buf to slab lookup by pointer arithmetic.
+ *
+ * Slab uses simple coloring scheme with and the memory block is always
+ * sizeof(void*) aligned.
+ *
+ * \param cache Parent cache.
+ * \retval Slab instance on success.
+ * \retval NULL on error.
+ */
+slab_t* slab_create(slab_cache_t* cache);
+
+/*!
+ * \brief Destroy slab instance.
+ *
+ * Slab is disconnected from any list and freed.
+ * Dereferenced slab parameter is set to NULL.
+ *
+ * \param slab Pointer to given slab.
+ */
+void slab_destroy(slab_t** slab);
+
+/*!
+ * \brief Allocate a buf from slab.
+ *
+ * Returns a pointer to allocated memory or NULL on error.
+ *
+ * \param slab Given slab instance.
+ * \retval Pointer to allocated memory.
+ * \retval NULL on error.
+ */
+void* slab_alloc(slab_t* slab);
+
+/*!
+ * \brief Recycle memory.
+ *
+ * Given memory is returned to owner slab.
+ * Memory content may be rewritten.
+ *
+ * \param ptr Returned memory.
+ */
+void slab_free(void* ptr);
+
+/*!
+ * \brief Create a slab cache.
+ *
+ * Create a slab cache with no allocated slabs.
+ * Slabs are allocated on-demand.
+ *
+ * \param cache Pointer to uninitialized cache.
+ * \param bufsize Single item size for later allocs.
+ * \retval 0 on success.
+ * \retval -1 on error;
+ */
+int slab_cache_init(slab_cache_t* cache, size_t bufsize);
+
+/*!
+ * \brief Destroy a slab cache.
+ *
+ * Destroy a slab cache and all associated slabs.
+ *
+ * \param cache Pointer to slab cache.
+ */
+void slab_cache_destroy(slab_cache_t* cache);
+
+/*!
+ * \brief Allocate from the cache.
+ *
+ * It tries to use partially free caches first,
+ * empty caches second and allocates a new cache
+ * as a last resort.
+ *
+ * \param cache Given slab cache.
+ * \retval Pointer to allocated memory.
+ * \retval NULL on error.
+ */
+void* slab_cache_alloc(slab_cache_t* cache);
+
+/*!
+ * \brief Free unused slabs from cache.
+ *
+ * \param cache Given slab cache.
+ * \return Number of freed slabs.
+ */
+int slab_cache_reap(slab_cache_t* cache);
+
+/*!
+ * \brief Create a general purpose slab allocator.
+ *
+ * \note Please consult struct slab_alloc_t for performance hints.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int slab_alloc_init(slab_alloc_t* alloc);
+
+/*!
+ * \brief Delete slab allocator.
+ *
+ * This destroys all associated caches and frees memory.
+ *
+ * \param alloc Given allocator instance.
+ */
+void slab_alloc_destroy(slab_alloc_t* alloc);
+
+/*!
+ * \brief Allocate a block of memory.
+ *
+ * Returns a block of allocated memory.
+ *
+ * \note At least SLAB_MIN_BUFSIZE bytes is allocated.
+ *
+ * \note Please consult struct slab_alloc_t for performance hints.
+ *
+ * \param alloc Allocator instance.
+ * \param size Requested block size.
+ * \retval Pointer to allocated memory.
+ * \retval NULL on error.
+ */
+void* slab_alloc_alloc(slab_alloc_t* alloc, size_t size);
+
+/*!
+ * \brief Reallocate data from one slab to another.
+ *
+ * \param alloc Allocator instance.
+ * \param ptr Pointer to allocated memory.
+ * \param size Requested memory block size.
+ * \retval Pointer to newly allocated memory.
+ * \retval NULL on error.
+ *
+ * \todo Realloc could be probably implement more effectively.
+ */
+void *slab_alloc_realloc(slab_alloc_t* alloc, void *ptr, size_t size);
+
+/*!
+ *
+ * \brief Dump allocator stats.
+ *
+ * \param alloc Allocator instance.
+ */
+void slab_alloc_stats(slab_alloc_t* alloc);
+
+#endif /* _KNOTD_COMMON_SLAB_H_ */
+
+/*! @} */
diff --git a/src/common/sockaddr.c b/src/common/sockaddr.c
new file mode 100644
index 0000000..cd3a4b9
--- /dev/null
+++ b/src/common/sockaddr.c
@@ -0,0 +1,174 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "common/sockaddr.h"
+
+int sockaddr_init(sockaddr_t *addr, int af)
+{
+ /* Reset pointer. */
+ memset(addr, 0, sizeof(sockaddr_t));
+ addr->family = -1;
+
+ /* Initialize address size. */
+ switch(af) {
+ case AF_INET:
+ addr->len = sizeof(struct sockaddr_in);
+ break;
+#ifndef DISABLE_IPV6
+ case AF_INET6:
+ addr->len = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ /* Update pointer. */
+ addr->family = af;
+ return sockaddr_update(addr);
+}
+
+int sockaddr_update(sockaddr_t *addr)
+{
+ /* Update internal pointer. */
+ switch(addr->len) {
+ case sizeof(struct sockaddr_in):
+ addr->ptr = (struct sockaddr*)&addr->addr4;
+ break;
+#ifndef DISABLE_IPV6
+ case sizeof(struct sockaddr_in6):
+ addr->ptr = (struct sockaddr*)&addr->addr6;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int sockaddr_set(sockaddr_t *dst, int family, const char* addr, int port)
+{
+ if (!dst || !addr || port < 0) {
+ return -1;
+ }
+
+ /* Initialize. */
+ dst->family = -1;
+ dst->ptr = 0;
+ dst->len = 0;
+ sockaddr_init(dst, family);
+
+ /* Initialize depending on address family. */
+ void *paddr = 0;
+ switch(family) {
+ case AF_INET:
+ dst->addr4.sin_family = family;
+ dst->addr4.sin_port = htons(port);
+ paddr = &dst->addr4.sin_addr;
+ dst->addr4.sin_addr.s_addr = INADDR_ANY;
+ break;
+#ifndef DISABLE_IPV6
+ case AF_INET6:
+ dst->addr6.sin6_family = family;
+ dst->addr6.sin6_port = htons(port);
+ paddr = &dst->addr6.sin6_addr;
+ memcpy(&dst->addr6.sin6_addr,
+ &in6addr_any, sizeof(in6addr_any));
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ /* Convert address. */
+ return inet_pton(family, addr, paddr);
+}
+
+int sockaddr_tostr(sockaddr_t *addr, char *dst, size_t size)
+{
+ if (!addr || !dst || size == 0) {
+ return -1;
+ }
+
+ /* Minimum length. */
+ size_t minlen = INET_ADDRSTRLEN;
+
+ /* Check unsupported IPv6. */
+#ifdef DISABLE_IPV6
+ if (addr->family == AF_INET6) {
+ return -1;
+ }
+#else
+ minlen = INET6_ADDRSTRLEN;
+#endif
+
+ /* Check minimum length. */
+ if (size < minlen) {
+ return -1;
+ }
+
+ /* Convert. */
+#ifdef DISABLE_IPV6
+ dst[0] = '\0';
+#else
+ /* Load IPv6 addr if default. */
+ if (addr->family == AF_INET6) {
+ inet_ntop(addr->family, &addr->addr6.sin6_addr,
+ dst, size);
+ }
+#endif
+ /* Load IPv4 if set. */
+ if (addr->family == AF_INET) {
+ inet_ntop(addr->family, &addr->addr4.sin_addr,
+ dst, size);
+ }
+
+ return 0;
+}
+
+int sockaddr_portnum(sockaddr_t *addr)
+{
+ if (!addr) {
+ return -1;
+ }
+
+ switch(addr->family) {
+
+ /* IPv4 */
+ case AF_INET:
+ return ntohs(addr->addr4.sin_port);
+ break;
+
+ /* IPv6 */
+#ifndef DISABLE_IPV6
+ case AF_INET6:
+ return ntohs(addr->addr6.sin6_port);
+ break;
+#endif
+
+ /* N/A */
+ default:
+ return -1;
+ break;
+ }
+}
diff --git a/src/common/sockaddr.h b/src/common/sockaddr.h
new file mode 100644
index 0000000..51ba779
--- /dev/null
+++ b/src/common/sockaddr.h
@@ -0,0 +1,120 @@
+/* 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 sockaddr.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Socket address abstraction layer.
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _KNOTD_SOCKADDR_H_
+#define _KNOTD_SOCKADDR_H_
+
+/* BSD IPv6 */
+#ifndef __POSIX_VISIBLE
+#define __POSIX_VISIBLE = 200112
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*! \brief Universal socket address. */
+typedef struct sockaddr_t {
+ int family; /*!< Address family. */
+ struct sockaddr* ptr; /*!< Pointer to used sockaddr. */
+ socklen_t len; /*!< Length of used sockaddr. */
+ union {
+ struct sockaddr_in addr4; /*!< IPv4 sockaddr. */
+#ifndef DISABLE_IPV6
+ struct sockaddr_in6 addr6; /*!< IPv6 sockaddr. */
+#endif
+ };
+} sockaddr_t;
+
+/*! \brief Maximum address length in string format. */
+#ifdef DISABLE_IPV6
+#define SOCKADDR_STRLEN INET_ADDRSTRLEN
+#else
+#define SOCKADDR_STRLEN INET6_ADDRSTRLEN
+#endif
+
+/*!
+ * \brief Initialize address structure.
+ *
+ * Members ptr and len will be initialized to correct address family.
+ *
+ * \param addr Socket address structure.
+ * \param af Requested address family.
+ *
+ * \retval 0 on success.
+ * \retval -1 on unsupported address family (probably INET6).
+ */
+int sockaddr_init(sockaddr_t *addr, int af);
+
+/*!
+ * \brief Update internal pointers according to length.
+ *
+ * \param addr Socket address structure.
+ *
+ * \retval 0 on success.
+ * \retval -1 on invalid size.
+ */
+int sockaddr_update(sockaddr_t *addr);
+
+/*!
+ * \brief Set address and port.
+ *
+ * \param dst Target address structure.
+ * \param family Address family.
+ * \param addr IP address in string format.
+ * \param port Port.
+ *
+ * \retval 0 if addr is not valid address in string format.
+ * \retval positive value in case of success.
+ * \retval -1 on error.
+ * \see inet_pton(3)
+ */
+int sockaddr_set(sockaddr_t *dst, int family, const char* addr, int port);
+
+/*!
+ * \brief Return string representation of socket address.
+ *
+ * \param addr Socket address structure.
+ * \param dst Destination for string representation.
+ * \param size Maximum number of written bytes.
+ *
+ * \retval 0 on success.
+ * \retval -1 on invalid parameters.
+ */
+int sockaddr_tostr(sockaddr_t *addr, char *dst, size_t size);
+
+/*!
+ * \brief Return port number from address.
+ *
+ * \param addr Socket address structure.
+ *
+ * \retval Port number on success.
+ * \retval -1 on errors.
+ */
+int sockaddr_portnum(sockaddr_t *addr);
+
+#endif /* _KNOTD_SOCKADDR_H_ */
+
+/*! @} */
diff --git a/src/common/tree.h b/src/common/tree.h
new file mode 100644
index 0000000..efea65b
--- /dev/null
+++ b/src/common/tree.h
@@ -0,0 +1,268 @@
+/* tree.h -- AVL trees (in the spirit of BSD's 'queue.h') -*- C -*- */
+
+/* Copyright (c) 2005 Ian Piumarta
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the 'Software'), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
+ */
+
+/* This file defines an AVL balanced binary tree [Georgii M. Adelson-Velskii and
+ * Evgenii M. Landis, 'An algorithm for the organization of information',
+ * Doklady Akademii Nauk SSSR, 146:263-266, 1962 (Russian). Also in Myron
+ * J. Ricci (trans.), Soviet Math, 3:1259-1263, 1962 (English)].
+ *
+ * An AVL tree is headed by pointers to the root node and to a function defining
+ * the ordering relation between nodes. Each node contains an arbitrary payload
+ * plus three fields per tree entry: the depth of the subtree for which it forms
+ * the root and two pointers to child nodes (singly-linked for minimum space, at
+ * the expense of direct access to the parent node given a pointer to one of the
+ * children). The tree is rebalanced after every insertion or removal. The
+ * tree may be traversed in two directions: forward (in-order left-to-right) and
+ * reverse (in-order, right-to-left).
+ *
+ * Because of the recursive nature of many of the operations on trees it is
+ * necessary to define a number of helper functions for each type of tree node.
+ * The macro TREE_DEFINE(node_tag, entry_name) defines these functions with
+ * unique names according to the node_tag. This macro should be invoked,
+ * thereby defining the necessary functions, once per node tag in the program.
+ *
+ * For details on the use of these macros, see the tree(3) manual page.
+ */
+
+/* Downloaded from http://piumarta.com/software/tree/ */
+
+#ifndef __tree_h
+#define __tree_h
+
+
+#define TREE_DELTA_MAX 1
+
+#define TREE_ENTRY(type) \
+ struct { \
+ struct type *avl_left; \
+ struct type *avl_right; \
+ int avl_height; \
+ }
+
+#define TREE_HEAD(name, type) \
+ struct name { \
+ struct type *th_root; \
+ int (*th_cmp)(struct type *lhs, struct type *rhs); \
+ }
+
+#define TREE_INITIALIZER(cmp) { 0, cmp }
+
+#define TREE_DELTA(self, field) \
+ (( (((self)->field.avl_left) ? (self)->field.avl_left->field.avl_height : 0)) \
+ - (((self)->field.avl_right) ? (self)->field.avl_right->field.avl_height : 0))
+
+/* Recursion prevents the following from being defined as macros. */
+
+#define TREE_DEFINE(node, field) \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *); \
+ \
+ struct node *TREE_ROTL_##node##_##field(struct node *self) \
+ { \
+ struct node *r= self->field.avl_right; \
+ self->field.avl_right= r->field.avl_left; \
+ r->field.avl_left= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(r); \
+ } \
+ \
+ struct node *TREE_ROTR_##node##_##field(struct node *self) \
+ { \
+ struct node *l= self->field.avl_left; \
+ self->field.avl_left= l->field.avl_right; \
+ l->field.avl_right= TREE_BALANCE_##node##_##field(self); \
+ return TREE_BALANCE_##node##_##field(l); \
+ } \
+ \
+ struct node *TREE_BALANCE_##node##_##field(struct node *self) \
+ { \
+ int delta= TREE_DELTA(self, field); \
+ \
+ if (delta < -TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_right, field) > 0) \
+ self->field.avl_right= TREE_ROTR_##node##_##field(self->field.avl_right); \
+ return TREE_ROTL_##node##_##field(self); \
+ } \
+ else if (delta > TREE_DELTA_MAX) \
+ { \
+ if (TREE_DELTA(self->field.avl_left, field) < 0) \
+ self->field.avl_left= TREE_ROTL_##node##_##field(self->field.avl_left); \
+ return TREE_ROTR_##node##_##field(self); \
+ } \
+ self->field.avl_height= 0; \
+ if (self->field.avl_left && (self->field.avl_left->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_left->field.avl_height; \
+ if (self->field.avl_right && (self->field.avl_right->field.avl_height > self->field.avl_height)) \
+ self->field.avl_height= self->field.avl_right->field.avl_height; \
+ self->field.avl_height += 1; \
+ return self; \
+ } \
+ \
+ struct node *TREE_INSERT_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return elm; \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_INSERT_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_FIND_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) \
+ return 0; \
+ if (compare(elm, self) == 0) \
+ return self; \
+ if (compare(elm, self) < 0) \
+ return TREE_FIND_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare); \
+ } \
+ \
+ int TREE_FIND_LESS_EQUAL_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs), struct node **found, struct node **prev) \
+ { \
+ if (!self) \
+ return 0; \
+ if (compare(elm, self) == 0) { \
+ *found = self; \
+ return 1; \
+ } \
+ if (compare(elm, self) < 0) { \
+ int ret = TREE_FIND_LESS_EQUAL_##node##_##field(self->field.avl_left, elm, compare, found, prev); \
+ if (ret == 0 && *prev == NULL) { \
+ *prev = self; \
+ ret = -1; \
+ } \
+ return ret; \
+ } else { \
+ *found = self; \
+ *prev = self; \
+ return TREE_FIND_LESS_EQUAL_##node##_##field(self->field.avl_right, elm, compare, found, prev); \
+ } \
+ } \
+ \
+ struct node *TREE_MOVE_RIGHT_##node##_##field(struct node *self, struct node *rhs) \
+ { \
+ if (!self) \
+ return rhs; \
+ self->field.avl_right= TREE_MOVE_RIGHT_##node##_##field(self->field.avl_right, rhs); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ struct node *TREE_REMOVE_##node##_##field \
+ (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs)) \
+ { \
+ if (!self) return 0; \
+ \
+ if (compare(elm, self) == 0) \
+ { \
+ struct node *tmp= TREE_MOVE_RIGHT_##node##_##field(self->field.avl_left, self->field.avl_right); \
+ self->field.avl_left= 0; \
+ self->field.avl_right= 0; \
+ return tmp; \
+ } \
+ if (compare(elm, self) < 0) \
+ self->field.avl_left= TREE_REMOVE_##node##_##field(self->field.avl_left, elm, compare); \
+ else \
+ self->field.avl_right= TREE_REMOVE_##node##_##field(self->field.avl_right, elm, compare); \
+ return TREE_BALANCE_##node##_##field(self); \
+ } \
+ \
+ void TREE_FORWARD_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self, data); \
+ TREE_FORWARD_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ } \
+ } \
+ \
+ void TREE_REVERSE_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ TREE_REVERSE_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ } \
+ } \
+ \
+ void TREE_POST_ORDER_APPLY_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_POST_ORDER_APPLY_ALL_##node##_##field(self->field.avl_left, function, data); \
+ TREE_POST_ORDER_APPLY_ALL_##node##_##field(self->field.avl_right, function, data); \
+ function(self, data); \
+ } \
+ } \
+ \
+ void TREE_REVERSE_APPLY_POST_ALL_##node##_##field \
+ (struct node *self, void (*function)(struct node *node, void *data), void *data) \
+ { \
+ if (self) \
+ { \
+ TREE_REVERSE_APPLY_POST_ALL_##node##_##field(self->field.avl_right, function, data); \
+ TREE_REVERSE_APPLY_POST_ALL_##node##_##field(self->field.avl_left, function, data); \
+ function(self, data); \
+ } \
+}
+
+#define TREE_INSERT(head, node, field, elm) \
+ ((head)->th_root= TREE_INSERT_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_FIND(head, node, field, elm) \
+ (TREE_FIND_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_FIND_LESS_EQUAL(head, node, field, elm, found, prev) \
+ (TREE_FIND_LESS_EQUAL_##node##_##field((head)->th_root, (elm), (head)->th_cmp, found, prev))
+
+#define TREE_REMOVE(head, node, field, elm) \
+ ((head)->th_root= TREE_REMOVE_##node##_##field((head)->th_root, (elm), (head)->th_cmp))
+
+#define TREE_DEPTH(head, field) \
+ ((head)->th_root->field.avl_height)
+
+#define TREE_FORWARD_APPLY(head, node, field, function, data) \
+ TREE_FORWARD_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_REVERSE_APPLY(head, node, field, function, data) \
+ TREE_REVERSE_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_POST_ORDER_APPLY(head, node, field, function, data) \
+ TREE_POST_ORDER_APPLY_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_REVERSE_APPLY_POST(head, node, field, function, data) \
+ TREE_REVERSE_APPLY_POST_ALL_##node##_##field((head)->th_root, function, data)
+
+#define TREE_INIT(head, cmp) do { \
+ (head)->th_root= 0; \
+ (head)->th_cmp= (cmp); \
+ } while (0)
+
+
+#endif /* __tree_h */
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..521adb8
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,307 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Enable brief debugging messages. */
+#undef DEBUG_ENABLE_BRIEF
+
+/* Enable details debugging messages. */
+#undef DEBUG_ENABLE_DETAILS
+
+/* Enable verbose debugging messages. */
+#undef DEBUG_ENABLE_VERBOSE
+
+/* recvmmsg enabled */
+#undef ENABLE_RECVMMSG
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#undef HAVE_ARPA_NAMESER_H
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `epoll_wait' function. */
+#undef HAVE_EPOLL_WAIT
+
+/* Define to 1 if you have the <ev.h> header file. */
+#undef HAVE_EV_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `kqueue' function. */
+#undef HAVE_KQUEUE
+
+/* ldns present */
+#undef HAVE_LDNS
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Support mmx instructions */
+#undef HAVE_MMX
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the `regcomp' function. */
+#undef HAVE_REGCOMP
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#undef HAVE_RESOLV_H
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if you have the `sqrt' function. */
+#undef HAVE_SQRT
+
+/* Support SSE (Streaming SIMD Extensions) instructions */
+#undef HAVE_SSE
+
+/* Support SSE2 (Streaming SIMD Extensions 2) instructions */
+#undef HAVE_SSE2
+
+/* Support SSE3 (Streaming SIMD Extensions 3) instructions */
+#undef HAVE_SSE3
+
+/* Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions */
+#undef HAVE_SSSE3
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <urcu.h> header file. */
+#undef HAVE_URCU_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+ `char[]'. */
+#undef YYTEXT_POINTER
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int64_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
diff --git a/src/knot/common.h b/src/knot/common.h
new file mode 100644
index 0000000..e9cfce1
--- /dev/null
+++ b/src/knot/common.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 common.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Common macros, includes and utilities.
+ *
+ * \addtogroup utils
+ * @{
+ */
+
+#ifndef _KNOTD_COMMON_H_
+#define _KNOTD_COMMON_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <config.h>
+
+/*
+ * Common types and constants.
+ */
+
+#ifndef UINT_DEFINED
+typedef unsigned int uint; /*!< \brief Unsigned. */
+#define UINT_DEFINED
+#endif
+
+#define PROJECT_EXEC SBINDIR "/" "knotd" /*!< \brief Project executable. */
+#define ZONEPARSER_EXEC LIBEXECDIR "/" "knot-zcompile" /*!< \brief Zoneparser executable. */
+#define PID_FILE "knot.pid" /*!< \brief Server PID file name. */
+
+/*
+ * Server.
+ */
+
+#define CPU_ESTIMATE_MAGIC 0 /*!< \brief Extra threads to the number of cores.*/
+#define DEFAULT_THR_COUNT 2 /*!< \brief Default thread count. */
+#define DEFAULT_PORT 53531 /*!< \brief Default interface port. */
+#define TCP_BACKLOG_SIZE 5 /*!< \brief TCP listen backlog size. */
+#define XFR_THREADS_COUNT 3 /*!< \brief Number of threads for XFR handler. */
+#define RECVMMSG_BATCHLEN 32 /*!< \brief Define for recvmmsg() batch size. */
+
+///*! \brief If defined, zone structures will use hash table for lookup. */
+//#define COMPRESSION_PEDANTIC
+
+///*!
+// * \brief If defined, tests will use ldns library to parse sample data.
+// *
+// * If not defined, some tests will be disabled.
+// */
+//#define TEST_WITH_LDNS
+
+///*! \brief If defined, the statistics module will be enabled. */
+//#define STAT_COMPILE
+
+
+#ifdef HAVE_LDNS
+#define TEST_WITH_LDNS
+#endif
+
+/*
+ * Common includes.
+ */
+
+#include "common/latency.h"
+#include "common/print.h"
+#include "knot/other/log.h"
+#include "knot/other/debug.h"
+
+/*! \brief Eliminate compiler warning with unused parameters. */
+#define UNUSED(param) (void)(param)
+
+/*! \brief Type-safe minimum macro. */
+#define MIN(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; })
+
+/*! \brief Type-safe maximum macro. */
+#define MAX(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
+
+/* Optimisation macros. */
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+/*! \todo Refactor theese. We should have an allocator function handling this.*/
+#ifndef ERR_ALLOC_FAILED
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d (%s)\n", \
+ __FILE__, __LINE__, PACKAGE_STRING)
+#endif
+
+#ifndef CHECK_ALLOC_LOG
+#define CHECK_ALLOC_LOG(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ ERR_ALLOC_FAILED; \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#ifndef CHECK_ALLOC
+#define CHECK_ALLOC(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#endif /* _KNOTD_COMMON_H_ */
+
+/*! @} */
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
new file mode 100644
index 0000000..97ac8f8
--- /dev/null
+++ b/src/knot/conf/cf-lex.l
@@ -0,0 +1,218 @@
+/*!
+ * \file cf-lex.l
+ *
+ * \author Ondrej Sury <ondrej.sury@nic.cz>
+ *
+ * \brief Server configuration structures and API.
+ *
+ * IP address conversions from BIRD, (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ */
+%{
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "common/sockaddr.h"
+#include "knot/conf/conf.h"
+#include "knot/other/log.h"
+#include "libknotd_la-cf-parse.h" /* Automake generated header. */
+
+/* Imported symbols. */
+#define lval (yylval->tok)
+extern void cf_error(void *scanner, const char *msg);
+extern int (*cf_read_hook)(char *buf, size_t nbytes);
+void switch_input(const char *str, void *scanner)
+{
+ yy_scan_string(str, scanner);
+}
+
+//#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max);
+#define YY_NO_UNPUT
+
+%}
+
+%option reentrant
+%option bison-bridge
+%option noyywrap
+%option noinput
+%option nounput
+%option noreject
+%option yylineno
+%option prefix = "cf_"
+%option outfile = "lex.yy.c"
+
+ALPHA [a-zA-Z_]
+DIGIT [0-9]
+HEXA [0-9a-fA-F]
+ALNUM [a-zA-Z_0-9]
+BLANK [ \t\n]
+
+%%
+\#.*\n /* Ignore comments */;
+{BLANK}+ /* Ignore whitespace */;
+: /* Optional : in assignments. */;
+[\!\$\%\^\&\*\(\)\/\+\-\@\{\}\;\,] { return yytext[0]; }
+system { lval.t = yytext; return SYSTEM; }
+identity { lval.t = yytext; return IDENTITY; }
+version { lval.t = yytext; return VERSION; }
+storage { lval.t = yytext; return STORAGE; }
+key { lval.t = yytext; return KEY; }
+keys { lval.t = yytext; return KEYS; }
+remotes { lval.t = yytext; return REMOTES; }
+
+zones { lval.t = yytext; return ZONES; }
+file { lval.t = yytext; return FILENAME; }
+semantic-checks { lval.t = yytext; return SEMANTIC_CHECKS; }
+notify-retries { lval.t = yytext; return NOTIFY_RETRIES; }
+notify-timeout { lval.t = yytext; return NOTIFY_TIMEOUT; }
+zonefile-sync { lval.t = yytext; return DBSYNC_TIMEOUT; }
+ixfr-fslimit { lval.t = yytext; return IXFR_FSLIMIT; }
+xfr-in { lval.t = yytext; return XFR_IN; }
+xfr-out { lval.t = yytext; return XFR_OUT; }
+notify-in { lval.t = yytext; return NOTIFY_IN; }
+notify-out { lval.t = yytext; return NOTIFY_OUT; }
+workers { lval.t = yytext; return WORKERS; }
+
+interfaces { lval.t = yytext; return INTERFACES; }
+address { lval.t = yytext; return ADDRESS; }
+port { lval.t = yytext; return PORT; }
+
+log { lval.t = yytext; return LOG; }
+
+any { lval.t = yytext; lval.i = LOG_ANY; return LOG_SRC; }
+server { lval.t = yytext; lval.i = LOG_SERVER; return LOG_SRC; }
+answering { lval.t = yytext; lval.i = LOG_ANSWER; return LOG_SRC; }
+zone { lval.t = yytext; lval.i = LOG_ZONE; return LOG_SRC; }
+stdout { lval.t = yytext; lval.i = LOGT_STDOUT; return LOG_DEST; }
+stderr { lval.t = yytext; lval.i = LOGT_STDERR; return LOG_DEST; }
+syslog { lval.t = yytext; lval.i = LOGT_SYSLOG; return LOG_DEST; }
+all { lval.t = yytext; lval.i = LOG_UPTO(LOG_DEBUG); return LOG_LEVEL; }
+debug { lval.t = yytext; lval.i = LOG_MASK(LOG_DEBUG); return LOG_LEVEL; }
+info { lval.t = yytext; lval.i = LOG_MASK(LOG_INFO); return LOG_LEVEL; }
+notice { lval.t = yytext; lval.i = LOG_MASK(LOG_NOTICE); return LOG_LEVEL; }
+warning { lval.t = yytext; lval.i = LOG_MASK(LOG_WARNING); return LOG_LEVEL; }
+error { lval.t = yytext; lval.i = LOG_MASK(LOG_ERR); return LOG_LEVEL; }
+
+on|off {
+ lval.t = yytext;
+ lval.i = 0;
+ if (strcmp(yytext, "on") == 0) {
+ lval.i = 1;
+ }
+ return BOOL;
+}
+
+{DIGIT}+[smhd] {
+ size_t mpos = strlen(yytext) - 1;
+ char multiplier = yytext[mpos];
+ yytext[mpos] = '\0';
+ lval.i = atoi(yytext);
+ if (lval.i < 1) {
+ cf_error(yyscanner, "interval must be a positive integer");
+ return END;
+ }
+
+ /* Handle multiplier. */
+ switch(multiplier) {
+ case 'm': lval.i *= 60; break; /* minutes */
+ case 'h': lval.i *= 60*60; break; /* hours */
+ case 'd': lval.i *= 24*60*60; break; /* days */
+ case 's': /* seconds */
+ default: break;
+ }
+
+ return INTERVAL;
+}
+
+{DIGIT}+[kMG] {
+ size_t mpos = strlen(yytext) - 1;
+ char multiplier = yytext[mpos];
+ yytext[mpos] = '\0';
+ lval.i = atol(yytext);
+ if (lval.i < 1) {
+ cf_error(yyscanner, "size must be a positive integer");
+ return END;
+ }
+
+ /* Handle multiplier. */
+ switch(multiplier) {
+ case 'k': lval.l = lval.i * 1024; break; /* kB */
+ case 'M': lval.l = lval.i * 1024*1024; break; /* MB */
+ case 'G': lval.l = lval.i * 1024*1024*1024; break; /* GB */
+ default: break;
+ }
+
+ return SIZE;
+}
+
+{DIGIT}+ {
+ lval.i = atol(yytext);
+ return NUM;
+}
+
+{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
+ unsigned char buf[sizeof(struct in_addr)];
+ if (inet_pton(AF_INET, yytext, buf)) {
+ lval.t = strdup(yytext);
+ return IPA;
+ }
+ cf_error(yyscanner, "Invalid IP address.");
+}
+
+\[({HEXA}*::|({HEXA}*:){3,})({HEXA}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+)\] {
+#ifdef DISABLE_IPV6
+ lval.t = strdup(yytext);
+ cf_error(yyscanner, "IPv6 address support not compiled.");
+ return TEXT;
+#else
+ unsigned char buf[sizeof(struct in6_addr)];
+ yytext[strlen(yytext)-1] = '\0';
+ if (inet_pton(AF_INET6, yytext+1, buf)) {
+ lval.t = strdup(yytext+1);
+ return IPA6;
+ }
+ cf_error(yyscanner, "Invalid IPv6 address.");
+#endif
+ }
+
+({HEXA}*::|({HEXA}*:){3,})({HEXA}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
+#ifdef DISABLE_IPV6
+ lval.t = strdup(yytext);
+ cf_error(yyscanner, "IPv6 address support not compiled.");
+ return TEXT;
+#else
+ unsigned char buf[sizeof(struct in6_addr)];
+ if (inet_pton(AF_INET6, yytext, buf)) {
+ lval.t = strdup(yytext);
+ return IPA6;
+ }
+ cf_error(yyscanner, "Invalid IPv6 address.");
+#endif
+}
+
+gss-tsig { lval.alg = KNOT_TSIG_ALG_GSS_TSIG; return TSIG_ALGO_NAME; }
+hmac-md5 { lval.alg = KNOT_TSIG_ALG_HMAC_MD5; return TSIG_ALGO_NAME; }
+hmac-sha1 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA1; return TSIG_ALGO_NAME; }
+hmac-sha224 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA224; return TSIG_ALGO_NAME; }
+hmac-sha256 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA256; return TSIG_ALGO_NAME; }
+hmac-sha384 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA384; return TSIG_ALGO_NAME; }
+hmac-sha512 { lval.alg = KNOT_TSIG_ALG_HMAC_SHA512; return TSIG_ALGO_NAME; }
+
+["][^"\n]*["] {
+ yytext[yyleng-1] = 0;
+ lval.t = strdup(yytext + 1);
+ return TEXT;
+}
+
+["][^"\n]*\n cf_error(yyscanner, "Unterminated string.");
+
+[a-zA-Z0-9\.\-\_]+ {
+ lval.t = strdup(yytext);
+ return TEXT /* Last resort, alphanumeric word. */;
+}
+
+<<EOF>> return END;
+
+%%
+
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
new file mode 100644
index 0000000..d793865
--- /dev/null
+++ b/src/knot/conf/cf-parse.y
@@ -0,0 +1,646 @@
+/*!
+ * \file cf-parse.y
+ *
+ * \author Ondrej Sury <ondrej.sury@nic.cz>
+ *
+ * \brief Server configuration structures and API.
+ */
+%{
+/* Headers */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libknot/dname.h"
+#include "knot/conf/conf.h"
+#include "libknotd_la-cf-parse.h" /* Automake generated header. */
+
+extern int cf_lex (YYSTYPE *lvalp, void *scanner);
+extern void cf_error(void *scanner, const char *msg);
+extern conf_t *new_config;
+static conf_iface_t *this_iface = 0;
+static conf_iface_t *this_remote = 0;
+static conf_zone_t *this_zone = 0;
+static list *this_list = 0;
+static conf_log_t *this_log = 0;
+static conf_log_map_t *this_logmap = 0;
+//#define YYERROR_VERBOSE 1
+
+static void conf_start_iface(char* ifname)
+{
+ this_iface = malloc(sizeof(conf_iface_t));
+ memset(this_iface, 0, sizeof(conf_iface_t));
+ this_iface->name = ifname;
+ this_iface->address = 0; // No default address (mandatory)
+ this_iface->port = CONFIG_DEFAULT_PORT;
+ add_tail(&new_config->ifaces, &this_iface->n);
+ ++new_config->ifaces_count;
+}
+
+static void conf_start_remote(char *remote)
+{
+ this_remote = malloc(sizeof(conf_iface_t));
+ memset(this_remote, 0, sizeof(conf_iface_t));
+ this_remote->name = remote;
+ this_remote->address = 0; // No default address (mandatory)
+ this_remote->port = 0; // Port wildcard
+ add_tail(&new_config->remotes, &this_remote->n);
+ ++new_config->remotes_count;
+}
+
+static void conf_acl_item(void *scanner, char *item)
+{
+ /* Find existing node in remotes. */
+ node* r = 0; conf_iface_t* found = 0;
+ WALK_LIST (r, new_config->remotes) {
+ if (strcmp(((conf_iface_t*)r)->name, item) == 0) {
+ found = (conf_iface_t*)r;
+ break;
+ }
+ }
+
+ /* Append to list if found. */
+ if (!found) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "remote '%s' is not defined", item);
+ cf_error(scanner, buf);
+ } else {
+ /* check port if xfrin/notify-out */
+ if (this_list == &this_zone->acl.xfr_in ||
+ this_list == &this_zone->acl.notify_out) {
+ if (found->port == 0) {
+ cf_error(scanner, "remote specified for XFR/IN or NOTIFY/OUT "
+ " needs to have valid port!");
+ free(item);
+ return;
+ }
+ }
+ conf_remote_t *remote = malloc(sizeof(conf_remote_t));
+ if (!remote) {
+ cf_error(scanner, "out of memory");
+ } else {
+ remote->remote = found;
+ add_tail(this_list, &remote->n);
+ }
+ }
+
+ /* Free text token. */
+ free(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);
+ char buf[512];
+ conf_key_t* r = 0;
+ WALK_LIST (r, new_config->keys) {
+ if (knot_dname_compare(r->k.name, sample) == 0) {
+ snprintf(buf, sizeof(buf), "key '%s' is already defined", item);
+ cf_error(scanner, buf);
+ knot_dname_free(&sample);
+ return 1;
+ }
+ }
+
+ knot_dname_free(&sample);
+ return 0;
+}
+
+static int conf_key_add(void *scanner, knot_key_t **key, char *item)
+{
+ /* Reset */
+ *key = 0;
+
+ /* Find in keys */
+ knot_dname_t *sample = knot_dname_new_from_str(item, strlen(item), 0);
+
+ conf_key_t* r = 0;
+ WALK_LIST (r, new_config->keys) {
+ if (knot_dname_compare(r->k.name, sample) == 0) {
+ *key = &r->k;
+ knot_dname_free(&sample);
+ return 0;
+ }
+ }
+
+ char buf[512];
+ snprintf(buf, sizeof(buf), "key '%s' is not defined", item);
+ cf_error(scanner, buf);
+ knot_dname_free(&sample);
+ return 1;
+}
+
+%}
+
+%pure-parser
+%parse-param{void *scanner}
+%lex-param{void *scanner}
+%name-prefix = "cf_"
+
+%union {
+ struct {
+ char *t;
+ long i;
+ size_t l;
+ tsig_algorithm_t alg;
+ } tok;
+}
+
+%token END INVALID_TOKEN
+%token <tok> TEXT
+%token <tok> NUM
+%token <tok> INTERVAL
+%token <tok> SIZE
+%token <tok> BOOL
+
+%token <tok> SYSTEM IDENTITY VERSION STORAGE KEY KEYS
+%token <tok> TSIG_ALGO_NAME
+%token <tok> WORKERS
+
+%token <tok> REMOTES
+
+%token <tok> ZONES FILENAME
+%token <tok> SEMANTIC_CHECKS
+%token <tok> NOTIFY_RETRIES
+%token <tok> NOTIFY_TIMEOUT
+%token <tok> DBSYNC_TIMEOUT
+%token <tok> IXFR_FSLIMIT
+%token <tok> XFR_IN
+%token <tok> XFR_OUT
+%token <tok> NOTIFY_IN
+%token <tok> NOTIFY_OUT
+
+%token <tok> INTERFACES ADDRESS PORT
+%token <tok> IPA
+%token <tok> IPA6
+
+%token <tok> LOG
+%token <tok> LOG_DEST
+%token <tok> LOG_SRC
+%token <tok> LOG_LEVEL
+
+%%
+
+config: conf_entries END { return 0; } ;
+
+conf_entries:
+ /* EMPTY */
+ | conf_entries conf
+ ;
+
+interface_start:
+ | TEXT { conf_start_iface($1.t); }
+ | REMOTES { conf_start_iface(strdup($1.t)); } /* Allow strings reserved by token. */
+ | LOG_SRC { conf_start_iface(strdup($1.t)); }
+ | LOG { conf_start_iface(strdup($1.t)); }
+ | LOG_LEVEL { conf_start_iface(strdup($1.t)); }
+ ;
+
+interface:
+ interface_start '{'
+ | interface PORT NUM ';' {
+ if (this_iface->port != CONFIG_DEFAULT_PORT) {
+ cf_error(scanner, "only one port definition is allowed in interface section\n");
+ } else {
+ this_iface->port = $3.i;
+ }
+ }
+ | interface ADDRESS IPA ';' {
+ if (this_iface->address != 0) {
+ cf_error(scanner, "only one address is allowed in interface section\n");
+ } else {
+ this_iface->address = $3.t;
+ this_iface->family = AF_INET;
+ }
+ }
+ | interface ADDRESS IPA '@' NUM ';' {
+ if (this_iface->address != 0) {
+ cf_error(scanner, "only one address is allowed in interface section\n");
+ } else {
+ this_iface->address = $3.t;
+ this_iface->family = AF_INET;
+ if (this_iface->port != CONFIG_DEFAULT_PORT) {
+ cf_error(scanner, "only one port definition is allowed in interface section\n");
+ } else {
+ this_iface->port = $5.i;
+ }
+ }
+ }
+ | interface ADDRESS IPA6 ';' {
+ if (this_iface->address != 0) {
+ cf_error(scanner, "only one address is allowed in interface section\n");
+ } else {
+ this_iface->address = $3.t;
+ this_iface->family = AF_INET6;
+ }
+ }
+ | interface ADDRESS IPA6 '@' NUM ';' {
+ if (this_iface->address != 0) {
+ cf_error(scanner, "only one address is allowed in interface section\n");
+ } else {
+ this_iface->address = $3.t;
+ this_iface->family = AF_INET6;
+ if (this_iface->port != CONFIG_DEFAULT_PORT) {
+ cf_error(scanner, "only one port definition is allowed in interface section\n");
+ } else {
+ this_iface->port = $5.i;
+ }
+ }
+ }
+ ;
+
+interfaces:
+ INTERFACES '{'
+ | interfaces interface '}' {
+ if (this_iface->address == 0) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "interface '%s' has no defined address", this_iface->name);
+ cf_error(scanner, buf);
+ }
+ }
+ ;
+
+system:
+ SYSTEM '{'
+ | system VERSION TEXT ';' { new_config->version = $3.t; }
+ | system IDENTITY TEXT ';' { new_config->identity = $3.t; }
+ | system STORAGE TEXT ';' { new_config->storage = $3.t; }
+ | system KEY TSIG_ALGO_NAME TEXT ';' {
+ fprintf(stderr, "warning: Config option 'system.key' is deprecated "
+ "and has no effect.\n");
+ free($4.t);
+ }
+ | system WORKERS NUM ';' {
+ if ($3.i <= 0) {
+ cf_error(scanner, "worker count must be greater than 0\n");
+ } else {
+ new_config->workers = $3.i;
+ }
+ }
+ ;
+
+keys:
+ KEYS '{'
+ | keys TEXT TSIG_ALGO_NAME TEXT ';' {
+ /* Normalize to FQDN */
+ char *fqdn = $2.t;
+ if (fqdn[strlen(fqdn) - 1] != '.') {
+ char* tmp = malloc(strlen(fqdn) + 1 + 1); /* '.', '\0' */
+ if (!tmp) {
+ cf_error(scanner, "out of memory when allocating string");
+ free(fqdn);
+ fqdn = 0;
+ } else {
+ strcpy(tmp, fqdn);
+ strcat(tmp, ".");
+ free(fqdn);
+ fqdn = tmp;
+ }
+ }
+
+ if (!conf_key_exists(scanner, fqdn)) {
+ knot_dname_t *dname = knot_dname_new_from_str(fqdn, strlen(fqdn), 0);
+ if (!dname) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "key name '%s' not in valid domain "
+ "name format", fqdn);
+ cf_error(scanner, buf);
+ free(fqdn);
+ free($4.t);
+ } else {
+ conf_key_t *k = malloc(sizeof(conf_key_t));
+ memset(k, 0, sizeof(conf_key_t));
+ k->k.name = dname;
+ k->k.algorithm = $3.alg;
+ k->k.secret = $4.t;
+ add_tail(&new_config->keys, &k->n);
+ ++new_config->key_count;
+ free(fqdn);
+ }
+ } else {
+ free(fqdn);
+ free($4.t);
+ }
+}
+
+remote_start:
+ | TEXT { conf_start_remote($1.t); }
+ | LOG_SRC { conf_start_remote(strdup($1.t)); }
+ | LOG { conf_start_remote(strdup($1.t)); }
+ | LOG_LEVEL { conf_start_remote(strdup($1.t)); }
+ ;
+
+remote:
+ remote_start '{'
+ | remote PORT NUM ';' {
+ if (this_remote->port != 0) {
+ cf_error(scanner, "only one port definition is allowed in remote section\n");
+ } else {
+ this_remote->port = $3.i;
+ }
+ }
+ | remote ADDRESS IPA ';' {
+ if (this_remote->address != 0) {
+ cf_error(scanner, "only one address is allowed in remote section\n");
+ } else {
+ this_remote->address = $3.t;
+ this_remote->family = AF_INET;
+ }
+ }
+ | remote ADDRESS IPA '@' NUM ';' {
+ if (this_remote->address != 0) {
+ cf_error(scanner, "only one address is allowed in remote section\n");
+ } else {
+ this_remote->address = $3.t;
+ this_remote->family = AF_INET;
+ if (this_remote->port != 0) {
+ cf_error(scanner, "only one port definition is allowed in remote section\n");
+ } else {
+ this_remote->port = $5.i;
+ }
+ }
+ }
+ | remote ADDRESS IPA6 ';' {
+ if (this_remote->address != 0) {
+ cf_error(scanner, "only one address is allowed in remote section\n");
+ } else {
+ this_remote->address = $3.t;
+ this_remote->family = AF_INET6;
+ }
+ }
+ | remote ADDRESS IPA6 '@' NUM ';' {
+ if (this_remote->address != 0) {
+ cf_error(scanner, "only one address is allowed in remote section\n");
+ } else {
+ this_remote->address = $3.t;
+ this_remote->family = AF_INET6;
+ if (this_remote->port != 0) {
+ cf_error(scanner, "only one port definition is allowed in remote section\n");
+ } else {
+ this_remote->port = $5.i;
+ }
+ }
+ }
+ | remote KEY TEXT ';' {
+ if (this_remote->key != 0) {
+ cf_error(scanner, "only one TSIG key definition is allowed in remote section\n");
+ } else {
+ conf_key_add(scanner, &this_remote->key, $3.t);
+ }
+ }
+ ;
+
+remotes:
+ REMOTES '{'
+ | remotes remote '}' {
+ if (this_remote->address == 0) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "remote '%s' has no defined address", this_remote->name);
+ cf_error(scanner, buf);
+ }
+ }
+ ;
+
+zone_acl_start:
+ XFR_IN {
+ this_list = &this_zone->acl.xfr_in;
+ }
+ | XFR_OUT {
+ this_list = &this_zone->acl.xfr_out;
+ }
+ | NOTIFY_IN {
+ this_list = &this_zone->acl.notify_in;
+ }
+ | NOTIFY_OUT {
+ this_list = &this_zone->acl.notify_out;
+ }
+ ;
+
+zone_acl_item:
+ | TEXT { conf_acl_item(scanner, $1.t); }
+ | LOG_SRC { conf_acl_item(scanner, strdup($1.t)); }
+ | LOG { conf_acl_item(scanner, strdup($1.t)); }
+ | LOG_LEVEL { conf_acl_item(scanner, strdup($1.t)); }
+ ;
+
+zone_acl_list:
+ zone_acl_start
+ | zone_acl_list zone_acl_item ','
+ | zone_acl_list zone_acl_item ';'
+ ;
+
+zone_acl:
+ zone_acl_start '{'
+ | zone_acl TEXT ';' {
+ /* Find existing node in remotes. */
+ node* 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;
+ break;
+ }
+ }
+
+ /* Append to list if found. */
+ if (!found) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "remote '%s' is not defined", $2.t);
+ cf_error(scanner, buf);
+ } else {
+ conf_remote_t *remote = malloc(sizeof(conf_remote_t));
+ if (!remote) {
+ cf_error(scanner, "out of memory");
+ } else {
+ remote->remote = found;
+ add_tail(this_list, &remote->n);
+ }
+ }
+
+ /* Free text token. */
+ free($2.t);
+ }
+ ;
+
+zone_start: TEXT {
+ this_zone = malloc(sizeof(conf_zone_t));
+ memset(this_zone, 0, sizeof(conf_zone_t));
+ this_zone->enable_checks = -1; // Default policy applies
+ this_zone->notify_timeout = -1; // Default policy applies
+ this_zone->notify_retries = 0; // Default policy applies
+ this_zone->ixfr_fslimit = -1; // Default policy applies
+ this_zone->dbsync_timeout = -1; // Default policy applies
+ this_zone->name = $1.t;
+
+ // Append mising dot to ensure FQDN
+ size_t nlen = strlen(this_zone->name);
+ if (this_zone->name[nlen - 1] != '.') {
+ this_zone->name = realloc(this_zone->name, nlen + 1 + 1);
+ strcat(this_zone->name, ".");
+ }
+
+ /* Check domain name. */
+ knot_dname_t *dn = knot_dname_new_from_str(this_zone->name,
+ nlen + 1,
+ 0);
+ if (dn == 0) {
+ free(this_zone->name);
+ free(this_zone);
+ cf_error(scanner, "invalid zone origin");
+ } else {
+ /* Directly discard dname, won't be needed. */
+ knot_dname_free(&dn);
+ add_tail(&new_config->zones, &this_zone->n);
+ ++new_config->zones_count;
+
+ /* Initialize ACL lists. */
+ init_list(&this_zone->acl.xfr_in);
+ init_list(&this_zone->acl.xfr_out);
+ init_list(&this_zone->acl.notify_in);
+ init_list(&this_zone->acl.notify_out);
+ }
+ }
+ ;
+
+zone:
+ zone_start '{'
+ | zone zone_acl '}'
+ | zone zone_acl_list
+ | zone FILENAME TEXT ';' { this_zone->file = $3.t; }
+ | zone SEMANTIC_CHECKS BOOL ';' { this_zone->enable_checks = $3.i; }
+ | zone DBSYNC_TIMEOUT NUM ';' { this_zone->dbsync_timeout = $3.i; }
+ | zone DBSYNC_TIMEOUT INTERVAL ';' { this_zone->dbsync_timeout = $3.i; }
+ | zone IXFR_FSLIMIT SIZE ';' { new_config->ixfr_fslimit = $3.l; }
+ | zone IXFR_FSLIMIT NUM ';' { this_zone->ixfr_fslimit = $3.i; }
+ | zone NOTIFY_RETRIES NUM ';' {
+ if ($3.i < 1) {
+ cf_error(scanner, "notify retries must be positive integer");
+ } else {
+ this_zone->notify_retries = $3.i;
+ }
+ }
+ | zone NOTIFY_TIMEOUT NUM ';' {
+ if ($3.i < 1) {
+ cf_error(scanner, "notify timeout must be positive integer");
+ } else {
+ this_zone->notify_timeout = $3.i;
+ }
+ }
+ ;
+
+zones:
+ ZONES '{'
+ | zones zone '}'
+ | zones SEMANTIC_CHECKS BOOL ';' { new_config->zone_checks = $3.i; }
+ | zones IXFR_FSLIMIT SIZE ';' { new_config->ixfr_fslimit = $3.l; }
+ | zones IXFR_FSLIMIT NUM ';' { new_config->ixfr_fslimit = $3.i; }
+ | zones NOTIFY_RETRIES NUM ';' {
+ if ($3.i < 1) {
+ cf_error(scanner, "notify retries must be positive integer");
+ } else {
+ new_config->notify_retries = $3.i;
+ }
+ }
+ | zones NOTIFY_TIMEOUT NUM ';' {
+ if ($3.i < 1) {
+ cf_error(scanner, "notify timeout must be positive integer");
+ } else {
+ new_config->notify_timeout = $3.i;
+ }
+ }
+ | zones DBSYNC_TIMEOUT NUM ';' {
+ if ($3.i < 1) {
+ cf_error(scanner, "zonefile sync timeout must be positive integer");
+ } else {
+ new_config->dbsync_timeout = $3.i;
+ }
+ }
+ | zones DBSYNC_TIMEOUT INTERVAL ';' { new_config->dbsync_timeout = $3.i; }
+ ;
+
+log_prios_start: {
+ this_logmap = malloc(sizeof(conf_log_map_t));
+ this_logmap->source = 0;
+ this_logmap->prios = 0;
+ add_tail(&this_log->map, &this_logmap->n);
+}
+;
+
+log_prios:
+ log_prios_start
+ | log_prios LOG_LEVEL ',' { this_logmap->prios |= $2.i; }
+ | log_prios LOG_LEVEL ';' { this_logmap->prios |= $2.i; }
+ ;
+
+log_src:
+ | log_src LOG_SRC log_prios {
+ this_logmap->source = $2.i;
+ this_logmap = 0;
+ }
+ ;
+
+log_dest: LOG_DEST {
+ /* Find already existing rule. */
+ this_log = 0;
+ node *n = 0;
+ WALK_LIST(n, new_config->logs) {
+ conf_log_t* log = (conf_log_t*)n;
+ if (log->type == $1.i) {
+ this_log = log;
+ break;
+ }
+ }
+
+ if (!this_log) {
+ this_log = malloc(sizeof(conf_log_t));
+ this_log->type = $1.i;
+ this_log->file = 0;
+ init_list(&this_log->map);
+ add_tail(&new_config->logs, &this_log->n);
+ ++new_config->logs_count;
+ }
+}
+;
+
+log_file: FILENAME TEXT {
+ /* Find already existing rule. */
+ this_log = 0;
+ node *n = 0;
+ WALK_LIST(n, new_config->logs) {
+ conf_log_t* log = (conf_log_t*)n;
+ if (log->type == LOGT_FILE) {
+ if (strcmp($2.t, log->file) == 0) {
+ this_log = log;
+ free($2.t);
+ break;
+ }
+ }
+ }
+
+ /* Create new rule. */
+ if (!this_log) {
+ this_log = malloc(sizeof(conf_log_t));
+ this_log->type = LOGT_FILE;
+ this_log->file = strcpath($2.t);
+ init_list(&this_log->map);
+ add_tail(&new_config->logs, &this_log->n);
+ ++new_config->logs_count;
+ }
+}
+;
+
+log_end: {
+}
+;
+
+log_start:
+ | log_start log_dest '{' log_src '}'
+ | log_start log_file '{' log_src '}'
+ ;
+
+log: LOG '{' log_start log_end;
+
+
+conf: ';' | system '}' | interfaces '}' | keys '}' | remotes '}' | zones '}' | log '}';
+
+%%
+
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
new file mode 100644
index 0000000..4e2d665
--- /dev/null
+++ b/src/knot/conf/conf.c
@@ -0,0 +1,715 @@
+/* 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 <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <urcu.h>
+#include "knot/conf/conf.h"
+#include "knot/common.h"
+#include "knot/other/error.h"
+
+/*
+ * Defaults.
+ */
+
+/*! \brief Default config paths. */
+static const char *DEFAULT_CONFIG[] = {
+ SYSCONFDIR "/" "knot.conf",
+};
+
+#define DEFAULT_CONF_COUNT 1 /*!< \brief Number of default config paths. */
+
+/*
+ * Utilities.
+ */
+
+/*!
+ * \brief Recursively create directories.
+ *
+ * Similar to "mkdir -p".
+ * * \retval 0 on success.
+ * \retval <0 on error.
+ */
+static int rmkdir(char *path, int mode)
+{
+ char *p = path;
+ while((p = strchr(p + 1, '/'))) {
+ *p = '\0';
+ mkdir(path, mode);
+ *p = '/';
+ }
+
+ // Final path
+ return mkdir(path, mode);
+}
+
+/* Prototypes for cf-parse.y */
+extern int cf_parse(void *scanner);
+extern int cf_get_lineno(void *scanner);
+extern char *cf_get_text(void *scanner);
+extern int cf_lex_init(void *scanner);
+extern void cf_set_in(FILE *f, void *scanner);
+extern void cf_lex_destroy(void *scanner);
+extern void switch_input(const char *str, void *scanner);
+
+conf_t *new_config = 0; /*!< \brief Currently parsed config. */
+static volatile int _parser_res = 0; /*!< \brief Parser result. */
+static pthread_mutex_t _parser_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*! \brief Config error report. */
+void cf_error(void *scanner, const char *msg)
+{
+ int lineno = -1;
+ char *text = "???";
+ if (scanner) {
+ lineno = cf_get_lineno(scanner);
+ text = (char *)cf_get_text(scanner);
+ }
+
+ log_server_error("Config '%s' - %s on line %d (current token '%s').\n",
+ new_config->filename, msg, lineno, text);
+
+
+ _parser_res = KNOTD_EPARSEFAIL;
+}
+
+/*
+ * Config helper functions.
+ */
+
+/*! \brief Free TSIG key. */
+static void key_free(conf_key_t *k)
+{
+ /* Secure erase. */
+ if (k->k.secret) {
+ memset(k->k.secret, 0, strlen(k->k.secret));
+ }
+ free(k->k.secret);
+ knot_dname_free(&k->k.name);
+ free(k);
+}
+
+/*! \brief Free config interfaces. */
+static void iface_free(conf_iface_t *iface)
+{
+ if (!iface) {
+ return;
+ }
+
+ free(iface->name);
+ free(iface->address);
+ free(iface);
+}
+
+/*! \brief Free config logs. */
+static void log_free(conf_log_t *log)
+{
+ if (!log) {
+ return;
+ }
+
+ if (log->file) {
+ free(log->file);
+ }
+
+ /* Free loglevel mapping. */
+ node *n = 0, *nxt = 0;
+ WALK_LIST_DELSAFE(n, nxt, log->map) {
+ free((conf_log_map_t*)n);
+ }
+
+ free(log);
+}
+
+/*! \brief Free config zones. */
+static void zone_free(conf_zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ /* Free ACL lists. */
+ WALK_LIST_FREE(zone->acl.xfr_in);
+ WALK_LIST_FREE(zone->acl.xfr_out);
+ WALK_LIST_FREE(zone->acl.notify_in);
+ WALK_LIST_FREE(zone->acl.notify_out);
+
+ free(zone->name);
+ free(zone->file);
+ free(zone->db);
+ free(zone->ixfr_db);
+ free(zone);
+}
+
+/*!
+ * \brief Call config hooks that need updating.
+ *
+ * This function is called automatically after config update.
+ *
+ * \todo Selective hooks.
+ */
+static void conf_update_hooks(conf_t *conf)
+{
+ node *n = 0;
+ conf->_touched = CONF_ALL;
+ WALK_LIST (n, conf->hooks) {
+ conf_hook_t *hook = (conf_hook_t*)n;
+ if ((hook->sections & conf->_touched) && hook->update) {
+ hook->update(conf, hook->data);
+ }
+ }
+}
+
+/*!
+ * \brief Process parsed configuration.
+ *
+ * This functions is called automatically after config parsing.
+ * It is needed to setup needed primitives, check and update paths.
+ *
+ * \retval 0 on success.
+ * \retval <0 on error.
+ */
+static int conf_process(conf_t *conf)
+{
+ // Check
+ if (!conf->storage) {
+ conf->storage = strdup("/var/lib/"PROJECT_EXEC);
+ }
+
+ // Normalize paths
+ conf->storage = strcpath(conf->storage);
+ struct stat st;
+ if (stat(conf->storage, &st) != 0) {
+ rmkdir(conf->storage, S_IRWXU);
+ }
+
+ // Create PID file
+ conf->pidfile = strcdup(conf->storage, "/" PID_FILE);
+
+ // Postprocess zones
+ node *n = 0;
+ WALK_LIST (n, conf->zones) {
+ conf_zone_t *zone = (conf_zone_t*)n;
+
+ // Default policy for dbsync timeout
+ if (zone->dbsync_timeout < 0) {
+ zone->dbsync_timeout = conf->dbsync_timeout;
+ }
+
+ // Default policy for semantic checks
+ if (zone->enable_checks < 0) {
+ zone->enable_checks = conf->zone_checks;
+ }
+
+ // Default policy for NOTIFY retries
+ if (zone->notify_retries <= 0) {
+ zone->notify_retries = conf->notify_retries;
+ }
+
+ // Default policy for NOTIFY timeout
+ if (zone->notify_timeout <= 0) {
+ zone->notify_timeout = conf->notify_timeout;
+ }
+
+ // Default policy for IXFR FSLIMIT
+ if (zone->ixfr_fslimit == 0) {
+ zone->ixfr_fslimit = conf->ixfr_fslimit;
+ }
+
+ // Normalize zone filename
+ zone->file = strcpath(zone->file);
+
+ // Create zone db filename
+ size_t stor_len = strlen(conf->storage);
+ size_t size = stor_len + strlen(zone->name) + 4; // db/,\0
+ char *dest = malloc(size);
+ strcpy(dest, conf->storage);
+ if (conf->storage[stor_len - 1] != '/') {
+ strcat(dest, "/");
+ }
+
+ strcat(dest, zone->name);
+ strcat(dest, "db");
+ zone->db = dest;
+
+ // Create IXFR db filename
+ stor_len = strlen(conf->storage);
+ size = stor_len + strlen(zone->name) + 9; // diff.db/,\0
+ dest = malloc(size);
+ strcpy(dest, conf->storage);
+ if (conf->storage[stor_len - 1] != '/') {
+ strcat(dest, "/");
+ }
+
+ strcat(dest, zone->name);
+ strcat(dest, "diff.db");
+ zone->ixfr_db = dest;
+ }
+
+ return 0;
+}
+
+/*
+ * Singletion configuration API.
+ */
+
+conf_t *s_config = 0; /*! \brief Singleton config instance. */
+
+/*! \brief Singleton config constructor (automatically called on load). */
+void __attribute__ ((constructor)) conf_init()
+{
+ // Create new config
+ s_config = conf_new(0);
+
+ /* Create default interface. */
+ conf_iface_t * iface = malloc(sizeof(conf_iface_t));
+ memset(iface, 0, sizeof(conf_iface_t));
+ iface->name = strdup("any");
+ iface->address = strdup("0.0.0.0");
+ iface->port = CONFIG_DEFAULT_PORT;
+ add_tail(&s_config->ifaces, &iface->n);
+ ++s_config->ifaces_count;
+
+ /* Create default storage. */
+ s_config->storage = strdup("/var/lib/"PROJECT_EXEC);
+
+ /* Create default logs. */
+
+ /* Syslog */
+ conf_log_t *log = malloc(sizeof(conf_log_t));
+ log->type = LOGT_SYSLOG;
+ log->file = 0;
+ init_list(&log->map);
+
+ conf_log_map_t *map = malloc(sizeof(conf_log_map_t));
+ map->source = LOG_ANY;
+ map->prios = LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR);
+ add_tail(&log->map, &map->n);
+ add_tail(&s_config->logs, &log->n);
+ ++s_config->logs_count;
+
+ /* Stderr */
+ log = malloc(sizeof(conf_log_t));
+ log->type = LOGT_STDERR;
+ log->file = 0;
+ init_list(&log->map);
+
+ map = malloc(sizeof(conf_log_map_t));
+ map->source = LOG_ANY;
+ map->prios = LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR);
+ add_tail(&log->map, &map->n);
+ add_tail(&s_config->logs, &log->n);
+ ++s_config->logs_count;
+
+ /* Process config. */
+ conf_process(s_config);
+}
+
+/*! \brief Singleton config destructor (automatically called on exit). */
+void __attribute__ ((destructor)) conf_deinit()
+{
+ if (s_config) {
+ conf_free(s_config);
+ s_config = 0;
+ }
+}
+
+/*!
+ * \brief Parse config (from file).
+ * \return yyparser return value.
+ */
+static int conf_fparser(conf_t *conf)
+{
+ if (!conf->filename) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = KNOTD_EOK;
+ pthread_mutex_lock(&_parser_lock);
+ // {
+ // Hook new configuration
+ new_config = conf;
+ FILE *f = fopen(conf->filename, "r");
+ if (f == 0) {
+ pthread_mutex_unlock(&_parser_lock);
+ return KNOTD_ENOENT;
+ }
+
+ // Parse config
+ _parser_res = KNOTD_EOK;
+ new_config->filename = conf->filename;
+ void *sc = NULL;
+ cf_lex_init(&sc);
+ cf_set_in(f, sc);
+ cf_parse(sc);
+ cf_lex_destroy(sc);
+ ret = _parser_res;
+ fclose(f);
+ // }
+ pthread_mutex_unlock(&_parser_lock);
+ return ret;
+}
+
+/*! \brief Parse config (from string).
+ * \return yyparser return value.
+ */
+static int conf_strparser(conf_t *conf, const char *src)
+{
+ if (!src) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = KNOTD_EOK;
+ pthread_mutex_lock(&_parser_lock);
+ // {
+ // Hook new configuration
+ new_config = conf;
+ if (src == 0) {
+ pthread_mutex_unlock(&_parser_lock);
+ return KNOTD_ENOENT;
+ }
+
+ // Parse config
+ _parser_res = KNOTD_EOK;
+ char *oldfn = new_config->filename;
+ new_config->filename = "(stdin)";
+ void *sc = NULL;
+ cf_lex_init(&sc);
+ switch_input(src, sc);
+ cf_parse(sc);
+ cf_lex_destroy(sc);
+ new_config->filename = oldfn;
+ ret = _parser_res;
+ // }
+ pthread_mutex_unlock(&_parser_lock);
+ return ret;
+}
+
+/*
+ * API functions.
+ */
+
+conf_t *conf_new(const char* path)
+{
+ conf_t *c = malloc(sizeof(conf_t));
+ memset(c, 0, sizeof(conf_t));
+
+ // Add path
+ if (path) {
+ c->filename = strdup(path);
+ }
+
+ // Initialize lists
+ init_list(&c->logs);
+ init_list(&c->ifaces);
+ init_list(&c->zones);
+ init_list(&c->hooks);
+ init_list(&c->remotes);
+ init_list(&c->keys);
+
+ // Defaults
+ c->zone_checks = 0;
+ c->notify_retries = CONFIG_NOTIFY_RETRIES;
+ c->notify_timeout = CONFIG_NOTIFY_TIMEOUT;
+ c->dbsync_timeout = CONFIG_DBSYNC_TIMEOUT;
+ c->ixfr_fslimit = -1;
+
+ return c;
+}
+
+int conf_add_hook(conf_t * conf, int sections,
+ int (*on_update)(const conf_t*, void*), void *data)
+{
+ conf_hook_t *hook = malloc(sizeof(conf_hook_t));
+ if (!hook) {
+ return KNOTD_ENOMEM;
+ }
+
+ hook->sections = sections;
+ hook->update = on_update;
+ hook->data = data;
+ add_tail(&conf->hooks, &hook->n);
+ ++conf->hooks_count;
+
+ return KNOTD_EOK;
+}
+
+int conf_parse(conf_t *conf)
+{
+ /* Parse file. */
+ int ret = conf_fparser(conf);
+
+ /* Postprocess config. */
+ conf_process(conf);
+
+ /* Update hooks. */
+ conf_update_hooks(conf);
+
+ if (ret < 0) {
+ return KNOTD_EPARSEFAIL;
+ }
+
+ return KNOTD_EOK;
+}
+
+int conf_parse_str(conf_t *conf, const char* src)
+{
+ /* Parse config from string. */
+ int ret = conf_strparser(conf, src);
+
+ /* Postprocess config. */
+ conf_process(conf);
+
+ /* Update hooks */
+ conf_update_hooks(conf);
+
+ if (ret < 0) {
+ return KNOTD_EPARSEFAIL;
+ }
+
+ return KNOTD_EOK;
+}
+
+void conf_truncate(conf_t *conf, int unload_hooks)
+{
+ if (!conf) {
+ return;
+ }
+
+ node *n = 0, *nxt = 0;
+
+ // Unload hooks
+ if (unload_hooks) {
+ WALK_LIST_DELSAFE(n, nxt, conf->hooks) {
+ //! \todo call hook unload.
+ free((conf_hook_t*)n);
+ }
+ conf->hooks_count = 0;
+ init_list(&conf->hooks);
+ }
+
+ // Free keys
+ WALK_LIST_DELSAFE(n, nxt, conf->keys) {
+ key_free((conf_key_t *)n);
+ }
+
+ // Free interfaces
+ WALK_LIST_DELSAFE(n, nxt, conf->ifaces) {
+ iface_free((conf_iface_t*)n);
+ }
+ conf->ifaces_count = 0;
+ init_list(&conf->ifaces);
+
+ // Free logs
+ WALK_LIST_DELSAFE(n, nxt, conf->logs) {
+ log_free((conf_log_t*)n);
+ }
+ conf->logs_count = 0;
+ init_list(&conf->logs);
+
+ // Free remotes
+ WALK_LIST_DELSAFE(n, nxt, conf->remotes) {
+ iface_free((conf_iface_t*)n);
+ }
+ conf->remotes_count = 0;
+ init_list(&conf->remotes);
+
+ // Free zones
+ WALK_LIST_DELSAFE(n, nxt, conf->zones) {
+ zone_free((conf_zone_t*)n);
+ }
+ conf->zones_count = 0;
+ init_list(&conf->zones);
+
+ if (conf->filename) {
+ free(conf->filename);
+ conf->filename = 0;
+ }
+ if (conf->identity) {
+ free(conf->identity);
+ conf->identity = 0;
+ }
+ if (conf->version) {
+ free(conf->version);
+ conf->version = 0;
+ }
+ if (conf->storage) {
+ free(conf->storage);
+ conf->storage = 0;
+ }
+ if (conf->pidfile) {
+ free(conf->pidfile);
+ conf->pidfile = 0;
+ }
+}
+
+void conf_free(conf_t *conf)
+{
+ if (!conf) {
+ return;
+ }
+
+ // Truncate config
+ conf_truncate(conf, 1);
+
+ // Free config
+ free(conf);
+}
+
+char* conf_find_default()
+{
+ /* Try sequentially each default path. */
+ char *path = 0;
+ for (int i = 0; i < DEFAULT_CONF_COUNT; ++i) {
+ path = strcpath(strdup(DEFAULT_CONFIG[i]));
+
+ /* Break, if the path exists. */
+ struct stat st;
+ if (stat(path, &st) == 0) {
+ break;
+ }
+
+ log_server_notice("Config '%s' does not exist.\n",
+ path);
+
+ /* Keep the last item. */
+ if (i < DEFAULT_CONF_COUNT - 1) {
+ free(path);
+ path = 0;
+ }
+ }
+
+ log_server_info("Using '%s' as default configuration.\n",
+ path);
+ return path;
+}
+
+int conf_open(const char* path)
+{
+ /* Check path. */
+ if (!path) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Check if exists. */
+ FILE *fp = fopen(path, "r");
+ if (fp == 0) {
+ return KNOTD_ENOENT;
+ } else {
+ fclose(fp);
+ }
+
+ /* Create new config. */
+ conf_t *nconf = conf_new(path);
+
+ /* Parse config. */
+ int ret = conf_fparser(nconf);
+ if (ret != KNOTD_EOK) {
+ conf_free(nconf);
+ return ret;
+ }
+
+ /* Replace current config. */
+ conf_t *oldconf = rcu_xchg_pointer(&s_config, nconf);
+
+ /* Copy hooks. */
+ if (oldconf) {
+ node *n = 0, *nxt = 0;
+ WALK_LIST_DELSAFE (n, nxt, oldconf->hooks) {
+ conf_hook_t *hook = (conf_hook_t*)n;
+ conf_add_hook(nconf, hook->sections,
+ hook->update, hook->data);
+ }
+ }
+
+ /* Postprocess config. */
+ conf_process(nconf);
+
+ /* Synchronize. */
+ synchronize_rcu();
+
+ /* Free old config. */
+ if (oldconf) {
+ conf_free(oldconf);
+ }
+
+ /* Update hooks. */
+ conf_update_hooks(nconf);
+
+ return KNOTD_EOK;
+}
+
+char* strcdup(const char *s1, const char *s2)
+{
+ if (!s1 || !s2) {
+ return 0;
+ }
+
+ size_t slen = strlen(s1);
+ size_t nlen = slen + strlen(s2) + 1;
+ char* dst = malloc(nlen);
+ if (!dst) {
+ return 0;
+ }
+
+ memcpy(dst, s1, slen);
+ strcpy(dst + slen, s2); // With trailing '\0'
+ return dst;
+}
+
+char* strcpath(char *path)
+{
+ // NULL path
+ if (!path) {
+ return 0;
+ }
+
+ // Remote trailing slash
+ size_t plen = strlen(path);
+ if (path[plen - 1] == '/') {
+ path[--plen] = '\0';
+ }
+
+ // Expand '~'
+ char* tild_p = strchr(path,'~');
+ if (tild_p != 0) {
+ // Get full path
+ char *tild_exp = getenv("HOME");
+ size_t tild_len = strlen(tild_exp);
+ if (tild_exp[tild_len - 1] == '/') {
+ tild_exp[--tild_len] = '\0';
+ }
+
+ // Expand
+ char *npath = malloc(plen + tild_len + 1);
+ npath[0] = '\0';
+ strncpy(npath, path, (size_t)(tild_p - path));
+ strcat(npath, tild_exp);
+ strcat(npath, tild_p + 1);
+ free(path);
+ path = npath;
+ }
+
+ return path;
+}
+
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
new file mode 100644
index 0000000..ef35e41
--- /dev/null
+++ b/src/knot/conf/conf.h
@@ -0,0 +1,361 @@
+/* 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 conf.h
+ *
+ * \author Ondrej Sury <ondrej.sury@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Server configuration structures and API.
+ *
+ * \addtogroup config
+ * @{
+ */
+
+#ifndef _KNOTD_CONF_H_
+#define _KNOTD_CONF_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <urcu.h>
+
+#include "libknot/dname.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/tsig.h"
+#include "common/lists.h"
+#include "knot/other/log.h"
+
+/* Constants. */
+#define CONFIG_DEFAULT_PORT 53
+#define CONFIG_NOTIFY_RETRIES 5 /*!< 5 retries (suggested in RFC1996) */
+#define CONFIG_NOTIFY_TIMEOUT 60 /*!< 60s (suggested in RFC1996) */
+#define CONFIG_DBSYNC_TIMEOUT (60*60) /*!< 1 hour. */
+
+/*!
+ * \brief Configuration for the interface
+ *
+ * This structure holds the configuration of the various interfaces
+ * used in the configuration. Same interface could be used for
+ * listening and outgoing function.
+ */
+typedef struct conf_iface_t {
+ node n;
+ char *name; /*!< Internal name for the interface. */
+ char *address; /*!< IP (IPv4/v6) address for this interface */
+ int port; /*!< Port number for this interface */
+ int family; /*!< Address family. */
+ knot_key_t *key; /*!< TSIG key (only valid for remotes). */
+} conf_iface_t;
+
+/*!
+ * \brief Node containing poiner to remote.
+ *
+ * Used for zone ACL lists to prevent node duplication.
+ */
+typedef struct conf_remote_t {
+ node n; /*!< List node. */
+ conf_iface_t *remote; /*!< Pointer to interface descriptor. */
+} conf_remote_t;
+
+/*!
+ * \brief Zone configuration.
+ *
+ * This structure holds the configuration for the zone. In it's most
+ * basic form, it just allows to read a zone from the specific
+ * location on the disk. It also allows to have multiple DNS servers
+ * as a source for the zone transfer and multiple DNS servers to allow
+ * zone transfers. Same logic applies for the NOTIFY.
+ *
+ * \todo Missing XFR type (AXFR/IXFR/IXFR-ONLY) for each server.
+ */
+typedef struct conf_zone_t {
+ node n;
+ char *name; /*!< Zone name. */
+ enum knot_rr_class cls; /*!< Zone class (IN or CH). */
+ char *file; /*!< Path to a zone file. */
+ char *db; /*!< Path to a database 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 notify_retries; /*!< NOTIFY query retries. */
+ int notify_timeout; /*!< Timeout for NOTIFY response (s). */
+ 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.*/
+ } acl;
+} conf_zone_t;
+
+/*!
+ * \brief Mapping of loglevels to message sources.
+ */
+typedef struct conf_log_map_t {
+ node n;
+ int source; /*!< Log message source mask. */
+ int prios; /*!< Log priorities mask. */
+} conf_log_map_t;
+
+/*!
+ * \brief Log facility descriptor.
+ */
+typedef struct conf_log_t {
+ node 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. */
+} conf_log_t;
+
+/*!
+ * \brief Configuration sections.
+ */
+typedef enum conf_section_t {
+ CONF_LOG = 1 << 0, /*!< Log section. */
+ CONF_IFACES = 1 << 1, /*!< Interfaces. */
+ CONF_ZONES = 1 << 2, /*!< Zones. */
+ CONF_OTHER = 1 << 3, /*!< Other sections. */
+ CONF_ALL = ~0 /*!< All sections. */
+} conf_section_t;
+
+/*!
+ * \brief TSIG key list item.
+ */
+typedef struct conf_key_t {
+ node n;
+ knot_key_t k;
+} conf_key_t;
+
+/*!
+ * \brief Main config structure.
+ *
+ * Configuration structure.
+ */
+typedef struct conf_t {
+ /*
+ * System
+ */
+ char *filename; /*!< Name of the config file. */
+ char *identity; /*!< Identity to return on CH TXT id.server. */
+ char *version; /*!< Version for CH TXT version.{bind|server} */
+ char *storage; /*!< Persistent storage path for databases and such. */
+ char *pidfile; /*!< PID file path. */
+ int workers; /*!< Number of workers per interface. */
+
+ /*
+ * Log
+ */
+ list logs; /*!< List of logging facilites. */
+ int logs_count; /*!< Count of logging facilities. */
+
+ /*
+ * Interfaces
+ */
+ list ifaces; /*!< List of interfaces. */
+ int ifaces_count; /*!< Count of interfaces. */
+
+ /*
+ * TSIG keys
+ */
+ list keys; /*!< List of TSIG keys. */
+ int key_count; /*!< Count of TSIG keys. */
+
+ /*
+ * Remotes
+ */
+ list remotes; /*!< List of remotes. */
+ int remotes_count;/*!< Count of remotes. */
+
+ /*
+ * Zones
+ */
+ list zones; /*!< List of zones. */
+ int zones_count; /*!< Count of zones. */
+ int zone_checks; /*!< Semantic checks for parser.*/
+ 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. */
+
+ /*
+ * Implementation specifics
+ */
+ list hooks; /*!< List of config hooks. */
+ int hooks_count; /*!< Count of config hooks. */
+ int _touched; /*!< Bitmask of sections touched by last update. */
+} conf_t;
+
+/*!
+ * \brief Config hook prototype.
+ */
+typedef struct conf_hook_t {
+ node n;
+ int sections; /*!< Bitmask of watched sections. */
+ int (*update)(const conf_t*, void*); /*!< Function executed on config load. */
+ void *data;
+} conf_hook_t;
+
+/*
+ * Specific configuration API.
+ */
+
+/*!
+ * \brief Create new configuration structure.
+ *
+ * \param path Path to configuration file.
+ * \retval new structure if successful.
+ * \retval NULL on error.
+ */
+conf_t *conf_new(const char* path);
+
+/*!
+ * \brief Register on-update callback.
+ *
+ * \param conf Configuration context.
+ * \param sections Bitmask of watched sections or CONF_ALL.
+ * \param on_update Callback.
+ * \param data User specified data for hook.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ENOMEM out of memory error.
+ */
+int conf_add_hook(conf_t * conf, int sections,
+ int (*on_update)(const conf_t*, void*), void *data);
+
+/*!
+ * \brief Parse configuration from associated file.
+ *
+ * \note Registered callbacks may be executed if applicable.
+ *
+ * \param conf Configuration context.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EPARSEFAIL on parser error.
+ */
+int conf_parse(conf_t *conf);
+
+/*!
+ * \brief Parse configuration from string.
+ *
+ * \note Registered callbacks may be executed if applicable.
+ *
+ * \param conf Configuration context.
+ * \param src Source string.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EPARSEFAIL on parser error.
+ */
+int conf_parse_str(conf_t *conf, const char* src);
+
+/*!
+ * \brief Truncate configuration context.
+ *
+ * \param conf Configuration context.
+ * \param unload_hooks If true, hooks will be unregistered and freed as well.
+ */
+void conf_truncate(conf_t *conf, int unload_hooks);
+
+/*!
+ * \brief Destroy configuration context.
+ *
+ * \param conf Configuration context.
+ */
+void conf_free(conf_t *conf);
+
+/*
+ * Singleton configuration API.
+ */
+
+/*!
+ * \brief Find implicit configuration file.
+ *
+ * Ordering:
+ * 1. ~/.knot/knot.conf (if exists)
+ * 2. /etc/knot/knot.conf (fallback)
+ *
+ * \return Path to implicit configuration file.
+ */
+char* conf_find_default();
+
+/*!
+ * \brief Open singleton configuration from file.
+ *
+ * \note Registered callbacks may be executed if applicable.
+ *
+ * \param path Path to configuration file.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on null path.
+ * \retval KNOTD_ENOENT if the path doesn't exist.
+ */
+int conf_open(const char* path);
+
+/* Imported singleton */
+extern conf_t *s_config;
+
+/*!
+ * \brief Singleton configuration context accessor.
+ *
+ * \return Configuration context.
+ */
+static inline conf_t* conf() {
+ return s_config; // Inline for performance reasons.
+}
+
+/*!
+ * \brief Lock configuration for reading.
+ *
+ * \return Configuration context.
+ */
+static inline void conf_read_lock() {
+ rcu_read_lock();
+}
+
+/*!
+ * \brief Unlock configuration for reading.
+ */
+static inline void conf_read_unlock() {
+ rcu_read_unlock();
+}
+
+/*
+ * Utilities.
+ */
+
+/*!
+ * \brief Create new string from a concatenation of s1 and s2.
+ *
+ * \param s1 First string.
+ * \param s2 Second string.
+ *
+ * \retval Newly allocated string on success.
+ * \retval NULL on error.
+ */
+char* strcdup(const char *s1, const char *s2);
+
+/*!
+ * \brief Normalize file path and expand '~' placeholders.
+ *
+ * \note Old pointer may be freed.
+ *
+ * \retval Pointer to normalized path.
+ */
+char* strcpath(char *path);
+
+#endif /* _KNOTD_CONF_H_ */
+
+/*! @} */
diff --git a/src/knot/conf/logconf.c b/src/knot/conf/logconf.c
new file mode 100644
index 0000000..a57afd9
--- /dev/null
+++ b/src/knot/conf/logconf.c
@@ -0,0 +1,102 @@
+/* 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 <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "knot/other/debug.h"
+#include "knot/conf/logconf.h"
+#include "knot/conf/conf.h"
+#include "knot/other/log.h"
+#include "knot/other/error.h"
+#include "common/lists.h"
+#include "knot/common.h"
+
+int log_conf_hook(const struct conf_t *conf, void *data)
+{
+ // Data not used
+ int ret = 0;
+ UNUSED(data);
+
+ // Check if log declaration exists, otherwise ignore
+ if (conf->logs_count < 1) {
+ return KNOTD_EINVAL;
+ }
+
+ // Find maximum log facility id
+ node *n = 0; size_t files = 0;
+ WALK_LIST(n, conf->logs) {
+ conf_log_t* log = (conf_log_t*)n;
+ if (log->type == LOGT_FILE) {
+ ++files;
+ }
+ }
+
+ // Initialize logsystem
+ log_truncate();
+ if ((ret = log_setup(files)) < 0) {
+ return ret;
+ }
+
+ // Setup logs
+ int loaded_sections = 0;
+ n = 0;
+ WALK_LIST(n, conf->logs) {
+
+ // Calculate offset
+ conf_log_t* log = (conf_log_t*)n;
+ int facility = log->type;
+ if (facility == LOGT_FILE) {
+ facility = log_open_file(log->file);
+ if (facility < 0) {
+ log_server_error("Failed to open "
+ "logfile '%s'.\n", log->file);
+ continue;
+ }
+ }
+
+ // Auto-assign fatal errors to syslog or stderr
+ if (facility <= LOGT_STDERR) {
+ int mask = LOG_MASK(LOG_FATAL);
+ log_levels_add(facility, LOG_ANY, mask);
+ loaded_sections |= 1 << facility;
+ }
+
+ // Setup sources mapping
+ node *m = 0;
+ WALK_LIST(m, log->map) {
+
+ // Assign mapped level
+ conf_log_map_t *map = (conf_log_map_t*)m;
+ log_levels_add(facility, map->source, map->prios);
+ }
+ }
+
+ // Load defaults for syslog or stderr
+ int bmask = LOG_MASK(LOG_ERR)|LOG_MASK(LOG_FATAL);
+ if (!(loaded_sections & (1 << LOGT_SYSLOG))) {
+ log_levels_set(LOGT_SYSLOG, LOG_ANY, bmask);
+ }
+ if (!(loaded_sections & (1 << LOGT_STDERR))) {
+ log_levels_set(LOGT_STDERR, LOG_ANY, bmask);
+ }
+
+ return KNOTD_EOK;
+}
+
diff --git a/src/knot/conf/logconf.h b/src/knot/conf/logconf.h
new file mode 100644
index 0000000..7b9e054
--- /dev/null
+++ b/src/knot/conf/logconf.h
@@ -0,0 +1,45 @@
+/* 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 log.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Logging facility (configuration file interface).
+ *
+ * \addtogroup logging
+ * @{
+ */
+
+#ifndef _KNOTD_LOGCONF_H_
+#define _KNOTD_LOGCONF_H_
+
+struct conf_t;
+
+/*!
+ * \brief Setup logging facilities from config.
+ *
+ * \see syslog.h
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOMEM out of memory error.
+ */
+int log_conf_hook(const struct conf_t *conf, void *data);
+
+#endif /* _KNOTD_LOGCONF_H_ */
+
+/*! @} */
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
new file mode 100644
index 0000000..16c3863
--- /dev/null
+++ b/src/knot/ctl/knotc_main.c
@@ -0,0 +1,658 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <getopt.h>
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/ctl/process.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/logconf.h"
+#include "knot/zone/zone-load.h"
+
+/*! \brief Controller constants. */
+enum knotc_constants_t {
+ WAITPID_TIMEOUT = 10 /*!< \brief Timeout for waiting for process. */
+};
+
+/*! \brief Print help. */
+void help(int argc, char **argv)
+{
+ printf("Usage: %sc [parameters] start|stop|restart|reload|running|"
+ "compile\n", PACKAGE_NAME);
+ printf("Parameters:\n"
+ " -c [file], --config=[file] Select configuration file.\n"
+ " -j [num], --jobs=[num] Number of parallel tasks to run (only for 'compile').\n"
+ " -f, --force Force operation - override some checks.\n"
+ " -v, --verbose Verbose mode - additional runtime information.\n"
+ " -V, --version Print %s server version.\n"
+ " -w, --wait Wait for the server to finish start/stop operations.\n"
+ " -i, --interactive Interactive mode (do not daemonize).\n"
+ " -h, --help Print help and usage.\n",
+ PACKAGE_NAME);
+ printf("Actions:\n"
+ " start Start %s server zone (no-op if running).\n"
+ " stop Stop %s server (no-op if not running).\n"
+ " restart Stops and then starts %s server.\n"
+ " reload Reload %s configuration and compiled zones.\n"
+ " running check if server is running.\n"
+ "\n"
+ " compile Compile zone file.\n",
+ PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME);
+}
+
+/*!
+ * \brief Check if the zone needs recompilation.
+ *
+ * \param db Path to zone db file.
+ * \param source Path to zone source file.
+ *
+ * \retval KNOTD_EOK if up to date.
+ * \retval KNOTD_ERROR if needs recompilation.
+ */
+int check_zone(const char *db, const char* source)
+{
+ /* Check zonefile. */
+ struct stat st;
+ if (stat(source, &st) != 0) {
+ fprintf(stderr, "Zone file '%s' doesn't exist.\n", source);
+ return KNOTD_ENOENT;
+ }
+
+ /* Read zonedb header. */
+ zloader_t *zl = 0;
+ knot_zload_open(&zl, db);
+ if (!zl) {
+ return KNOTD_ERROR;
+ }
+
+ /* Check source files and mtime. */
+ int ret = KNOTD_ERROR;
+ int src_changed = strcmp(source, zl->source) != 0;
+ if (!src_changed && !knot_zload_needs_update(zl)) {
+ ret = KNOTD_EOK;
+ }
+
+ knot_zload_close(zl);
+ return ret;
+}
+
+pid_t wait_cmd(pid_t proc, int *rc)
+{
+ /* Wait for finish. */
+ sigset_t newset;
+ sigfillset(&newset);
+ sigprocmask(SIG_BLOCK, &newset, 0);
+ proc = waitpid(proc, rc, 0);
+ sigprocmask(SIG_UNBLOCK, &newset, 0);
+ return proc;
+}
+
+pid_t start_cmd(const char *argv[], int argc)
+{
+ pid_t chproc = fork();
+ if (chproc == 0) {
+
+ /* Duplicate, it doesn't run from stack address anyway. */
+ char **args = malloc((argc + 1) * sizeof(char*));
+ memset(args, 0, (argc + 1) * sizeof(char*));
+ int ci = 0;
+ for (int i = 0; i < argc; ++i) {
+ if (strlen(argv[i]) > 0) {
+ args[ci++] = strdup(argv[i]);
+ }
+ }
+ args[ci] = 0;
+
+ /* Execute command. */
+ fflush(stdout);
+ fflush(stderr);
+ execvp(args[0], args);
+
+ /* Execute failed. */
+ fprintf(stderr, "Failed to run executable '%s'\n", args[0]);
+ for (int i = 0; i < argc; ++i) {
+ free(args[i]);
+ }
+ free(args);
+
+ exit(1);
+ return -1;
+ }
+
+ return chproc;
+}
+
+int exec_cmd(const char *argv[], int argc)
+{
+ int ret = 0;
+ pid_t proc = start_cmd(argv, argc);
+ wait_cmd(proc, &ret);
+ return ret;
+}
+
+/*! \brief Zone compiler task. */
+typedef struct {
+ conf_zone_t *zone;
+ pid_t proc;
+} knotc_zctask_t;
+
+/*! \brief Create set of watched tasks. */
+knotc_zctask_t *zctask_create(int count)
+{
+ if (count <= 0) {
+ return 0;
+ }
+
+ knotc_zctask_t *t = malloc(count * sizeof(knotc_zctask_t));
+ for (unsigned i = 0; i < count; ++i) {
+ t[i].proc = -1;
+ t[i].zone = 0;
+ }
+
+ return t;
+}
+
+/*! \brief Wait for single task to finish. */
+int zctask_wait(knotc_zctask_t *tasks, int count)
+{
+ /* Wait for children to finish. */
+ int rc = 0;
+ pid_t pid = wait_cmd(-1, &rc);
+
+ /* Find task. */
+ conf_zone_t *z = 0;
+ for (unsigned i = 0; i < count; ++i) {
+ if (tasks[i].proc == pid) {
+ tasks[i].proc = -1; /* Invalidate. */
+ z = tasks[i].zone;
+ break;
+ }
+ }
+
+ if (z == 0) {
+ fprintf(stderr, "error: Failed to find zone for finished "
+ "zone compilation process.\n");
+ return 1;
+ }
+
+ /* Evaluate. */
+ if (!WIFEXITED(rc)) {
+ fprintf(stderr, "error: Compilation of '%s' "
+ "failed, process was killed.\n",
+ z->name);
+ return 1;
+ } else {
+ if (rc < 0 || WEXITSTATUS(rc) != 0) {
+ fprintf(stderr, "error: Compilation of "
+ "'%s' failed, knot-zcompile "
+ "return code was '%d'\n",
+ z->name, WEXITSTATUS(rc));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Register running zone compilation process. */
+int zctask_add(knotc_zctask_t *tasks, int count, pid_t pid, conf_zone_t *zone)
+{
+ /* Find free space. */
+ for (unsigned i = 0; i < count; ++i) {
+ if (tasks[i].proc == -1) {
+ tasks[i].proc = pid;
+ tasks[i].zone = zone;
+ return 0;
+ }
+ }
+
+ /* Free space not found. */
+ return -1;
+}
+
+/*!
+ * \brief Execute specified action.
+ *
+ * \param action Action to be executed (start, stop, restart...)
+ * \param argv Additional arguments vector.
+ * \param argc Addition arguments count.
+ * \param pid Specified PID for action.
+ * \param verbose True if running in verbose mode.
+ * \param force True if forced operation is required.
+ * \param wait Wait for the operation to finish.
+ * \param interactive Interactive mode.
+ * \param jobs Number of parallel tasks to run.
+ * \param pidfile Specified PID file for action.
+ *
+ * \retval 0 on success.
+ * \retval error return code for main on error.
+ *
+ * \todo Make enumerated flags instead of many parameters...
+ */
+int execute(const char *action, char **argv, int argc, pid_t pid, int verbose,
+ int force, int wait, int interactive, int jobs, const char *pidfile)
+{
+ int valid_cmd = 0;
+ int rc = 0;
+ if (strcmp(action, "start") == 0) {
+
+ // Check PID
+ valid_cmd = 1;
+// if (pid < 0 && pid == KNOT_ERANGE) {
+// fprintf(stderr, "control: Another server instance "
+// "is already starting.\n");
+// return 1;
+// }
+ if (pid > 0 && pid_running(pid)) {
+
+ fprintf(stderr, "control: Server PID found, "
+ "already running.\n");
+
+ if (!force) {
+ return 1;
+ } else {
+ fprintf(stderr, "control: forcing "
+ "server start, killing old pid=%ld.\n",
+ (long)pid);
+ kill(pid, SIGKILL);
+ pid_remove(pidfile);
+ }
+ }
+
+ // Lock configuration
+ conf_read_lock();
+
+ // Prepare command
+ const char *cfg = conf()->filename;
+ const char *args[] = {
+ PROJECT_EXEC,
+ interactive ? "" : "-d",
+ cfg ? "-c" : "",
+ cfg ? cfg : "",
+ verbose ? "-v" : "",
+ argc > 0 ? argv[0] : ""
+ };
+
+ // Unlock configuration
+ conf_read_unlock();
+
+ // Execute command
+ if (interactive) {
+ printf("control: Running in interactive mode.\n");
+ fflush(stderr);
+ fflush(stdout);
+ }
+ if ((rc = exec_cmd(args, 6)) < 0) {
+ pid_remove(pidfile);
+ rc = 1;
+ }
+ fflush(stderr);
+ fflush(stdout);
+
+ // Wait for finish
+ if (wait && !interactive) {
+ if (verbose) {
+ fprintf(stdout, "control: waiting for server "
+ "to load.\n");
+ }
+ /* Periodically read pidfile and wait for
+ * valid result. */
+ pid = 0;
+ while(pid == 0 || !pid_running(pid)) {
+ pid = pid_read(pidfile);
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 500 * 1000;
+ select(0, 0, 0, 0, &tv);
+ }
+ }
+ }
+ if (strcmp(action, "stop") == 0) {
+
+ // Check PID
+ valid_cmd = 1;
+ rc = 0;
+ if (pid <= 0 || !pid_running(pid)) {
+ fprintf(stderr, "Server PID not found, "
+ "probably not running.\n");
+
+ if (!force) {
+ rc = 1;
+ } else {
+ fprintf(stderr, "control: forcing "
+ "server stop.\n");
+ }
+ }
+
+ // Stop
+ if (rc == 0) {
+ if (kill(pid, SIGTERM) < 0) {
+ pid_remove(pidfile);
+ rc = 1;
+ }
+ }
+
+ // Wait for finish
+ if (rc == 0 && wait) {
+ if (verbose) {
+ fprintf(stdout, "control: waiting for server "
+ "to stop.\n");
+ }
+ /* Periodically read pidfile and wait for
+ * valid result. */
+ while(pid_running(pid)) {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 500 * 1000;
+ select(0, 0, 0, 0, &tv);
+ }
+ }
+ }
+ if (strcmp(action, "restart") == 0) {
+ valid_cmd = 1;
+ execute("stop", argv, argc, pid, verbose, force, wait,
+ interactive, jobs, pidfile);
+
+ int i = 0;
+ while((pid = pid_read(pidfile)) > 0) {
+
+ if (!pid_running(pid)) {
+ pid_remove(pidfile);
+ break;
+ }
+ if (i == WAITPID_TIMEOUT) {
+ fprintf(stderr, "Timeout while "
+ "waiting for the server to finish.\n");
+ //pid_remove(pidfile);
+ break;
+ } else {
+ sleep(1);
+ ++i;
+ }
+ }
+
+ printf("Restarting server.\n");
+ rc = execute("start", argv, argc, -1, verbose, force, wait,
+ interactive, jobs, pidfile);
+ }
+ if (strcmp(action, "reload") == 0) {
+
+ // Check PID
+ valid_cmd = 1;
+ if (pid <= 0 || !pid_running(pid)) {
+ fprintf(stderr, "Server PID not found, "
+ "probably not running.\n");
+
+ if (force) {
+ fprintf(stderr, "control: forcing "
+ "server stop.\n");
+ } else {
+ return 1;
+ }
+ }
+
+ // Stop
+ if (kill(pid, SIGHUP) < 0) {
+ pid_remove(pidfile);
+ rc = 1;
+ }
+ }
+ if (strcmp(action, "running") == 0) {
+
+ // Check PID
+ valid_cmd = 1;
+ if (pid <= 0) {
+ printf("Server PID not found, "
+ "probably not running.\n");
+ rc = 1;
+ } else {
+ if (!pid_running(pid)) {
+ printf("Server PID not found, "
+ "probably not running.\n");
+ fprintf(stderr,
+ "warning: PID file is stale.\n");
+ } else {
+ printf("Server running as PID %ld.\n",
+ (long)pid);
+ }
+ rc = 0;
+ }
+ }
+ if (strcmp(action, "compile") == 0) {
+
+ // Print job count
+ if (jobs > 1) {
+ printf("warning: Will attempt to compile %d zones "
+ "in parallel, this increases memory consumption "
+ "for large zones.\n", jobs);
+ }
+
+ // Check zone
+ valid_cmd = 1;
+
+ // Lock configuration
+ conf_read_lock();
+
+ // Generate databases for all zones
+ node *n = 0;
+ int running = 0;
+ knotc_zctask_t *tasks = zctask_create(jobs);
+ WALK_LIST(n, conf()->zones) {
+
+ // Fetch zone
+ conf_zone_t *zone = (conf_zone_t*)n;
+
+ // Check source files and mtime
+ int zone_status = check_zone(zone->db, zone->file);
+ if (zone_status == KNOTD_EOK) {
+ printf("Zone '%s' is up-to-date.\n",
+ zone->name);
+
+ if (force) {
+ fprintf(stderr, "control: forcing "
+ "zone recompilation.\n");
+ } else {
+ continue;
+ }
+ }
+
+ // Check for not existing source
+ if (zone_status == KNOTD_ENOENT) {
+ continue;
+ }
+
+ /* Evaluate space for new task. */
+ if (running == jobs) {
+ zctask_wait(tasks, jobs);
+ --running;
+ }
+
+ const char *args[] = {
+ ZONEPARSER_EXEC,
+ zone->enable_checks ? "-s" : "",
+ verbose ? "-v" : "",
+ "-o",
+ zone->db,
+ zone->name,
+ zone->file
+ };
+
+ // Execute command
+ if (verbose) {
+ printf("Compiling '%s' as '%s'...\n",
+ zone->name, zone->db);
+ }
+ fflush(stdout);
+ fflush(stderr);
+ pid_t zcpid = start_cmd(args, 7);
+ zctask_add(tasks, jobs, zcpid, zone);
+ ++running;
+ }
+
+ /* Wait for all running tasks. */
+ while (running > 0) {
+ zctask_wait(tasks, jobs);
+ --running;
+ }
+ free(tasks);
+
+ // Unlock configuration
+ conf_read_unlock();
+ }
+ if (!valid_cmd) {
+ fprintf(stderr, "Invalid command: '%s'\n", action);
+ return 1;
+ }
+
+ // Log
+ if (verbose) {
+ printf("'%s' finished (return code %d)\n", action, rc);
+ }
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ // Parse command line arguments
+ int c = 0, li = 0;
+ int force = 0;
+ int verbose = 0;
+ int wait = 0;
+ int interactive = 0;
+ int jobs = 1;
+ const char* config_fn = 0;
+
+ /* Long options. */
+ struct option opts[] = {
+ {"wait", no_argument, 0, 'w'},
+ {"force", no_argument, 0, 'f'},
+ {"config", required_argument, 0, 'c'},
+ {"verbose", no_argument, 0, 'v'},
+ {"interactive", no_argument, 0, 'i'},
+ {"jobs", required_argument, 0, 'c'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "wfc:vij:Vh", opts, &li)) != -1) {
+ switch (c)
+ {
+ case 'w':
+ wait = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'c':
+ config_fn = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'j':
+ jobs = atoi(optarg);
+ if (jobs < 1) {
+ fprintf(stderr, "Invalid parameter '%s' to "
+ "'-j', expects number <1..n>\n",
+ optarg);
+ help(argc, argv);
+ return 1;
+ }
+ break;
+ case 'V':
+ printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
+ return 0;
+ case 'h':
+ case '?':
+ default:
+ help(argc, argv);
+ return 1;
+ }
+ }
+
+ // Check if there's at least one remaining non-option
+ if (argc - optind < 1) {
+ help(argc, argv);
+ return 1;
+ }
+
+ // Initialize log (no output)
+ log_init();
+ log_levels_set(LOGT_SYSLOG, LOG_ANY, 0);
+ log_levels_set(LOGT_STDOUT, LOG_ANY, 0);
+ closelog();
+
+ // Find implicit configuration file
+ char *default_fn = 0;
+ if (!config_fn) {
+ default_fn = conf_find_default();
+ config_fn = default_fn;
+ }
+
+ // Open configuration
+ if (conf_open(config_fn) != 0) {
+ fprintf(stderr, "Failed to parse configuration '%s'.\n",
+ config_fn);
+ free(default_fn);
+ return 1;
+ }
+
+ // Free default config filename if exists
+ free(default_fn);
+
+ // Verbose mode
+ if (verbose) {
+ int mask = LOG_MASK(LOG_INFO)|LOG_MASK(LOG_DEBUG);
+ log_levels_add(LOGT_STDOUT, LOG_ANY, mask);
+ }
+
+ // Fetch PID
+ char* pidfile = pid_filename();
+ if (!pidfile) {
+ fprintf(stderr, "No configuration found, "
+ "please specify with '-c' parameter.\n");
+ log_close();
+ return 1;
+ }
+
+ pid_t pid = pid_read(pidfile);
+
+ // Actions
+ const char* action = argv[optind];
+
+ // Execute action
+ int rc = execute(action, argv + optind + 1, argc - optind - 1,
+ pid, verbose, force, wait, interactive, jobs, pidfile);
+
+ // Finish
+ free(pidfile);
+ log_close();
+ return rc;
+}
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
new file mode 100644
index 0000000..e46fa37
--- /dev/null
+++ b/src/knot/ctl/process.c
@@ -0,0 +1,126 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include "knot/common.h"
+#include "knot/ctl/process.h"
+#include "knot/conf/conf.h"
+#include "knot/other/error.h"
+
+char* pid_filename()
+{
+ conf_read_lock();
+
+ /* Read configuration. */
+ char* ret = 0;
+ if (conf()) {
+ ret = strdup(conf()->pidfile);
+ }
+
+ conf_read_unlock();
+
+ return ret;
+}
+
+pid_t pid_read(const char* fn)
+{
+ char buf[64];
+
+ if (fn) {
+ FILE *fp = fopen(fn, "r");
+ if (!fp) {
+ return KNOTD_ENOENT;
+ }
+
+ int readb = 0;
+ int rc = fread(buf, 1, 1, fp);
+ while (rc > 0) {
+ if (++readb == sizeof(buf) - 1) {
+ break;
+ }
+ rc = fread(buf + readb, 1, 1, fp);
+ }
+ buf[readb] = '\0';
+ fclose(fp);
+
+ // Check read result
+ if (readb < 1) {
+ return KNOTD_ENOENT;
+ }
+
+ // Convert pid
+ char* ep = 0;
+ unsigned long pid = strtoul(buf, &ep, 10);
+ if ((errno == ERANGE) || (*ep && !isspace(*ep))) {
+ return KNOTD_ERANGE;
+ }
+
+ return (pid_t)pid;
+ }
+
+ return KNOTD_EINVAL;
+}
+
+int pid_write(const char* fn)
+{
+ if (!fn) {
+ return KNOTD_EINVAL;
+ }
+
+ // Convert
+ char buf[64];
+ int wbytes = 0;
+ wbytes = snprintf(buf, sizeof(buf), "%lu", (unsigned long) getpid());
+ if (wbytes < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Write
+ FILE *fp = fopen(fn, "w");
+ if (fp) {
+ int rc = fwrite(buf, wbytes, 1, fp);
+ fclose(fp);
+ if (rc < 0) {
+ return KNOTD_ERROR;
+ }
+
+ return 0;
+ }
+
+ return KNOTD_ENOENT;
+}
+
+int pid_remove(const char* fn)
+{
+ if (unlink(fn) < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ return KNOTD_EOK;
+}
+
+int pid_running(pid_t pid)
+{
+ return kill(pid, 0) == 0;
+}
+
diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h
new file mode 100644
index 0000000..d8f2f4c
--- /dev/null
+++ b/src/knot/ctl/process.h
@@ -0,0 +1,88 @@
+/* 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 process.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Functions for POSIX process handling.
+ *
+ * \addtogroup ctl
+ * @{
+ */
+
+#ifndef _KNOTD_PROCESS_H_
+#define _KNOTD_PROCESS_H_
+
+#include <unistd.h>
+
+/*!
+ * \brief Return a filename of the default compiled database file.
+ *
+ * \retval Filename of the database file.
+ * \retval NULL if not exists.
+ */
+char* pid_filename();
+
+/*!
+ * \brief Read PID from given file.
+ *
+ * \param fn Filename containing PID.
+ *
+ * \retval PID on success (positive integer).
+ * \retval KNOTD_EINVAL on null path.
+ * \retval KNOTD_ENOENT if the filename content cannot be read.
+ * \retval KNOTD_ERANGE if the stored PID is out of range.
+ */
+pid_t pid_read(const char* fn);
+
+/*!
+ * \brief Write PID to given file.
+ *
+ * \param fn Filename containing PID.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on null path.
+ * \retval KNOTD_ENOENT filename cannot be opened for writing.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int pid_write(const char* fn);
+
+/*!
+ * \brief Remove file containing PID.
+ *
+ * \param fn Filename containing PID.
+ *
+ * \warning Filename content won't be checked.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL failed to remove filename.
+ */
+int pid_remove(const char* fn);
+
+/*!
+ * \brief Return true if the PID is running.
+ *
+ * \param pid Process ID.
+ *
+ * \retval 1 if running.
+ * \retval 0 if not running (or error).
+ */
+int pid_running(pid_t pid);
+
+#endif // _KNOTD_PROCESS_H_
+
+/*! @} */
diff --git a/src/knot/main.c b/src/knot/main.c
new file mode 100644
index 0000000..4091055
--- /dev/null
+++ b/src/knot/main.c
@@ -0,0 +1,336 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "common.h"
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/server/server.h"
+#include "zcompile/zcompile.h"
+#include "knot/ctl/process.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/logconf.h"
+#include "common/evqueue.h"
+#include "knot/server/zones.h"
+
+/*----------------------------------------------------------------------------*/
+
+/* Signal flags. */
+static volatile short sig_req_stop = 0;
+static volatile short sig_req_reload = 0;
+static volatile short sig_stopping = 0;
+
+// SIGINT signal handler
+void interrupt_handle(int s)
+{
+ // Reload configuration
+ if (s == SIGHUP) {
+ sig_req_reload = 1;
+ return;
+ }
+
+ // Stop server
+ if (s == SIGINT || s == SIGTERM) {
+ if (sig_stopping == 0) {
+ sig_req_stop = 1;
+ sig_stopping = 1;
+ } else {
+ log_server_notice("OK! Exiting immediately.\n");
+ exit(1);
+ }
+ }
+}
+
+void help(int argc, char **argv)
+{
+ printf("Usage: %sd [parameters]\n",
+ PACKAGE_NAME);
+ printf("Parameters:\n"
+ " -c, --config [file] Select configuration file.\n"
+ " -d, --daemonize Run server as a daemon.\n"
+ " -v, --verbose Verbose mode - additional runtime information.\n"
+ " -V, --version Print version of the server.\n"
+ " -h, --help Print help and usage.\n");
+}
+
+int main(int argc, char **argv)
+{
+ // Parse command line arguments
+ int c = 0, li = 0;
+ int verbose = 0;
+ int daemonize = 0;
+ char* config_fn = 0;
+
+ /* Long options. */
+ struct option opts[] = {
+ {"config", required_argument, 0, 'c'},
+ {"daemonize", no_argument, 0, 'd'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "c:dvVh", opts, &li)) != -1) {
+ switch (c)
+ {
+ case 'c':
+ config_fn = strdup(optarg);
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
+ return 0;
+ case 'h':
+ case '?':
+ default:
+ help(argc, argv);
+ return 1;
+ }
+ }
+
+ // Now check if we want to daemonize
+ if (daemonize) {
+ if (daemon(1, 0) != 0) {
+ fprintf(stderr, "Daemonization failed, "
+ "shutting down...\n");
+ return 1;
+ }
+ }
+
+ // Register service and signal handler
+ struct sigaction emptyset;
+ emptyset.sa_handler = interrupt_handle;
+ sigemptyset(&emptyset.sa_mask);
+ emptyset.sa_flags = 0;
+ sigaction(SIGALRM, &emptyset, NULL); // Interrupt
+
+ // Setup event queue
+ evqueue_set(evqueue_new());
+
+ // Initialize log
+ log_init();
+
+ // Verbose mode
+ if (verbose) {
+ int mask = LOG_MASK(LOG_INFO)|LOG_MASK(LOG_DEBUG);
+ log_levels_add(LOGT_STDOUT, LOG_ANY, mask);
+ }
+
+ // Initialize pseudorandom number generator
+ srand(time(0));
+
+ // Create server
+ server_t *server = server_create();
+
+ // Initialize configuration
+ conf_read_lock();
+ conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
+ conf_add_hook(conf(), CONF_LOG, zones_ns_conf_hook, server->nameserver);
+ conf_add_hook(conf(), CONF_LOG, server_conf_hook, server);
+ conf_read_unlock();
+
+ // Find implicit configuration file
+ if (!config_fn) {
+ config_fn = conf_find_default();
+ }
+
+ // Find absolute path for config file
+ if (config_fn[0] != '/')
+ {
+ // Get absolute path to cwd
+ size_t cwbuflen = 64;
+ char *cwbuf = malloc((cwbuflen + 2) * sizeof(char));
+ while (getcwd(cwbuf, cwbuflen) == 0) {
+ cwbuflen *= 2;
+ cwbuf = realloc(cwbuf, (cwbuflen + 2) * sizeof(char));
+ }
+ cwbuflen = strlen(cwbuf);
+
+ // Append ending slash
+ if (cwbuf[cwbuflen - 1] != '/') {
+ cwbuf = strcat(cwbuf, "/");
+ }
+
+ // Assemble path to config file
+ char *abs_cfg = strcdup(cwbuf, config_fn);
+ free(config_fn);
+ free(cwbuf);
+ config_fn = abs_cfg;
+ }
+
+ // Open configuration
+ log_server_info("Parsing configuration '%s' ...\n", config_fn);
+ if (conf_open(config_fn) != KNOTD_EOK) {
+
+ log_server_error("Failed to parse configuration file '%s'.\n",
+ config_fn);
+ server_destroy(&server);
+ free(config_fn);
+ return 1;
+ } else {
+ log_server_info("Configured %d interfaces and %d zones.\n",
+ conf()->ifaces_count, conf()->zones_count);
+ }
+ log_server_info("\n");
+
+ // Create server instance
+ char* pidfile = pid_filename();
+
+ // Run server
+ int res = 0;
+ log_server_info("Starting server...\n");
+ if ((res = server_start(server)) == KNOTD_EOK) {
+
+ // Save PID
+ int has_pid = 1;
+ int rc = pid_write(pidfile);
+ if (rc < 0) {
+ has_pid = 0;
+ log_server_warning("Failed to create "
+ "PID file '%s'.\n", pidfile);
+ }
+
+ // Change directory if daemonized
+ if (daemonize) {
+ log_server_info("Server started as a daemon, "
+ "PID = %ld\n", (long)getpid());
+ res = chdir("/");
+ } else {
+ log_server_info("Server started in foreground, "
+ "PID = %ld\n", (long)getpid());
+ }
+ if (has_pid) {
+ log_server_info("PID stored in %s\n", pidfile);
+ } else {
+ log_server_warning("Server running without PID file.\n");
+ }
+ size_t zcount = server->nameserver->zone_db->zone_count;
+ if (!zcount) {
+ log_server_warning("Server started, but no zones served.\n");
+ }
+
+ // Setup signal handler
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sa.sa_flags = 0;
+ pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
+
+ /* Run event loop. */
+ for(;;) {
+ pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
+ int ret = evqueue_poll(evqueue(), 0, 0);
+ pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
+
+ /* Interrupts. */
+ /*! \todo More robust way to exit evloop.
+ * Event loop should exit with a special
+ * event.
+ */
+ if (sig_req_stop) {
+ sig_req_stop = 0;
+ server_stop(server);
+ break;
+ }
+ if (sig_req_reload) {
+ log_server_info("Reloading configuration...\n");
+ sig_req_reload = 0;
+ int cf_ret = cf_ret = conf_open(config_fn);
+ switch (cf_ret) {
+ case KNOTD_EOK:
+ log_server_info("Configuration "
+ "reloaded.\n");
+ break;
+ case KNOTD_ENOENT:
+ log_server_error("Configuration "
+ "file '%s' "
+ "not found.\n",
+ conf()->filename);
+ break;
+ default:
+ log_server_error("Configuration "
+ "reload failed.\n");
+ break;
+ }
+ }
+
+ /* Events. */
+ if (ret > 0) {
+ event_t ev;
+ if (evqueue_get(evqueue(), &ev) == 0) {
+ dbg_server_verb("Event: "
+ "received new event.\n");
+ if (ev.cb) {
+ ev.cb(&ev);
+ }
+ }
+ }
+ }
+ pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
+
+ if ((res = server_wait(server)) != KNOTD_EOK) {
+ log_server_error("An error occured while "
+ "waiting for server to finish.\n");
+ } else {
+ log_server_info("Server finished.\n");
+ }
+
+ } else {
+ log_server_fatal("An error occured while "
+ "starting the server.\n");
+ }
+
+ // Stop server and close log
+ server_destroy(&server);
+
+ // Remove PID file
+ if (pid_remove(pidfile) < 0) {
+ log_server_warning("Failed to remove PID file.\n");
+ }
+
+ log_server_info("Shut down.\n");
+ log_close();
+ free(pidfile);
+
+ // Destroy event loop
+ evqueue_t *q = evqueue();
+ evqueue_free(&q);
+
+ // Free default config filename if exists
+ free(config_fn);
+
+ if (!daemonize) {
+ fflush(stdout);
+ fflush(stderr);
+ }
+
+ return res;
+}
diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h
new file mode 100644
index 0000000..aa80373
--- /dev/null
+++ b/src/knot/other/debug.h
@@ -0,0 +1,426 @@
+/* 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 other/debug.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Debugging facility, uses log.h.
+ *
+ * \addtogroup debugging
+ * @{
+ */
+
+#ifndef _KNOTD_DEBUG_H_
+#define _KNOTD_DEBUG_H_
+
+#include "config.h" /* autoconf generated */
+
+#include "knot/other/log.h"
+#include "common/print.h"
+
+/*! \todo Set these during configure as well. */
+//#define KNOTD_SERVER_DEBUG
+//#define KNOTD_THREADS_DEBUG
+//#define KNOTD_JOURNAL_DEBUG
+//#define KNOTD_NET_DEBUG
+//#define KNOTD_ZONES_DEBUG
+//#define KNOTD_XFR_DEBUG
+//#define KNOTD_NOTIFY_DEBUG
+//#define KNOTD_ZDUMP_DEBUG
+//#define KNOTD_ZLOAD_DEBUG
+
+/******************************************************************************/
+
+#ifdef KNOTD_NOTIFY_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_notify(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_notify_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_notify(msg...)
+#define dbg_notify_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_notify_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_notify_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_notify_verb(msg...)
+#define dbg_notify_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_notify_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_notify_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_notify_detail(msg...)
+#define dbg_notify_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_notify(msg...)
+#define dbg_notify_hex(data, len)
+#define dbg_notify_verb(msg...)
+#define dbg_notify_hex_verb(data, len)
+#define dbg_notify_detail(msg...)
+#define dbg_notify_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_SERVER_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_server(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_server_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_server(msg...)
+#define dbg_server_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_server_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_server_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_server_verb(msg...)
+#define dbg_server_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_server_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_server_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_server_detail(msg...)
+#define dbg_server_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_server(msg...)
+#define dbg_server_hex(data, len)
+#define dbg_server_verb(msg...)
+#define dbg_server_hex_verb(data, len)
+#define dbg_server_detail(msg...)
+#define dbg_server_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_NET_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_net(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_net_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_net(msg...)
+#define dbg_net_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_net_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_net_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_net_verb(msg...)
+#define dbg_net_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_net_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_net_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_net_detail(msg...)
+#define dbg_net_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_net(msg...)
+#define dbg_net_hex(data, len)
+#define dbg_net_verb(msg...)
+#define dbg_net_hex_verb(data, len)
+#define dbg_net_detail(msg...)
+#define dbg_net_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_THREADS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_dt(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dt_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_dt(msg...)
+#define dbg_dt_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_dt_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dt_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_dt_verb(msg...)
+#define dbg_dt_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_dt_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_dt_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_dt_detail(msg...)
+#define dbg_dt_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_dt(msg...)
+#define dbg_dt_hex(data, len)
+#define dbg_dt_verb(msg...)
+#define dbg_dt_hex_verb(data, len)
+#define dbg_dt_detail(msg...)
+#define dbg_dt_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_JOURNAL_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_journal(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_journal_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_journal(msg...)
+#define dbg_journal_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_journal_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_journal_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_journal_verb(msg...)
+#define dbg_journal_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_journal_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_journal_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_journal_detail(msg...)
+#define dbg_journal_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_journal(msg...)
+#define dbg_journal_hex(data, len)
+#define dbg_journal_verb(msg...)
+#define dbg_journal_hex_verb(data, len)
+#define dbg_journal_detail(msg...)
+#define dbg_journal_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_ZONES_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zones(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zones_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#define dbg_zones_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_zones(msg...)
+#define dbg_zones_hex(data, len)
+#define dbg_zones_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zones_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zones_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#define dbg_zones_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_zones_verb(msg...)
+#define dbg_zones_hex_verb(data, len)
+#define dbg_zones_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zones_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zones_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#define dbg_zones_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zones_detail(msg...)
+#define dbg_zones_hex_detail(data, len)
+#define dbg_zones_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zones(msg...)
+#define dbg_zones_hex(data, len)
+#define dbg_zones_verb(msg...)
+#define dbg_zones_hex_verb(data, len)
+#define dbg_zones_detail(msg...)
+#define dbg_zones_hex_detail(data, len)
+#define dbg_zones_exec(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_XFR_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_xfr(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_xfr_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_xfr(msg...)
+#define dbg_xfr_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_xfr_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_xfr_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_xfr_verb(msg...)
+#define dbg_xfr_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_xfr_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_xfr_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_xfr_detail(msg...)
+#define dbg_xfr_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_xfr(msg...)
+#define dbg_xfr_hex(data, len)
+#define dbg_xfr_verb(msg...)
+#define dbg_xfr_hex_verb(data, len)
+#define dbg_xfr_detail(msg...)
+#define dbg_xfr_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_ZDUMP_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zdump(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zdump_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zdump(msg...)
+#define dbg_zdump_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zdump_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zdump_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zdump_verb(msg...)
+#define dbg_zdump_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zdump_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zdump_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zdump_detail(msg...)
+#define dbg_zdump_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zdump(msg...)
+#define dbg_zdump_hex(data, len)
+#define dbg_zdump_verb(msg...)
+#define dbg_zdump_hex_verb(data, len)
+#define dbg_zdump_detail(msg...)
+#define dbg_zdump_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOTD_ZLOAD_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zload(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zload_hex(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zload(msg...)
+#define dbg_zload_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zload_verb(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zload_hex_verb(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zload_verb(msg...)
+#define dbg_zload_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zload_detail(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+#define dbg_zload_hex_detail(data, len) hex_log(LOG_SERVER, (data), (len))
+#else
+#define dbg_zload_detail(msg...)
+#define dbg_zload_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zload(msg...)
+#define dbg_zload_hex(data, len)
+#define dbg_zload_verb(msg...)
+#define dbg_zload_hex_verb(data, len)
+#define dbg_zload_detail(msg...)
+#define dbg_zload_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#endif /* _KNOTD_DEBUG_H_ */
+
+/*! @} */
diff --git a/src/knot/other/error.c b/src/knot/other/error.c
new file mode 100644
index 0000000..a149966
--- /dev/null
+++ b/src/knot/other/error.c
@@ -0,0 +1,46 @@
+/* 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 "knot/other/error.h"
+#include "common/errors.h"
+
+const error_table_t knotd_error_msgs[] = {
+
+ /* Mapped errors. */
+ {KNOTD_EOK, "OK"},
+ {KNOTD_ENOMEM, "Not enough memory."},
+ {KNOTD_EINVAL, "Invalid parameter passed."},
+ {KNOTD_ENOTSUP, "Parameter not supported."},
+ {KNOTD_EBUSY, "Requested resource is busy."},
+ {KNOTD_EAGAIN, "The system lacked the necessary resource, try again."},
+ {KNOTD_EACCES, "Permission to perform requested operation is denied."},
+ {KNOTD_ECONNREFUSED, "Connection is refused."},
+ {KNOTD_EISCONN, "Already connected."},
+ {KNOTD_EADDRINUSE, "Address already in use."},
+ {KNOTD_ENOENT, "Resource not found."},
+ {KNOTD_ERANGE, "Value is out of range."},
+
+ /* Custom errors. */
+ {KNOTD_ERROR, "Generic error."},
+ {KNOTD_EZONEINVAL, "Invalid zone file."},
+ {KNOTD_ENOTRUNNING, "Resource is not running."},
+ {KNOTD_EPARSEFAIL, "Parser failed."},
+ {KNOTD_ENOIPV6, "IPv6 support disabled."},
+ {KNOTD_EMALF, "Malformed data."},
+ {KNOTD_ESPACE, "Not enough space provided."},
+ {KNOTD_EEXPIRED, "Resource is expired."},
+ {KNOTD_ERROR, 0}
+};
diff --git a/src/knot/other/error.h b/src/knot/other/error.h
new file mode 100644
index 0000000..65c51cf
--- /dev/null
+++ b/src/knot/other/error.h
@@ -0,0 +1,99 @@
+/* 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 other/error.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Error codes and function for getting error message.
+ *
+ * \addtogroup utils
+ * @{
+ */
+
+#ifndef _KNOTD__ERROR_H_
+#define _KNOTD__ERROR_H_
+
+#include <errno.h>
+
+#include "common/errors.h"
+
+/*!
+ * \brief Error codes used in the server.
+ *
+ * Some viable errors are directly mapped
+ * to libc errno codes.
+ */
+enum knot_error_t {
+
+ /* Directly mapped error codes. */
+ KNOTD_EOK = 0,
+ KNOTD_ENOMEM = -ENOMEM, /*!< \brief Out of memory. */
+ KNOTD_EINVAL = -EINVAL, /*!< \brief Invalid parameter passed. */
+ KNOTD_ENOTSUP = -ENOTSUP, /*!< \brief Parameter not supported. */
+ KNOTD_EBUSY = -EBUSY, /*!< \brief Requested resource is busy. */
+ KNOTD_EAGAIN = -EAGAIN, /*!< \brief OS lacked necessary resources. */
+ KNOTD_EACCES = -EACCES, /*!< \brief Permission is denied. */
+ KNOTD_ECONNREFUSED = -ECONNREFUSED, /*!< \brief Connection is refused. */
+ KNOTD_EISCONN = -EISCONN, /*!< \brief Already connected. */
+ KNOTD_EADDRINUSE = -EADDRINUSE, /*!< \brief Address already in use. */
+ KNOTD_ENOENT = -ENOENT, /*!< \brief Resource not found. */
+ KNOTD_ERANGE = -ERANGE, /*!< \brief Value is out of range. */
+
+ /* Custom error codes. */
+ KNOTD_ERROR = -16384, /*!< \brief Generic error. */
+ KNOTD_EZONEINVAL, /*!< \brief Invalid zone file. */
+ KNOTD_ENOTRUNNING, /*!< \brief Resource is not running. */
+ KNOTD_EPARSEFAIL, /*!< \brief Parser fail. */
+ KNOTD_ENOIPV6, /*!< \brief No IPv6 support. */
+ KNOTD_EMALF, /*!< \brief Malformed data. */
+ KNOTD_ESPACE, /*!< \brief Not enough space provided. */
+ KNOTD_EEXPIRED, /*!< \brief Resource is expired. */
+
+ KNOTD_ERROR_COUNT = 21
+};
+
+/*! \brief Table linking error messages to error codes. */
+extern const error_table_t knotd_error_msgs[KNOTD_ERROR_COUNT];
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+static inline const char *knotd_strerror(int code)
+{
+ return error_to_str((const error_table_t*)knotd_error_msgs, code);
+}
+
+/*!
+ * \brief errno mapper that automatically prepends fallback value.
+ *
+ * \see map_errno()
+ *
+ * \param err POSIX errno.
+ * \param ... List of handled codes.
+ *
+ * \return Mapped error code.
+ */
+#define knot_map_errno(err...) map_errno(KNOTD_ERROR, err);
+
+#endif /* _KNOTD__ERROR_H_ */
+
+/*! @} */
diff --git a/src/knot/other/log.c b/src/knot/other/log.c
new file mode 100644
index 0000000..9318d5f
--- /dev/null
+++ b/src/knot/other/log.c
@@ -0,0 +1,305 @@
+/* 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 <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/other/log.h"
+#include "common/lists.h"
+#include "knot/conf/conf.h"
+
+/*! Log source table. */
+static uint8_t *LOG_FCL = 0;
+static volatile size_t LOG_FCL_SIZE = 0;
+static FILE** LOG_FDS = 0;
+static ssize_t LOG_FDS_OPEN = 0;
+
+#define facility_at(i) (LOG_FCL + ((i) << LOG_SRC_BITS))
+#define facility_next(f) (f) += (1 << LOG_SRC_BITS)
+#define facility_levels(f, i) *((f) + (i))
+
+int log_setup(int logfiles)
+{
+ /* Check facilities count. */
+ if (logfiles < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Ensure minimum facilities count. */
+ int facilities = LOGT_FILE + logfiles;
+
+ /* Reserve space for facilities. */
+ size_t new_size = facilities << LOG_SRC_BITS;
+ LOG_FDS = 0;
+ LOG_FDS_OPEN = 0;
+ LOG_FCL = 0;
+ LOG_FCL_SIZE = 0;
+ LOG_FCL = malloc(new_size);
+ if (!LOG_FCL) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Reserve space for logfiles. */
+ if (logfiles > 0) {
+ LOG_FDS = malloc(sizeof(FILE*) * logfiles);
+ if (!LOG_FDS) {
+ free(LOG_FCL);
+ LOG_FCL = 0;
+ return KNOTD_ENOMEM;
+ }
+ memset(LOG_FDS, 0, sizeof(FILE*) * logfiles);
+ }
+
+ memset(LOG_FCL, 0, new_size);
+ LOG_FCL_SIZE = new_size; // Assign only when all is set
+ return KNOTD_EOK;
+}
+
+
+
+int log_init()
+{
+ /* Initialize globals. */
+ LOG_FCL = 0;
+ LOG_FCL_SIZE = 0;
+ LOG_FDS = 0;
+ LOG_FDS_OPEN = 0;
+
+ /* Setup initial state. */
+ int ret = KNOTD_EOK;
+ int emask = LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR)|LOG_MASK(LOG_FATAL);
+ int imask = LOG_MASK(LOG_INFO)|LOG_MASK(LOG_NOTICE);
+
+ /* Add debug messages. */
+ emask |= LOG_MASK(LOG_DEBUG);
+
+ ret = log_setup(0);
+ log_levels_set(LOGT_SYSLOG, LOG_ANY, emask);
+ log_levels_set(LOGT_STDERR, LOG_ANY, emask);
+ log_levels_set(LOGT_STDOUT, LOG_ANY, imask);
+
+ /// \todo May change to LOG_DAEMON.
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
+ return ret;
+}
+
+void log_close()
+{
+ log_truncate();
+ closelog();
+}
+
+void log_truncate()
+{
+ LOG_FCL_SIZE = 0;
+ if (LOG_FCL) {
+ free(LOG_FCL);
+ LOG_FCL = 0;
+ }
+ if (LOG_FDS) {
+
+ /* Close open logfiles. */
+ for (int i = 0; i < LOG_FDS_OPEN; ++i) {
+ fclose(LOG_FDS[i]);
+ }
+
+ free(LOG_FDS);
+ LOG_FDS = 0;
+ LOG_FDS_OPEN = 0;
+ }
+}
+
+int log_isopen()
+{
+ return LOG_FCL_SIZE;
+}
+
+int log_open_file(const char* filename)
+{
+ // Check facility
+ if (unlikely(!LOG_FCL_SIZE || LOGT_FILE + LOG_FDS_OPEN >= LOG_FCL_SIZE)) {
+ return KNOTD_ERROR;
+ }
+
+ // Open file
+ LOG_FDS[LOG_FDS_OPEN] = fopen(filename, "w");
+ if (!LOG_FDS[LOG_FDS_OPEN]) {
+ return KNOTD_EINVAL;
+ }
+
+ // Disable buffering
+ setvbuf(LOG_FDS[LOG_FDS_OPEN], (char *)0, _IONBF, 0);
+
+ return LOGT_FILE + LOG_FDS_OPEN++;
+}
+
+uint8_t log_levels(int facility, logsrc_t src)
+{
+ // Check facility
+ if (unlikely(!LOG_FCL_SIZE || facility >= LOG_FCL_SIZE)) {
+ return 0;
+ }
+
+ return *(LOG_FCL + (facility << LOG_SRC_BITS) + src);
+}
+
+int log_levels_set(int facility, logsrc_t src, uint8_t levels)
+{
+ // Check facility
+ if (unlikely(!LOG_FCL_SIZE || facility >= LOG_FCL_SIZE)) {
+ return KNOTD_EINVAL;
+ }
+
+ // Get facility pointer from offset
+ uint8_t *lp = LOG_FCL + (facility << LOG_SRC_BITS);
+
+ // Assign level if not multimask
+ if (src != LOG_ANY) {
+ *(lp + src) = levels;
+ } else {
+ // Any == set to all sources
+ for (int i = 0; i <= LOG_ANY; ++i) {
+ *(lp + i) = levels;
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+int log_levels_add(int facility, logsrc_t src, uint8_t levels)
+{
+ uint8_t new_levels = log_levels(facility, src) | levels;
+ return log_levels_set(facility, src, new_levels);
+}
+
+static int _log_msg(logsrc_t src, int level, const char *msg)
+{
+ if(!log_isopen()) {
+ return KNOTD_ERROR;
+ }
+
+ int ret = 0;
+ FILE *stream = stdout;
+ uint8_t *f = facility_at(LOGT_SYSLOG);
+
+ // Syslog
+ if (facility_levels(f, src) & LOG_MASK(level)) {
+ syslog(level, "%s", msg);
+ ret = 1; // To prevent considering the message as ignored.
+ }
+
+ // Convert level to mask
+ level = LOG_MASK(level);
+
+ // Log streams
+ for (int i = LOGT_STDERR; i < LOGT_FILE + LOG_FDS_OPEN; ++i) {
+
+ // Check facility levels mask
+ f = facility_at(i);
+ if (facility_levels(f, src) & level) {
+
+ // Select stream
+ switch(i) {
+ case LOGT_STDERR: stream = stderr; break;
+ case LOGT_STDOUT: stream = stdout; break;
+ default: stream = LOG_FDS[i - LOGT_FILE]; break;
+ }
+
+ // Print
+ ret = fprintf(stream, "%s", msg);
+ if (stream == stdout) {
+ fflush(stream);
+ }
+ }
+ }
+
+ if (ret < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ return ret;
+}
+
+int log_msg(logsrc_t src, int level, const char *msg, ...)
+{
+ /* Buffer for log message. */
+ char sbuf[4096];
+ char *buf = sbuf;
+ int buflen = sizeof(sbuf) - 1;
+
+ /* Prefix error level. */
+ const char *prefix = "";
+ switch (level) {
+ case LOG_DEBUG: break;
+ case LOG_INFO: break;
+ case LOG_NOTICE: prefix = "notice: "; break;
+ case LOG_WARNING: prefix = "warning: "; break;
+ case LOG_ERR: prefix = "error: "; break;
+ case LOG_FATAL: prefix = "fatal: "; break;
+ default: break;
+ }
+
+ /* Prepend prefix. */
+ int plen = strlen(prefix);
+ if (plen > 0) {
+ strcpy(buf, prefix);
+ buf += plen;
+ buflen -= plen;
+ }
+
+ /* Compile log message. */
+ int ret = 0;
+ va_list ap;
+ va_start(ap, msg);
+ ret = vsnprintf(buf, buflen, msg, ap);
+ va_end(ap);
+
+ /* Send to logging facilities. */
+ if (ret > 0) {
+ ret = _log_msg(src, level, sbuf);
+ }
+
+ return ret;
+}
+
+int log_vmsg(logsrc_t src, int level, const char *msg, va_list ap)
+{
+ int ret = 0;
+ char buf[2048];
+ ret = vsnprintf(buf, sizeof(buf) - 1, msg, ap);
+
+ if (ret > 0) {
+ ret = _log_msg(src, level, buf);
+ }
+
+ return ret;
+}
+
+void hex_log(int source, const char *data, int length)
+{
+ int ptr = 0;
+ for (; ptr < length; ptr++) {
+ log_msg(source, LOG_DEBUG, "0x%02x ",
+ (unsigned char)*(data + ptr));
+ }
+ log_msg(source, LOG_DEBUG, "\n");
+}
diff --git a/src/knot/other/log.h b/src/knot/other/log.h
new file mode 100644
index 0000000..305020c
--- /dev/null
+++ b/src/knot/other/log.h
@@ -0,0 +1,208 @@
+/* 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 log.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Logging facility.
+ *
+ * \note Loglevel defined in syslog.h, may be redefined in other backend, but
+ * keep naming. LOG_FATAL, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG
+ *
+ * In standard mode, only LOG_FATAL, LOG_ERR and LOG_WARNING is logged.
+ * Verbose mode enables LOG_NOTICE and LOG_INFO for additional information.
+ *
+ * \addtogroup logging
+ * @{
+ */
+
+#ifndef _KNOTD_LOG_H_
+#define _KNOTD_LOG_H_
+
+/*
+ */
+#include <syslog.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+/*! \brief Log facility types. */
+typedef enum {
+ LOGT_SYSLOG = 0, /*!< Logging to syslog(3) facility. */
+ LOGT_STDERR = 1, /*!< Print log messages to the stderr. */
+ LOGT_STDOUT = 2, /*!< Print log messages to the stdout. */
+ LOGT_FILE = 3 /*!< Generic logging to (unbuffered) file on the disk. */
+} logtype_t;
+
+/*! \brief Log sources width (bits). */
+#define LOG_SRC_BITS 3
+
+/*! \brief Log sources (max. LOG_SRC_BITS bits). */
+typedef enum {
+ LOG_SERVER = 0, /*!< Server module. */
+ LOG_ANSWER = 1, /*!< Query answering module. */
+ LOG_ZONE = 2, /*!< Zone manipulation module. */
+ LOG_ANY = 7 /*!< Any module. */
+} logsrc_t;
+
+/*! \brief Severity mapping. */
+#define LOG_FATAL LOG_CRIT /*!< Fatal errors cannot be masked. */
+
+/* Logging facility setup. */
+
+/*!
+ * \brief Create logging facilities respecting their
+ * canonical order.
+ *
+ * Facilities ordering: Syslog, Stderr, Stdout, File0...
+ * \see logtype_t
+ *
+ * \param logfiles Number of extra logfiles.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid number of logfiles (negative).
+ */
+int log_setup(int logfiles);
+
+/*!
+ * \brief Setup logging subsystem.
+ *
+ * \see syslog.h
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ENOMEM out of memory error.
+ */
+int log_init();
+
+/*!
+ * \brief Close and deinitialize log.
+ */
+void log_close();
+
+/*!
+ * \brief Truncate current log setup.
+ */
+void log_truncate();
+
+/*!
+ * \brief Return positive number if open.
+ *
+ * \return 1 if open (boolean true)
+ * \return 0 if closed (boolean false)
+ */
+int log_isopen();
+
+/*!
+ * \brief Open file as a logging facility.
+ *
+ * \param filename File path.
+ *
+ * \retval associated facility index on success.
+ * \retval KNOTD_EINVAL filename cannot be opened for writing.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int log_open_file(const char* filename);
+
+/*!
+ * \brief Return log levels for a given facility.
+ *
+ * \param facility Given log facility index.
+ * \param src Given log source in the context of current facility.
+ *
+ * \retval Associated log level flags on success.
+ * \retval 0 on error.
+ */
+uint8_t log_levels(int facility, logsrc_t src);
+
+/*!
+ * \brief Set log levels for given facility.
+ *
+ * \param facility Logging facility index (LOGT_SYSLOG...).
+ * \param src Logging source (LOG_SERVER...LOG_ANY).
+ * \param levels Bitmask of specified log levels.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters (facility out of range).
+ */
+int log_levels_set(int facility, logsrc_t src, uint8_t levels);
+
+/*!
+ * \brief Add log levels to a given facility.
+ *
+ * New levels are added on top of existing, the resulting
+ * levels set is "old_levels OR new_levels".
+ *
+ * \param facility Logging facility index (LOGT_SYSLOG...).
+ * \param src Logging source (LOG_SERVER...LOG_ANY).
+ * \param levels Bitmask of specified log levels.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters (facility out of range).
+ */
+int log_levels_add(int facility, logsrc_t src, uint8_t levels);
+
+/*!
+ * \brief Log message.
+ *
+ * Function follows printf() format.
+ *
+ * \param src Origin of the message.
+ * \param level Message error level.
+ * \param msg Content of the logged message.
+ *
+ * \retval Number of logged bytes on success.
+ * \retval 0 When the message is ignored.
+ * \retval KNOTD_EINVAL invalid parameters.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int log_msg(logsrc_t src, int level, const char *msg, ...)
+ __attribute__((format(printf, 3, 4)));
+
+/*!
+ * \brief Log message for stdarg.
+ *
+ * \see log_msg
+ */
+int log_vmsg(logsrc_t src, int level, const char *msg, va_list ap);
+
+void hex_log(int source, const char *data, int length);
+
+/* Convenient logging. */
+#define log_server_fatal(msg...) log_msg(LOG_SERVER, LOG_FATAL, msg)
+#define log_server_error(msg...) log_msg(LOG_SERVER, LOG_ERR, msg)
+#define log_server_warning(msg...) log_msg(LOG_SERVER, LOG_WARNING, msg)
+#define log_server_notice(msg...) log_msg(LOG_SERVER, LOG_NOTICE, msg)
+#define log_server_info(msg...) log_msg(LOG_SERVER, LOG_INFO, msg)
+#define log_server_debug(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
+
+#define log_answer_fatal(msg...) log_msg(LOG_ANSWER, LOG_FATAL, msg)
+#define log_answer_error(msg...) log_msg(LOG_ANSWER, LOG_ERR, msg)
+#define log_answer_warning(msg...) log_msg(LOG_ANSWER, LOG_WARNING, msg)
+#define log_answer_notice(msg...) log_msg(LOG_ANSWER, LOG_NOTICE, msg)
+#define log_answer_info(msg...) log_msg(LOG_ANSWER, LOG_INFO, msg)
+#define log_answer_debug(msg...) log_msg(LOG_ANSWER, LOG_DEBUG, msg)
+
+#define log_zone_fatal(msg...) log_msg(LOG_ZONE, LOG_FATAL, msg)
+#define log_zone_error(msg...) log_msg(LOG_ZONE, LOG_ERR, msg)
+#define log_zone_warning(msg...) log_msg(LOG_ZONE, LOG_WARNING, msg)
+#define log_zone_notice(msg...) log_msg(LOG_ZONE, LOG_NOTICE, msg)
+#define log_zone_info(msg...) log_msg(LOG_ZONE, LOG_INFO, msg)
+#define log_zone_debug(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
+
+#endif /* _KNOTD_LOG_H_ */
+
+/*! @} */
diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c
new file mode 100644
index 0000000..9707e57
--- /dev/null
+++ b/src/knot/server/dthreads.c
@@ -0,0 +1,1006 @@
+/* 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "knot/common.h"
+#include "knot/server/dthreads.h"
+#include "knot/other/log.h"
+#include "knot/other/error.h"
+
+/*! \brief Lock thread state for R/W. */
+static inline void lock_thread_rw(dthread_t *thread)
+{
+ pthread_mutex_lock(&thread->_mx);
+}
+/*! \brief Unlock thread state for R/W. */
+static inline void unlock_thread_rw(dthread_t *thread)
+{
+ pthread_mutex_unlock(&thread->_mx);
+}
+
+/*! \brief Signalize thread state change. */
+static inline void unit_signalize_change(dt_unit_t *unit)
+{
+ pthread_mutex_lock(&unit->_report_mx);
+ pthread_cond_signal(&unit->_report);
+ pthread_mutex_unlock(&unit->_report_mx);
+}
+
+/*!
+ * \brief Update thread state with notification.
+ * \param thread Given thread.
+ * \param state New state for thread.
+ * \retval 0 on success.
+ * \retval <0 on error (EINVAL, ENOTSUP).
+ */
+static inline int dt_update_thread(dthread_t *thread, int state)
+{
+ // Check
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Cancel with lone thread
+ dt_unit_t *unit = thread->unit;
+ if (unit == 0) {
+ return KNOTD_ENOTSUP;
+ }
+
+ // Cancel current runnable if running
+ pthread_mutex_lock(&unit->_notify_mx);
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+
+ // Update state
+ thread->state = state;
+ unlock_thread_rw(thread);
+
+ // Notify thread
+ dt_signalize(thread, SIGALRM);
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ } else {
+ /* Unable to update thread, it is already dead. */
+ unlock_thread_rw(thread);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return KNOTD_EINVAL;
+ }
+
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Thread entrypoint function.
+ *
+ * When a thread is created and started, it immediately enters this function.
+ * Depending on thread state, it either enters runnable or
+ * blocks until it is awakened.
+ *
+ * This function also handles "ThreadIdle" state to quickly suspend and resume
+ * threads and mitigate thread creation costs. Also, thread runnable may
+ * be changed to alter the thread behavior on runtime
+ */
+static void *thread_ep(void *data)
+{
+ // Check data
+ dthread_t *thread = (dthread_t *)data;
+ if (thread == 0) {
+ return 0;
+ }
+
+ // Check if is a member of unit
+ dt_unit_t *unit = thread->unit;
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Ignore specific signals (except SIGALRM)
+ sigset_t ignset;
+ sigemptyset(&ignset);
+ sigaddset(&ignset, SIGINT);
+ sigaddset(&ignset, SIGTERM);
+ sigaddset(&ignset, SIGHUP);
+ pthread_sigmask(SIG_BLOCK, &ignset, 0); /*! \todo Review under BSD. */
+
+ dbg_dt("dthreads: [%p] entered ep\n", thread);
+
+ // Run loop
+ for (;;) {
+
+ // Check thread state
+ lock_thread_rw(thread);
+ if (thread->state == ThreadDead) {
+ dbg_dt("dthreads: [%p] marked as dead\n", thread);
+ unlock_thread_rw(thread);
+ break;
+ }
+
+ // Update data
+ thread->data = thread->_adata;
+ runnable_t _run = thread->run;
+
+ // Start runnable if thread is marked Active
+ if ((thread->state == ThreadActive) && (thread->run != 0)) {
+ unlock_thread_rw(thread);
+ dbg_dt("dthreads: [%p] entering runnable\n", thread);
+ _run(thread);
+ dbg_dt("dthreads: [%p] exited runnable\n", thread);
+ } else {
+ unlock_thread_rw(thread);
+ }
+
+ // If the runnable was cancelled, start new iteration
+ lock_thread_rw(thread);
+ if (thread->state & ThreadCancelled) {
+ dbg_dt("dthreads: [%p] cancelled\n", thread);
+ thread->state &= ~ThreadCancelled;
+ unlock_thread_rw(thread);
+ continue;
+ }
+ unlock_thread_rw(thread);
+
+ // Runnable finished without interruption, mark as Idle
+ pthread_mutex_lock(&unit->_notify_mx);
+ lock_thread_rw(thread);
+ if (thread->state & ThreadActive) {
+ thread->state &= ~ThreadActive;
+ thread->state |= ThreadIdle;
+ }
+
+ // Go to sleep if idle
+ if (thread->state & ThreadIdle) {
+ unlock_thread_rw(thread);
+
+ // Signalize state change
+ unit_signalize_change(unit);
+
+ // Wait for notification from unit
+ dbg_dt("dthreads: [%p] going idle\n", thread);
+ /*! \todo Check return value. */
+ pthread_cond_wait(&unit->_notify, &unit->_notify_mx);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ dbg_dt("dthreads: [%p] resumed from idle\n", thread);
+ } else {
+ unlock_thread_rw(thread);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ }
+ }
+
+ // Report thread state change
+ dbg_dt("dthreads: [%p] thread finished\n", thread);
+ unit_signalize_change(unit);
+ dbg_dt("dthreads: [%p] thread exited ep\n", thread);
+ lock_thread_rw(thread);
+ thread->state |= ThreadJoinable;
+ unlock_thread_rw(thread);
+
+ // Return
+ return 0;
+}
+
+/*!
+ * \brief Create single thread.
+ * \retval New thread instance on success.
+ * \retval NULL on error.
+ */
+static dthread_t *dt_create_thread(dt_unit_t *unit)
+{
+ // Alloc thread
+ dthread_t *thread = malloc(sizeof(dthread_t));
+ if (thread == 0) {
+ return 0;
+ }
+
+ memset(thread, 0, sizeof(dthread_t));
+
+ // Blank thread state
+ thread->state = ThreadJoined;
+ pthread_mutex_init(&thread->_mx, 0);
+
+ // Set membership in unit
+ thread->unit = unit;
+
+ // Initialize attribute
+ pthread_attr_t *attr = &thread->_attr;
+ pthread_attr_init(attr);
+ pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED);
+ pthread_attr_setschedpolicy(attr, SCHED_OTHER);
+ return thread;
+}
+
+/*! \brief Delete single thread. */
+static void dt_delete_thread(dthread_t **thread)
+{
+ // Check
+ if (thread == 0) {
+ return;
+ }
+ if (*thread == 0) {
+ return;
+ }
+
+ dthread_t* thr = *thread;
+ thr->unit = 0;
+ *thread = 0;
+
+ // Delete attribute
+ pthread_attr_destroy(&(thr)->_attr);
+
+ // Delete mutex
+ pthread_mutex_destroy(&(thr)->_mx);
+
+ // Free memory
+ free(thr);
+}
+
+/*
+ * Public APIs.
+ */
+
+dt_unit_t *dt_create(int count)
+{
+ // Check count
+ if (count <= 0) {
+ return 0;
+ }
+
+ dt_unit_t *unit = malloc(sizeof(dt_unit_t));
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Initialize conditions
+ if (pthread_cond_init(&unit->_notify, 0) != 0) {
+ free(unit);
+ return 0;
+ }
+ if (pthread_cond_init(&unit->_report, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ free(unit);
+ return 0;
+ }
+
+ // Initialize mutexes
+ if (pthread_mutex_init(&unit->_notify_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ free(unit);
+ return 0;
+ }
+ if (pthread_mutex_init(&unit->_report_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ free(unit);
+ return 0;
+ }
+ if (pthread_mutex_init(&unit->_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ free(unit);
+ return 0;
+ }
+
+ // Save unit size
+ unit->size = count;
+
+ // Alloc threads
+ unit->threads = malloc(count * sizeof(dthread_t *));
+ if (unit->threads == 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ pthread_mutex_destroy(&unit->_mx);
+ free(unit);
+ return 0;
+ }
+
+ // Initialize threads
+ int init_success = 1;
+ for (int i = 0; i < count; ++i) {
+ unit->threads[i] = dt_create_thread(unit);
+ if (unit->threads[i] == 0) {
+ init_success = 0;
+ break;
+ }
+ }
+
+ // Check thread initialization
+ if (!init_success) {
+
+ // Delete created threads
+ for (int i = 0; i < count; ++i) {
+ dt_delete_thread(&unit->threads[i]);
+ }
+
+ // Free rest of the unit
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ pthread_mutex_destroy(&unit->_mx);
+ free(unit->threads);
+ free(unit);
+ return 0;
+ }
+
+ return unit;
+}
+
+dt_unit_t *dt_create_coherent(int count, runnable_t runnable, void *data)
+{
+ // Check count
+ if (count <= 0) {
+ return 0;
+ }
+
+ // Create unit
+ dt_unit_t *unit = dt_create(count);
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Set threads common purpose
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ for (int i = 0; i < count; ++i) {
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ thread->run = runnable;
+ thread->_adata = data;
+ unlock_thread_rw(thread);
+ }
+
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ return unit;
+}
+
+void dt_delete(dt_unit_t **unit)
+{
+ /*
+ * All threads must be stopped or idle at this point,
+ * or else the behavior is undefined.
+ * Sorry.
+ */
+
+ // Check
+ if (unit == 0) {
+ return;
+ }
+ if (*unit == 0) {
+ return;
+ }
+
+ // Compact and reclaim idle threads
+ dt_unit_t *d_unit = *unit;
+ dt_compact(d_unit);
+
+ // Delete threads
+ for (int i = 0; i < d_unit->size; ++i) {
+ dt_delete_thread(&d_unit->threads[i]);
+ }
+
+ // Deinit mutexes
+ pthread_mutex_destroy(&d_unit->_notify_mx);
+ pthread_mutex_destroy(&d_unit->_report_mx);
+
+ // Deinit conditions
+ pthread_cond_destroy(&d_unit->_notify);
+ pthread_cond_destroy(&d_unit->_report);
+
+ // Free memory
+ free(d_unit->threads);
+ free(d_unit);
+ *unit = 0;
+}
+
+int dt_resize(dt_unit_t *unit, int size)
+{
+ // Check input
+ if (unit == 0 || size <= 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Evaluate delta
+ int delta = unit->size - size;
+
+ // Same size
+ if (delta == 0) {
+ return 0;
+ }
+
+ // Unit expansion
+ if (delta < 0) {
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Realloc threads
+ dbg_dt("dthreads: growing from %d to %d threads\n",
+ unit->size, size);
+
+ dthread_t **threads = realloc(unit->threads,
+ size * sizeof(dthread_t *));
+ if (threads == NULL) {
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return -1;
+ }
+
+ // Reassign
+ unit->threads = threads;
+
+ // Create new threads
+ for (int i = unit->size; i < size; ++i) {
+ threads[i] = dt_create_thread(unit);
+ }
+
+ // Update unit
+ unit->size = size;
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return 0;
+ }
+
+
+ // Unit shrinking
+ int remaining = size;
+ dbg_dt("dthreads: shrinking from %d to %d threads\n",
+ unit->size, size);
+
+ // New threads vector
+ dthread_t **threads = malloc(size * sizeof(dthread_t *));
+ if (threads == 0) {
+ return KNOTD_ENOMEM;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Iterate while there is space in new unit
+ memset(threads, 0, size * sizeof(dthread_t *));
+ int threshold = ThreadActive;
+ for (;;) {
+
+ // Find threads matching given criterias
+ int inspected = 0;
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Get thread
+ dthread_t *thread = unit->threads[i];
+ if (thread == 0) {
+ continue;
+ }
+
+ // Count thread as inspected
+ ++inspected;
+
+ lock_thread_rw(thread);
+
+ // Populate with matching threads
+ if ((remaining > 0) &&
+ (!threshold || (thread->state & threshold))) {
+
+ // Append to new vector
+ threads[size - remaining] = thread;
+ --remaining;
+
+ // Invalidate in old vector
+ unit->threads[i] = 0;
+ dbg_dt_verb("dthreads: [%p] dt_resize: elected\n",
+ thread);
+
+ } else if (remaining <= 0) {
+
+ // Not enough space, delete thread
+ if (thread->state & ThreadDead) {
+ unlock_thread_rw(thread);
+ --inspected;
+ continue;
+ }
+
+ // Signalize thread to stop
+ thread->state = ThreadDead | ThreadCancelled;
+ dt_signalize(thread, SIGALRM);
+ dbg_dt_verb("dthreads: [%p] dt_resize: "
+ "is discarded\n", thread);
+ }
+
+ // Unlock thread and continue
+ unlock_thread_rw(thread);
+ }
+
+ // Finished inspecting running threads
+ if (inspected == 0) {
+ break;
+ }
+
+ // Lower threshold
+ switch (threshold) {
+ case ThreadActive:
+ threshold = ThreadIdle;
+ break;
+ case ThreadIdle:
+ threshold = ThreadDead;
+ break;
+ default:
+ threshold = ThreadJoined;
+ break;
+ }
+ }
+
+ // Notify idle threads to wake up
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ // Join discarded threads
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Get thread
+ dthread_t *thread = unit->threads[i];
+ if (thread == 0) {
+ continue;
+ }
+
+ pthread_join(thread->_thr, 0);
+ thread->state = ThreadJoined;
+
+ // Delete thread
+ dt_delete_thread(&thread);
+ unit->threads[i] = 0;
+ }
+
+ // Reassign unit threads vector
+ unit->size = size;
+ free(unit->threads);
+ unit->threads = threads;
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ return 0;
+}
+
+int dt_start(dt_unit_t *unit)
+{
+ // Check input
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+ for (int i = 0; i < unit->size; ++i) {
+
+ dthread_t *thread = unit->threads[i];
+ int res = dt_start_id(thread);
+ if (res != 0) {
+ dbg_dt("dthreads: failed to create thread '%d'.", i);
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return res;
+ }
+
+ dbg_dt("dthreads: [%p] %s: thread started\n",
+ thread, __func__);
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return 0;
+}
+
+int dt_start_id(dthread_t *thread)
+{
+ // Check input
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ lock_thread_rw(thread);
+
+ // Update state
+ int prev_state = thread->state;
+ thread->state |= ThreadActive;
+ thread->state &= ~ThreadIdle;
+ thread->state &= ~ThreadDead;
+ thread->state &= ~ThreadJoined;
+ thread->state &= ~ThreadJoinable;
+
+ // Do not re-create running threads
+ if (prev_state != ThreadJoined) {
+ dbg_dt("dthreads: [%p] %s: refused to recreate thread\n",
+ thread, __func__);
+ unlock_thread_rw(thread);
+ return 0;
+ }
+
+ // Start thread
+ int res = pthread_create(&thread->_thr, /* pthread_t */
+ &thread->_attr, /* pthread_attr_t */
+ thread_ep, /* routine: thread_ep */
+ thread); /* passed object: dthread_t */
+
+ // Unlock thread
+ unlock_thread_rw(thread);
+ return res;
+}
+
+int dt_signalize(dthread_t *thread, int signum)
+{
+ // Check input
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = pthread_kill(thread->_thr, signum);
+
+ /* Not thread id found or invalid signum. */
+ if (ret == EINVAL || ret == ESRCH) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Generic error. */
+ if (ret < 0) {
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_join(dt_unit_t *unit)
+{
+ // Check input
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ for (;;) {
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_report_mx);
+ dt_unit_lock(unit);
+
+ // Browse threads
+ int active_threads = 0;
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Count active or cancelled but pending threads
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadActive|ThreadCancelled)) {
+ ++active_threads;
+ }
+
+ // Reclaim dead threads, but only fast
+ if (thread->state & ThreadJoinable) {
+ unlock_thread_rw(thread);
+ dbg_dt_verb("dthreads: [%p] %s: reclaiming\n",
+ thread, __func__);
+ pthread_join(thread->_thr, 0);
+ dbg_dt("dthreads: [%p] %s: reclaimed\n",
+ thread, __func__);
+ thread->state = ThreadJoined;
+ } else {
+ unlock_thread_rw(thread);
+ }
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ // Check result
+ if (active_threads == 0) {
+ pthread_mutex_unlock(&unit->_report_mx);
+ break;
+ }
+
+ // Wait for a thread to finish
+ pthread_cond_wait(&unit->_report, &unit->_report_mx);
+ pthread_mutex_unlock(&unit->_report_mx);
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_stop_id(dthread_t *thread)
+{
+ // Check input
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Signalize active thread to stop
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+ thread->state = ThreadDead | ThreadCancelled;
+ dt_signalize(thread, SIGALRM);
+ }
+ unlock_thread_rw(thread);
+
+ // Broadcast notification
+ dt_unit_t *unit = thread->unit;
+ if (unit != 0) {
+ pthread_mutex_lock(&unit->_notify_mx);
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_stop(dt_unit_t *unit)
+{
+ // Check unit
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Signalize all threads to stop
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Lock thread
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+ thread->state = ThreadDead | ThreadCancelled;
+ dbg_dt("dthreads: [%p] %s: stopping thread\n",
+ thread, __func__);
+ dt_signalize(thread, SIGALRM);
+ }
+ unlock_thread_rw(thread);
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ // Broadcast notification
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ return KNOTD_EOK;
+}
+
+int dt_setprio(dthread_t *thread, int prio)
+{
+ // Check input
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Clamp priority
+ int policy = SCHED_FIFO;
+ prio = MIN(MAX(sched_get_priority_min(policy), prio),
+ sched_get_priority_max(policy));
+
+ // Update scheduler policy
+ int ret = pthread_attr_setschedpolicy(&thread->_attr, policy);
+
+ // Update priority
+ if (ret >= 0) {
+ struct sched_param sp;
+ sp.sched_priority = prio;
+ ret = pthread_attr_setschedparam(&thread->_attr, &sp);
+ }
+
+ /* Map error codes. */
+ if (ret < 0) {
+ dbg_dt("dthreads: [%p] %s(%d): failed",
+ thread, __func__, prio);
+
+ /* Map "not supported". */
+ if (ret == ENOTSUP) {
+ return KNOTD_ENOTSUP;
+ }
+
+ return KNOTD_EINVAL;
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_repurpose(dthread_t *thread, runnable_t runnable, void *data)
+{
+ // Check
+ if (thread == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Stop here if thread isn't a member of a unit
+ dt_unit_t *unit = thread->unit;
+ if (unit == 0) {
+ lock_thread_rw(thread);
+ thread->state = ThreadActive | ThreadCancelled;
+ unlock_thread_rw(thread);
+ return KNOTD_ENOTSUP;
+ }
+
+ // Lock thread state changes
+ pthread_mutex_lock(&unit->_notify_mx);
+ lock_thread_rw(thread);
+
+ // Repurpose it's object and runnable
+ thread->run = runnable;
+ thread->_adata = data;
+
+ // Cancel current runnable if running
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+
+ // Update state
+ thread->state = ThreadActive | ThreadCancelled;
+ unlock_thread_rw(thread);
+
+ // Notify thread
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ } else {
+ unlock_thread_rw(thread);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_activate(dthread_t *thread)
+{
+ return dt_update_thread(thread, ThreadActive);
+}
+
+int dt_cancel(dthread_t *thread)
+{
+ return dt_update_thread(thread, ThreadIdle | ThreadCancelled);
+}
+
+int dt_compact(dt_unit_t *unit)
+{
+ // Check input
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Reclaim all Idle threads
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Locked state update
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle)) {
+ thread->state = ThreadDead | ThreadCancelled;
+ dt_signalize(thread, SIGALRM);
+ }
+ unlock_thread_rw(thread);
+ }
+
+ // Notify all threads
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ // Join all threads
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Reclaim all dead threads
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadDead)) {
+ dbg_dt_verb("dthreads: [%p] %s: reclaiming thread\n",
+ thread, __func__);
+ unlock_thread_rw(thread);
+ pthread_join(thread->_thr, 0);
+ dbg_dt("dthreads: [%p] %s: thread reclaimed\n",
+ thread, __func__);
+ thread->state = ThreadJoined;
+ } else {
+ unlock_thread_rw(thread);
+ }
+ }
+
+ dbg_dt_verb("dthreads: compact: joined all threads\n");
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ return KNOTD_EOK;
+}
+
+int dt_optimal_size()
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ int ret = (int) sysconf(_SC_NPROCESSORS_ONLN);
+ if (ret >= 1) {
+ return ret + CPU_ESTIMATE_MAGIC;
+ }
+#endif
+ dbg_dt("dthreads: failed to fetch the number of online CPUs.");
+ return DEFAULT_THR_COUNT;
+}
+
+/*!
+ * \todo Use memory barriers or asynchronous read-only access, locking
+ * poses a thread performance decrease by 1.31%.
+ */
+
+int dt_is_cancelled(dthread_t *thread)
+{
+ // Check input
+ if (thread == 0) {
+ return 0;
+ }
+
+ lock_thread_rw(thread);
+ int ret = thread->state & ThreadCancelled;
+ unlock_thread_rw(thread);
+ return ret;
+}
+
+int dt_unit_lock(dt_unit_t *unit)
+{
+ // Check input
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = pthread_mutex_lock(&unit->_mx);
+
+ /* Map errors. */
+ if (ret < 0) {
+ return knot_map_errno(EINVAL, EAGAIN);
+ }
+
+ return KNOTD_EOK;
+}
+
+int dt_unit_unlock(dt_unit_t *unit)
+{
+ // Check input
+ if (unit == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = pthread_mutex_unlock(&unit->_mx);
+
+ /* Map errors. */
+ if (ret < 0) {
+ return knot_map_errno(EINVAL, EAGAIN);
+ }
+
+ return KNOTD_EOK;
+}
diff --git a/src/knot/server/dthreads.h b/src/knot/server/dthreads.h
new file mode 100644
index 0000000..8a5e2b4
--- /dev/null
+++ b/src/knot/server/dthreads.h
@@ -0,0 +1,353 @@
+/* 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 dthreads.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief Threading API.
+ *
+ * Dynamic threads provide:
+ * - coherent and incoherent threading capabilities
+ * - thread repurposing
+ * - thread prioritization
+ * - on-the-fly changing of threading unit size
+ *
+ * Coherent threading unit is when all threads execute
+ * the same runnable function.
+ *
+ * Incoherent function is when at least one thread executes
+ * a different runnable than the others.
+ *
+ * \addtogroup threading
+ * @{
+ */
+
+#ifndef _KNOTD_DTHREADS_H_
+#define _KNOTD_DTHREADS_H_
+
+#include <pthread.h>
+
+/* Forward decls */
+struct dthread_t;
+struct dt_unit_t;
+
+/*!
+ * \brief Thread state enumeration.
+ */
+typedef enum {
+ ThreadJoined = 1 << 0, /*!< Thread is finished and joined. */
+ ThreadJoinable = 1 << 1, /*!< Thread is waiting to be reclaimed. */
+ ThreadCancelled = 1 << 2, /*!< Thread is cancelled, finishing task. */
+ ThreadDead = 1 << 3, /*!< Thread is finished, exiting. */
+ ThreadIdle = 1 << 4, /*!< Thread is idle, waiting for purpose. */
+ ThreadActive = 1 << 5 /*!< Thread is active, working on a task. */
+
+} dt_state_t;
+
+/*!
+ * \brief Thread runnable prototype.
+ *
+ * Runnable is basically a pointer to function which is called on active
+ * thread runtime.
+ *
+ * \note When implementing a runnable, keep in mind to check thread state as
+ * it may change, and implement a cooperative cancellation point.
+ *
+ * Implement this by checking dt_is_cancelled() and return
+ * as soon as possible.
+ */
+typedef int (*runnable_t)(struct dthread_t *);
+
+/*!
+ * \brief Single thread descriptor public API.
+ */
+typedef struct dthread_t {
+ volatile unsigned state; /*!< Bitfield of dt_flag flags. */
+ runnable_t run; /*!< Runnable function or 0. */
+ void *data; /*!< Currently active data */
+ struct dt_unit_t *unit; /*!< Reference to assigned unit. */
+ void *_adata; /* Thread-specific data. */
+ pthread_t _thr; /* Thread */
+ pthread_attr_t _attr; /* Thread attributes */
+ pthread_mutex_t _mx; /* Thread state change lock. */
+} dthread_t;
+
+/*!
+ * \brief Thread unit descriptor API.
+ *
+ * Thread unit consists of 1..N threads.
+ * Unit is coherent if all threads execute
+ * the same runnable.
+ */
+typedef struct dt_unit_t {
+ int size; /*!< Unit width (number of threads) */
+ struct dthread_t **threads; /*!< Array of threads */
+ pthread_cond_t _notify; /* Notify thread */
+ pthread_mutex_t _notify_mx; /* Condition mutex */
+ pthread_cond_t _report; /* Report thread state */
+ pthread_mutex_t _report_mx; /* Condition mutex */
+ pthread_mutex_t _mx; /* Unit lock */
+} dt_unit_t;
+
+/*!
+ * \brief Create a set of threads with no initial runnable.
+ *
+ * \note All threads are created with Dead state.
+ * This means, they're not physically created unit dt_start() is called.
+ *
+ * \param count Requested thread count.
+ *
+ * \retval New instance if successful
+ * \retval NULL on error
+ */
+dt_unit_t *dt_create(int count);
+
+/*!
+ * \brief Create a set of coherent threads.
+ *
+ * Coherent means, that the threads will share a common runnable and the data.
+ *
+ * \param count Requested thread count.
+ * \param runnable Runnable function for all threads.
+ * \param data Any data passed onto threads.
+ *
+ * \retval New instance if successful
+ * \retval NULL on error
+ */
+dt_unit_t *dt_create_coherent(int count, runnable_t runnable, void *data);
+
+/*!
+ * \brief Free unit.
+ *
+ * \warning Behavior is undefined if threads are still active, make sure
+ * to call dt_join() first.
+ *
+ * \param unit Unit to be deleted.
+ */
+void dt_delete(dt_unit_t **unit);
+
+/*!
+ * \brief Resize unit to given number.
+ *
+ * \note Newly created dthreads will have no runnable or data, their state
+ * will be ThreadJoined (that means no thread will be physically created
+ * until the next dt_start()).
+ *
+ * \warning Be careful when shrinking unit, joined and idle threads are
+ * reclaimed first, but it may kill your active threads
+ * as a last resort.
+ * Threads will stop at their nearest cancellation point,
+ * so this is potentially an expensive and blocking operation.
+ *
+ * \param unit Unit to be resized.
+ * \param size New unit size.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOMEM out of memory error.
+ */
+int dt_resize(dt_unit_t *unit, int size);
+
+/*!
+ * \brief Start all threads in selected unit.
+ *
+ * \param unit Unit to be started.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters (unit is null).
+ */
+int dt_start(dt_unit_t *unit);
+
+/*!
+ * \brief Start given thread.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_start_id(dthread_t *thread);
+
+/*!
+ * \brief Send given signal to thread.
+ *
+ * \note This is useful to interrupt some blocking I/O as well, for example
+ * with SIGALRM, which is handled by default.
+ * \note Signal handler may be overriden in runnable.
+ *
+ * \param thread Target thread instance.
+ * \param signum Signal code.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int dt_signalize(dthread_t *thread, int signum);
+
+/*!
+ * \brief Wait for all thread in unit to finish.
+ *
+ * \param unit Unit to be joined.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_join(dt_unit_t *unit);
+
+/*!
+ * \brief Stop thread from running.
+ *
+ * Active thread is interrupted at the nearest runnable cancellation point.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_stop_id(dthread_t *thread);
+
+/*!
+ * \brief Stop all threads in unit.
+ *
+ * Thread is interrupted at the nearest runnable cancellation point.
+ *
+ * \param unit Unit to be stopped.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_stop(dt_unit_t *unit);
+
+/*!
+ * \brief Modify thread priority.
+ *
+ * \param thread Target thread instance.
+ * \param prio Requested priority (positive integer, default is 0).
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_setprio(dthread_t *thread, int prio);
+
+/*!
+ * \brief Set thread to execute another runnable.
+ *
+ * \param thread Target thread instance.
+ * \param runnable Runnable function for target thread.
+ * \param data Data passed to target thread.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOTSUP operation not supported.
+ */
+int dt_repurpose(dthread_t *thread, runnable_t runnable, void *data);
+
+/*!
+ * \brief Wake up thread from idle state.
+ *
+ * Thread is awoken from idle state and reenters runnable.
+ * This function only affects idle threads.
+ *
+ * \note Unit needs to be started with dt_start() first, as the function
+ * doesn't affect dead threads.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOTSUP operation not supported.
+ */
+int dt_activate(dthread_t *thread);
+
+/*!
+ * \brief Put thread to idle state, cancells current runnable function.
+ *
+ * Thread is flagged with Cancel flag and returns from runnable at the nearest
+ * cancellation point, which requires complying runnable function.
+ *
+ * \note Thread isn't disposed, but put to idle state until it's requested
+ * again or collected by dt_compact().
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_cancel(dthread_t *thread);
+
+/*!
+ * \brief Collect and dispose idle threads.
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int dt_compact(dt_unit_t *unit);
+
+/*!
+ * \brief Return optimal number of threads for instance.
+ *
+ * It is estimated as NUM_CPUs + 1.
+ * Fallback is DEFAULT_THR_COUNT (\see common.h).
+ *
+ * \return Number of threads.
+ */
+int dt_optimal_size();
+
+/*!
+ * \brief Return true if thread is cancelled.
+ *
+ * Synchronously check for ThreadCancelled flag.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval 1 if cancelled.
+ * \retval 0 if not cancelled.
+ */
+int dt_is_cancelled(dthread_t *thread);
+
+/*!
+ * \brief Lock unit to prevent parallel operations which could alter unit
+ * at the same time.
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_EAGAIN lack of resources to lock unit, try again.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int dt_unit_lock(dt_unit_t *unit);
+
+/*!
+ * \brief Unlock unit.
+ *
+ * \see dt_unit_lock()
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_EAGAIN lack of resources to unlock unit, try again.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int dt_unit_unlock(dt_unit_t *unit);
+
+#endif // _KNOTD_DTHREADS_H_
+
+/*! @} */
diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c
new file mode 100644
index 0000000..651f0f3
--- /dev/null
+++ b/src/knot/server/journal.c
@@ -0,0 +1,636 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "knot/other/error.h"
+#include "knot/other/debug.h"
+#include "journal.h"
+
+/*! \brief Infinite file size limit. */
+#define FSLIMIT_INF (~((size_t)0))
+
+/*! \brief Node classification macros. */
+#define jnode_flags(j, i) ((j)->nodes[(i)].flags)
+
+/*! \brief Next node. */
+#define jnode_next(j, i) (((i) + 1) % (j)->max_nodes)
+
+/*! \brief Previous node. */
+#define jnode_prev(j, i) (((i) == 0) ? (j)->max_nodes - 1 : (i) - 1)
+
+static inline int sfread(void *dst, size_t len, int fd)
+{
+ return read(fd, dst, len) == len;
+}
+
+static inline int sfwrite(const void *src, size_t len, int fd)
+{
+ return write(fd, src, len) == len;
+}
+
+/*! \brief Equality compare function. */
+static inline int journal_cmp_eq(uint64_t k1, uint64_t k2)
+{
+ if (k1 == k2) {
+ return 0;
+ }
+
+ if (k1 < k2) {
+ return -1;
+ }
+
+ return 1;
+}
+
+/*! \brief Recover metadata from journal. */
+static int journal_recover(journal_t *j)
+{
+ /* Attempt to recover queue. */
+ int qstate[2] = { -1, -1 };
+ unsigned c = 0, p = j->max_nodes - 1;
+ while (1) {
+
+ /* Fetch previous and current node. */
+ journal_node_t *np = j->nodes + p;
+ journal_node_t *nc = j->nodes + c;
+
+ /* Check flags
+ * p c (0 = free, 1 = non-free)
+ * 0 0 - in free segment
+ * 0 1 - c-node is qhead
+ * 1 0 - c-node is qtail
+ * 1 1 - in full segment
+ */
+ unsigned c_set = (nc->flags > JOURNAL_FREE);
+ unsigned p_set = (np->flags > JOURNAL_FREE);
+ if (!p_set && c_set && qstate[0] < 0) {
+ qstate[0] = c; /* Recovered qhead. */
+ dbg_journal_verb("journal: recovered qhead=%u\n",
+ qstate[0]);
+ }
+ if (p_set && !c_set && qstate[1] < 0) {\
+ qstate[1] = c; /* Recovered qtail. */
+ dbg_journal_verb("journal: recovered qtail=%u\n",
+ qstate[1]);
+ }
+
+ /* Both qstates set. */
+ if (qstate[0] > -1 && qstate[1] > -1) {
+ break;
+ }
+
+ /* Set prev and next. */
+ p = c;
+ c = (c + 1) % j->max_nodes;
+
+ /* All nodes probed. */
+ if (c == 0) {
+ dbg_journal("journal: failed to recover node queue\n");
+ break;
+ }
+ }
+
+ /* Evaluate */
+ if (qstate[0] < 0 || qstate[1] < 0) {
+ return KNOTD_ERANGE;
+ }
+
+ /* Write back. */
+ lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (!sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) {
+ dbg_journal("journal: failed to write back queue state\n");
+ return KNOTD_ERROR;
+ }
+
+ /* Reset queue state. */
+ j->qhead = qstate[0];
+ j->qtail = qstate[1];
+ dbg_journal("journal: node queue=<%u,%u> recovered\n",
+ qstate[0], qstate[1]);
+
+
+ return KNOTD_EOK;
+}
+
+int journal_create(const char *fn, uint16_t max_nodes)
+{
+ /* File lock. */
+ struct flock fl;
+ memset(&fl, 0, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = getpid();
+
+ /* Create journal file. */
+ int fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG);
+ if (fd < 0) {
+ dbg_journal("journal: failed to create file '%s'\n", fn);
+ return KNOTD_EINVAL;
+ }
+
+ /* Lock. */
+ fcntl(fd, F_SETLKW, &fl);
+ fl.l_type = F_UNLCK;
+
+ /* Create journal header. */
+ dbg_journal("journal: creating header\n");
+ if (!sfwrite(&max_nodes, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ remove(fn);
+ return KNOTD_ERROR;
+ }
+
+ /* Create node queue head + tail.
+ * qhead points to least recent node
+ * qtail points to next free node
+ * qhead == qtail means empty queue
+ */
+ uint16_t zval = 0;
+ if (!sfwrite(&zval, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ remove(fn);
+ return KNOTD_ERROR;
+ }
+
+ if (!sfwrite(&zval, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ remove(fn);
+ return KNOTD_ERROR;
+ }
+
+ dbg_journal_verb("journal: creating free segment descriptor\n");
+
+ /* Create free segment descriptor. */
+ journal_node_t jn;
+ memset(&jn, 0, sizeof(journal_node_t));
+ jn.id = 0;
+ jn.flags = JOURNAL_VALID;
+ jn.pos = JOURNAL_HSIZE + (max_nodes + 1) * sizeof(journal_node_t);
+ jn.len = 0;
+ if (!sfwrite(&jn, sizeof(journal_node_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ remove(fn);
+ return KNOTD_ERROR;
+ }
+
+ /* Create nodes. */
+ dbg_journal("journal: creating node table, size=%u\n", max_nodes);
+ memset(&jn, 0, sizeof(journal_node_t));
+ for(uint16_t i = 0; i < max_nodes; ++i) {
+ if (!sfwrite(&jn, sizeof(journal_node_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ remove(fn);
+ return KNOTD_ERROR;
+ }
+ }
+
+ /* Unlock and close. */
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+
+ /* Journal file created. */
+ dbg_journal("journal: file '%s' initialized\n", fn);
+ return KNOTD_EOK;
+}
+
+journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags)
+{
+ /*! \todo Memory mapping may be faster than stdio? */
+
+ /* File lock. */
+ struct flock fl;
+ memset(&fl, 0, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = getpid();
+
+ /* Check file. */
+ struct stat st;
+ if (stat(fn, &st) < 0) {
+ return 0;
+ }
+
+ /* Open journal file for r/w. */
+ int fd = open(fn, O_RDWR);
+ if (fd < 0) {
+ dbg_journal("journal: failed to open file '%s'\n", fn);
+ return 0;
+ }
+
+ /* Attempt to lock. */
+ dbg_journal_verb("journal: locking journal %s\n", fn);
+ int ret = fcntl(fd, F_SETLK, &fl);
+
+ /* Lock. */
+ if (ret < 0) {
+ struct flock efl;
+ memcpy(&efl, &fl, sizeof(struct flock));
+ fcntl(fd, F_GETLK, &efl);
+ log_server_warning("Journal file '%s' is locked by process "
+ "PID=%d, waiting for process to "
+ "release lock.\n",
+ fn, efl.l_pid);
+ ret = fcntl(fd, F_SETLKW, &fl);
+ }
+ fl.l_type = F_UNLCK;
+ dbg_journal("journal: locked journal %s (returned %d)\n", fn, ret);
+
+ /* Read maximum number of entries. */
+ uint16_t max_nodes = 512;
+ if (!sfread(&max_nodes, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ return 0;
+ }
+
+ /* Allocate journal structure. */
+ const size_t node_len = sizeof(journal_node_t);
+ journal_t *j = malloc(sizeof(journal_t) + max_nodes * node_len);
+ if (!j) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ return 0;
+ }
+ j->qhead = j->qtail = 0;
+ j->fd = fd;
+ j->max_nodes = max_nodes;
+ j->bflags = bflags;
+
+ /* Load node queue state. */
+ if (!sfread(&j->qhead, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return 0;
+ }
+
+ /* Load queue tail. */
+ if (!sfread(&j->qtail, sizeof(uint16_t), fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return 0;
+ }
+
+ /* Load empty segment descriptor. */
+ if (!sfread(&j->free, node_len, fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return 0;
+ }
+
+ /* Read journal descriptors table. */
+ if (!sfread(&j->nodes, max_nodes * node_len, fd)) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return 0;
+ }
+
+ /* Set file size. */
+ j->fsize = st.st_size;
+ if (fslimit == 0) {
+ j->fslimit = FSLIMIT_INF;
+ } else {
+ j->fslimit = (size_t)fslimit;
+ }
+
+ dbg_journal("journal: opened journal size=%u, queue=<%u, %u>, fd=%d\n",
+ max_nodes, j->qhead, j->qtail, j->fd);
+
+ /* Check node queue. */
+ unsigned qtail_free = (jnode_flags(j, j->qtail) <= JOURNAL_FREE);
+ unsigned qhead_free = j->max_nodes - 1; /* Left of qhead must be free.*/
+ if (j->qhead > 0) {
+ qhead_free = (j->qhead - 1);
+ }
+ qhead_free = (jnode_flags(j, qhead_free) <= JOURNAL_FREE);
+ if ((j->qhead != j->qtail) && (!qtail_free || !qhead_free)) {
+ log_server_warning("Recovering journal '%s' metadata "
+ "after crash.\n",
+ fn);
+ ret = journal_recover(j);
+ if (ret != KNOTD_EOK) {
+ log_server_error("Journal file '%s' is unrecoverable, "
+ "metadata corrupted - %s\n",
+ fn, knotd_strerror(ret));
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return 0;
+ }
+ }
+
+ /* Save file lock. */
+ fl.l_type = F_WRLCK;
+ memcpy(&j->fl, &fl, sizeof(struct flock));
+
+ return j;
+}
+
+int journal_fetch(journal_t *journal, uint64_t id,
+ journal_cmp_t cf, journal_node_t** dst)
+{
+ if (journal == 0 || dst == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Check compare function. */
+ if (!cf) {
+ cf = journal_cmp_eq;
+ }
+
+ /*! \todo Organize journal descriptors in btree? */
+ size_t i = jnode_prev(journal, journal->qtail);
+ size_t endp = jnode_prev(journal, journal->qhead);
+ for(; i != endp; i = jnode_prev(journal, i)) {
+ if (cf(journal->nodes[i].id, id) == 0) {
+ *dst = journal->nodes + i;
+ return KNOTD_EOK;
+ }
+ }
+
+ return KNOTD_ENOENT;
+}
+
+int journal_read(journal_t *journal, uint64_t id, journal_cmp_t cf, char *dst)
+{
+ if (journal == 0 || dst == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ journal_node_t *n = 0;
+ if(journal_fetch(journal, id, cf, &n) != 0) {
+ dbg_journal("journal: failed to fetch node with id=%llu\n",
+ (unsigned long long)id);
+ return KNOTD_ENOENT;
+ }
+
+ /* Check valid flag. */
+ if (!(n->flags & JOURNAL_VALID)) {
+ dbg_journal("journal: node with id=%llu is invalid "
+ "(flags=0x%hx)\n", (unsigned long long)id, n->flags);
+ return KNOTD_EINVAL;
+ }
+
+ dbg_journal("journal: reading node with id=%llu, data=<%u, %u>, flags=0x%hx\n",
+ (unsigned long long)id, n->pos, n->pos + n->len, n->flags);
+
+ /* Seek journal node. */
+ lseek(journal->fd, n->pos, SEEK_SET);
+
+ /* Read journal node content. */
+ if (!sfread(dst, n->len, journal->fd)) {
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+
+int journal_write(journal_t *journal, uint64_t id, const char *src, size_t size)
+{
+ if (journal == 0 || src == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ const size_t node_len = sizeof(journal_node_t);
+
+ /* Find next free node. */
+ uint16_t jnext = (journal->qtail + 1) % journal->max_nodes;
+
+ dbg_journal("journal: will write id=%llu, node=%u, size=%zu, fsize=%zu\n",
+ (unsigned long long)id, journal->qtail, size, journal->fsize);
+
+ /* Calculate remaining bytes to reach file size limit. */
+ size_t fs_remaining = journal->fslimit - journal->fsize;
+
+ /* Increase free segment if on the end of file. */
+ journal_node_t *n = journal->nodes + journal->qtail;
+ if (journal->free.pos + journal->free.len == journal->fsize) {
+
+ dbg_journal_verb("journal: * is last node\n");
+
+ /* Grow journal file until the size limit. */
+ if(journal->free.len < size && size <= fs_remaining) {
+ size_t diff = size - journal->free.len;
+ dbg_journal("journal: * growing by +%zu, pos=%u, "
+ "new fsize=%zu\n",
+ diff, journal->free.pos,
+ journal->fsize + diff);
+ journal->fsize += diff; /* Appending increases file size. */
+ journal->free.len += diff;
+
+ }
+
+ /* Rewind if resize is needed, but the limit is reached. */
+ if(journal->free.len < size && size > fs_remaining) {
+ journal_node_t *head = journal->nodes + journal->qhead;
+ journal->fsize = journal->free.pos;
+ journal->free.pos = head->pos;
+ journal->free.len = 0;
+ dbg_journal_verb("journal: * fslimit reached, "
+ "rewinding to %u\n",
+ head->pos);
+ dbg_journal_verb("journal: * file size trimmed to %zu\n",
+ journal->fsize);
+ }
+ }
+
+ /* Evict occupied nodes if necessary. */
+ while (journal->free.len < size ||
+ journal->nodes[jnext].flags > JOURNAL_FREE) {
+
+ /* Evict least recent node if not empty. */
+ journal_node_t *head = journal->nodes + journal->qhead;
+
+ /* Check if it has been synced to disk. */
+ if (head->flags & JOURNAL_DIRTY) {
+ return KNOTD_EAGAIN;
+ }
+
+ /* Write back evicted node. */
+ head->flags = JOURNAL_FREE;
+ lseek(journal->fd, JOURNAL_HSIZE + (journal->qhead + 1) * node_len, SEEK_SET);
+ if (!sfwrite(head, node_len, journal->fd)) {
+ return KNOTD_ERROR;
+ }
+
+ dbg_journal("journal: * evicted node=%u, growing by +%u\n",
+ journal->qhead, head->len);
+
+ /* Write back query state. */
+ journal->qhead = (journal->qhead + 1) % journal->max_nodes;
+ uint16_t qstate[2] = {journal->qhead, journal->qtail};
+ lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (!sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) {
+ return KNOTD_ERROR;
+ }
+
+ /* Increase free segment. */
+ journal->free.len += head->len;
+ }
+
+ /* Invalidate node and write back. */
+ n->id = id;
+ n->pos = journal->free.pos;
+ n->len = size;
+ n->flags = JOURNAL_FREE;
+ journal_update(journal, n);
+
+ /* Write data to permanent storage. */
+ lseek(journal->fd, n->pos, SEEK_SET);
+ if (!sfwrite(src, size, journal->fd)) {
+ return KNOTD_ERROR;
+ }
+
+ /* Mark node as valid and write back. */
+ n->flags = JOURNAL_VALID | journal->bflags;
+ journal_update(journal, n);
+
+ /* Handle free segment on node rotation. */
+ if (journal->qtail > jnext && journal->fslimit == FSLIMIT_INF) {
+ /* Trim free space. */
+ journal->fsize -= journal->free.len;
+ dbg_journal_verb("journal: * trimmed filesize to %zu\n",
+ journal->fsize);
+
+ /* Rewind free segment. */
+ journal_node_t *n = journal->nodes + jnext;
+ journal->free.pos = n->pos;
+ journal->free.len = 0;
+
+ } else {
+ /* Mark used space. */
+ journal->free.pos += size;
+ journal->free.len -= size;
+ }
+ dbg_journal("journal: finished node=%u, data=<%u, %u> free=<%u, %u>\n",
+ journal->qtail, n->pos, n->pos + n->len,
+ journal->free.pos,
+ journal->free.pos + journal->free.len);
+
+ /* Write back free segment state. */
+ lseek(journal->fd, JOURNAL_HSIZE, SEEK_SET);
+ if (!sfwrite(&journal->free, node_len, journal->fd)) {
+ /* Node is marked valid and failed to shrink free space,
+ * node will be overwritten on the next write. Return error.
+ */
+ dbg_journal("journal: failed to write back "
+ "free segment descriptor\n");
+ return KNOTD_ERROR;
+ }
+
+ /* Node write successful. */
+ journal->qtail = jnext;
+
+ /* Write back queue state, not essential as it may be recovered.
+ * qhead - lowest valid node identifier (least recent)
+ * qtail - highest valid node identifier (most recently used)
+ */
+ uint16_t qstate[2] = {journal->qhead, journal->qtail};
+ lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (!sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) {
+ dbg_journal("journal: failed to write back queue state\n");
+ return KNOTD_ERROR;
+ }
+
+ /*! \todo Delayed write-back? */
+ dbg_journal_verb("journal: write of finished, nqueue=<%u, %u>\n",
+ journal->qhead, journal->qtail);
+
+ return KNOTD_EOK;
+}
+
+int journal_walk(journal_t *journal, journal_apply_t apply)
+{
+ int ret = KNOTD_EOK;
+ size_t i = journal->qhead;
+ for(; i != journal->qtail; i = (i + 1) % journal->max_nodes) {
+ /* Apply function. */
+ ret = apply(journal, journal->nodes + i);
+ }
+
+ return ret;
+}
+
+int journal_update(journal_t *journal, journal_node_t *n)
+{
+ if (!journal || !n) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Calculate node offset. */
+ const size_t node_len = sizeof(journal_node_t);
+ size_t i = n - journal->nodes;
+ if (i > journal->max_nodes) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Calculate node position in permanent storage. */
+ long jn_fpos = JOURNAL_HSIZE + (i + 1) * node_len;
+
+ dbg_journal("journal: syncing journal node=%zu at %ld\n",
+ i, jn_fpos);
+
+ /* Write back. */
+ lseek(journal->fd, jn_fpos, SEEK_SET);
+ if (!sfwrite(n, node_len, journal->fd)) {
+ dbg_journal("journal: failed to writeback node=%llu to %ld\n",
+ (unsigned long long)n->id, jn_fpos);
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+
+int journal_close(journal_t *journal)
+{
+ /* Check journal. */
+ if (!journal) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Unlock journal file. */
+ journal->fl.l_type = F_UNLCK;
+ fcntl(journal->fd, F_SETLK, &journal->fl);
+ dbg_journal("journal: unlocked journal %p\n", journal);
+
+ /* Close file. */
+ close(journal->fd);
+
+ dbg_journal("journal: closed journal %p\n", journal);
+
+ /* Free allocated resources. */
+ free(journal);
+
+ return KNOTD_EOK;
+}
diff --git a/src/knot/server/journal.h b/src/knot/server/journal.h
new file mode 100644
index 0000000..321b591
--- /dev/null
+++ b/src/knot/server/journal.h
@@ -0,0 +1,243 @@
+/* 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 journal.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Journal for storing transactions on permanent storage.
+ *
+ * Journal stores entries on a permanent storage.
+ * Each written entry is guaranteed to persist until
+ * the maximum file size or node count is reached.
+ * Entries are removed from the least recent.
+ *
+ * Journal file structure
+ * <pre>
+ * uint16_t node_count
+ * uint16_t node_queue_head
+ * uint16_t node_queue_tail
+ * journal_entry_t free_segment
+ * node_count *journal_entry_t
+ * ...data...
+ * </pre>
+ * \addtogroup utils
+ * @{
+ */
+
+#ifndef _KNOTD_JOURNAL_H_
+#define _KNOTD_JOURNAL_H_
+
+#include <stdint.h>
+#include <fcntl.h>
+
+/*!
+ * \brief Journal entry flags.
+ */
+typedef enum journal_flag_t {
+ JOURNAL_NULL = 0 << 0, /*!< Invalid journal entry. */
+ JOURNAL_FREE = 1 << 0, /*!< Free journal entry. */
+ JOURNAL_VALID = 1 << 1, /*!< Valid journal entry. */
+ JOURNAL_DIRTY = 1 << 2 /*!< Journal entry cannot be evicted. */
+} journal_flag_t;
+
+/*!
+ * \brief Journal node structure.
+ *
+ * Each node represents journal entry and points
+ * to position of the data in the permanent storage.
+ */
+typedef struct journal_node_t
+{
+ uint64_t id; /*!< Node ID. */
+ uint16_t flags; /*!< Node flags. */
+ uint32_t pos; /*!< Position in journal file. */
+ uint32_t len; /*!< Entry data length. */
+} journal_node_t;
+
+/*!
+ * \brief Journal structure.
+ *
+ * Journal organizes entries as nodes.
+ * Nodes are stored in-memory for fast lookup and also
+ * backed by a permanent storage.
+ * Each journal has a fixed number of nodes.
+ *
+ * \todo Organize nodes in an advanced structure, like
+ * btree or hash table to improve lookup time.
+ */
+typedef struct journal_t
+{
+ int fd;
+ struct flock fl; /*!< File lock. */
+ uint16_t max_nodes; /*!< Number of nodes. */
+ uint16_t qhead; /*!< Node queue head. */
+ uint16_t qtail; /*!< Node queue tail. */
+ uint16_t bflags; /*!< Initial flags for each written node. */
+ size_t fsize; /*!< Journal file size. */
+ size_t fslimit; /*!< File size limit. */
+ journal_node_t free; /*!< Free segment. */
+ journal_node_t nodes[]; /*!< Array of nodes. */
+} journal_t;
+
+/*!
+ * \brief Entry identifier compare function.
+ *
+ * \retval -n if k1 < k2
+ * \retval +n if k1 > k2
+ * \retval 0 if k1 == k2
+ */
+typedef int (*journal_cmp_t)(uint64_t k1, uint64_t k2);
+
+/*!
+ * \brief Function prototype for journal_walk() function.
+ *
+ * \param j Associated journal.
+ * \param n Pointer to target node.
+ */
+typedef int (*journal_apply_t)(journal_t *j, journal_node_t *n);
+
+/*
+ * Journal defaults and constants.
+ */
+#define JOURNAL_NCOUNT 1024 /*!< Default node count. */
+#define JOURNAL_HSIZE (sizeof(uint16_t) * 3) /*!< max_entries, qhead, qtail */
+
+/*!
+ * \brief Create new journal.
+ *
+ * \param fn Journal file name, will be created if not exist.
+ * \param max_nodes Maximum number of nodes in journal.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_EINVAL if the file with given name cannot be created.
+ * \retval KNOTD_ERROR on I/O error.
+ */
+int journal_create(const char *fn, uint16_t max_nodes);
+
+/*!
+ * \brief Open journal file for read/write.
+ *
+ * \param fn Journal file name.
+ * \param fslimit File size limit (0 for no limit).
+ * \param bflags Initial flags for each written node.
+ *
+ * \retval new journal instance if successful.
+ * \retval NULL on error.
+ */
+journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags);
+
+/*!
+ * \brief Fetch entry node for given identifier.
+ *
+ * \param journal Associated journal.
+ * \param id Entry identifier.
+ * \param cf Compare function (NULL for equality).
+ * \param dst Destination for journal entry.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_ENOENT if not found.
+ */
+int journal_fetch(journal_t *journal, uint64_t id,
+ journal_cmp_t cf, journal_node_t** dst);
+
+/*!
+ * \brief Read journal entry data.
+ *
+ * \param journal Associated journal.
+ * \param id Entry identifier.
+ * \param cf Compare function (NULL for equality).
+ * \param dst Pointer to destination memory.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_ENOENT if the entry cannot be found.
+ * \retval KNOTD_EINVAL if the entry is invalid.
+ * \retval KNOTD_ERROR on I/O error.
+ */
+int journal_read(journal_t *journal, uint64_t id, journal_cmp_t cf, char *dst);
+
+/*!
+ * \brief Write journal entry data.
+ *
+ * \param journal Associated journal.
+ * \param id Entry identifier.
+ * \param src Pointer to source data.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_EAGAIN if no free node is available, need to remove dirty nodes.
+ * \retval KNOTD_ERROR on I/O error.
+ */
+int journal_write(journal_t *journal, uint64_t id, const char *src, size_t size);
+
+/*!
+ * \brief Return least recent node (journal head).
+ *
+ * \param journal Associated journal.
+ *
+ * \retval node if successful.
+ * \retval NULL if empty.
+ */
+static inline journal_node_t *journal_head(journal_t *journal) {
+ return journal->nodes + journal->qhead;
+}
+
+/*!
+ * \brief Return node after most recent node (journal tail).
+ *
+ * \param journal Associated journal.
+ *
+ * \retval node if successful.
+ * \retval NULL if empty.
+ */
+static inline journal_node_t *journal_end(journal_t *journal) {
+ return journal->nodes + journal->qtail;
+}
+
+/*!
+ * \brief Apply function to each node.
+ *
+ * \param journal Associated journal.
+ * \param apply Function to apply to each node.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int journal_walk(journal_t *journal, journal_apply_t apply);
+
+/*!
+ * \brief Sync node state to permanent storage.
+ *
+ * \note May be used for journal_walk().
+ *
+ * \param journal Associated journal.
+ * \param n Pointer to node (must belong to associated journal).
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int journal_update(journal_t *journal, journal_node_t *n);
+
+/*!
+ * \brief Close journal file.
+ *
+ * \param journal Associated journal.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameter.
+ */
+int journal_close(journal_t *journal);
+
+#endif /* _KNOTD_JOURNAL_H_ */
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
new file mode 100644
index 0000000..3966b26
--- /dev/null
+++ b/src/knot/server/notify.c
@@ -0,0 +1,327 @@
+/* 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 "knot/server/notify.h"
+
+#include "libknot/dname.h"
+#include "libknot/packet/packet.h"
+#include "libknot/rrset.h"
+#include "libknot/packet/response.h"
+#include "libknot/packet/query.h"
+#include "libknot/consts.h"
+#include "knot/other/error.h"
+#include "libknot/zone/zonedb.h"
+#include "libknot/common.h"
+#include "libknot/util/error.h"
+#include "libknot/util/wire.h"
+#include "knot/server/zones.h"
+#include "common/acl.h"
+#include "common/evsched.h"
+#include "knot/other/debug.h"
+#include "knot/server/server.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+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);
+ CHECK_ALLOC_LOG(pkt, KNOTD_ENOMEM);
+
+ /*! \todo Get rid of the numeric constant. */
+ int rc = knot_packet_set_max_size(pkt, 512);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOTD_ERROR;
+ }
+
+ rc = knot_query_init(pkt);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOTD_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);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOTD_ERROR;
+ }
+
+ /* 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!! */
+// rc = knot_response_add_rrset_answer(pkt, rrset, 0, 0, 0);
+// if (rc != KNOT_EOK) {
+// knot_packet_free(&pkt);
+// return rc;
+// }
+
+ /*! \todo this should not use response API!! */
+ knot_response_set_aa(pkt);
+
+ knot_query_set_opcode(pkt, KNOT_OPCODE_NOTIFY);
+
+ /*! \todo OPT RR ?? */
+
+ uint8_t *wire = NULL;
+ size_t wire_size = 0;
+ rc = knot_packet_to_wire(pkt, &wire, &wire_size);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOTD_ERROR;
+ }
+
+ if (wire_size > *size) {
+ knot_packet_free(&pkt);
+ return KNOTD_ESPACE;
+ }
+
+ memcpy(buffer, wire, wire_size);
+ *size = wire_size;
+
+ knot_packet_dump(pkt);
+
+ knot_packet_free(&pkt);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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);
+ CHECK_ALLOC_LOG(response, KNOTD_ENOMEM);
+
+ /* Set maximum packet size. */
+ knot_packet_set_max_size(response, *size);
+ knot_response_init_from_query(response, request);
+
+ // TODO: copy the SOA in Answer section
+ uint8_t *wire = NULL;
+ size_t wire_size = 0;
+ int rc = knot_packet_to_wire(response, &wire, &wire_size);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&response);
+ return rc;
+ }
+
+ if (wire_size > *size) {
+ knot_packet_free(&response);
+ return KNOTD_ESPACE;
+ }
+
+ memcpy(buffer, wire, wire_size);
+ *size = wire_size;
+
+ knot_packet_dump(response);
+ knot_packet_free(&response);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int notify_create_request(const knot_zone_contents_t *zone, uint8_t *buffer,
+ size_t *size)
+{
+ const knot_rrset_t *soa_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
+ if (soa_rrset == NULL) {
+ return KNOTD_ERROR;
+ }
+
+ return notify_request(soa_rrset, buffer, size);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int notify_check_and_schedule(knot_nameserver_t *nameserver,
+ const knot_zone_t *zone,
+ sockaddr_t *from)
+{
+ if (zone == NULL || from == NULL || knot_zone_data(zone) == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Check ACL for notify-in. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (from) {
+ if (acl_match(zd->notify_in, from) == ACL_DENY) {
+ /* rfc1996: Ignore request and report incident. */
+ char straddr[SOCKADDR_STRLEN];
+ sockaddr_tostr(from, straddr, sizeof(straddr));
+ log_zone_notice("Unauthorized NOTIFY query "
+ "from %s:%d to zone '%s'.\n",
+ straddr, sockaddr_portnum(from),
+ zd->conf->name);
+ return KNOT_ERROR;
+ } else {
+ dbg_notify("notify: authorized NOTIFY query.\n");
+ }
+ }
+
+ /*! \todo Packet may contain updated RRs. */
+
+ /* Cancel REFRESH/RETRY timer. */
+ evsched_t *sched = ((server_t *)knot_ns_get_data(nameserver))->sched;
+ event_t *refresh_ev = zd->xfr_in.timer;
+ if (refresh_ev) {
+ dbg_notify("notify: expiring REFRESH timer\n");
+ evsched_cancel(sched, refresh_ev);
+
+ /* Set REFRESH timer for now. */
+ evsched_schedule(sched, refresh_ev, 0);
+ }
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int notify_process_request(knot_nameserver_t *ns,
+ knot_packet_t *notify,
+ sockaddr_t *from,
+ uint8_t *buffer, size_t *size)
+{
+ /*! \todo Most of this function is identical to xfrin_transfer_needed()
+ * - it will be fine to merge the code somehow.
+ */
+
+ if (notify == NULL || ns == NULL || buffer == NULL
+ || size == NULL || from == NULL) {
+ dbg_notify("notify: invalid parameters for %s()\n",
+ "notify_process_request");
+ return KNOTD_EINVAL;
+ }
+
+ int ret = KNOTD_EOK;
+
+ dbg_notify("notify: parsing rest of the packet\n");
+ if (notify->parsed < notify->size) {
+ ret = knot_packet_parse_rest(notify);
+ if (ret != KNOT_EOK) {
+ dbg_notify("notify: failed to parse NOTIFY query\n");
+ knot_ns_error_response(ns, knot_packet_id(notify),
+ KNOT_RCODE_FORMERR, buffer,
+ size);
+ return KNOTD_EOK;
+ }
+ }
+
+ // create NOTIFY response
+ dbg_notify("notify: creating response\n");
+ ret = notify_create_response(notify, buffer, size);
+ if (ret != KNOTD_EOK) {
+ dbg_notify("notify: failed to create NOTIFY response\n");
+ knot_ns_error_response(ns, knot_packet_id(notify),
+ KNOT_RCODE_SERVFAIL, buffer,
+ size);
+ return KNOTD_EOK;
+ }
+
+ // find the zone
+ const knot_dname_t *qname = knot_packet_qname(notify);
+ const knot_zone_t *z = knot_zonedb_find_zone_for_name(
+ ns->zone_db, qname);
+ if (z == NULL) {
+ dbg_notify("notify: failed to find zone by name\n");
+ knot_ns_error_response(ns, knot_packet_id(notify),
+ KNOT_RCODE_REFUSED, buffer,
+ size);
+ return KNOTD_EOK;
+ }
+
+ notify_check_and_schedule(ns, z, from);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int notify_process_response(knot_nameserver_t *nameserver,
+ knot_packet_t *notify,
+ sockaddr_t *from,
+ uint8_t *buffer, size_t *size)
+{
+ if (nameserver == NULL || notify == NULL || from == NULL
+ || buffer == NULL || size == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Assert no response size. */
+ *size = 0;
+
+ /* Find matching zone. */
+ const knot_dname_t *zone_name = knot_packet_qname(notify);
+ knot_zone_t *zone = knot_zonedb_find_zone(nameserver->zone_db,
+ zone_name);
+ if (!zone) {
+ return KNOTD_ENOENT;
+ }
+ if (!knot_zone_data(zone)) {
+ return KNOTD_ENOENT;
+ }
+
+ /* Match ID against awaited. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ pthread_mutex_lock(&zd->lock);
+ uint16_t pkt_id = knot_packet_id(notify);
+ notify_ev_t *ev = 0, *match = 0;
+ WALK_LIST(ev, zd->notify_pending) {
+ if ((int)pkt_id == ev->msgid) {
+ match = ev;
+ break;
+ }
+ }
+
+ /* Found waiting NOTIFY query? */
+ if (!match) {
+ log_server_notice("No pending NOTIFY query found for ID=%u\n",
+ pkt_id);
+ return KNOTD_ERROR;
+ }
+
+ /* NOTIFY is now finished. */
+ zones_cancel_notify(zd, match);
+
+ /* Zone was removed/reloaded. */
+ pthread_mutex_unlock(&zd->lock);
+
+ log_server_info("Received response for pending NOTIFY query ID=%u\n",
+ pkt_id);
+
+ return KNOTD_EOK;
+}
+
diff --git a/src/knot/server/notify.h b/src/knot/server/notify.h
new file mode 100644
index 0000000..c1bebb8
--- /dev/null
+++ b/src/knot/server/notify.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 notify.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief NOTIFY request/reply API.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+
+#ifndef _KNOTD_NOTIFY_H_
+#define _KNOTD_NOTIFY_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "libknot/zone/zone.h"
+#include "libknot/packet/packet.h"
+#include "libknot/zone/zonedb.h"
+#include "common/lists.h"
+#include "common/sockaddr.h"
+#include "libknot/nameserver/name-server.h"
+
+/*!
+ * \brief Pending NOTIFY event.
+ * \see knot_zone_t.notify_pending
+ */
+typedef struct notify_ev_t {
+ node n;
+ int timeout; /*!< Timeout for events. */
+ int retries; /*!< Number of retries. */
+ int msgid; /*!< ID of pending NOTIFY. */
+ sockaddr_t addr; /*!< Slave server address. */
+ struct event_t *timer; /*!< Event timer. */
+ knot_zone_t *zone; /*!< Associated zone. */
+} notify_ev_t;
+
+/*!
+ * \brief Creates a NOTIFY request message for SOA RR of the given zone.
+ *
+ * \param zone Zone from which to take the SOA RR.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_ESPACE
+ * \retval KNOTD_ERROR
+ */
+int notify_create_request(const knot_zone_contents_t *zone, uint8_t *buffer,
+ size_t *size);
+
+/*!
+ * \brief Creates a response for NOTIFY query.
+ *
+ * Valid NOTIFY query expires REFRESH timer for received qname.
+ *
+ * \see RFC1996 for query and response format.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param query Response structure with parsed query.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ * size of the response.
+ *
+ * \retval KNOTD_EOK if a valid response was created.
+ * \retval KNOTD_EACCES sender is not authorized to request NOTIFY.
+ * \retval KNOTD_EMALF if an error occured and the response is not valid.
+ */
+/*!
+ * \brief Evaluates incoming NOTIFY request and produces a reply.
+ *
+ * \param notify (Partially) parsed packet with the NOTIFY request.
+ * \param zonedb Zone database of the server.
+ * \param zone Zone which is probably out-of-date or NULL if there either is no
+ * zone corresponding to the request or if the zone is up-to-date.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * response message in bytes.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_EINVAL
+ * \retval KNOTD_EMALF
+ * \retval KNOTD_ERROR
+ */
+int notify_process_request(knot_nameserver_t *nameserver,
+ knot_packet_t *notify,
+ sockaddr_t *from,
+ uint8_t *buffer, size_t *size);
+
+/*!
+ * \brief Processes NOTIFY response packet.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param from Address of the response sender.
+ * \param packet Parsed response packet.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ * size of the response.
+ *
+ * \retval KNOTD_EOK if a valid response was created.
+ * \retval KNOTD_EINVAL on invalid parameters or packet.
+ * \retval KNOTD_EMALF if an error occured and the response is not valid.
+ */
+int notify_process_response(knot_nameserver_t *nameserver,
+ knot_packet_t *notify,
+ sockaddr_t *from,
+ uint8_t *buffer, size_t *size);
+
+#endif /* _KNOTD_NOTIFY_H_ */
+
+/*! @} */
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
new file mode 100644
index 0000000..80db35d
--- /dev/null
+++ b/src/knot/server/server.c
@@ -0,0 +1,730 @@
+/* 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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <openssl/evp.h>
+#include <assert.h>
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/server/server.h"
+#include "knot/server/udp-handler.h"
+#include "knot/server/tcp-handler.h"
+#include "knot/server/xfr-handler.h"
+#include "libknot/nameserver/name-server.h"
+#include "knot/stat/stat.h"
+#include "libknot/zone/zonedb.h"
+#include "knot/zone/zone-load.h"
+#include "libknot/dname.h"
+#include "knot/conf/conf.h"
+#include "knot/server/zones.h"
+
+/*! \brief Event scheduler loop. */
+static int evsched_run(dthread_t *thread)
+{
+ iohandler_t *sched_h = (iohandler_t *)thread->data;
+ evsched_t *s = (evsched_t*)sched_h->data;
+ if (!s) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Run event loop. */
+ event_t *ev = 0;
+ while((ev = evsched_next(s))) {
+
+ /* Error. */
+ if (!ev) {
+ return KNOTD_ERROR;
+ }
+
+ /* Process termination event. */
+ if (ev->type == EVSCHED_TERM) {
+ evsched_event_finished(s);
+ evsched_event_free(s, ev);
+ break;
+ }
+
+ /* Process event. */
+ if (ev->type == EVSCHED_CB && ev->cb) {
+ ev->cb(ev);
+ evsched_event_finished(s);
+ } else {
+ evsched_event_finished(s);
+ evsched_event_free(s, ev);
+ }
+
+ /* Check for thread cancellation. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+/*! \brief List item for generic pointers. */
+typedef struct pnode_t {
+ struct node *next, *prev; /* Keep the ordering for lib/lists.h */
+ void *p; /*!< \brief Useful data pointer. */
+} pnode_t;
+
+/*! \brief Unbind and dispose given interface. */
+static void server_remove_iface(iface_t *iface)
+{
+ /* Free UDP handler. */
+ iohandler_t *handler = iface->handler[UDP_ID];
+ if (handler) {
+ server_remove_handler(handler->server, handler);
+ } else {
+ if (iface->fd[UDP_ID] > -1) {
+ close(iface->fd[UDP_ID]);
+ }
+ }
+
+ /* Free TCP handler. */
+ handler = iface->handler[TCP_ID];
+ if (handler) {
+ server_remove_handler(handler->server, handler);
+ } else {
+ if (iface->fd[TCP_ID] > -1) {
+ close(iface->fd[TCP_ID]);
+ }
+ }
+
+ /* Free interface. */
+ free(iface->addr);
+ free(iface);
+}
+
+/*!
+ * \brief Initialize new interface from config value.
+ *
+ * Both TCP and UDP sockets will be created for the interface.
+ *
+ * \param new_if Allocated memory for the interface.
+ * \param cfg_if Interface template from config.
+ *
+ * \retval 0 if successful (EOK).
+ * \retval <0 on errors (EACCES, EINVAL, ENOMEM, EADDRINUSE).
+ */
+static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if)
+{
+ /* Initialize interface. */
+ char errbuf[128];
+ int opt = 1024 * 1024;
+ int snd_opt = 1024 * 1024;
+ memset(new_if, 0, sizeof(iface_t));
+
+ /* Create UDP socket. */
+ int sock = socket_create(cfg_if->family, SOCK_DGRAM);
+ if (sock <= 0) {
+ strerror_r(errno, errbuf, sizeof(errbuf));
+ log_server_error("Could not create UDP socket: %s.\n",
+ errbuf);
+ return sock;
+ }
+ if (socket_bind(sock, cfg_if->family,
+ cfg_if->address, cfg_if->port) < 0) {
+ socket_close(sock);
+ log_server_error("Could not bind to "
+ "UDP interface %s port %d.\n",
+ cfg_if->address, cfg_if->port);
+ return knot_map_errno(EACCES, EINVAL, ENOMEM);
+ }
+
+ new_if->fd[UDP_ID] = sock;
+ new_if->type[UDP_ID] = cfg_if->family;
+
+ /* Set socket options - voluntary. */
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &snd_opt, sizeof(snd_opt)) < 0) {
+ // log_server_warning("Failed to configure socket "
+ // "write buffers.\n");
+ }
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ // log_server_warning("Failed to configure socket read buffers.\n");
+ }
+
+ /* Create TCP socket. */
+ int ret = 0;
+ sock = socket_create(cfg_if->family, SOCK_STREAM);
+ if (sock <= 0) {
+ socket_close(new_if->fd[UDP_ID]);
+ strerror_r(errno, errbuf, sizeof(errbuf));
+ log_server_error("Could not create TCP socket: %s.\n",
+ errbuf);
+ return sock;
+ }
+
+ ret = socket_bind(sock, cfg_if->family, cfg_if->address, cfg_if->port);
+ if (ret < 0) {
+ socket_close(new_if->fd[UDP_ID]);
+ socket_close(sock);
+ log_server_error("Could not bind to "
+ "TCP interface %s port %d.\n",
+ cfg_if->address, cfg_if->port);
+ return ret;
+ }
+
+ ret = socket_listen(sock, TCP_BACKLOG_SIZE);
+ if (ret < 0) {
+ socket_close(new_if->fd[UDP_ID]);
+ socket_close(sock);
+ log_server_error("Failed to listen on "
+ "TCP interface %s port %d.\n",
+ cfg_if->address, cfg_if->port);
+ return ret;
+ }
+
+ new_if->fd[TCP_ID] = sock;
+ new_if->type[TCP_ID] = cfg_if->family;
+ new_if->port = cfg_if->port;
+ new_if->addr = strdup(cfg_if->address);
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Update bound sockets according to configuration.
+ *
+ * \param server Server instance.
+ * \return number of added sockets.
+ */
+static int server_bind_sockets(server_t *server)
+{
+ /*! \todo This requires locking to disable parallel updates. */
+
+ /* Lock configuration. */
+ conf_read_lock();
+
+ /* Prepare helper lists. */
+ int bound = 0;
+ node *m = 0;
+ list *newlist, unmatched;
+ newlist = malloc(sizeof(list));
+ init_list(newlist);
+ init_list(&unmatched);
+
+ /* Duplicate current list. */
+ /*! \note Pointers to addr, handlers etc. will be shared. */
+ list_dup(&unmatched, server->ifaces, sizeof(iface_t));
+
+ /* Update pointers. */
+ WALK_LIST(m, unmatched) {
+
+ /* Interfaces. */
+ iface_t *m_if = (iface_t*)m;
+ for (int i = 0; i <= TCP_ID; ++i) {
+ iohandler_t *h = m_if->handler[i];
+ if (h) {
+ h->iface = m_if;
+ }
+
+ }
+ }
+
+ /* Update bound interfaces. */
+ node *n = 0;
+ WALK_LIST(n, conf()->ifaces) {
+
+ /* Find already matching interface. */
+ int found_match = 0;
+ conf_iface_t *cfg_if = (conf_iface_t*)n;
+ WALK_LIST(m, unmatched) {
+ iface_t *srv_if = (iface_t*)m;
+
+ /* Matching port and address. */
+ if (cfg_if->port == srv_if->port) {
+ if (strcmp(cfg_if->address, srv_if->addr) == 0) {
+ found_match = 1;
+ break;
+ }
+ }
+ }
+
+ /* Found already bound interface. */
+ if (found_match) {
+ rem_node(m);
+ } else {
+
+ /* Create new interface. */
+ m = malloc(sizeof(iface_t));
+ if (server_init_iface((iface_t*)m, cfg_if) < 0) {
+ free(m);
+ m = 0;
+ }
+
+ log_server_info("Binding to interface %s port %d.\n",
+ cfg_if->address, cfg_if->port);
+ }
+
+ /* Move to new list. */
+ if (m) {
+ add_tail(newlist, m);
+ ++bound;
+ }
+ }
+
+ /* Unlock configuration. */
+ conf_read_unlock();
+
+ /* Publish new list. */
+ list* oldlist = rcu_xchg_pointer(&server->ifaces, newlist);
+
+ /* Ensure no one is reading old interfaces. */
+ synchronize_rcu();
+
+ /* Remove deprecated interfaces. */
+ WALK_LIST_DELSAFE(n, m, unmatched) {
+ iface_t *rm_if = (iface_t*)n;
+ log_server_info("Removing interface %s port %d.\n",
+ rm_if->addr, rm_if->port);
+ server_remove_iface(rm_if);
+ }
+
+ /* Free original list. */
+ WALK_LIST_DELSAFE(n, m, *oldlist) {
+ /*! \note Need to keep internal pointers, as they are shared
+ * with the newly published list. */
+ free(n);
+ }
+ free(oldlist);
+
+ return bound;
+}
+
+/*!
+ * \brief Update socket handlers according to configuration.
+ *
+ * \param server Server instance.
+ * \retval 0 if successful (EOK).
+ * \retval <0 on errors (EINVAL).
+ */
+static int server_bind_handlers(server_t *server)
+{
+ if (!server || !server->ifaces) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Lock config. */
+ conf_read_lock();
+
+ /* Estimate number of threads/manager. */
+ int thr_count = 0;
+ int tcp_unit_size = 0;
+ if (conf()->workers < 1) {
+ thr_count = dt_optimal_size();
+ tcp_unit_size = (thr_count * 2) + 1; /* Will be always odd. */
+ } else {
+ thr_count = conf()->workers;
+ tcp_unit_size = thr_count + 1; /* Force configured value. */
+ }
+
+ dbg_server("server: configured %d worker%s per UDP iface\n",
+ thr_count, thr_count > 1 ? "s" : "");
+ dbg_server("server: configured %d worker%s per TCP iface\n",
+ tcp_unit_size - 1, (tcp_unit_size - 1) > 1 ? "s" : "");
+
+ /* Create socket handlers. */
+ node *n = 0;
+ iohandler_t* h = 0;
+ WALK_LIST(n, *server->ifaces) {
+
+ iface_t *iface = (iface_t*)n;
+
+ /* Create UDP handlers. */
+ dt_unit_t *unit = 0;
+ if (!iface->handler[UDP_ID]) {
+ unit = dt_create_coherent(thr_count, &udp_master, 0);
+ h = server_create_handler(server, iface->fd[UDP_ID], unit);
+ h->type = iface->type[UDP_ID];
+ h->iface = iface;
+
+ /* Save pointer. */
+ rcu_set_pointer(&iface->handler[UDP_ID], h);
+ dbg_server("server: creating UDP socket handlers for '%s:%d'\n",
+ iface->addr, iface->port);
+
+ }
+
+ /* Create TCP handlers. */
+ if (!iface->handler[TCP_ID]) {
+ unit = dt_create(tcp_unit_size);
+ h = server_create_handler(server, iface->fd[TCP_ID], unit);
+ tcp_loop_unit(h, unit);
+ h->type = iface->type[TCP_ID];
+ h->iface = iface;
+
+ /* Save pointer. */
+ rcu_set_pointer(&iface->handler[TCP_ID], h);
+ dbg_server("server: creating TCP socket handlers for '%s:%d'\n",
+ iface->addr, iface->port);
+ }
+
+ }
+
+ /* Unlock config. */
+ conf_read_unlock();
+
+ return KNOTD_EOK;
+}
+
+server_t *server_create()
+{
+ // Create server structure
+ server_t *server = malloc(sizeof(server_t));
+ if (server == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ server->state = ServerIdle;
+ init_list(&server->handlers);
+ server->ifaces = malloc(sizeof(list));
+ init_list(server->ifaces);
+
+ // Create event scheduler
+ dbg_server("server: creating event scheduler\n");
+ server->sched = evsched_new();
+ dt_unit_t *unit = dt_create_coherent(1, evsched_run, 0);
+ iohandler_t *h = server_create_handler(server, -1, unit);
+ h->data = server->sched;
+
+ // Create name server
+ dbg_server("server: creating Name Server structure\n");
+ server->nameserver = knot_ns_create();
+ if (server->nameserver == NULL) {
+ free(server);
+ return NULL;
+ }
+ knot_ns_set_data(server->nameserver, server);
+ dbg_server("server: initializing OpenSSL\n");
+ OpenSSL_add_all_digests();
+
+ // Create XFR handler
+ server->xfr_h = xfr_create(XFR_THREADS_COUNT, server->nameserver);
+ if (!server->xfr_h) {
+ knot_ns_destroy(&server->nameserver);
+ free(server);
+ return NULL;
+ }
+
+ dbg_server("server: created server instance\n");
+ return server;
+}
+
+iohandler_t *server_create_handler(server_t *server, int fd, dt_unit_t *unit)
+{
+ // Create new worker
+ iohandler_t *handler = malloc(sizeof(iohandler_t));
+ if (handler == 0) {
+ ERR_ALLOC_FAILED;
+ return 0;
+ }
+
+ // Initialize
+ handler->fd = fd;
+ handler->type = 0;
+ handler->state = ServerIdle;
+ handler->server = server;
+ handler->unit = unit;
+ handler->iface = 0;
+ handler->data = 0;
+ handler->interrupt = 0;
+
+ // Update unit data object
+ for (int i = 0; i < unit->size; ++i) {
+ dthread_t *thread = unit->threads[i];
+ if (thread->run) {
+ dt_repurpose(thread, thread->run, handler);
+ }
+ }
+
+ /*! \todo This requires either RCU compatible ptr swap or locking. */
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ // Update list
+ add_tail(&server->handlers, (node*)handler);
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+
+ return handler;
+}
+
+int server_remove_handler(server_t *server, iohandler_t *h)
+{
+ // Check
+ if (h == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ /*! \todo This requires either RCU compatible ptr swap or locking. */
+
+ // Remove node
+ rem_node((node*)h);
+
+ // Wait for dispatcher to finish
+ if (h->state & ServerRunning) {
+ h->state = ServerIdle;
+ dt_stop(h->unit);
+
+ /* Call interrupt handler. */
+ if (h->interrupt) {
+ h->interrupt(h);
+ }
+
+ dt_join(h->unit);
+ }
+
+ // Close socket
+ if (h->fd >= 0) {
+ socket_close(h->fd);
+ h->fd = -1;
+ }
+
+ // Update interface
+ if (h->iface) {
+ int id = UDP_ID;
+ if (h->iface->handler[TCP_ID] == h) {
+ id = TCP_ID;
+ }
+
+ h->iface->fd[id] = h->fd;
+ h->iface->handler[id] = 0;
+ }
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+
+ /* RCU synchronize. */
+ synchronize_rcu();
+
+ // Destroy dispatcher and worker
+ dt_delete(&h->unit);
+ free(h);
+ return KNOTD_EOK;
+}
+
+int server_start(server_t *server)
+{
+ // Check server
+ if (server == 0) {
+ return KNOTD_EINVAL;
+ }
+
+ dbg_server("server: starting server instance\n");
+
+ /* Start XFR handler. */
+ xfr_start(server->xfr_h);
+
+ /* Lock configuration. */
+ conf_read_lock();
+
+ // Start dispatchers
+ int ret = KNOTD_EOK;
+ server->state |= ServerRunning;
+ iohandler_t *h = 0;
+ WALK_LIST(h, server->handlers) {
+
+ /* Already running. */
+ if (h->state & ServerRunning) {
+ continue;
+ }
+
+ h->state = ServerRunning;
+ ret = dt_start(h->unit);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ /* Unlock configuration. */
+ conf_read_unlock();
+
+ dbg_server("server: server started\n");
+
+ return ret;
+}
+
+int server_wait(server_t *server)
+{
+ /* Join threading unit. */
+ xfr_join(server->xfr_h);
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ // Wait for handlers to finish
+ int ret = 0;
+ iohandler_t *h = 0, *nxt = 0;
+ WALK_LIST_DELSAFE(h, nxt, server->handlers) {
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+
+ /* Remove handler. */
+ int dret = dt_join(h->unit);
+ if (dret < 0) {
+ ret = dret;
+ }
+ server_remove_handler(server, h);
+
+ /* Relock RCU. */
+ rcu_read_lock();
+ }
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+
+ return ret;
+}
+
+void server_stop(server_t *server)
+{
+ dbg_server("server: stopping server\n");
+
+ /* Wait for XFR master. */
+ xfr_stop(server->xfr_h);
+
+ /* Interrupt XFR handler execution. */
+ if (server->xfr_h->interrupt) {
+ server->xfr_h->interrupt(server->xfr_h);
+ }
+
+ /* Send termination event. */
+ evsched_schedule_term(server->sched, 0);
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ /* Notify servers to stop. */
+ log_server_info("Stopping server...\n");
+ server->state &= ~ServerRunning;
+ iohandler_t *h = 0;
+ WALK_LIST(h, server->handlers) {
+ h->state = ServerIdle;
+ dt_stop(h->unit);
+
+ /* Call interrupt handler. */
+ if (h->interrupt) {
+ h->interrupt(h);
+ }
+ }
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+}
+
+void server_destroy(server_t **server)
+{
+ // Check server
+ if (!server) {
+ return;
+ }
+ if (!*server) {
+ return;
+ }
+
+ dbg_server("server: destroying server instance\n");
+
+ // Free XFR master
+ xfr_free((*server)->xfr_h);
+
+ // Free interfaces
+ node *n = 0, *nxt = 0;
+ if ((*server)->ifaces) {
+ WALK_LIST_DELSAFE(n, nxt, *(*server)->ifaces) {
+ iface_t *iface = (iface_t*)n;
+ server_remove_iface(iface);
+ }
+ free((*server)->ifaces);
+ }
+
+ stat_static_gath_free();
+ knot_ns_destroy(&(*server)->nameserver);
+
+ // Delete event scheduler
+ evsched_delete(&(*server)->sched);
+
+ free(*server);
+
+ EVP_cleanup();
+
+ *server = NULL;
+}
+
+int server_conf_hook(const struct conf_t *conf, void *data)
+{
+ server_t *server = (server_t *)data;
+
+ if (!server) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Update bound sockets. */
+ int ret = KNOTD_EOK;
+ if ((ret = server_bind_sockets(server)) < 0) {
+ log_server_error("Failed to bind configured "
+ "interfaces.\n");
+ return KNOTD_ERROR;
+ }
+
+ /* Update handlers. */
+ if ((ret = server_bind_handlers(server)) < 0) {
+ log_server_error("Failed to create handlers for "
+ "configured interfaces.\n");
+ return ret;
+ }
+
+ /* Exit if the server is not running. */
+ if (!(server->state & ServerRunning)) {
+ return KNOTD_ENOTRUNNING;
+ }
+
+ /* Lock configuration. */
+ conf_read_lock();
+
+ /* Start new handlers. */
+ iohandler_t *h = 0;
+ WALK_LIST(h, server->handlers) {
+ if (!(h->state & ServerRunning)) {
+ h->state = ServerRunning;
+ ret = dt_start(h->unit);
+ if (ret < 0) {
+ log_server_error("Handler for %s:%d "
+ "has failed to start.\n",
+ h->iface->addr,
+ h->iface->port);
+ break;
+ }
+ }
+ }
+
+ /* Unlock config. */
+ conf_read_unlock();
+
+ return ret;
+}
+
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
new file mode 100644
index 0000000..480219b
--- /dev/null
+++ b/src/knot/server/server.h
@@ -0,0 +1,211 @@
+/* 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 server.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Core server functions.
+ *
+ * Contains the main high-level server structure (server_t) and interface
+ * to functions taking care of proper initialization of the server and clean-up
+ * when terminated.
+ *
+ * As of now, the server supports only one zone file and only in a special
+ * format.
+ *
+ * \see zone-parser.h
+ *
+ * \addtogroup server
+ * @{
+ */
+
+#ifndef _KNOTD_SERVER_H_
+#define _KNOTD_SERVER_H_
+
+#include "knot/common.h"
+#include "libknot/nameserver/name-server.h"
+#include "knot/server/xfr-handler.h"
+#include "knot/server/socket.h"
+#include "knot/server/dthreads.h"
+#include "libknot/zone/zonedb.h"
+#include "common/evsched.h"
+#include "common/lists.h"
+
+/* Forwad declarations. */
+struct iface_t;
+struct iohandler_t;
+struct server_t;
+struct conf_t;
+
+/*! \brief I/O handler structure.
+ */
+typedef struct iohandler_t {
+ struct node *next, *prev;
+ int fd; /*!< I/O filedescriptor */
+ int type; /*!< Descriptor type/family. */
+ unsigned state; /*!< Handler state */
+ dt_unit_t *unit; /*!< Threading unit */
+ struct iface_t *iface; /*!< Reference to associated interface. */
+ struct server_t *server; /*!< Reference to server */
+ void *data; /*!< Persistent data for I/O handler. */
+ void (*interrupt)(struct iohandler_t *h); /*!< Interrupt handler. */
+
+} iohandler_t;
+
+/*! \brief Round-robin mechanism of switching.
+ */
+#define get_next_rr(current, count) \
+ (((current) + 1) % (count))
+
+/*! \brief Server state flags.
+ */
+typedef enum {
+ ServerIdle = 0 << 0, /*!< Server is idle. */
+ ServerRunning = 1 << 0 /*!< Server is running. */
+} server_state;
+
+/*!
+ * \brief Server interface structure.
+ */
+typedef struct iface_t {
+ struct node *next, *prev;
+ int fd[2]; /*!< \brief Socket filedescriptors (UDP, TCP). */
+ int type[2]; /*!< \brief Socket type. */
+ int port; /*!< \brief Socket port. */
+ char* addr; /*!< \brief Socket address. */
+ iohandler_t* handler[2]; /*!< \brief Associated I/O handlers. */
+} iface_t;
+
+/* Interface indexes. */
+#define UDP_ID 0
+#define TCP_ID 1
+
+/*!
+ * \brief Main server structure.
+ *
+ * Keeps references to all important structures needed for operation.
+ */
+typedef struct server_t {
+
+ /*! \brief Server state tracking. */
+ volatile unsigned state;
+
+ /*! \brief Reference to the name server structure. */
+ knot_nameserver_t *nameserver;
+
+ /*! \brief XFR handler. */
+ xfrhandler_t *xfr_h;
+
+ /*! \brief Event scheduler. */
+ evsched_t *sched;
+
+ /*! \brief I/O handlers list. */
+ list handlers;
+
+ /*! \brief List of interfaces. */
+ list* ifaces;
+
+} server_t;
+
+/*!
+ * \brief Allocates and initializes the server structure.
+ *
+ * Creates all other main structures.
+ *
+ * \retval New instance if successful.
+ * \retval NULL If an error occured.
+ */
+server_t *server_create();
+
+/*!
+ * \brief Create and bind handler to given filedescriptor.
+ *
+ * Pointer to handler instance is used as native unique identifier.
+ * This requests instance not to be reallocated.
+ *
+ * \param server Server structure to be used for operation.
+ * \param fd I/O filedescriptor.
+ * \param unit Threading unit to serve given filedescriptor.
+ *
+ * \retval Handler instance if successful.
+ * \retval NULL If an error occured.
+ */
+iohandler_t *server_create_handler(server_t *server, int fd, dt_unit_t *unit);
+
+/*!
+ * \brief Delete handler.
+ *
+ * \param server Server structure to be used for operation.
+ * \param ref I/O handler instance.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ */
+int server_remove_handler(server_t *server, iohandler_t *ref);
+
+/*!
+ * \brief Starts the server.
+ *
+ * \param server Server structure to be used for operation.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ *
+ * \todo When a module for configuration is added, the filename parameter will
+ * be removed.
+ */
+int server_start(server_t *server);
+
+/*!
+ * \brief Waits for the server to finish.
+ *
+ * \param server Server structure to be used for operation.
+ *
+ * \retval 0 On success (EOK).
+ * \retval <0 If an error occured (EINVAL).
+ */
+int server_wait(server_t *server);
+
+/*!
+ * \brief Requests server to stop.
+ *
+ * \param server Server structure to be used for operation.
+ */
+void server_stop(server_t *server);
+
+/*!
+ * \brief Properly destroys the server structure.
+ *
+ * \param server Server structure to be used for operation.
+ */
+void server_destroy(server_t **server);
+
+/*!
+ * \brief Server config hook.
+ *
+ * Routine for dynamic server reconfiguration.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ENOTRUNNING if the server is not running.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int server_conf_hook(const struct conf_t *conf, void *data);
+
+#endif // _KNOTD_SERVER_H_
+
+/*! @} */
diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c
new file mode 100644
index 0000000..d3dd664
--- /dev/null
+++ b/src/knot/server/socket.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>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "knot/other/error.h"
+#include "knot/common.h"
+#include "knot/server/socket.h"
+
+int socket_create(int family, int type)
+{
+ /* Create socket. */
+ int ret = socket(family, type, 0);
+ if (ret < 0) {
+ return knot_map_errno(EACCES, EINVAL, ENOMEM);
+ }
+
+ return ret;
+}
+
+int socket_connect(int fd, const char *addr, unsigned short port)
+{
+ /* NULL address => any */
+ if (!addr) {
+ addr = "0.0.0.0";
+ }
+
+ /* Resolve address. */
+ int ret = KNOTD_EOK;
+ struct addrinfo hints, *res;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((ret = getaddrinfo(addr, NULL, &hints, &res)) != 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Evaluate address type. */
+ struct sockaddr *saddr = 0;
+ socklen_t addrlen = 0;
+#ifndef DISABLE_IPV6
+ if (res->ai_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)res->ai_addr;
+ ipv6->sin6_port = htons(port);
+ saddr = (struct sockaddr*)ipv6;
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+#endif
+ if (res->ai_family == AF_INET) {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in*)res->ai_addr;
+ ipv4->sin_port = htons(port);
+ saddr = (struct sockaddr*)ipv4;
+ addrlen = sizeof(struct sockaddr_in);
+ }
+
+ /* Connect. */
+ ret = -1;
+ if (addr) {
+ ret = connect(fd, saddr, addrlen);
+ if (ret < 0) {
+ ret = knot_map_errno(EACCES, EADDRINUSE, EAGAIN,
+ ECONNREFUSED, EISCONN);
+ }
+ } else {
+ ret = KNOTD_EINVAL;
+ }
+
+
+ /* Free addresses. */
+ freeaddrinfo(res);
+
+ return ret;
+}
+
+int socket_bind(int socket, int family, const char *addr, unsigned short port)
+{
+ /* Check address family. */
+ struct sockaddr* paddr = 0;
+ socklen_t addrlen = 0;
+ struct sockaddr_in saddr;
+#ifndef DISABLE_IPV6
+ struct sockaddr_in6 saddr6;
+#endif
+ if (family == AF_INET) {
+
+ /* Initialize socket address. */
+ paddr = (struct sockaddr*)&saddr;
+ addrlen = sizeof(saddr);
+ if (getsockname(socket, paddr, &addrlen) < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Set address and port. */
+ saddr.sin_port = htons(port);
+ if (inet_pton(family, addr, &saddr.sin_addr) < 0) {
+ saddr.sin_addr.s_addr = INADDR_ANY;
+ char buf[INET_ADDRSTRLEN];
+ inet_ntop(family, &saddr.sin_addr, buf, sizeof(buf));
+ log_server_error("Address '%s' is invalid, "
+ "using '%s' instead.\n",
+ addr, buf);
+
+ }
+
+ } else {
+
+#ifdef DISABLE_IPV6
+ log_server_error("ipv6 support disabled\n");
+ return KNOTD_ENOIPV6;
+#else
+ /* Initialize socket address. */
+ paddr = (struct sockaddr*)&saddr6;
+ addrlen = sizeof(saddr6);
+ if (getsockname(socket, paddr, &addrlen) < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Set address and port. */
+ saddr6.sin6_port = htons(port);
+ if (inet_pton(family, addr, &saddr6.sin6_addr) < 0) {
+ memcpy(&saddr6.sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ char buf[INET6_ADDRSTRLEN];
+ inet_ntop(family, &saddr6.sin6_addr, buf, sizeof(buf));
+ log_server_error("Address '%s' is invalid, "
+ "using '%s' instead\n",
+ addr, buf);
+
+ }
+#endif
+ }
+
+ /* Reuse old address if taken. */
+ int flag = 1;
+ int ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
+ &flag, sizeof(flag));
+ if (ret < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Bind to specified address. */
+ int res = bind(socket, paddr, addrlen);
+ if (res < 0) {
+ log_server_error("Cannot bind to socket (%d).\n",
+ errno);
+ return knot_map_errno(EADDRINUSE, EINVAL, EACCES, ENOMEM);
+ }
+
+ return KNOTD_EOK;
+}
+
+int socket_listen(int socket, int backlog_size)
+{
+ int ret = listen(socket, backlog_size);
+ if (ret < 0) {
+ return knot_map_errno(EADDRINUSE);
+ }
+
+ return KNOTD_EOK;
+}
+
+int socket_close(int socket)
+{
+ if (close(socket) < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ return KNOTD_EOK;
+}
+
diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h
new file mode 100644
index 0000000..dff5216
--- /dev/null
+++ b/src/knot/server/socket.h
@@ -0,0 +1,120 @@
+/* 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 socket.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief Generic sockets APIs.
+ *
+ * This file provides platform-independent sockets.
+ * Functions work on sockets created via system socket(2) functions.
+ *
+ * You can use standard I/O functions send(), sendto(), recv(), recvfrom()
+ * like you would with a normal sockets.
+ *
+ * \addtogroup network
+ * @{
+ */
+
+#ifndef _KNOTD_SOCKET_H_
+#define _KNOTD_SOCKET_H_
+
+/* POSIX only. */
+#include <sys/socket.h>
+#include "common/sockaddr.h"
+
+/*! \brief Socket-related constants. */
+typedef enum {
+ SOCKET_MTU_SZ = 8192, /*!< \todo Determine UDP MTU size. */
+} socket_const_t;
+
+/*!
+ * \brief Create socket.
+ *
+ * \param family Socket family (PF_INET, PF_IPX, PF_PACKET, PF_UNIX).
+ * \param type Socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW).
+ *
+ * \retval new socket filedescriptor on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOMEM out of memory error.
+ * \retval KNOTD_EACCES process does not have appropriate privileges.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int socket_create(int family, int type);
+
+/*!
+ * \brief Connect to remote host.
+ *
+ * \param fd Socket filedescriptor.
+ * \param addr Requested address.
+ * \param port Requested port.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ * \retval KNOTD_EACCES process does not have appropriate privileges.
+ * \retval KNOTD_EAGAIN lack of resources, try again.
+ * \retval KNOTD_EADDRINUSE address already in use.
+ * \retval KNOTD_ECONNREFUSED connection refused.
+ * \retval KNOTD_EISCONN already connected.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int socket_connect(int fd, const char *addr, unsigned short port);
+
+/*!
+ * \brief Listen on given socket.
+ *
+ * \param fd Socket filedescriptor.
+ * \param family Socket family.
+ * \param addr Requested address.
+ * \param port Requested port.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ * \retval KNOTD_EACCES process does not have appropriate privileges.
+ * \retval KNOTD_EADDRINUSE address already in use.
+ * \retval KNOTD_ENOMEM out of memory error.
+ * \retval KNOTD_ENOIPV6 IPv6 support is not available.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int socket_bind(int fd, int family, const char *addr, unsigned short port);
+
+/*!
+ * \brief Listen on given TCP socket.
+ *
+ * \param fd Socket filedescriptor.
+ * \param backlog_size Requested TCP backlog size.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EADDRINUSE address already in use.
+ * \retval KNOTD_ERROR unspecified error.
+ */
+int socket_listen(int fd, int backlog_size);
+
+/*!
+ * \brief Close and deinitialize socket.
+ *
+ * \param fd Socket filedescriptor.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int socket_close(int fd);
+
+
+#endif // _KNOTD_SOCKET_H_
+
+/*! @} */
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
new file mode 100644
index 0000000..db58fef
--- /dev/null
+++ b/src/knot/server/tcp-handler.c
@@ -0,0 +1,511 @@
+/* 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common/sockaddr.h"
+#include "common/skip-list.h"
+#include "common/fdset.h"
+#include "knot/common.h"
+#include "knot/server/tcp-handler.h"
+#include "knot/server/xfr-handler.h"
+#include "libknot/nameserver/name-server.h"
+#include "knot/other/error.h"
+#include "knot/stat/stat.h"
+#include "libknot/util/wire.h"
+#include "knot/server/zones.h"
+
+/*! \brief TCP worker data. */
+typedef struct tcp_worker_t {
+ iohandler_t *ioh; /*!< Shortcut to I/O handler. */
+ fdset_t *fdset; /*!< File descriptor set. */
+ int pipe[2]; /*!< Master-worker signalization pipes. */
+} tcp_worker_t;
+
+/*
+ * Forward decls.
+ */
+
+/*! \brief Wrapper for TCP send. */
+static int xfr_send_cb(int session, sockaddr_t *addr, uint8_t *msg, size_t msglen)
+{
+ UNUSED(addr);
+ return tcp_send(session, msg, msglen);
+}
+
+/*!
+ * \brief TCP event handler function.
+ *
+ * Handle single TCP event.
+ *
+ * \param w Associated I/O event.
+ * \param revents Returned events.
+ */
+static void tcp_handle(tcp_worker_t *w, int fd)
+{
+ if (fd < 0 || !w || !w->ioh) {
+ dbg_net("tcp: tcp_handle(%p, %d) - invalid parameters\n", w, fd);
+ return;
+ }
+
+ dbg_net("tcp: handling TCP event on fd=%d in thread %p.\n",
+ fd, (void*)pthread_self());
+
+ knot_nameserver_t *ns = w->ioh->server->nameserver;
+ xfrhandler_t *xfr_h = w->ioh->server->xfr_h;
+
+ /* Check address type. */
+ sockaddr_t addr;
+ if (sockaddr_init(&addr, w->ioh->type) != KNOTD_EOK) {
+ log_server_error("Socket type %d is not supported, "
+ "IPv6 support is probably disabled.\n",
+ w->ioh->type);
+ return;
+ }
+
+ /* Receive data. */
+ uint8_t qbuf[65535]; /*! \todo This may be problematic. */
+ size_t qbuf_maxlen = sizeof(qbuf);
+ int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr);
+ if (n <= 0) {
+ dbg_net("tcp: client on fd=%d disconnected\n", fd);
+ fdset_remove(w->fdset, fd);
+ close(fd);
+ return;
+ }
+
+ /* Parse query. */
+// knot_response_t *resp = knot_response_new(qbuf_maxlen);
+ size_t resp_len = qbuf_maxlen; // 64K
+
+ /* Parse query. */
+ knot_packet_type_t qtype = KNOT_QUERY_NORMAL;
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ if (packet == NULL) {
+ uint16_t pkt_id = knot_wire_get_id(qbuf);
+ knot_ns_error_response(ns, pkt_id, KNOT_RCODE_SERVFAIL,
+ qbuf, &resp_len);
+ return;
+ }
+
+ int res = knot_ns_parse_packet(qbuf, n, packet, &qtype);
+ if (unlikely(res != KNOTD_EOK)) {
+
+ /* Send error response on dnslib RCODE. */
+ if (res > 0) {
+ uint16_t pkt_id = knot_wire_get_id(qbuf);
+ knot_ns_error_response(ns, pkt_id, res,
+ qbuf, &resp_len);
+ }
+
+// knot_response_free(&resp);
+ knot_packet_free(&packet);
+ return;
+ }
+
+ /* Handle query. */
+ knot_ns_xfr_t xfr;
+ res = KNOTD_ERROR;
+ switch(qtype) {
+
+ /* Query types. */
+ case KNOT_QUERY_NORMAL:
+ res = knot_ns_answer_normal(ns, packet, qbuf, &resp_len);
+ break;
+ case KNOT_QUERY_IXFR:
+ res = xfr_request_init(&xfr, XFR_TYPE_IOUT, XFR_FLAG_TCP, packet);
+ if (res != KNOTD_EOK) {
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_SERVFAIL, qbuf,
+ &resp_len);
+ res = KNOTD_EOK;
+ break;
+ }
+ xfr.send = xfr_send_cb;
+ xfr.session = fd;
+ memcpy(&xfr.addr, &addr, sizeof(sockaddr_t));
+ xfr_request(xfr_h, &xfr);
+ dbg_net("tcp: enqueued IXFR query on fd=%d\n", fd);
+ return;
+ case KNOT_QUERY_AXFR:
+ res = xfr_request_init(&xfr, XFR_TYPE_AOUT, XFR_FLAG_TCP, packet);
+ if (res != KNOTD_EOK) {
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_SERVFAIL, qbuf,
+ &resp_len);
+ res = KNOTD_EOK;
+ break;
+ }
+ xfr.send = xfr_send_cb;
+ xfr.session = fd;
+ memcpy(&xfr.addr, &addr, sizeof(sockaddr_t));
+ xfr_request(xfr_h, &xfr);
+ dbg_net("tcp: enqueued AXFR query on fd=%d\n", fd);
+ return;
+
+ /*! \todo Implement query notify/update. */
+ case KNOT_QUERY_UPDATE:
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_NOTIMPL, qbuf,
+ &resp_len);
+ res = KNOTD_EOK;
+ break;
+
+ /* Unhandled opcodes. */
+ case KNOT_QUERY_NOTIFY: /*!< Only in UDP. */
+ case KNOT_RESPONSE_NOTIFY: /*!< Only in UDP. */
+ case KNOT_RESPONSE_NORMAL: /*!< TCP handler doesn't send queries. */
+ case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */
+ case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_REFUSED, qbuf,
+ &resp_len);
+ res = KNOTD_EOK;
+ break;
+
+ /* Unknown opcodes. */
+ default:
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_FORMERR, qbuf,
+ &resp_len);
+ res = KNOTD_EOK;
+ break;
+ }
+
+ knot_packet_free(&packet);
+
+ /* Send answer. */
+ if (res == KNOTD_EOK) {
+
+ dbg_net("tcp: got answer of size %zd.\n",
+ resp_len);
+
+ assert(resp_len > 0);
+ res = tcp_send(fd, qbuf, resp_len);
+
+ /* Check result. */
+ if (res != (int)resp_len) {
+ dbg_net("tcp: %s: failed: %d - %d.\n",
+ "socket_send()",
+ res, errno);
+ }
+ } else {
+ dbg_net("tcp: failed to respond to query type=%d on fd=%d - %s\n",
+ qtype, fd, knotd_strerror(res));;
+ }
+
+ return;
+}
+
+static int tcp_accept(int fd)
+{
+ /* Accept incoming connection. */
+ int incoming = accept(fd, 0, 0);
+
+ /* Evaluate connection. */
+ if (incoming < 0) {
+ if (errno != EINTR) {
+ log_server_error("Cannot accept connection "
+ "(%d).\n", errno);
+ }
+ } else {
+ dbg_net("tcp: accepted connection fd=%d\n", incoming);
+ }
+
+ return incoming;
+}
+
+tcp_worker_t* tcp_worker_create()
+{
+ tcp_worker_t *w = malloc(sizeof(tcp_worker_t));
+ if (!w) {
+ dbg_net("tcp: out of memory when creating worker\n");
+ return 0;
+ }
+
+ /* Create signal pipes. */
+ memset(w, 0, sizeof(tcp_worker_t));
+ if (pipe(w->pipe) < 0) {
+ free(w);
+ return 0;
+ }
+
+ /* Create fdset. */
+ w->fdset = fdset_new();
+ if (!w->fdset) {
+ close(w->pipe[0]);
+ close(w->pipe[1]);
+ free(w);
+ }
+
+ fdset_add(w->fdset, w->pipe[0], OS_EV_READ);
+
+ return w;
+}
+
+void tcp_worker_free(tcp_worker_t* w)
+{
+ if (!w) {
+ return;
+ }
+
+ /* Destroy fdset. */
+ fdset_destroy(w->fdset);
+
+ /* Close pipe write end and worker. */
+ close(w->pipe[0]);
+ close(w->pipe[1]);
+ free(w);
+}
+
+/*
+ * Public APIs.
+ */
+
+int tcp_send(int fd, uint8_t *msg, size_t msglen)
+{
+
+ /*! \brief TCP corking.
+ * \see http://vger.kernel.org/~acme/unbehaved.txt
+ */
+#ifdef TCP_CORK
+ int cork = 1;
+ setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+#endif
+
+ /* Send message size. */
+ unsigned short pktsize = htons(msglen);
+ int sent = send(fd, &pktsize, sizeof(pktsize), 0);
+ if (sent < 0) {
+ return KNOTD_ERROR;
+ }
+
+ /* Send message data. */
+ sent = send(fd, msg, msglen, 0);
+ if (sent < 0) {
+ return KNOTD_ERROR;
+ }
+
+#ifdef TCP_CORK
+ /* Uncork. */
+ cork = 0;
+ setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+#endif
+ return sent;
+}
+
+int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr)
+{
+ /* Receive size. */
+ unsigned short pktsize = 0;
+ int n = recv(fd, &pktsize, sizeof(unsigned short), MSG_WAITALL);
+ if (n < 0) {
+ return KNOTD_ERROR;
+ }
+
+ pktsize = ntohs(pktsize);
+
+ // Check packet size for NULL
+ if (pktsize == 0) {
+ return KNOTD_ERROR;
+ }
+
+ dbg_net("tcp: incoming packet size=%hu on fd=%d\n",
+ pktsize, fd);
+
+ // Check packet size
+ if (len < pktsize) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Receive payload. */
+ n = recv(fd, buf, pktsize, MSG_WAITALL);
+
+ /* Get peer name. */
+ if (addr) {
+ socklen_t alen = addr->len;
+ getpeername(fd, addr->ptr, &alen);
+ }
+
+ dbg_net("tcp: received packet size=%d on fd=%d\n",
+ n, fd);
+
+ return n;
+}
+
+int tcp_loop_master(dthread_t *thread)
+{
+ iohandler_t *handler = (iohandler_t *)thread->data;
+ dt_unit_t *unit = thread->unit;
+ tcp_worker_t **workers = handler->data;
+
+ /* Check socket. */
+ if (!handler || handler->fd < 0 || !workers) {
+ dbg_net("tcp: failed to initialize master thread\n");
+ return KNOTD_EINVAL;
+ }
+
+ /* Accept connections. */
+ int id = 0;
+ dbg_net("tcp: created 1 master with %d workers, backend is '%s' \n",
+ unit->size - 1, fdset_method());
+ while(1) {
+ /* Check for cancellation. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Accept client. */
+ int client = tcp_accept(handler->fd);
+ if (client < 0) {
+ continue;
+ }
+
+ /* Add to worker in RR fashion. */
+ if (write(workers[id]->pipe[1], &client, sizeof(int)) < 0) {
+ dbg_net("tcp: failed to register fd=%d to worker=%d\n",
+ client, id);
+ close(client);
+ continue;
+ }
+ id = get_next_rr(id, unit->size - 1);
+ }
+
+ dbg_net("tcp: master thread finished\n");
+ free(workers);
+
+ return KNOTD_EOK;
+}
+
+int tcp_loop_worker(dthread_t *thread)
+{
+ tcp_worker_t *w = thread->data;
+ if (!w) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Accept clients. */
+ dbg_net_verb("tcp: worker %p started\n", w);
+ for (;;) {
+
+ /* Cancellation point. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Wait for events. */
+ int nfds = fdset_wait(w->fdset);
+ if (nfds <= 0) {
+ continue;
+ }
+
+ /* Process incoming events. */
+ dbg_net_verb("tcp: worker %p registered %d events\n",
+ w, nfds);
+ fdset_it_t it;
+ fdset_begin(w->fdset, &it);
+ while(1) {
+
+ /* Handle incoming clients. */
+ if (it.fd == w->pipe[0]) {
+ int client = 0;
+ if (read(it.fd, &client, sizeof(int)) < 0) {
+ continue;
+ }
+
+ dbg_net_verb("tcp: worker %p registered "
+ "client %d\n",
+ w, client);
+ fdset_add(w->fdset, client, OS_EV_READ);
+ } else {
+ /* Handle other events. */
+ tcp_handle(w, it.fd);
+ }
+
+ /* Check if next exists. */
+ if (fdset_next(w->fdset, &it) != 0) {
+ break;
+ }
+ }
+
+ }
+
+ /* Stop whole unit. */
+ dbg_net_verb("tcp: worker %p finished\n", w);
+ tcp_worker_free(w);
+ return KNOTD_EOK;
+}
+
+int tcp_loop_unit(iohandler_t *ioh, dt_unit_t *unit)
+{
+ if (unit->size < 1) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Create unit data. */
+ tcp_worker_t **workers = malloc((unit->size - 1) *
+ sizeof(tcp_worker_t *));
+ if (!workers) {
+ dbg_net("tcp: cannot allocate list of workers\n");
+ return KNOTD_EINVAL;
+ }
+
+ /* Prepare worker data. */
+ unsigned allocated = 0;
+ for (unsigned i = 0; i < unit->size - 1; ++i) {
+ workers[i] = tcp_worker_create();
+ if (workers[i] == 0) {
+ break;
+ }
+ workers[i]->ioh = ioh;
+ ++allocated;
+ }
+
+ /* Check allocated workers. */
+ if (allocated != unit->size - 1) {
+ for (unsigned i = 0; i < allocated; ++i) {
+ tcp_worker_free(workers[i]);
+ }
+
+ free(workers);
+ dbg_net("tcp: cannot create workers\n");
+ return KNOTD_EINVAL;
+ }
+
+ /* Store worker data. */
+ ioh->data = workers;
+
+ /* Repurpose workers. */
+ for (unsigned i = 0; i < allocated; ++i) {
+ dt_repurpose(unit->threads[i + 1], tcp_loop_worker, workers[i]);
+ }
+
+ /* Repurpose first thread as master (unit controller). */
+ dt_repurpose(unit->threads[0], tcp_loop_master, ioh);
+
+ return KNOTD_EOK;
+}
diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h
new file mode 100644
index 0000000..f5fd17a
--- /dev/null
+++ b/src/knot/server/tcp-handler.h
@@ -0,0 +1,103 @@
+/* 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 tcp-handler.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief TCP sockets threading model.
+ *
+ * The master socket distributes incoming connections among
+ * the worker threads ("buckets"). Each threads processes it's own
+ * set of sockets, and eliminates mutual exclusion problem by doing so.
+ *
+ * \todo Improve documentation of TCP pool API and use proper error codes.
+ *
+ * \addtogroup server
+ * @{
+ */
+
+#ifndef _KNOTD_TCPHANDLER_H_
+#define _KNOTD_TCPHANDLER_H_
+
+#include <stdint.h>
+
+#include "knot/server/socket.h"
+#include "knot/server/server.h"
+#include "knot/server/dthreads.h"
+
+/*!
+ * \brief Send TCP message.
+ *
+ * \param fd Associated socket.
+ * \param msg Buffer for a query wireformat.
+ * \param msglen Buffer maximum size.
+ *
+ * \retval Number of sent data on success.
+ * \retval KNOTD_ERROR on error.
+ */
+int tcp_send(int fd, uint8_t *msg, size_t msglen);
+
+/*!
+ * \brief Send TCP message.
+ *
+ * \param fd Associated socket.
+ * \param buf Buffer for incoming bytestream.
+ * \param len Buffer maximum size.
+ * \param addr Source address.
+ *
+ * \retval Number of read bytes on success.
+ * \retval KNOTD_ERROR on error.
+ * \retval KNOTD_ENOMEM on potential buffer overflow.
+ */
+int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr);
+
+/*!
+ * \brief TCP event loop for accepting connections.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int tcp_loop_master(dthread_t *thread);
+
+/*!
+ * \brief TCP event loop for processing requests.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int tcp_loop_worker(dthread_t *thread);
+
+/*!
+ * \brief Create TCP event handler from threading unit.
+ *
+ * Set-up threading unit for processing TCP requests.
+ *
+ * \param ioh Associated I/O handler.
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int tcp_loop_unit(iohandler_t *ioh, dt_unit_t *unit);
+
+#endif // _KNOTD_TCPHANDLER_H_
+
+/*! @} */
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
new file mode 100644
index 0000000..f7ae550
--- /dev/null
+++ b/src/knot/server/udp-handler.c
@@ -0,0 +1,438 @@
+/* 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 <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "common/sockaddr.h"
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/server/udp-handler.h"
+#include "libknot/nameserver/name-server.h"
+#include "knot/stat/stat.h"
+#include "knot/server/server.h"
+#include "libknot/util/wire.h"
+#include "libknot/consts.h"
+#include "libknot/packet/packet.h"
+#include "knot/server/zones.h"
+#include "knot/server/notify.h"
+
+///*! \brief Wrapper for UDP send. */
+//static int xfr_send_udp(int session, sockaddr_t *addr, uint8_t *msg, size_t msglen)
+//{
+// return sendto(session, msg, msglen, 0, addr->ptr, addr->len);
+//}
+
+int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
+ sockaddr_t* addr, knot_nameserver_t *ns)
+{
+ dbg_net("udp: fd=%d received %zd bytes.\n", fd, qbuflen);
+
+ knot_packet_type_t qtype = KNOT_QUERY_NORMAL;
+ *resp_len = SOCKET_MTU_SZ;
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ if (packet == NULL) {
+ dbg_net("udp: failed to create packet on fd=%d\n", fd);
+ uint16_t pkt_id = knot_wire_get_id(qbuf);
+ knot_ns_error_response(ns, pkt_id, KNOT_RCODE_SERVFAIL,
+ qbuf, resp_len);
+ return KNOTD_EOK; /* Created error response. */
+ }
+
+ /* Parse query. */
+ int res = knot_ns_parse_packet(qbuf, qbuflen, packet, &qtype);
+ if (unlikely(res != KNOTD_EOK)) {
+ dbg_net("udp: failed to parse packet on fd=%d\n", fd);
+ /* Send error response on dnslib RCODE. */
+ if (res > 0) {
+ uint16_t pkt_id = knot_wire_get_id(qbuf);
+ knot_ns_error_response(ns, pkt_id, res,
+ qbuf, resp_len);
+ }
+
+ knot_packet_free(&packet);
+ return KNOTD_EOK; /* Created error response. */
+ }
+
+ /* Handle query. */
+// server_t *srv = (server_t *)knot_ns_get_data(ns);
+// knot_ns_xfr_t xfr;
+ res = KNOTD_ERROR;
+ switch(qtype) {
+
+ /* Response types. */
+ case KNOT_RESPONSE_NORMAL:
+ res = zones_process_response(ns, addr, packet,
+ qbuf, resp_len);
+ break;
+ case KNOT_RESPONSE_NOTIFY:
+ res = notify_process_response(ns, packet, addr,
+ qbuf, resp_len);
+ break;
+
+ /* Query types. */
+ case KNOT_QUERY_NORMAL:
+ res = knot_ns_answer_normal(ns, packet, qbuf,
+ resp_len);
+ break;
+ case KNOT_QUERY_AXFR:
+ /* RFC1034, p.28 requires reliable transfer protocol.
+ * Bind responds with FORMERR.
+ */
+ /*! \todo Draft exists for AXFR/UDP, but has not been standardized. */
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_FORMERR, qbuf,
+ resp_len);
+ res = KNOTD_EOK;
+ break;
+
+// /* Process AXFR over UDP. */
+// res = xfr_request_init(&xfr, XFR_TYPE_AOUT, XFR_FLAG_UDP, packet);
+// if (res != KNOTD_EOK) {
+// knot_ns_error_response(ns, knot_packet_id(packet),
+// KNOT_RCODE_SERVFAIL, qbuf,
+// resp_len);
+// res = KNOTD_EOK;
+// break;
+// }
+// xfr.send = xfr_send_udp;
+// xfr.session = dup(fd);
+// memcpy(&xfr.addr, addr, sizeof(sockaddr_t));
+// xfr_request(srv->xfr_h, &xfr);
+// dbg_net("udp: enqueued AXFR query on fd=%d\n", xfr.session);
+// *resp_len = 0;
+// return KNOTD_EOK;
+ case KNOT_QUERY_IXFR:
+ /* According to RFC1035, respond with SOA.
+ * Draft proposes trying to fit response into one packet,
+ * but I have found no tool or slave server to actually attempt
+ * IXFR/UDP.
+ */
+ knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA);
+ res = knot_ns_answer_normal(ns, packet, qbuf,
+ resp_len);
+ break;
+// /* Process IXFR over UDP. */
+// res = xfr_request_init(&xfr, XFR_TYPE_IOUT, XFR_FLAG_UDP, packet);
+// if (res != KNOTD_EOK) {
+// knot_ns_error_response(ns, knot_packet_id(packet),
+// KNOT_RCODE_SERVFAIL, qbuf,
+// resp_len);
+// res = KNOTD_EOK;
+// break;
+// }
+// xfr.send = xfr_send_udp;
+// xfr.session = dup(fd);
+// memcpy(&xfr.addr, addr, sizeof(sockaddr_t));
+// xfr_request(srv->xfr_h, &xfr);
+// dbg_net("udp: enqueued IXFR query on fd=%d\n", xfr.session);
+// *resp_len = 0;
+// return KNOTD_EOK;
+ case KNOT_QUERY_NOTIFY:
+ res = notify_process_request(ns, packet, addr,
+ qbuf, resp_len);
+ break;
+
+ /*! \todo Implement query notify/update. */
+ case KNOT_QUERY_UPDATE:
+ dbg_net("udp: UPDATE query on fd=%d not implemented\n", fd);
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_NOTIMPL, qbuf,
+ resp_len);
+ res = KNOTD_EOK;
+ break;
+
+ /* Unhandled opcodes. */
+ case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */
+ case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_REFUSED, qbuf,
+ resp_len);
+ res = KNOTD_EOK;
+ break;
+
+ /* Unknown opcodes */
+ default:
+ knot_ns_error_response(ns, knot_packet_id(packet),
+ KNOT_RCODE_FORMERR, qbuf,
+ resp_len);
+ res = KNOTD_EOK;
+ break;
+ }
+
+ knot_packet_free(&packet);
+
+ return res;
+}
+
+static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
+{
+ iohandler_t *h = (iohandler_t *)thread->data;
+ knot_nameserver_t *ns = h->server->nameserver;
+ int sock = dup(h->fd);
+
+ sockaddr_t addr;
+ if (sockaddr_init(&addr, h->type) != KNOTD_EOK) {
+ log_server_error("Socket type %d is not supported, "
+ "IPv6 support is probably disabled.\n",
+ h->type);
+ return KNOTD_ENOTSUP;
+ }
+
+ uint8_t qbuf[SOCKET_MTU_SZ];
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(struct msghdr));
+ struct iovec iov;
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = qbuf;
+ iov.iov_len = SOCKET_MTU_SZ;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = addr.ptr;
+ msg.msg_namelen = addr.len;
+
+ /* Loop until all data is read. */
+ ssize_t n = 0;
+ while (n >= 0) {
+
+ /* Receive packet. */
+ n = recvmsg(sock, &msg, 0);
+
+ /* Cancellation point. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Error and interrupt handling. */
+ if (unlikely(n <= 0)) {
+ if (errno != EINTR && errno != 0) {
+ dbg_net("udp: recvmsg() failed: %d\n",
+ errno);
+ }
+
+ if (!(h->state & ServerRunning)) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* Handle received pkt. */
+ size_t resp_len = 0;
+ int rc = udp_handle(sock, qbuf, n, &resp_len, &addr, ns);
+
+ /* Send response. */
+ if (rc == KNOTD_EOK && resp_len > 0) {
+
+ dbg_net("udp: on fd=%d, sending answer size=%zd.\n",
+ sock, resp_len);
+
+ // Send datagram
+ rc = sendto(sock, qbuf, resp_len,
+ 0, addr.ptr, addr.len);
+
+ // Check result
+ if (rc != (int)resp_len) {
+ dbg_net("udp: sendto(): failed: %d - %d.\n",
+ rc, errno);
+ }
+ }
+ }
+
+ /* Free allocd resources. */
+ close(sock);
+
+ return KNOTD_EOK;
+}
+
+#ifdef ENABLE_RECVMMSG
+#ifdef MSG_WAITFORONE
+static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
+{
+ iohandler_t *h = (iohandler_t *)thread->data;
+ knot_nameserver_t *ns = h->server->nameserver;
+ int sock = dup(h->fd);
+
+ /* Allocate batch for N packets. */
+ char *iobuf = malloc(SOCKET_MTU_SZ * RECVMMSG_BATCHLEN);
+ sockaddr_t *addrs = malloc(sizeof(sockaddr_t) * RECVMMSG_BATCHLEN);
+ struct iovec *iov = malloc(sizeof(struct iovec) * RECVMMSG_BATCHLEN);
+ struct mmsghdr *msgs = malloc(sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN);
+
+ /* Prepare batch. */
+ memset(msgs, 0, sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN);
+ for (unsigned i = 0; i < RECVMMSG_BATCHLEN; ++i) {
+ sockaddr_init(addrs + i, h->type);
+ iov[i].iov_base = iobuf + i * SOCKET_MTU_SZ;
+ iov[i].iov_len = SOCKET_MTU_SZ;
+ msgs[i].msg_hdr.msg_iov = iov + i;
+ msgs[i].msg_hdr.msg_iovlen = 1;
+ msgs[i].msg_hdr.msg_name = addrs[i].ptr;
+ msgs[i].msg_hdr.msg_namelen = addrs[i].len;
+ }
+
+ /* Loop until all data is read. */
+ ssize_t n = 0;
+ while (n >= 0) {
+
+ /* Receive multiple messages. */
+ n = recvmmsg(sock, msgs, RECVMMSG_BATCHLEN, MSG_WAITFORONE, 0);
+
+ /* Cancellation point. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Error and interrupt handling. */
+ if (unlikely(n <= 0)) {
+ if (errno != EINTR && errno != 0) {
+ dbg_net("udp: recvmmsg() failed: %d\n",
+ errno);
+ }
+
+ if (!(h->state & ServerRunning)) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* Handle each received msg. */
+ int ret = 0;
+ for (unsigned i = 0; i < n; ++i) {
+ struct iovec *cvec = msgs[i].msg_hdr.msg_iov;
+ size_t resp_len = msgs[i].msg_len;
+ ret = udp_handle(sock, cvec->iov_base, resp_len, &resp_len,
+ addrs + i, ns);
+ if (ret == KNOTD_EOK) {
+ msgs[i].msg_len = resp_len;
+ } else {
+ msgs[i].msg_len = 0;
+ }
+
+ }
+
+ /* Gather results. */
+ /*! \todo Implement with sendmmsg() when it's ready. */
+ for (unsigned i = 0; i < n; ++i) {
+ const size_t resp_len = msgs[i].msg_len;
+ if (resp_len > 0) {
+ dbg_net("udp: on fd=%d, sending answer size=%zd.\n",
+ sock, resp_len);
+
+ // Send datagram
+ sockaddr_t *addr = addrs + i;
+ struct iovec *cvec = msgs[i].msg_hdr.msg_iov;
+ int res = sendto(sock, cvec->iov_base, resp_len,
+ 0, addr->ptr, addr->len);
+
+ // Check result
+ if (res != (int)resp_len) {
+ dbg_net("udp: sendto(): failed: %d - %d.\n",
+ res, errno);
+ }
+ }
+ }
+ }
+
+ /* Free allocd resources. */
+ free(iobuf);
+ free(addrs);
+ free(iov);
+ free(msgs);
+ close(sock);
+ return KNOTD_EOK;
+}
+#endif
+#endif
+
+int udp_master(dthread_t *thread)
+{
+ iohandler_t *handler = (iohandler_t *)thread->data;
+ int sock = handler->fd;
+
+ /* Check socket. */
+ if (sock < 0) {
+ dbg_net("udp: null socket recevied, finishing.\n");
+ return KNOTD_EINVAL;
+ }
+
+ /* Set socket options. */
+ int flag = 1;
+#ifndef DISABLE_IPV6
+#
+ if (handler->type == AF_INET6) {
+ /* Disable dual-stack for performance reasons. */
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag));
+
+ /* UDP packets will not exceed a minimum MTU size. */
+ /*flag = IPV6_MIN_MTU;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &flag, sizeof(flag));
+ flag = 1; */
+ }
+#endif
+ if (handler->type == AF_INET) {
+
+//#ifdef IP_PMTUDISC_DONT
+// /* Disable fragmentation. */
+// flag = IP_PMTUDISC_DONT;
+// setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &flag, sizeof(flag));
+// flag = 1;
+//#endif
+ }
+
+ /* in case of STAT_COMPILE the following code will declare thread_stat
+ * variable in following fashion: stat_t *thread_stat;
+ */
+
+ stat_t *thread_stat = 0;
+ STAT_INIT(thread_stat); //XXX new stat instance every time.
+ stat_set_protocol(thread_stat, stat_UDP);
+
+ /* Execute proper handler. */
+ dbg_net_verb("udp: thread started (worker %p).\n", thread);
+ int ret = KNOTD_EOK;
+
+#ifdef ENABLE_RECVMMSG
+
+/* Check if MSG_WAITFORONE is supported. */
+#ifdef MSG_WAITFORONE
+ ret = udp_master_recvmmsg(thread, thread_stat);
+#else /* Don't have MSG_WAITFORONE */
+ ret = udp_master_recvfrom(thread, thread_stat);
+#endif
+
+#else /* Don't have ENABLE_RECVMMSG */
+ ret = udp_master_recvfrom(thread, thread_stat);
+#endif
+
+
+ stat_free(thread_stat);
+ dbg_net_verb("udp: worker %p finished.\n", thread);
+ return ret;
+}
+
diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h
new file mode 100644
index 0000000..f5fcd04
--- /dev/null
+++ b/src/knot/server/udp-handler.h
@@ -0,0 +1,74 @@
+/* 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 udp-handler.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief UDP sockets threading model.
+ *
+ * The master socket locks one worker thread at a time
+ * and saves events in it's own backing store for asynchronous processing.
+ * The worker threads work asynchronously in thread pool.
+ *
+ * \addtogroup server
+ * @{
+ */
+
+#ifndef _KNOTD_UDPHANDLER_H_
+#define _KNOTD_UDPHANDLER_H_
+
+#include "knot/server/socket.h"
+#include "knot/server/server.h"
+#include "knot/server/dthreads.h"
+
+/*!
+ * \brief Handle single packet.
+ *
+ * Function processses packet and prepares answer to qbuf,
+ * response length is set to resp_len.
+ *
+ * \param sock
+ * \param qbuf
+ * \param qbuflen
+ * \param resp_len
+ * \param addr
+ * \param ns
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ERROR
+ * \retval KNOTD_ENOMEM
+ */
+int udp_handle(int sock, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
+ sockaddr_t* addr, knot_nameserver_t *ns);
+
+/*!
+ * \brief UDP handler thread runnable.
+ *
+ * Listen to DNS datagrams in a loop on a UDP socket and
+ * reply to them. This runnable is designed to be used as coherent
+ * and implements cancellation point.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int udp_master(dthread_t *thread);
+
+#endif
+
+/*! @} */
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
new file mode 100644
index 0000000..45817cb
--- /dev/null
+++ b/src/knot/server/xfr-handler.c
@@ -0,0 +1,1296 @@
+/* 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <urcu.h>
+
+#include "knot/common.h"
+#include "knot/server/xfr-handler.h"
+#include "libknot/nameserver/name-server.h"
+#include "knot/other/error.h"
+#include "knot/server/socket.h"
+#include "knot/server/udp-handler.h"
+#include "knot/server/tcp-handler.h"
+#include "libknot/updates/xfr-in.h"
+#include "knot/server/zones.h"
+#include "libknot/util/error.h"
+#include "libknot/tsig-op.h"
+#include "common/evsched.h"
+
+void xfr_interrupt(xfrhandler_t *h)
+{
+ for(unsigned i = 0; i < h->unit->size; ++i) {
+ evqueue_write(h->workers[i]->q, "", 1);
+ }
+}
+
+/*!
+ * \brief SOA query timeout handler.
+ */
+static int xfr_udp_timeout(event_t *e)
+{
+ knot_ns_xfr_t *data = (knot_ns_xfr_t *)e->data;
+ if (!data) {
+ return KNOTD_EINVAL;
+ }
+
+ sockaddr_update(&data->addr);
+ char r_addr[SOCKADDR_STRLEN];
+ sockaddr_tostr(&data->addr, r_addr, sizeof(r_addr));
+ int r_port = sockaddr_portnum(&data->addr);
+
+ /* Close socket. */
+ knot_zone_t *z = data->zone;
+ if (z && knot_zone_get_contents(z) && knot_zone_data(z)) {
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(z);
+ log_zone_info("%s '%s' query to %s:%d - timeout exceeded.\n",
+ data->type == XFR_TYPE_SOA ? "SOA" : "NOTIFY",
+ zd->conf->name,
+ r_addr, r_port);
+ }
+
+ knot_ns_xfr_t cr = {};
+ cr.type = XFR_TYPE_CLOSE;
+ cr.session = data->session;
+ cr.data = data;
+ cr.zone = data->zone;
+ xfrworker_t *w = (xfrworker_t *)data->owner;
+ if (w) {
+ evqueue_write(w->q, &cr, sizeof(knot_ns_xfr_t));
+ }
+
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Query reponse event handler function.
+ *
+ * Handle single query response event.
+ *
+ * \param loop Associated event pool.
+ * \param w Associated socket watcher.
+ * \param revents Returned events.
+ */
+static int xfr_process_udp_query(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
+{
+ /* Prepare msg header. */
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(struct msghdr));
+ struct iovec iov;
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = data->wire;
+ iov.iov_len = data->wire_size;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = data->addr.ptr;
+ msg.msg_namelen = data->addr.len;
+
+ /* Receive msg. */
+ ssize_t n = recvmsg(data->session, &msg, 0);
+ size_t resp_len = data->wire_size;
+ if (n > 0) {
+ udp_handle(fd, data->wire, n, &resp_len, &data->addr, w->ns);
+ }
+
+ /* Disable timeout. */
+ evsched_t *sched =
+ ((server_t *)knot_ns_get_data(w->ns))->sched;
+ event_t *ev = (event_t *)data->data;
+ if (ev) {
+ dbg_xfr("xfr: cancelling UDP query timeout\n");
+ evsched_cancel(sched, ev);
+ ev = (event_t *)data->data;
+ if (ev) {
+ evsched_event_free(sched, ev);
+ data->data = 0;
+ }
+
+ /* Close after receiving response. */
+ knot_ns_xfr_t cr = {};
+ cr.type = XFR_TYPE_CLOSE;
+ cr.session = data->session;
+ cr.data = data;
+ cr.zone = data->zone;
+ evqueue_write(w->q, &cr, sizeof(knot_ns_xfr_t));
+ }
+
+ return KNOTD_EOK;
+}
+
+/*! \todo Document me. */
+static void xfr_free_task(knot_ns_xfr_t *task)
+{
+ if (!task) {
+ return;
+ }
+
+ xfrworker_t *w = (xfrworker_t *)task->owner;
+ if (!w) {
+ free(task);
+ return;
+ }
+
+ /* Remove from fdset. */
+ if (w->fdset) {
+ dbg_xfr("xfr_free_task: freeing fd=%d.\n", task->session);
+ fdset_remove(w->fdset, task->session);
+ }
+
+ /* Unlock if XFR/IN.*/
+ if (task->type == XFR_TYPE_AIN || task->type == XFR_TYPE_IIN) {
+ knot_zone_t *zone = task->zone;
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (zd) {
+ zd->xfr_in.wrkr = 0;
+ pthread_mutex_unlock(&zd->xfr_in.lock);
+ }
+ }
+
+ /* Remove fd-related data. */
+ xfrhandler_t *h = w->master;
+ pthread_mutex_lock(&h->tasks_mx);
+ skip_remove(h->tasks, (void*)((size_t)task->session), 0, 0);
+ pthread_mutex_unlock(&h->tasks_mx);
+
+ /*! \todo Free data. */
+ close(task->session);
+ free(task);
+}
+
+/*! \todo Document me. */
+static knot_ns_xfr_t *xfr_register_task(xfrworker_t *w, knot_ns_xfr_t *req)
+{
+ knot_ns_xfr_t *t = malloc(sizeof(knot_ns_xfr_t));
+ if (!t) {
+ return 0;
+ }
+
+ memcpy(t, req, sizeof(knot_ns_xfr_t));
+ sockaddr_update(&t->addr);
+
+ /* Update request. */
+ t->wire = 0; /* Invalidate shared buffer. */
+ t->wire_size = 0;
+ t->data = 0; /* New zone will be built. */
+
+ /* Register data. */
+ xfrhandler_t * h = w->master;
+ pthread_mutex_lock(&h->tasks_mx);
+ skip_insert(h->tasks, (void*)((ssize_t)t->session), t, 0);
+ pthread_mutex_unlock(&h->tasks_mx);
+
+ /* Add to set. */
+ fdset_add(w->fdset, t->session, OS_EV_READ);
+ t->owner = w;
+ return t;
+}
+
+/*!
+ * \brief Clean pending transfer data.
+ */
+static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data)
+{
+ int ret = KNOTD_EOK;
+ knot_changesets_t *chs = 0;
+
+ switch(data->type) {
+ case XFR_TYPE_AIN:
+ if (data->data) {
+ knot_zone_contents_deep_free(
+ (knot_zone_contents_t **)&data->data, 0);
+ data->data = 0;
+ }
+ break;
+ case XFR_TYPE_IIN:
+ if (data->data) {
+ chs = (knot_changesets_t *)data->data;
+ knot_free_changesets(&chs);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * \brief Finalize XFR/IN transfer.
+ *
+ * \param w XFR worker.
+ * \param data Associated data.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ERROR
+ */
+static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
+{
+ knot_zone_t *zone = (knot_zone_t *)data->zone;
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ const char *zorigin = zd->conf->name;
+
+ /* CLEANUP */
+// // get the zone name from Question
+// dbg_xfr_verb("Query: %p, response: %p\n", data->query, data->response);
+// const knot_dname_t *qname = knot_packet_qname(data->query);
+// char *zorigin = "(unknown)";
+// if (qname != NULL) {
+// zorigin = knot_dname_to_str(qname);
+// }
+
+ int ret = KNOTD_EOK;
+
+ switch(data->type) {
+ case XFR_TYPE_AIN:
+ dbg_xfr("xfr: AXFR/IN saving new zone\n");
+ ret = zones_save_zone(data);
+ if (ret != KNOTD_EOK) {
+ xfr_xfrin_cleanup(w, data);
+ log_zone_error("AXFR failed to save "
+ "transferred zone '%s/IN' - %s\n",
+ zorigin, knotd_strerror(ret));
+ } else {
+ dbg_xfr("xfr: AXFR/IN new zone saved.\n");
+ ret = knot_ns_switch_zone(w->ns, data);
+ if (ret != KNOTD_EOK) {
+ log_zone_error("AXFR failed to "
+ "switch in-memory zone "
+ "'%s/IN' - %s\n",
+ zorigin,
+ knotd_strerror(ret));
+ }
+ }
+ log_zone_info("AXFR transfer of zone '%s/IN' "
+ "%s.\n", zorigin,
+ ret == KNOTD_EOK ? "finished" : "failed");
+ break;
+ case XFR_TYPE_IIN:
+ /* Save changesets. */
+ dbg_xfr("xfr: IXFR/IN saving changesets\n");
+ ret = zones_store_changesets(data);
+ if (ret != KNOTD_EOK) {
+ log_zone_error("IXFR failed to save "
+ "transferred changesets "
+ "for zone '%s/IN' - %s\n",
+ zorigin, knotd_strerror(ret));
+ } else {
+ /* Update zone. */
+ ret = zones_apply_changesets(data);
+ if (ret != KNOTD_EOK) {
+ log_zone_error("IXFR failed to "
+ "apply changesets to "
+ "zone '%s/IN' - %s\n",
+ zorigin,
+ knotd_strerror(ret));
+ }
+ }
+ /* Free changesets, but not the data. */
+ knot_changesets_t *chs = (knot_changesets_t *)data->data;
+ knot_free_changesets(&chs);
+ /* CLEANUP */
+// free(chs->sets);
+// free(chs);
+ data->data = 0;
+ log_zone_info("IXFR transfer of zone '%s/IN' "
+ "%s.\n", zorigin,
+ ret == KNOTD_EOK ? "finished" : "failed");
+ break;
+ default:
+ ret = KNOTD_EINVAL;
+ break;
+ }
+
+ /* CLEANUP */
+// if (qname != NULL) {
+// free(zorigin);
+// }
+
+ return ret;
+}
+
+/*!
+ * \brief Prepare TSIG for XFR.
+ */
+static int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key)
+{
+ int ret = KNOT_EOK;
+ xfr->tsig_key = 0; /*key;*/ /*!< \todo [TSIG] DISABLED */
+// xfr->tsig_size = tsig_wire_maxsize(key);
+// xfr->digest_max_size = tsig_alg_digest_length(
+// key->algorithm);
+// xfr->digest = malloc(xfr->digest_max_size);
+// memset(xfr->digest, 0 , xfr->digest_max_size);
+// dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n",
+// xfr->digest_max_size);
+
+ return ret;
+}
+
+/*!
+ * \brief Check TSIG if exists.
+ */
+static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
+{
+ /*!< \todo [TSIG] DISABLED */
+ return knot_packet_parse_rest(xfr->query);
+
+// /* Parse rest of the packet. */
+// int ret = KNOT_EOK;
+// knot_packet_t *qry = xfr->query;
+// knot_key_t *key = 0;
+// const knot_rrset_t *tsig_rr = 0;
+// ret = knot_packet_parse_rest(qry);
+// if (ret == KNOT_EOK) {
+
+// /* Find TSIG key name from query. */
+// const knot_dname_t* kname = 0;
+// int tsig_pos = knot_packet_additional_rrset_count(qry) - 1;
+// if (tsig_pos >= 0) {
+// tsig_rr = knot_packet_additional_rrset(qry, tsig_pos);
+// if (knot_rrset_type(tsig_rr) == KNOT_RRTYPE_TSIG) {
+// dbg_xfr("xfr: found TSIG in AR\n");
+// kname = knot_rrset_owner(tsig_rr);
+// ret = KNOT_TSIG_EBADKEY;
+// }
+// }
+// if (!kname) {
+// dbg_xfr("xfr: TSIG not found in AR\n");
+// }
+
+// /* Find configured key for claimed key name. */
+// conf_key_t *ck = 0;
+// WALK_LIST(ck, conf()->keys) {
+// if (!kname) {
+// break;
+// }
+// /* Compare stored keys to claimed. */
+// if (knot_dname_compare(ck->k.name,
+// kname) == 0) {
+// dbg_xfr("xfr: found claimed "
+// "TSIG key for "
+// "comparison\n");
+// key = &ck->k;
+// break;
+// }
+// }
+
+// /* Validate with TSIG. */
+// if (key) {
+// /* Prepare variables for TSIG */
+// xfr_prepare_tsig(xfr, key);
+
+// /* Copy MAC from query. */
+// dbg_xfr("xfr: validating TSIG from query\n");
+// const uint8_t* mac = tsig_rdata_mac(tsig_rr);
+// size_t mac_len = tsig_rdata_mac_length(tsig_rr);
+// if (mac_len > xfr->digest_max_size) {
+// ret = KNOT_EMALF;
+// dbg_xfr("xfr: MAC length %zu exceeds digest "
+// "maximum size %zu\n",
+// mac_len, xfr->digest_max_size);
+// } else {
+// memcpy(xfr->digest, mac, mac_len);
+// xfr->digest_size = mac_len;
+
+// /* Check query TSIG. */
+// ret = knot_tsig_server_check(
+// tsig_rr,
+// knot_packet_wireformat(qry),
+// knot_packet_size(qry),
+// key);
+// dbg_xfr("knot_tsig_server_check() returned %s\n",
+// knot_strerror(ret));
+// }
+
+// /* Evaluate TSIG check results. */
+// switch(ret) {
+// case KNOT_EOK:
+// *rcode = KNOT_RCODE_NOERROR;
+// break;
+// case KNOT_TSIG_EBADKEY:
+// case KNOT_TSIG_EBADSIG:
+// // delete the TSIG key so that the error
+// // response is not signed
+// xfr->tsig_key = NULL;
+// case KNOT_TSIG_EBADTIME:
+// /*! \note [TSIG] Set TSIG rcode in TSIG RR. */
+
+// *rcode = KNOT_RCODE_NOTAUTH;
+// break;
+// case KNOT_EMALF:
+// *rcode = KNOT_RCODE_FORMERR;
+// break;
+// default:
+// *rcode = KNOT_RCODE_SERVFAIL;
+// }
+// } else {
+// dbg_xfr("xfr: no claimed key configured, "
+// "treating as bad key\n");
+// }
+// } else {
+// dbg_xfr("xfr: failed to parse rest of the packet\n");
+// *rcode = KNOT_RCODE_FORMERR;
+// }
+
+// return ret;
+}
+
+/*!
+ * \brief XFR-IN event handler function.
+ *
+ * Handle single XFR client event.
+ *
+ * \param w Associated XFR worker.
+ * \param fd Associated file descriptor.
+ * \param data Transfer data.
+ */
+int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
+{
+ /* Buffer for answering. */
+ uint8_t buf[65535];
+
+ /* Update xfer state. */
+ data->wire = buf;
+ data->wire_size = sizeof(buf);
+
+ /* Handle SOA/NOTIFY responses. */
+ if (data->type == XFR_TYPE_NOTIFY || data->type == XFR_TYPE_SOA) {
+ return xfr_process_udp_query(w, fd, data);
+ }
+
+ /* Read DNS/TCP packet. */
+ int ret = 0;
+ int rcvd = tcp_recv(fd, buf, sizeof(buf), 0);
+ data->wire_size = rcvd;
+ if (rcvd <= 0) {
+ data->wire_size = 0;
+ ret = KNOT_ECONN;
+ } else {
+
+ /*!
+ * \todo [TSIG] Somewhere before this fetch the query digest and the TSIG
+ * associated with this transfer and save them to 'data'.
+ */
+
+ /* Process incoming packet. */
+ switch(data->type) {
+ case XFR_TYPE_AIN:
+ ret = knot_ns_process_axfrin(w->ns, data);
+ break;
+ case XFR_TYPE_IIN:
+ ret = knot_ns_process_ixfrin(w->ns, data);
+ break;
+ default:
+ ret = KNOT_EBADARG;
+ break;
+ }
+ }
+
+ /* AXFR-style IXFR. */
+ if (ret == KNOT_ENOIXFR) {
+ dbg_xfr("xfr: Fallback to AXFR/IN.\n");
+ assert(data->type == XFR_TYPE_IIN);
+ data->type = XFR_TYPE_AIN;
+ ret = knot_ns_process_axfrin(w->ns, data);
+ }
+
+ /* Check return code for errors. */
+ dbg_xfr_verb("xfr: processed incoming XFR packet (res = %d)\n", ret);
+
+ /* Finished xfers. */
+ int xfer_finished = 0;
+ if (ret != KNOT_EOK) {
+ xfer_finished = 1;
+ }
+
+ /* IXFR refused, try again with AXFR. */
+ knot_zone_t *zone = (knot_zone_t *)data->zone;
+ if (zone && data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) {
+ log_server_notice("IXFR/IN failed, attempting to use "
+ "AXFR/IN instead.\n");
+ size_t bufsize = sizeof(buf);
+ data->wire_size = sizeof(buf); /* Reset maximum bufsize */
+ ret = xfrin_create_axfr_query(zone->name, data,
+ &bufsize, 1);
+ /* Send AXFR/IN query. */
+ if (ret == KNOTD_EOK) {
+ ret = data->send(data->session, &data->addr,
+ data->wire, bufsize);
+ /* Switch to AIN type XFR and return now. */
+ if (ret == bufsize) {
+ data->type = XFR_TYPE_AIN;
+ return KNOTD_EOK;
+ }
+ }
+ }
+
+ /* Handle errors. */
+ if (ret < 0 && ret != KNOT_ENOXFR) {
+ log_server_error("%cXFR/IN request failed - %s\n",
+ data->type == XFR_TYPE_AIN ? 'A' : 'I',
+ knot_strerror(ret));
+ }
+
+ /* Check finished zone. */
+ if (xfer_finished) {
+
+ knot_zone_t *zone = (knot_zone_t *)data->zone;
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ const char *zorigin = zd->conf->name;
+
+ /* Only for successful xfers. */
+ if (ret > 0) {
+ ret = xfr_xfrin_finalize(w, data);
+
+ /* AXFR bootstrap timeout. */
+ rcu_read_lock();
+ if (!knot_zone_contents(zone) && data->type == XFR_TYPE_AIN) {
+ /* Schedule request (60 - 90s random delay). */
+ int tmr_s = AXFR_BOOTSTRAP_RETRY;
+ tmr_s += (30.0 * 1000) * (rand() / (RAND_MAX + 1.0));
+ zd->xfr_in.bootstrap_retry = tmr_s;
+ log_zone_info("Another attempt to AXFR bootstrap "
+ "zone '%s' in %d seconds.\n",
+ zorigin, tmr_s/1000);
+ }
+ rcu_read_unlock();
+
+ /* Update timers. */
+ server_t *server = (server_t *)knot_ns_get_data(w->ns);
+ zones_timers_update(zone, zd->conf, server->sched);
+
+ } else {
+ /* Cleanup */
+ xfr_xfrin_cleanup(w, data);
+ }
+
+ /* Free TSIG buffers. */
+ if (data->digest) {
+ free(data->digest);
+ data->digest = 0;
+ data->digest_size = 0;
+ }
+ if (data->tsig_data) {
+ free(data->tsig_data);
+ data->tsig_data = 0;
+ data->tsig_data_size = 0;
+ }
+
+ /* Disconnect. */
+ ret = KNOTD_ECONNREFUSED; /* Make it disconnect. */
+ } else {
+ ret = KNOTD_EOK;
+ }
+
+ return ret;
+}
+
+/*! \todo Document me.
+ */
+static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
+{
+ /* Fetch associated zone. */
+ knot_zone_t *zone = (knot_zone_t *)data->zone;
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Check if not already processing. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (!zd) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Enqueue to worker that has zone locked for XFR/IN. */
+ int ret = pthread_mutex_trylock(&zd->xfr_in.lock);
+ if (ret != 0) {
+ dbg_xfr_verb("xfr: XFR/IN switching to another thread, "
+ "zone '%s' is already in transfer\n",
+ zd->conf->name);
+ xfrworker_t *nextw = (xfrworker_t *)zd->xfr_in.wrkr;
+ if (nextw == 0) {
+ nextw = w;
+ }
+ evqueue_write(nextw->q, data, sizeof(knot_ns_xfr_t));
+ return KNOTD_EOK;
+ } else {
+ zd->xfr_in.wrkr = w;
+ }
+
+ /* Update address. */
+ sockaddr_update(&data->addr);
+ char r_addr[SOCKADDR_STRLEN];
+ sockaddr_tostr(&data->addr, r_addr, sizeof(r_addr));
+ int r_port = sockaddr_portnum(&data->addr);
+
+ /* Connect to remote. */
+ if (data->session <= 0) {
+ int fd = socket_create(data->addr.family, SOCK_STREAM);
+ if (fd < 0) {
+ log_server_warning("Failed to create socket "
+ "(type=%s, family=%s).\n",
+ "SOCK_STREAM",
+ data->addr.family == AF_INET ?
+ "AF_INET" : "AF_INET6");
+ return KNOTD_ERROR;
+ }
+ ret = connect(fd, data->addr.ptr, data->addr.len);
+ if (ret < 0) {
+ log_server_warning("Failed to connect to %cXFR master "
+ "at %s:%d.\n",
+ data->type == XFR_TYPE_AIN ? 'A' : 'I',
+ r_addr, r_port);
+ if (!knot_zone_contents(zone)) {
+ /* Reschedule request (120 - 240s random delay). */
+ int tmr_s = AXFR_BOOTSTRAP_RETRY * 2; /* Malus x2 */
+ tmr_s += (int)((120.0 * 1000) *
+ (rand() / (RAND_MAX + 1.0)));
+ event_t *ev = zd->xfr_in.timer;
+ if (ev) {
+ evsched_cancel(ev->parent, ev);
+ evsched_schedule(ev->parent, ev, tmr_s);
+ }
+ log_zone_notice("Zone AXFR bootstrap failed, "
+ "another attempt in %d seconds."
+ "\n", tmr_s / 1000);
+ }
+ return KNOTD_ERROR;
+ }
+
+ /* Store new socket descriptor. */
+ data->session = fd;
+ } else {
+ /* Duplicate existing socket descriptor. */
+ data->session = dup(data->session);
+ }
+
+ /* Fetch zone contents. */
+ rcu_read_lock();
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+ if (!contents && data->type == XFR_TYPE_IIN) {
+ rcu_read_unlock();
+ log_server_warning("Failed start IXFR on zone with no "
+ "contents\n");
+ return KNOTD_ERROR;
+ }
+
+ /*! \todo [TSIG] Somewhere before this determine if the server should
+ * use TSIG for this transfer and set appropriate fields
+ * in 'data'.
+ */
+ int add_tsig = 0;
+ if (data->tsig_key) {
+ if (xfr_prepare_tsig(data, data->tsig_key) == KNOT_EOK) {
+ size_t data_bufsize = KNOT_NS_TSIG_DATA_MAX_SIZE;
+ data->tsig_data = malloc(data_bufsize);
+ if (data->tsig_data) {
+ dbg_xfr("xfr: using TSIG for XFR/IN\n");
+ add_tsig = 1;
+ data->tsig_data_size = data_bufsize;
+ } else {
+ dbg_xfr("xfr: failed to allocate TSIG data "
+ "buffer (%zu kB)\n",
+ data_bufsize / 1024);
+ }
+ }
+ }
+
+ /* Create XFR query. */
+ size_t bufsize = data->wire_size;
+ switch(data->type) {
+ case XFR_TYPE_AIN:
+ ret = xfrin_create_axfr_query(zone->name, data, &bufsize, add_tsig);
+ break;
+ case XFR_TYPE_IIN:
+ ret = xfrin_create_ixfr_query(contents, data, &bufsize, add_tsig);
+ break;
+ default:
+ ret = KNOTD_EINVAL;
+ break;
+ }
+
+ /* Handle errors. */
+ if (ret != KNOT_EOK) {
+ dbg_xfr("xfr: failed to create XFR query type %d: %s\n",
+ data->type, knot_strerror(ret));
+ return ret;
+ }
+
+ /* Unlock zone contents. */
+ rcu_read_unlock();
+
+ /* Add to pending transfers. */
+ knot_ns_xfr_t *task = xfr_register_task(w, data);
+
+ ret = data->send(data->session, &data->addr, data->wire, bufsize);
+ if (ret != bufsize) {
+ log_server_notice("Failed to send %cXFR query.",
+ data->type == XFR_TYPE_AIN ? 'A' : 'I');
+ xfr_free_task(task);
+ return KNOTD_ERROR;
+ }
+
+ /* Send XFR query. */
+ log_server_info("%cXFR transfer of zone '%s/IN' with %s:%d started.\n",
+ data->type == XFR_TYPE_AIN ? 'A' : 'I',
+ zd->conf->name,
+ r_addr, r_port);
+
+ return KNOTD_EOK;
+}
+
+static int xfr_fd_compare(void *k1, void *k2)
+{
+ if (k1 < k2) {
+ return -1;
+ }
+
+ if (k1 > k2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Public APIs.
+ */
+
+static xfrworker_t* xfr_worker_create(xfrhandler_t *h, knot_nameserver_t *ns)
+{
+ xfrworker_t *w = malloc(sizeof(xfrworker_t));
+ if(!w) {
+ return 0;
+ }
+
+ /* Set nameserver and master. */
+ w->ns = ns;
+ w->master = h;
+
+ /* Create event queue. */
+ w->q = evqueue_new();
+ if (!w->q) {
+ free(w);
+ return 0;
+ }
+
+ /* Create fdset. */
+ w->fdset = fdset_new();
+ if (!w->fdset) {
+ evqueue_free(&w->q);
+ free(w);
+ return 0;
+ }
+
+ /* Add evqueue to fdset. */
+ fdset_add(w->fdset, evqueue_pollfd(w->q), OS_EV_READ);
+
+ return w;
+}
+
+static void xfr_worker_free(xfrworker_t *w) {
+ if (w) {
+ evqueue_free(&w->q);
+ fdset_destroy(w->fdset);
+ free(w);
+ }
+}
+
+xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns)
+{
+ /* Create XFR handler data. */
+ xfrhandler_t *data = malloc(sizeof(xfrhandler_t));
+ if (!data) {
+ return 0;
+ }
+ memset(data, 0, sizeof(xfrhandler_t));
+
+ /* Create RR mutex. */
+ pthread_mutex_init(&data->rr_mx, 0);
+
+ /* Create tasks structure and mutex. */
+ pthread_mutex_init(&data->tasks_mx, 0);
+ data->tasks = skip_create_list(xfr_fd_compare);
+
+ /* Initialize threads. */
+ data->workers = malloc(thrcount * sizeof(xfrhandler_t*));
+ if(data->workers == 0) {
+ pthread_mutex_destroy(&data->rr_mx);
+ free(data);
+ }
+
+ /* Create threading unit. */
+ dt_unit_t *unit = dt_create(thrcount);
+ if (!unit) {
+ pthread_mutex_destroy(&data->rr_mx);
+ free(data->workers);
+ free(data);
+ return 0;
+ }
+ data->unit = unit;
+
+ /* Create worker threads. */
+ unsigned initialized = 0;
+ for (unsigned i = 0; i < thrcount; ++i) {
+ data->workers[i] = xfr_worker_create(data, ns);
+ if(data->workers[i] == 0) {
+ break;
+ }
+ ++initialized;
+ }
+
+ /* Check for initialized. */
+ if (initialized != thrcount) {
+ for (unsigned i = 0; i < initialized; ++i) {
+ xfr_worker_free(data->workers[i]);
+ }
+ pthread_mutex_destroy(&data->rr_mx);
+ free(data->workers);
+ free(data->unit);
+ free(data);
+ return 0;
+ }
+
+ /* Assign worker threads. */
+ for (unsigned i = 0; i < thrcount; ++i) {
+ dt_repurpose(unit->threads[i], xfr_worker, data->workers[i]);
+ }
+
+ data->interrupt = xfr_interrupt;
+
+ return data;
+}
+
+int xfr_free(xfrhandler_t *handler)
+{
+ if (!handler) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Free RR mutex. */
+ pthread_mutex_destroy(&handler->rr_mx);
+
+ /* Free tasks and mutex. */
+ skip_destroy_list(&handler->tasks, 0,
+ (void(*)(void*))xfr_free_task);
+ pthread_mutex_destroy(&handler->tasks_mx);
+
+ /* Free workers. */
+ for (unsigned i = 0; i < handler->unit->size; ++i) {
+ xfr_worker_free(handler->workers[i]);
+ }
+ free(handler->workers);
+
+ /* Delete unit. */
+ dt_delete(&handler->unit);
+ free(handler);
+
+ return KNOTD_EOK;
+}
+
+int xfr_stop(xfrhandler_t *handler)
+{
+ /* Break loop. */
+ dt_stop(handler->unit);
+ return KNOTD_EOK;
+}
+
+int xfr_join(xfrhandler_t *handler) {
+ return dt_join(handler->unit);
+}
+
+int xfr_request_init(knot_ns_xfr_t *r, int type, int flags, knot_packet_t *pkt)
+{
+ if (!r || type < 0 || flags < 0) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Blank and init. */
+ memset(r, 0, sizeof(knot_ns_xfr_t));
+ r->type = type;
+ r->flags = flags;
+
+ /* Copy packet if applicable. */
+ if (pkt != 0) {
+ uint8_t *wire_copy = malloc(sizeof(uint8_t) * pkt->size);
+ if (!wire_copy) {
+ ERR_ALLOC_FAILED;
+ return KNOTD_ENOMEM;
+ }
+ memcpy(wire_copy, pkt->wireformat, pkt->size);
+ pkt->wireformat = wire_copy;
+ r->query = pkt;
+ }
+
+ return KNOTD_EOK;
+}
+
+int xfr_request(xfrhandler_t *handler, knot_ns_xfr_t *req)
+{
+ if (!handler || !req) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Get next worker in RR fashion */
+ pthread_mutex_lock(&handler->rr_mx);
+ evqueue_t *q = handler->workers[handler->rr]->q;
+ handler->rr = get_next_rr(handler->rr, handler->unit->size);
+ pthread_mutex_unlock(&handler->rr_mx);
+
+ /* Delegate request. */
+ int ret = evqueue_write(q, req, sizeof(knot_ns_xfr_t));
+ if (ret < 0) {
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+
+static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
+{
+ /* Read single request. */
+ knot_ns_xfr_t xfr = {};
+ int ret = evqueue_read(w->q, &xfr, sizeof(knot_ns_xfr_t));
+ if (ret != sizeof(knot_ns_xfr_t)) {
+ dbg_xfr_verb("xfr: evqueue_read() returned %d.\n", ret);
+ return KNOTD_ENOTRUNNING;
+ }
+
+ /* Update request. */
+ sockaddr_update(&xfr.addr);
+ xfr.wire = buf;
+ xfr.wire_size = buflen;
+ char r_addr[SOCKADDR_STRLEN];
+ sockaddr_tostr(&xfr.addr, r_addr, sizeof(r_addr));
+ int r_port = sockaddr_portnum(&xfr.addr);
+
+ conf_read_lock();
+
+ /* Handle request. */
+ knot_ns_xfr_t *task = 0;
+ evsched_t *sch = 0;
+ const char *req_type = "";
+ knot_rcode_t rcode = 0;
+ char *zname = "(unknown)";
+
+ /* XFR request state tracking. */
+ int init_failed = 0;
+ const char *errstr = "";
+ const knot_dname_t *qname = NULL;
+
+ dbg_xfr_verb("Query ptr: %p\n", xfr.query);
+
+ dbg_xfr_verb("xfr: processing request type '%d'\n", xfr.type);
+ switch(xfr.type) {
+ case XFR_TYPE_AOUT:
+ req_type = "AXFR/OUT";
+ ret = knot_ns_init_xfr(w->ns, &xfr);
+ init_failed = (ret != KNOT_EOK);
+ errstr = knot_strerror(ret);
+
+ // use the QNAME as the zone name to get names also for
+ // zones that are not in the server
+ qname = knot_packet_qname(xfr.query);
+ if (qname != NULL) {
+ zname = knot_dname_to_str(qname);
+ }
+
+ /* Check requested zone. */
+ if (!init_failed) {
+ ret = zones_xfr_check_zone(&xfr, &rcode);
+ init_failed = (ret != KNOTD_EOK);
+ errstr = knotd_strerror(ret);
+ }
+
+ /* Check TSIG. */
+ if (!init_failed) {
+ ret = xfr_check_tsig(&xfr, &rcode);
+ init_failed = (ret != KNOT_EOK);
+ errstr = knot_strerror(ret);
+ }
+
+ /* Evaluate progress and answer if passed. */
+ if (init_failed) {
+ knot_ns_xfr_send_error(w->ns, &xfr, rcode);
+ socket_close(xfr.session);
+ log_server_notice("AXFR transfer of zone '%s/OUT' "
+ "%s:%d failed: %s\n",
+ zname,
+ r_addr, r_port,
+ errstr);
+ } else {
+ ret = knot_ns_answer_axfr(w->ns, &xfr);
+ dbg_xfr("xfr: ns_answer_axfr() = %d.\n", ret);
+ if (ret != KNOTD_EOK) {
+ socket_close(xfr.session);
+ } else {
+ log_server_info("AXFR transfer of zone '%s/OUT' "
+ "to %s:%d successful.\n",
+ zname,
+ r_addr, r_port);
+ }
+ }
+
+ if (xfr.digest) {
+ free(xfr.digest);
+ xfr.digest_max_size = 0;
+ xfr.digest = 0;
+ }
+ free(xfr.query->wireformat);
+ xfr.query->wireformat = 0;
+ knot_packet_free(&xfr.query); /* Free query. */
+
+ if (qname != NULL) {
+ free(zname);
+ }
+
+ break;
+ case XFR_TYPE_IOUT:
+ req_type = "IXFR/OUT";
+ ret = knot_ns_init_xfr(w->ns, &xfr);
+ init_failed = (ret != KNOT_EOK);
+ errstr = knot_strerror(ret);
+
+ qname = knot_packet_qname(xfr.query);
+ if (qname != NULL) {
+ zname = knot_dname_to_str(qname);
+ }
+
+ /* Check requested zone. */
+ if (!init_failed) {
+ ret = zones_xfr_check_zone(&xfr, &rcode);
+ init_failed = (ret != KNOTD_EOK);
+ errstr = knotd_strerror(ret);
+ }
+
+ /* Check TSIG. */
+ if (!init_failed) {
+ ret = xfr_check_tsig(&xfr, &rcode);
+ init_failed = (ret != KNOT_EOK);
+ errstr = knot_strerror(ret);
+ }
+
+ uint32_t serial_from = 0;
+ uint32_t serial_to = 0;
+
+ // Check serial differeces
+ if (!init_failed) {
+ dbg_xfr_verb("Loading serials for IXFR.\n");
+ ret = ns_ixfr_load_serials(&xfr, &serial_from,
+ &serial_to);
+ dbg_xfr_detail("Loaded serials: from: %u, to: %u\n",
+ serial_from, serial_to);
+ init_failed = (ret != KNOT_EOK);
+ errstr = knot_strerror(ret);
+ }
+
+ /* Load changesets from journal. */
+ if (!init_failed) {
+ dbg_xfr_verb("Loading changesets from journal.\n");
+ ret = zones_xfr_load_changesets(&xfr, serial_from,
+ serial_to);
+ if (ret != KNOTD_EOK) {
+ /* History cannot be reconstructed, fallback to AXFR. */
+ if (ret == KNOTD_ERANGE || ret == KNOTD_ENOENT) {
+ log_server_info("IXFR transfer of zone '%s/OUT'"
+ " - failed to load data from journal: %s."
+ " Fallback to AXFR.\n",
+ knotd_strerror(ret), zname);
+ xfr.type = XFR_TYPE_AOUT;
+ xfr_request(w->master, &xfr);
+ conf_read_unlock();
+ return KNOTD_EOK;
+ } else if (ret == KNOTD_EMALF) {
+ rcode = KNOT_RCODE_FORMERR;
+ } else {
+ rcode = KNOT_RCODE_SERVFAIL;
+ }
+ init_failed = (ret != KNOTD_EOK);
+ errstr = knotd_strerror(ret);
+ }
+ }
+
+ /* Evaluate progress and answer if passed. */
+ if (init_failed) {
+ knot_ns_xfr_send_error(w->ns, &xfr, rcode);
+ log_server_notice("IXFR transfer of zone '%s/OUT' "
+ "%s:%d failed: %s\n",
+ zname,
+ r_addr, r_port,
+ errstr);
+ ret = KNOTD_ERROR;
+ } else {
+ ret = knot_ns_answer_ixfr(w->ns, &xfr);
+ dbg_xfr("xfr: ns_answer_ixfr() = %d.\n", ret);
+ if (ret != KNOTD_EOK) {
+ socket_close(xfr.session);
+ } else {
+ log_server_info("IXFR transfer of zone '%s/OUT'"
+ " - not enough data in journal,"
+ " fallback to AXFR.\n",
+ zname);
+ xfr.type = XFR_TYPE_AOUT;
+ xfr_request(w->master, &xfr);
+ return KNOTD_EOK;
+ }
+ }
+ if (xfr.digest) {
+ free(xfr.digest);
+ xfr.digest = 0;
+ xfr.digest_max_size = 0;
+ }
+ free(xfr.query->wireformat);
+ knot_packet_free(&xfr.query); /* Free query. */
+
+ if (qname) {
+ free(zname);
+ }
+
+ break;
+ case XFR_TYPE_AIN:
+ req_type = "AXFR/IN";
+ case XFR_TYPE_IIN:
+ if (xfr.type == XFR_TYPE_IIN) {
+ req_type = "IXFR/IN";
+ }
+
+ ret = xfr_client_start(w, &xfr);
+
+ /* Report. */
+ if (ret != KNOTD_EOK && ret != KNOTD_EACCES) {
+ log_server_error("%s request from %s:%d failed: %s\n",
+ req_type, r_addr, r_port,
+ knotd_strerror(ret));
+ }
+ break;
+ case XFR_TYPE_SOA:
+ case XFR_TYPE_NOTIFY:
+ /* Register task. */
+ task = xfr_register_task(w, &xfr);
+ if (!task) {
+ ret = KNOTD_ENOMEM;
+ break;
+ }
+
+ req_type = "SOA or NOTIFY";
+ dbg_xfr("xfr: waiting for %s query response\n",
+ xfr.type == XFR_TYPE_SOA ? "SOA" : "NOTIFY");
+
+ /* Add timeout. */
+ sch = ((server_t *)knot_ns_get_data(w->ns))->sched;
+ task->data = evsched_schedule_cb(sch, xfr_udp_timeout,
+ task, SOA_QRY_TIMEOUT);
+ ret = KNOTD_EOK;
+ break;
+ /* Socket close event. */
+ case XFR_TYPE_CLOSE:
+ xfr_free_task((knot_ns_xfr_t *)xfr.data);
+ ret = KNOTD_EOK;
+ default:
+ break;
+ }
+
+ conf_read_unlock();
+
+ return ret;
+}
+
+int xfr_worker(dthread_t *thread)
+{
+ xfrworker_t *w = (xfrworker_t *)thread->data;
+
+ /* Check data. */
+ if (w < 0) {
+ dbg_xfr("xfr: NULL worker data, worker cancelled\n");
+ return KNOTD_EINVAL;
+ }
+
+ /* Buffer for answering. */
+ uint8_t buf[65535];
+
+ /* Accept requests. */
+ int ret = 0;
+ dbg_xfr_verb("xfr: worker=%p starting\n", w);
+ for (;;) {
+
+ /* Check for cancellation. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Poll fdset. */
+ int nfds = fdset_wait(w->fdset);
+ if (nfds <= 0) {
+ continue;
+ }
+
+ /* Check for cancellation. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Iterate fdset. */
+ xfrhandler_t *h = w->master;
+ knot_ns_xfr_t *data = 0;
+ int rfd = evqueue_pollfd(w->q);
+ fdset_it_t it;
+ fdset_begin(w->fdset, &it);
+ while(1) {
+
+ /* Check if it request. */
+ if (it.fd == rfd) {
+ dbg_xfr_verb("xfr: worker=%p processing request\n",
+ w);
+ ret = xfr_process_request(w, buf, sizeof(buf));
+ if (ret == KNOTD_ENOTRUNNING) {
+ break;
+ }
+ } else {
+ /* Find data. */
+ pthread_mutex_lock(&h->tasks_mx);
+ data = skip_find(h->tasks, (void*)((size_t)it.fd));
+ pthread_mutex_unlock(&h->tasks_mx);
+ dbg_xfr_verb("xfr: worker=%p processing event on "
+ "fd=%d data=%p.\n",
+ w, it.fd, data);
+ ret = xfr_process_event(w, it.fd, data);
+ if (ret != KNOTD_EOK) {
+ xfr_free_task(data);
+ }
+ }
+
+ /* Next fd. */
+ if (fdset_next(w->fdset, &it) < 0) {
+ break;
+ }
+ }
+ }
+
+
+ /* Stop whole unit. */
+ dbg_xfr_verb("xfr: worker=%p finished.\n", w);
+ thread->data = 0;
+ return KNOTD_EOK;
+}
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
new file mode 100644
index 0000000..3587d93
--- /dev/null
+++ b/src/knot/server/xfr-handler.h
@@ -0,0 +1,163 @@
+/* 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 xfr-handler.h
+ *
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief XFR requests handler.
+ *
+ * \addtogroup server
+ * @{
+ */
+
+#ifndef _KNOTD_XFRHANDLER_H_
+#define _KNOTD_XFRHANDLER_H_
+
+#include "knot/server/dthreads.h"
+#include "libknot/nameserver/name-server.h"
+#include "common/evqueue.h"
+#include "common/fdset.h"
+#include "common/skip-list.h" /*!< \todo Consider another data struct. */
+
+struct xfrhandler_t;
+
+/*!
+ * \brief XFR worker structure.
+ */
+typedef struct xfrworker_t
+{
+ knot_nameserver_t *ns; /*!< \brief Pointer to nameserver.*/
+ evqueue_t *q; /*!< \brief Shared XFR requests queue.*/
+ fdset_t *fdset; /*!< \brief File descriptor set. */
+ struct xfrhandler_t *master; /*! \brief Worker master. */
+} xfrworker_t;
+
+/*!
+ * \brief XFR handler structure.
+ */
+typedef struct xfrhandler_t
+{
+ dt_unit_t *unit; /*!< \brief Threading unit. */
+ xfrworker_t **workers; /*!< \brief Workers. */
+ skip_list_t *tasks; /*!< \brief Pending tasks. */
+ pthread_mutex_t tasks_mx; /*!< \brief Tasks synchronisation. */
+ void (*interrupt)(struct xfrhandler_t *h); /*!< Interrupt handler. */
+ unsigned rr; /*!< \brief Round-Robin counter. */
+ pthread_mutex_t rr_mx; /*!< \brief RR mutex. */
+} xfrhandler_t;
+
+/*!
+ * \brief Create XFR threading unit.
+ *
+ * Unit can be controlled by standard DThreads API.
+ * Unit is created in Idle mode.
+ *
+ * \param thrcount Requested number of threads.
+ * \param ns Pointer to nameserver.
+ *
+ * \retval New handler on success.
+ * \retval NULL on error.
+ */
+xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns);
+
+/*!
+ * \brief Delete XFR handler.
+ *
+ * \warning Threading unit must be stopped and joined.
+ *
+ * \param handler XFR handler.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on NULL handler.
+ * \retval KNOTD_ERROR on error.
+ */
+int xfr_free(xfrhandler_t *handler);
+
+/*!
+ * \brief Start XFR handler.
+ *
+ * \param handler XFR handler.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ERROR on error.
+ */
+static inline int xfr_start(xfrhandler_t *handler) {
+ return dt_start(handler->unit);
+}
+
+/*!
+ * \brief Stop XFR handler.
+ *
+ * \param handler XFR handler.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ERROR on error.
+ */
+int xfr_stop(xfrhandler_t *handler);
+
+/*!
+ * \brief Wait for XFR handler to finish.
+ *
+ * \param handler XFR handler.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_ERROR on error.
+ */
+int xfr_join(xfrhandler_t *handler);
+
+/*!
+ * \brief Prepare XFR request.
+ *
+ * \param r XFR request.
+ * \param type Request type.
+ * \param flags Request flags.
+ * \param pkt Query packet or NULL.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_ENOMEM
+ * \retval KNOTD_EINVAL
+ */
+int xfr_request_init(knot_ns_xfr_t *r, int type, int flags, knot_packet_t *pkt);
+
+/*!
+ * \brief Enqueue XFR request.
+ *
+ * \param handler XFR handler instance.
+ * \param req XFR request.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on NULL handler or request.
+ * \retval KNOTD_ERROR on error.
+ */
+int xfr_request(xfrhandler_t *handler, knot_ns_xfr_t *req);
+
+/*!
+ * \brief XFR master runnable.
+ *
+ * Processes incoming AXFR/IXFR requests asynchonously.
+ * When no thread is available at the moment, request is enqueued.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL invalid parameters.
+ */
+int xfr_worker(dthread_t *thread);
+
+#endif // _KNOTD_XFRHANDLER_H_
+
+/*! @} */
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
new file mode 100644
index 0000000..4456dac
--- /dev/null
+++ b/src/knot/server/zones.c
@@ -0,0 +1,2352 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+
+#include "common/lists.h"
+#include "libknot/dname.h"
+#include "libknot/util/wire.h"
+#include "knot/zone/zone-dump-text.h"
+#include "knot/zone/zone-load.h"
+#include "libknot/zone/zone.h"
+#include "libknot/zone/zonedb.h"
+#include "knot/conf/conf.h"
+#include "knot/other/debug.h"
+#include "knot/other/error.h"
+#include "knot/other/log.h"
+#include "knot/server/notify.h"
+#include "knot/server/server.h"
+#include "libknot/updates/xfr-in.h"
+#include "knot/server/zones.h"
+#include "libknot/util/error.h"
+#include "knot/zone/zone-dump.h"
+#include "libknot/nameserver/name-server.h"
+#include "libknot/updates/changesets.h"
+
+static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100;
+static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Wrapper for TCP send.
+ * \todo Implement generic fd pool properly with callbacks.
+ */
+#include "knot/server/tcp-handler.h"
+static int zones_send_cb(int fd, sockaddr_t *addr, uint8_t *msg, size_t msglen)
+{
+ return tcp_send(fd, msg, msglen);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Zone data destructor function. */
+static int zonedata_destroy(knot_zone_t *zone)
+{
+ dbg_zones_verb("zones: zonedata_destroy(%p) called\n", zone);
+
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (!zd) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Cancel REFRESH timer. */
+ if (zd->xfr_in.timer) {
+ evsched_t *sch = zd->xfr_in.timer->parent;
+ evsched_cancel(sch, zd->xfr_in.timer);
+ evsched_event_free(sch, zd->xfr_in.timer);
+ zd->xfr_in.timer = 0;
+ }
+
+ /* Cancel EXPIRE timer. */
+ if (zd->xfr_in.expire) {
+ evsched_t *sch = zd->xfr_in.expire->parent;
+ evsched_cancel(sch, zd->xfr_in.expire);
+ evsched_event_free(sch, zd->xfr_in.expire);
+ zd->xfr_in.expire = 0;
+ }
+
+ /* Remove list of pending NOTIFYs. */
+ pthread_mutex_lock(&zd->lock);
+ notify_ev_t *ev = 0, *evn = 0;
+ WALK_LIST_DELSAFE(ev, evn, zd->notify_pending) {
+ zones_cancel_notify(zd, ev);
+ }
+ pthread_mutex_unlock(&zd->lock);
+
+ /* Cancel IXFR DB sync timer. */
+ if (zd->ixfr_dbsync) {
+ evsched_t *sch = zd->ixfr_dbsync->parent;
+ evsched_cancel(sch, zd->ixfr_dbsync);
+ evsched_event_free(sch, zd->ixfr_dbsync);
+ zd->ixfr_dbsync = 0;
+ }
+
+ /* Destroy mutex. */
+ pthread_mutex_destroy(&zd->lock);
+ pthread_mutex_destroy(&zd->xfr_in.lock);
+
+ acl_delete(&zd->xfr_in.acl);
+ acl_delete(&zd->xfr_out);
+ acl_delete(&zd->notify_in);
+ acl_delete(&zd->notify_out);
+
+ /* Close IXFR db. */
+ journal_close(zd->ixfr_db);
+
+ free(zd);
+
+ /* Invalidate. */
+ zone->dtor = 0;
+ zone->data = 0;
+
+ return KNOTD_EOK;
+}
+
+/*! \brief Zone data constructor function. */
+static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone)
+{
+ zonedata_t *zd = malloc(sizeof(zonedata_t));
+ if (!zd) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Link to config. */
+ zd->conf = cfg;
+ zd->server = 0;
+
+ /* Initialize mutex. */
+ pthread_mutex_init(&zd->lock, 0);
+
+ /* Initialize ACLs. */
+ zd->xfr_out = 0;
+ zd->notify_in = 0;
+ zd->notify_out = 0;
+
+ /* Initialize XFR-IN. */
+ sockaddr_init(&zd->xfr_in.master, -1);
+ zd->xfr_in.timer = 0;
+ zd->xfr_in.expire = 0;
+ zd->xfr_in.next_id = -1;
+ zd->xfr_in.acl = 0;
+ zd->xfr_in.wrkr = 0;
+ zd->xfr_in.bootstrap_retry = 0;
+ pthread_mutex_init(&zd->xfr_in.lock, 0);
+
+ /* Initialize NOTIFY. */
+ init_list(&zd->notify_pending);
+
+ /* Initialize IXFR database. */
+ zd->ixfr_db = journal_open(cfg->ixfr_db, cfg->ixfr_fslimit,
+ JOURNAL_DIRTY);
+ if (!zd->ixfr_db) {
+ int ret = journal_create(cfg->ixfr_db, JOURNAL_NCOUNT);
+ if (ret != KNOTD_EOK) {
+ log_server_error("Failed to create journal file "
+ "'%s'\n", cfg->ixfr_db);
+ }
+ zd->ixfr_db = journal_open(cfg->ixfr_db, cfg->ixfr_fslimit,
+ JOURNAL_DIRTY);
+ }
+
+ if (zd->ixfr_db == 0) {
+ log_server_error("Failed to open journal file "
+ "'%s'\n", cfg->ixfr_db);
+ }
+
+ /* Initialize IXFR database syncing event. */
+ zd->ixfr_dbsync = 0;
+
+ /* Set and install destructor. */
+ zone->data = zd;
+ zone->dtor = zonedata_destroy;
+
+ /* Set zonefile SOA serial. */
+ const knot_rrset_t *soa_rrs = 0;
+ const knot_rdata_t *soa_rr = 0;
+
+ /* Load serial. */
+ zd->zonefile_serial = 0;
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+ if (contents) {
+ soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ soa_rr = knot_rrset_rdata(soa_rrs);
+ int64_t serial = knot_rdata_soa_serial(soa_rr);
+ zd->zonefile_serial = (uint32_t)serial;
+ if (serial < 0) {
+ return KNOTD_EINVAL;
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Return SOA timer value.
+ *
+ * \param zone Pointer to zone.
+ * \param rr_func RDATA specificator.
+ * \return Timer in miliseconds.
+ */
+static uint32_t zones_soa_timer(knot_zone_t *zone,
+ uint32_t (*rr_func)(const knot_rdata_t*))
+{
+ if (!zone) {
+ dbg_zones_verb("zones: zones_soa_timer() called "
+ "with NULL zone\n");
+ }
+
+ uint32_t ret = 0;
+
+ /* Retrieve SOA RDATA. */
+ const knot_rrset_t *soa_rrs = 0;
+ const knot_rdata_t *soa_rr = 0;
+ knot_zone_contents_t * zc = knot_zone_get_contents((zone));
+ if (!zc) {
+ return 0;
+ }
+
+ soa_rrs = knot_node_rrset(knot_zone_contents_apex(zc),
+ KNOT_RRTYPE_SOA);
+ soa_rr = knot_rrset_rdata(soa_rrs);
+ ret = rr_func(soa_rr);
+
+ /* Convert to miliseconds. */
+ return ret * 1000;
+}
+
+/*!
+ * \brief Return SOA REFRESH timer value.
+ *
+ * \param zone Pointer to zone.
+ * \return REFRESH timer in miliseconds.
+ */
+static uint32_t zones_soa_refresh(knot_zone_t *zone)
+{
+ return zones_soa_timer(zone, knot_rdata_soa_refresh);
+}
+
+/*!
+ * \brief Return SOA RETRY timer value.
+ *
+ * \param zone Pointer to zone.
+ * \return RETRY timer in miliseconds.
+ */
+static uint32_t zones_soa_retry(knot_zone_t *zone)
+{
+ return zones_soa_timer(zone, knot_rdata_soa_retry);
+}
+
+/*!
+ * \brief Return SOA EXPIRE timer value.
+ *
+ * \param zone Pointer to zone.
+ * \return EXPIRE timer in miliseconds.
+ */
+static uint32_t zones_soa_expire(knot_zone_t *zone)
+{
+ return zones_soa_timer(zone, knot_rdata_soa_expire);
+}
+
+/*!
+ * \brief XFR/IN expire event handler.
+ */
+static int zones_expire_ev(event_t *e)
+{
+ rcu_read_lock();
+ dbg_zones("zones: EXPIRE timer event\n");
+ knot_zone_t *zone = (knot_zone_t *)e->data;
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+ if (!zone->data) {
+ return KNOTD_EINVAL;
+ }
+
+ zonedata_t *zd = (zonedata_t *)zone->data;
+
+ /* Won't accept any pending SOA responses. */
+ zd->xfr_in.next_id = -1;
+
+ /* 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);
+
+ if (contents == NULL) {
+ log_server_warning("Non-existent zone expired. Ignoring.\n");
+ rcu_read_unlock();
+ return 0;
+ }
+
+
+ rcu_read_unlock();
+
+ dbg_zones_verb("zones: zone %s expired, waiting for xfers to finish\n",
+ zd->conf->name);
+ pthread_mutex_lock(&zd->xfr_in.lock);
+ dbg_zones_verb("zones: zone %s locked, no xfers are running\n",
+ zd->conf->name);
+
+ synchronize_rcu();
+ pthread_mutex_unlock(&zd->xfr_in.lock);
+
+ log_server_info("Zone '%s' expired.\n", zd->conf->name);
+
+ /* Early finish this event to prevent lockup during cancellation. */
+ dbg_zones("zones: zone expired, removing from database\n");
+ evsched_event_finished(e->parent);
+
+ /* Cancel REFRESH timer. */
+ if (zd->xfr_in.timer) {
+ evsched_cancel(e->parent, zd->xfr_in.timer);
+ evsched_event_free(e->parent, zd->xfr_in.timer);
+ zd->xfr_in.timer = 0;
+ }
+
+ /* Free EXPIRE timer. */
+ if (zd->xfr_in.expire) {
+ evsched_event_free(e->parent, zd->xfr_in.expire);
+ zd->xfr_in.expire = 0;
+ }
+
+ knot_zone_contents_deep_free(&contents, 0);
+
+ return 0;
+}
+
+/*!
+ * \brief Zone REFRESH or RETRY event.
+ */
+static int zones_refresh_ev(event_t *e)
+{
+ dbg_zones("zones: REFRESH or RETRY timer event\n");
+ knot_zone_t *zone = (knot_zone_t *)e->data;
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Cancel pending timers. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (!zd) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Prepare buffer for query. */
+ uint8_t qbuf[SOCKET_MTU_SZ];
+ size_t buflen = SOCKET_MTU_SZ;
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ /* Check for contents. */
+ if (!knot_zone_contents(zone)) {
+
+ /* Bootstrap from XFR master. */
+ knot_ns_xfr_t xfr_req;
+ memset(&xfr_req, 0, sizeof(knot_ns_xfr_t));
+ memcpy(&xfr_req.addr, &zd->xfr_in.master, sizeof(sockaddr_t));
+ xfr_req.data = (void *)zone;
+ xfr_req.send = zones_send_cb;
+
+ /* Select transfer method. */
+ xfr_req.type = XFR_TYPE_AIN;
+ xfr_req.zone = zone;
+
+ /* Select TSIG key. */
+ /*!< \todo [TSIG] DISABLED */
+ xfr_req.tsig_key = 0;
+// if (zd->xfr_in.tsig_key.name) {
+// xfr_req.tsig_key = &zd->xfr_in.tsig_key;
+// }
+
+ /* Unlock zone contents. */
+ rcu_read_unlock();
+
+ /* Enqueue XFR request. */
+ int locked = pthread_mutex_trylock(&zd->xfr_in.lock);
+ if (locked != 0) {
+ dbg_zones("zones: already bootstrapping '%s'\n",
+ zd->conf->name);
+ return KNOTD_EOK;
+ } else {
+ log_zone_info("Attempting to bootstrap zone %s from master\n",
+ zd->conf->name);
+ pthread_mutex_unlock(&zd->xfr_in.lock);
+ }
+
+ return xfr_request(zd->server->xfr_h, &xfr_req);
+ }
+
+ /* Do not issue SOA query if transfer is pending. */
+ int locked = pthread_mutex_trylock(&zd->xfr_in.lock);
+ if (locked != 0) {
+ dbg_zones("zones: zone '%s' is being transferred, "
+ "deferring SOA query\n",
+ zd->conf->name);
+
+ /* Reschedule as RETRY timer. */
+ evsched_schedule(e->parent, e, zones_soa_retry(zone));
+ dbg_zones("zones: RETRY of '%s' after %u seconds\n",
+ zd->conf->name, zones_soa_retry(zone) / 1000);
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+ return KNOTD_EOK;
+ } else {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
+ }
+
+ /* Create query. */
+ /*! \todo API for retrieval of name. */
+
+ /*! \todo [TSIG] CHANGE!!! only for compatibility now. */
+ knot_ns_xfr_t xfr_req;
+ memset(&xfr_req, 0, sizeof(knot_ns_xfr_t));
+ xfr_req.wire = qbuf;
+
+ int ret = xfrin_create_soa_query(zone->name, &xfr_req, &buflen);
+ if (ret == KNOT_EOK) {
+
+ sockaddr_t *master = &zd->xfr_in.master;
+
+ /* Create socket on random port. */
+ int sock = socket_create(master->family, SOCK_DGRAM);
+
+ /* Send query. */
+ ret = -1;
+ if (sock > -1) {
+ ret = sendto(sock, qbuf, buflen, 0,
+ master->ptr, master->len);
+ }
+
+ /* Store ID of the awaited response. */
+ if (ret == buflen) {
+ zd->xfr_in.next_id = knot_wire_get_id(qbuf);
+ dbg_zones("zones: expecting SOA response "
+ "ID=%d for '%s'\n",
+ zd->xfr_in.next_id, zd->conf->name);
+ }
+
+ /* Watch socket. */
+ knot_ns_xfr_t req;
+ memset(&req, 0, sizeof(req));
+ req.session = sock;
+ req.type = XFR_TYPE_SOA;
+ req.zone = zone;
+ memcpy(&req.addr, master, sizeof(sockaddr_t));
+ sockaddr_update(&req.addr);
+ xfr_request(zd->server->xfr_h, &req);
+ } else {
+ ret = KNOTD_ERROR;
+ }
+
+ /* Schedule EXPIRE timer on first attempt. */
+ if (!zd->xfr_in.expire) {
+ uint32_t expire_tmr = zones_soa_expire(zone);
+ zd->xfr_in.expire = evsched_schedule_cb(
+ e->parent,
+ zones_expire_ev,
+ zone, expire_tmr);
+ dbg_zones("zones: EXPIRE of '%s' after %u seconds\n",
+ zd->conf->name, expire_tmr / 1000);
+ }
+
+ /* Reschedule as RETRY timer. */
+ evsched_schedule(e->parent, e, zones_soa_retry(zone));
+ dbg_zones("zones: RETRY of '%s' after %u seconds\n",
+ zd->conf->name, zones_soa_retry(zone) / 1000);
+
+ /* Unlock RCU. */
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/*!
+ * \brief Send NOTIFY to slave server.
+ */
+static int zones_notify_send(event_t *e)
+{
+ dbg_notify("notify: NOTIFY timer event\n");
+
+ notify_ev_t *ev = (notify_ev_t *)e->data;
+ knot_zone_t *zone = ev->zone;
+ if (!zone) {
+ log_zone_error("notify: NOTIFY invalid event received\n");
+ evsched_event_free(e->parent, e);
+ free(ev);
+ return KNOTD_EINVAL;
+ }
+
+ /* Check for answered/cancelled query. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+
+ /* Reduce number of available retries. */
+ --ev->retries;
+
+ /* Check number of retries. */
+ if (ev->retries < 0) {
+ log_server_notice("NOTIFY query maximum number of retries "
+ "for zone %s exceeded.\n",
+ zd->conf->name);
+ pthread_mutex_lock(&zd->lock);
+ rem_node(&ev->n);
+ evsched_event_free(e->parent, e);
+ free(ev);
+ pthread_mutex_unlock(&zd->lock);
+ return KNOTD_EMALF;
+ }
+
+ /* Prepare buffer for query. */
+ uint8_t qbuf[SOCKET_MTU_SZ];
+ size_t buflen = sizeof(qbuf);
+
+ /* RFC suggests 60s, but it is configurable. */
+ int retry_tmr = ev->timeout * 1000;
+
+ /* Reschedule. */
+ conf_read_lock();
+ evsched_schedule(e->parent, e, retry_tmr);
+ dbg_notify("notify: Query RETRY after %u secs (zone '%s')\n",
+ retry_tmr / 1000, zd->conf->name);
+ conf_read_unlock();
+
+ /* Create query. */
+ int ret = notify_create_request(contents, qbuf, &buflen);
+ if (ret == KNOTD_EOK && zd->server) {
+
+ /* Lock RCU. */
+ rcu_read_lock();
+
+ /* Create socket on random port. */
+ int sock = socket_create(ev->addr.family, SOCK_DGRAM);
+
+ /* Send query. */
+ ret = -1;
+ if (sock > -1) {
+ ret = sendto(sock, qbuf, buflen, 0,
+ ev->addr.ptr, ev->addr.len);
+ }
+
+ /* Store ID of the awaited response. */
+ if (ret == buflen) {
+ char r_addr[SOCKADDR_STRLEN];
+ sockaddr_tostr(&ev->addr, r_addr, sizeof(r_addr));
+ int r_port = sockaddr_portnum(&ev->addr);
+ ev->msgid = knot_wire_get_id(qbuf);
+ log_server_info("Issued NOTIFY query to %s:%d, expecting "
+ "response ID=%d\n",
+ r_addr, r_port,
+ ev->msgid);
+
+ }
+
+ /* Watch socket. */
+ knot_ns_xfr_t req;
+ memset(&req, 0, sizeof(req));
+ req.session = sock;
+ req.type = XFR_TYPE_NOTIFY;
+ req.zone = zone;
+ memcpy(&req.addr, &ev->addr, sizeof(sockaddr_t));
+ xfr_request(zd->server->xfr_h, &req);
+
+ /* Unlock RCU */
+ rcu_read_unlock();
+ }
+
+ return ret;
+}
+
+/*! \brief Function for marking nodes as synced and updated. */
+static int zones_ixfrdb_sync_apply(journal_t *j, journal_node_t *n)
+{
+ /* Check for dirty bit (not synced to permanent storage). */
+ if (n->flags & JOURNAL_DIRTY) {
+
+ /* Remove dirty bit. */
+ n->flags = n->flags & ~JOURNAL_DIRTY;
+
+ /* Sync. */
+ journal_update(j, n);
+ }
+
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Sync chagnes in zone to zonefile.
+ */
+static int zones_zonefile_sync_ev(event_t *e)
+{
+ dbg_zones("zones: IXFR database SYNC timer event\n");
+
+ /* Fetch zone. */
+ knot_zone_t *zone = (knot_zone_t *)e->data;
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Fetch zone data. */
+ zonedata_t *zd = (zonedata_t *)zone->data;
+ if (!zd) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Execute zonefile sync. */
+ int ret = zones_zonefile_sync(zone);
+ if (ret == KNOTD_EOK) {
+ log_zone_info("Applied differences of '%s' to zonefile.\n",
+ zd->conf->name);
+ } else {
+ log_zone_warning("Failed to apply differences of '%s' "
+ "to zonefile.\n",
+ zd->conf->name);
+ }
+
+ /* Reschedule. */
+ conf_read_lock();
+ evsched_schedule(e->parent, e, zd->conf->dbsync_timeout * 1000);
+ dbg_zones("zones: next IXFR database SYNC of '%s' in %d seconds\n",
+ zd->conf->name, zd->conf->dbsync_timeout);
+ conf_read_unlock();
+
+ return ret;
+}
+
+/*!
+ * \brief Update ACL list from configuration.
+ *
+ * \param acl Pointer to existing or NULL ACL.
+ * \param acl_list List of remotes from configuration.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOMEM on failed memory allocation.
+ */
+static int zones_set_acl(acl_t **acl, list* acl_list)
+{
+ if (!acl || !acl_list) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Truncate old ACL. */
+ acl_delete(acl);
+
+ /* Create new ACL. */
+ *acl = acl_new(ACL_DENY, 0);
+ if (!*acl) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Load ACL rules. */
+ conf_remote_t *r = 0;
+ WALK_LIST(r, *acl_list) {
+
+ /* Initialize address. */
+ /*! Port matching disabled, port = 0. */
+ sockaddr_t addr;
+ conf_iface_t *cfg_if = r->remote;
+ int ret = sockaddr_set(&addr, cfg_if->family,
+ cfg_if->address, 0);
+
+ /* Load rule. */
+ if (ret > 0) {
+ acl_create(*acl, &addr, ACL_ACCEPT);
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+/*!
+ * \brief Load zone to zone database.
+ *
+ * \param zonedb Zone database to load the zone into.
+ * \param zone_name Zone name (owner of the apex node).
+ * \param source Path to zone file source.
+ * \param filename Path to requested compiled zone file.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_EINVAL
+ * \retval KNOTD_EZONEINVAL
+ */
+static int zones_load_zone(knot_zonedb_t *zonedb, const char *zone_name,
+ const char *source, const char *filename)
+{
+ knot_zone_t *zone = NULL;
+
+ /* Check path */
+ if (filename) {
+ dbg_zones("zones: parsing zone database '%s'\n", filename);
+ zloader_t *zl = 0;
+ int ret = knot_zload_open(&zl, filename);
+ switch(ret) {
+ case KNOT_EOK:
+ /* OK */
+ break;
+ case KNOT_EFEWDATA:
+ log_server_error("Compiled zone db '%s' not exists.\n",
+ filename);
+ return KNOTD_EZONEINVAL;
+ case KNOT_ECRC:
+ log_server_error("Compiled zone db CRC mismatches, "
+ "db is corrupted or .crc file is "
+ "deleted.\n");
+ return KNOTD_EZONEINVAL;
+ case KNOT_EMALF:
+ log_server_error("Compiled db '%s' is too old, "
+ " please recompile.\n",
+ filename);
+ return KNOTD_EZONEINVAL;
+ case KNOT_ERROR:
+ case KNOT_ENOMEM:
+ default:
+ log_server_error("Failed to read zone db file '%s'.\n",
+ filename);
+ return KNOTD_EZONEINVAL;
+ }
+
+ /* Check if the db is up-to-date */
+ int src_changed = strcmp(source, zl->source) != 0;
+ if (src_changed || knot_zload_needs_update(zl)) {
+ log_server_warning("Database for zone '%s' is not "
+ "up-to-date. Please recompile.\n",
+ zone_name);
+ }
+
+ zone = knot_zload_load(zl);
+
+ /* Check loaded name. */
+ const knot_dname_t *dname = knot_zone_name(zone);
+ knot_dname_t *dname_req = 0;
+ dname_req = knot_dname_new_from_str(zone_name,
+ strlen(zone_name), 0);
+ if (knot_dname_compare(dname, dname_req) != 0) {
+ log_server_warning("Origin of the zone db file is "
+ "different than '%s'\n",
+ zone_name);
+ knot_zone_deep_free(&zone, 0);
+ zone = 0;
+
+ }
+ knot_dname_free(&dname_req);
+
+ /* CLEANUP */
+ //knot_zone_contents_dump(zone->contents, 1);
+
+ if (zone) {
+ /* save the timestamp from the zone db file */
+ struct stat s;
+ stat(filename, &s);
+ knot_zone_set_version(zone, s.st_mtime);
+
+ if (knot_zonedb_add_zone(zonedb, zone) != 0){
+ knot_zone_deep_free(&zone, 0);
+ zone = 0;
+ }
+ }
+
+ knot_zload_close(zl);
+
+ if (!zone) {
+ log_server_error("Failed to load "
+ "db '%s' for zone '%s'.\n",
+ filename, zone_name);
+ return KNOTD_EZONEINVAL;
+ }
+ } else {
+ /* db is null. */
+ return KNOTD_EINVAL;
+ }
+
+ /* CLEANUP */
+// knot_zone_dump(zone, 1);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Return 'serial_from' part of the key. */
+static inline uint32_t ixfrdb_key_from(uint64_t k)
+{
+ /* 64 32 0
+ * key = [TO | FROM]
+ * Need: Least significant 32 bits.
+ */
+ return (uint32_t)(k & ((uint64_t)0x00000000ffffffff));
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Return 'serial_to' part of the key. */
+static inline uint32_t ixfrdb_key_to(uint64_t k)
+{
+ /* 64 32 0
+ * key = [TO | FROM]
+ * Need: Most significant 32 bits.
+ */
+ return (uint32_t)(k >> (uint64_t)32);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Compare function to match entries with target serial. */
+static inline int ixfrdb_key_to_cmp(uint64_t k, uint64_t to)
+{
+ /* 64 32 0
+ * key = [TO | FROM]
+ * Need: Most significant 32 bits.
+ */
+ return ((uint64_t)ixfrdb_key_to(k)) - to;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Compare function to match entries with starting serial. */
+static inline int ixfrdb_key_from_cmp(uint64_t k, uint64_t from)
+{
+ /* 64 32 0
+ * key = [TO | FROM]
+ * Need: Least significant 32 bits.
+ */
+ return ((uint64_t)ixfrdb_key_from(k)) - from;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Make key for journal from serials. */
+static inline uint64_t ixfrdb_key_make(uint32_t from, uint32_t to)
+{
+ /* 64 32 0
+ * key = [TO | FROM]
+ */
+ return (((uint64_t)to) << ((uint64_t)32)) | ((uint64_t)from);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_changesets_from_binary(knot_changesets_t *chgsets)
+{
+ assert(chgsets != NULL);
+ assert(chgsets->allocated >= chgsets->count);
+ /*
+ * Parses changesets from the binary format stored in chgsets->data
+ * into the changeset_t structures.
+ */
+ knot_rrset_t *rrset = 0;
+ int ret = 0;
+
+ for (int i = 0; i < chgsets->count; ++i) {
+
+ /* Read initial changeset RRSet - SOA. */
+ knot_changeset_t* chs = chgsets->sets + i;
+ size_t remaining = chs->size;
+ ret = knot_zload_rrset_deserialize(&rrset, chs->data, &remaining);
+ if (ret != KNOT_EOK) {
+ dbg_xfr("xfr: SOA: failed to deserialize data "
+ "from changeset, %s\n", knot_strerror(ret));
+ return KNOTD_EMALF;
+ }
+
+ /* in this special case (changesets loaded
+ * from journal) the SOA serial should already
+ * be set, check it.
+ */
+ assert(knot_rrset_type(rrset) == KNOT_RRTYPE_SOA);
+ assert(chs->serial_from ==
+ knot_rdata_soa_serial(knot_rrset_rdata(rrset)));
+ knot_changeset_store_soa(&chs->soa_from, &chs->serial_from,
+ rrset);
+
+ dbg_xfr_verb("xfr: reading RRSets to REMOVE\n");
+
+ /* Read remaining RRSets */
+ int in_remove_section = 1;
+ while (remaining > 0) {
+
+ /* Parse next RRSet. */
+ rrset = 0;
+ uint8_t *stream = chs->data + (chs->size - remaining);
+ ret = knot_zload_rrset_deserialize(&rrset, stream, &remaining);
+ if (ret != KNOT_EOK) {
+ dbg_xfr("xfr: failed to deserialize data "
+ "from changeset, %s\n",
+ knot_strerror(ret));
+ return KNOTD_EMALF;
+ }
+
+ /* Check for next SOA. */
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
+
+ /* 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);
+ 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_free(&rrset);
+ 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);
+ } else {
+ /* Add RRSets. */
+ ret = knot_changeset_add_rrset(
+ &chgsets->sets[i].add,
+ &chgsets->sets[i].add_count,
+ &chgsets->sets[i].add_allocated,
+ rrset);
+ }
+
+ /* Check result. */
+ if (ret != KNOT_EOK) {
+ dbg_xfr("xfr: failed to add/remove "
+ "RRSet to changeset: %s\n",
+ knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+ }
+ }
+
+ dbg_xfr_verb("xfr: read all RRSets in changeset\n");
+ }
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_load_changesets(const knot_zone_t *zone,
+ knot_changesets_t *dst,
+ uint32_t from, uint32_t to)
+{
+ if (!zone || !dst) {
+ dbg_zones_detail("Bad arguments: zone=%p, dst=%p\n", zone, dst);
+ return KNOTD_EINVAL;
+ }
+ if (!zone->data) {
+ dbg_zones_detail("Bad arguments: zone->data=%p\n", zone->data);
+ return KNOTD_EINVAL;
+ }
+
+ dbg_xfr("Loading changesets from serial %u to %u\n", from, to);
+
+ /* Fetch zone-specific data. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (!zd->ixfr_db) {
+ dbg_zones_detail("Bad arguments: zd->ixfr_db=%p\n", zone->data);
+ return KNOTD_EINVAL;
+ }
+
+ /* Read entries from starting serial until finished. */
+ uint32_t found_to = from;
+ journal_node_t *n = 0;
+ int ret = journal_fetch(zd->ixfr_db, from, ixfrdb_key_from_cmp, &n);
+ if (ret != KNOTD_EOK) {
+ dbg_xfr("xfr: failed to fetch starting changeset: %s\n",
+ knotd_strerror(ret));
+ return ret;
+ }
+
+ while (n != 0 && n != journal_end(zd->ixfr_db)) {
+
+ /* Check for history end. */
+ if (to == found_to) {
+ 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_strerror(ret));
+ return KNOTD_ERROR;
+ }
+
+ /* 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);
+ if (!chs->data) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Read journal entry. */
+ ret = journal_read(zd->ixfr_db, n->id,
+ 0, (char*)chs->data);
+ if (ret != KNOTD_EOK) {
+ dbg_xfr("xfr: failed to read data from journal\n");
+ free(chs->data);
+ return KNOTD_ERROR;
+ }
+
+ /* Update changeset binary size. */
+ chs->size = chs->allocated = n->len;
+
+ /* Next node. */
+ found_to = chs->serial_to;
+ ++dst->count;
+ ++n;
+
+ /*! \todo Check consistency. */
+ }
+
+ dbg_xfr_detail("xfr: Journal entries read.\n");
+
+ /* Unpack binary data. */
+ ret = zones_changesets_from_binary(dst);
+ if (ret != KNOT_EOK) {
+ dbg_xfr("xfr: failed to unpack changesets "
+ "from binary, %s\n", knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+
+ /* Check for complete history. */
+ if (to != found_to) {
+ dbg_xfr_detail("Returning ERANGE\n");
+ return KNOTD_ERANGE;
+ }
+
+ /* History reconstructed. */
+ dbg_xfr_detail("Returning EOK\n");
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Apply changesets to zone from journal.
+ *
+ * \param zone Specified zone.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ENOENT if zone has no contents.
+ * \retval KNOTD_ERROR on unspecified error.
+ */
+static int zones_journal_apply(knot_zone_t *zone)
+{
+ /* Fetch zone. */
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+
+ knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ if (!contents || !zd) {
+ return KNOTD_ENOENT;
+ }
+
+ /* Fetch SOA serial. */
+ const knot_rrset_t *soa_rrs = 0;
+ const knot_rdata_t *soa_rr = 0;
+ soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ soa_rr = knot_rrset_rdata(soa_rrs);
+ int64_t serial_ret = knot_rdata_soa_serial(soa_rr);
+ if (serial_ret < 0) {
+ return KNOTD_EINVAL;
+ }
+ uint32_t serial = (uint32_t)serial_ret;
+
+ /* 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));
+ /*! \todo Check what should be the upper bound. */
+ int ret = zones_load_changesets(zone, chsets, serial, serial - 1);
+ if (ret == KNOTD_EOK || ret == KNOTD_ERANGE) {
+ if (chsets->count > 0) {
+ /* Apply changesets. */
+ log_server_info("Applying '%zu' changesets from journal "
+ "to zone '%s'.\n",
+ chsets->count, zd->conf->name);
+ ret = xfrin_apply_changesets_to_zone(zone, chsets);
+ if (ret != KNOT_EOK) {
+ log_server_error("Failed to apply changesets to "
+ "'%s' - %s\n",
+ zd->conf->name,
+ knot_strerror(ret));
+ ret = KNOTD_ERROR;
+ }
+ }
+ } else {
+ dbg_zones("zones: failed to load changesets - %s\n",
+ knotd_strerror(ret));
+ }
+
+ /* Free changesets and return. */
+ knot_free_changesets(&chsets);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Fill the new database with zones.
+ *
+ * Zones that should be retained are just added from the old database to the
+ * new. New zones are loaded.
+ *
+ * \param ns Name server instance.
+ * \param zone_conf Zone configuration.
+ * \param db_old Old zone database.
+ * \param db_new New zone database.
+ *
+ * \return Number of inserted zones.
+ */
+static int zones_insert_zones(knot_nameserver_t *ns,
+ const list *zone_conf,
+ const knot_zonedb_t *db_old,
+ knot_zonedb_t *db_new)
+{
+ /*! \todo Change to zone contents. */
+
+ node *n = 0;
+ int inserted = 0;
+ /* for all zones in the configuration */
+ WALK_LIST(n, *zone_conf) {
+ conf_zone_t *z = (conf_zone_t *)n;
+
+ /* Convert the zone name into a domain name. */
+ /* Local allocation, will be discarded. */
+ knot_dname_t *zone_name = knot_dname_new_from_str(z->name,
+ strlen(z->name), NULL);
+ if (zone_name == NULL) {
+ log_server_error("Error creating domain name from zone"
+ " name\n");
+ return inserted;
+ }
+
+ dbg_zones_verb("zones: inserting zone %s into the new database.\n",
+ z->name);
+
+ /* try to find the zone in the current zone db */
+ knot_zone_t *zone = knot_zonedb_find_zone(db_old,
+ zone_name);
+ int reload = 0;
+
+ /* Attempt to bootstrap if db or source does not exist. */
+ struct stat s;
+ int stat_ret = stat(z->file, &s);
+ if (zone != NULL) {
+ /* if found, check timestamp of the file against the
+ * loaded zone
+ */
+ if (knot_zone_version(zone) < s.st_mtime) {
+ /* the file is newer, reload! */
+ reload = 1;
+ }
+ } else {
+ reload = 1;
+ }
+
+ /* Reload zone file. */
+ int ret = KNOTD_ERROR;
+ if (reload) {
+ /* Zone file not exists and has master set. */
+ if (stat_ret < 0 && !EMPTY_LIST(z->acl.xfr_in)) {
+
+ /* Create stub database. */
+ dbg_zones_verb("zones: loading stub zone '%s' "
+ "for bootstrap.\n",
+ z->name);
+ knot_dname_t *owner = 0;
+ owner = knot_dname_deep_copy(zone_name);
+ knot_zone_t* sz = knot_zone_new_empty(owner);
+ if (sz) {
+ /* Add stub zone to db_new. */
+ ret = knot_zonedb_add_zone(db_new, sz);
+ if (ret != KNOT_EOK) {
+ dbg_zones("zones: failed to add "
+ "stub zone '%s'.\n",
+ z->name);
+ knot_zone_deep_free(&sz, 0);
+ sz = 0;
+ ret = KNOTD_ERROR;
+ } else {
+ log_server_info("Will attempt to "
+ "bootstrap zone "
+ "%s from AXFR "
+ "master.\n",
+ z->name);
+ --inserted;
+ }
+
+ } else {
+ dbg_zones("zones: failed to create "
+ "stub zone '%s'.\n",
+ z->name);
+ ret = KNOTD_ERROR;
+ }
+
+ } else {
+ dbg_zones_verb("zones: loading zone '%s' "
+ "from '%s'\n",
+ z->name,
+ z->db);
+ ret = zones_load_zone(db_new, z->name,
+ z->file, z->db);
+ if (ret == KNOTD_EOK) {
+ log_server_info("Loaded zone '%s'\n",
+ z->name);
+ } else {
+ log_server_error("Failed to load zone "
+ "'%s' - %s\n",
+ z->name,
+ knotd_strerror(ret));
+ }
+ }
+
+ /* Find zone. */
+ if (ret == KNOTD_EOK) {
+ /* Find the new zone */
+ zone = knot_zonedb_find_zone(db_new,
+ zone_name);
+ ++inserted;
+
+ dbg_zones_verb("zones: inserted '%s' into "
+ "database, initializing data\n",
+ z->name);
+
+ /* Initialize zone-related data. */
+ zonedata_init(z, zone);
+
+ }
+ /* unused return value, if not loaded, just continue */
+ } else {
+ /* just insert the zone into the new zone db */
+ dbg_zones_verb("zones: found '%s' in old database, "
+ "copying to new.\n",
+ z->name);
+ log_server_info("Zone '%s' is up-to-date, no need "
+ "for reload.\n", z->name);
+ int ret = knot_zonedb_add_zone(db_new, zone);
+ if (ret != KNOT_EOK) {
+ log_server_error("Error adding known zone '%s' to"
+ " the new database - %s\n",
+ z->name, knot_strerror(ret));
+ ret = KNOTD_ERROR;
+ } else {
+ ++inserted;
+ }
+ }
+
+ /* Update zone data. */
+ if (zone) {
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+
+ /* Update refs. */
+ zd->conf = z;
+
+ /* Update ACLs. */
+ dbg_zones("Updating zone ACLs.\n");
+ zones_set_acl(&zd->xfr_in.acl, &z->acl.xfr_in);
+ zones_set_acl(&zd->xfr_out, &z->acl.xfr_out);
+ zones_set_acl(&zd->notify_in, &z->acl.notify_in);
+ zones_set_acl(&zd->notify_out, &z->acl.notify_out);
+
+ /* Update server pointer. */
+ zd->server = (server_t *)knot_ns_get_data(ns);
+
+ /* Update master server address. */
+ memset(&zd->xfr_in.tsig_key, 0, sizeof(knot_key_t));
+ sockaddr_init(&zd->xfr_in.master, -1);
+ if (!EMPTY_LIST(z->acl.xfr_in)) {
+ conf_remote_t *r = HEAD(z->acl.xfr_in);
+ conf_iface_t *cfg_if = r->remote;
+ sockaddr_set(&zd->xfr_in.master,
+ cfg_if->family,
+ cfg_if->address,
+ cfg_if->port);
+ /*!< \todo [TSIG] DISABLED */
+// if (cfg_if->key) {
+// memcpy(&zd->xfr_in.tsig_key,
+// cfg_if->key,
+// sizeof(knot_key_t));
+// }
+
+ dbg_zones("zones: using %s:%d as XFR master "
+ "for '%s'\n",
+ cfg_if->address,
+ cfg_if->port,
+ z->name);
+ }
+
+ /* Apply changesets from journal. */
+ zones_journal_apply(zone);
+
+ /* Update events scheduled for zone. */
+ zones_timers_update(zone, z,
+ ((server_t *)knot_ns_get_data(ns))->sched);
+ }
+
+ /* CLEANUP */
+// knot_zone_contents_dump(knot_zone_get_contents(zone), 1);
+
+ /* Directly discard zone. */
+ knot_dname_free(&zone_name);
+ }
+ return inserted;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Remove zones present in the configuration from the old database.
+ *
+ * After calling this function, the old zone database should contain only zones
+ * that should be completely deleted.
+ *
+ * \param zone_conf Zone configuration.
+ * \param db_old Old zone database to remove zones from.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_ERROR
+ */
+static int zones_remove_zones(const knot_zonedb_t *db_new,
+ knot_zonedb_t *db_old)
+{
+ const knot_zone_t **new_zones = knot_zonedb_zones(db_new);
+ if (new_zones == NULL) {
+ return KNOTD_ENOMEM;
+ }
+
+ for (int i = 0; i < knot_zonedb_zone_count(db_new); ++i) {
+ /* try to find the new zone in the old DB
+ * if the pointers match, remove the zone from old DB
+ */
+ /*! \todo Find better way of removing zone with given pointer.*/
+ knot_zone_t *old_zone = knot_zonedb_find_zone(
+ db_old, knot_zone_name(new_zones[i]));
+ if (old_zone == new_zones[i]) {
+dbg_zones_exec(
+ char *name = knot_dname_to_str(knot_zone_name(old_zone));
+ dbg_zones_verb("zones: zone pointers match, removing zone %s "
+ "from database.\n", name);
+ free(name);
+);
+ knot_zone_t * rm = knot_zonedb_remove_zone(db_old,
+ knot_zone_name(old_zone));
+ assert(rm == old_zone);
+ }
+ }
+
+ free(new_zones);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns,
+ knot_zonedb_t **db_old)
+{
+ /* Check parameters */
+ if (conf == NULL || ns == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Lock RCU to ensure none will deallocate any data under our hands. */
+ rcu_read_lock();
+
+ /* Grab a pointer to the old database */
+ *db_old = ns->zone_db;
+ if (*db_old == NULL) {
+ log_server_error("Missing zone database in nameserver structure"
+ ".\n");
+ return KNOTD_ERROR;
+ }
+
+ /* Create new zone DB */
+ knot_zonedb_t *db_new = knot_zonedb_new();
+ if (db_new == NULL) {
+ return KNOTD_ERROR;
+ }
+
+ log_server_info("Loading %d compiled zones...\n", conf->zones_count);
+
+ /* Insert all required zones to the new zone DB. */
+ int inserted = zones_insert_zones(ns, &conf->zones, *db_old, db_new);
+
+ log_server_info("Loaded %d out of %d zones.\n", inserted,
+ conf->zones_count);
+
+ if (inserted != conf->zones_count) {
+ log_server_warning("Not all the zones were loaded.\n");
+ }
+
+ dbg_zones_detail("zones: old db in nameserver: %p, old db stored: %p, new db: %p\n",
+ ns->zone_db, *db_old, db_new);
+
+ /* Switch the databases. */
+ (void)rcu_xchg_pointer(&ns->zone_db, db_new);
+
+ dbg_zones_detail("db in nameserver: %p, old db stored: %p, new db: %p\n",
+ ns->zone_db, *db_old, db_new);
+
+ /*
+ * Remove all zones present in the new DB from the old DB.
+ * No new thread can access these zones in the old DB, as the
+ * databases are already switched.
+ *
+ * Beware - only the exact same zones (same pointer) may be removed.
+ * All other have been loaded again so that the old must be destroyed.
+ */
+ int ret = zones_remove_zones(db_new, *db_old);
+ if (ret != KNOTD_EOK) {
+ return ret;
+ }
+
+ /* Unlock RCU, messing with any data will not affect us now */
+ rcu_read_unlock();
+
+ return KNOTD_EOK;
+}
+
+int zones_zonefile_sync(knot_zone_t *zone)
+{
+ if (!zone) {
+ return KNOTD_EINVAL;
+ }
+ if (!zone->data) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Fetch zone data. */
+ zonedata_t *zd = (zonedata_t *)zone->data;
+
+ /* Lock zone data. */
+ pthread_mutex_lock(&zd->lock);
+
+ knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+ if (!contents) {
+ pthread_mutex_unlock(&zd->lock);
+ return KNOTD_EINVAL;
+ }
+
+ /* Latest zone serial. */
+ const knot_rrset_t *soa_rrs = 0;
+ const knot_rdata_t *soa_rr = 0;
+ soa_rrs = knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ soa_rr = knot_rrset_rdata(soa_rrs);
+ int64_t serial_ret = knot_rdata_soa_serial(soa_rr);
+ if (serial_ret < 0) {
+ return KNOTD_EINVAL;
+ }
+ uint32_t serial_to = (uint32_t)serial_ret;
+
+ /* Check for difference against zonefile serial. */
+ if (zd->zonefile_serial != serial_to) {
+
+ /* Save zone to zonefile. */
+ conf_read_lock();
+ dbg_zones("zones: syncing '%s' differences to '%s' "
+ "(SOA serial %u)\n",
+ zd->conf->name, zd->conf->file, serial_to);
+ zone_dump_text(contents, zd->conf->file);
+ conf_read_unlock();
+
+ /* Update journal entries. */
+ dbg_zones_verb("zones: unmarking all dirty nodes "
+ "in '%s' journal\n",
+ zd->conf->name);
+ journal_walk(zd->ixfr_db, zones_ixfrdb_sync_apply);
+
+ /* Update zone file serial. */
+ dbg_zones("zones: new '%s' zonefile serial is %u\n",
+ zd->conf->name, serial_to);
+ zd->zonefile_serial = serial_to;
+ } else {
+ dbg_zones_verb("zones: '%s' zonefile is in sync "
+ "with differences\n", zd->conf->name);
+ }
+
+ /* Unlock zone data. */
+ pthread_mutex_unlock(&zd->lock);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_xfr_check_zone(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
+{
+ if (xfr == NULL || rcode == NULL) {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_EINVAL;
+ }
+
+ /* Check if the zone is found. */
+ if (xfr->zone == NULL) {
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOTD_EACCES;
+ }
+
+ /* Check zone data. */
+ zonedata_t *zd = (zonedata_t *)xfr->zone->data;
+ if (zd == NULL) {
+ dbg_zones("zones: invalid zone data for zone %p\n", xfr->zone);
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_ERROR;
+ }
+
+ /* Check zone contents. */
+ if (knot_zone_contents(xfr->zone) == NULL) {
+ dbg_zones("zones: invalid zone contents for zone %p\n", xfr->zone);
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_EEXPIRED;
+ }
+
+ // Check xfr-out ACL
+ if (acl_match(zd->xfr_out, &xfr->addr) == ACL_DENY) {
+ log_answer_warning("Unauthorized request for XFR '%s/OUT'.\n",
+ zd->conf->name);
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOTD_EACCES;
+ } else {
+ dbg_zones("zones: authorized XFR '%s/OUT'\n",
+ zd->conf->name);
+ }
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_process_response(knot_nameserver_t *nameserver,
+ sockaddr_t *from,
+ knot_packet_t *packet, uint8_t *response_wire,
+ size_t *rsize)
+{
+ if (!packet || !rsize || nameserver == NULL || from == NULL ||
+ response_wire == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Handle SOA query response, cancel EXPIRE timer
+ * and start AXFR transfer if needed.
+ * Reset REFRESH timer on finish.
+ */
+ if (knot_packet_qtype(packet) == KNOT_RRTYPE_SOA) {
+
+ if (knot_packet_rcode(packet) != KNOT_RCODE_NOERROR) {
+ /*! \todo Handle error response. */
+ return KNOTD_ERROR;
+ }
+
+ /* No response. */
+ *rsize = 0;
+
+ /* Find matching zone and ID. */
+ const knot_dname_t *zone_name = knot_packet_qname(packet);
+ /*! \todo Change the access to the zone db. */
+ knot_zone_t *zone = knot_zonedb_find_zone(
+ nameserver->zone_db,
+ zone_name);
+
+ /* Get zone contents. */
+ rcu_read_lock();
+ const knot_zone_contents_t *contents =
+ knot_zone_contents(zone);
+
+ if (!zone || !knot_zone_data(zone) || !contents) {
+ rcu_read_unlock();
+ return KNOTD_EINVAL;
+ }
+
+ /* Match ID against awaited. */
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+ uint16_t pkt_id = knot_packet_id(packet);
+ if ((int)pkt_id != zd->xfr_in.next_id) {
+ rcu_read_unlock();
+ return KNOTD_ERROR;
+ }
+
+ /* Check SOA SERIAL. */
+ int ret = xfrin_transfer_needed(contents, packet);
+ dbg_zones_verb("xfrin_transfer_needed() returned %d\n", ret);
+ if (ret < 0) {
+ /* RETRY/EXPIRE timers running, do not interfere. */
+ return KNOTD_ERROR;
+ }
+
+ /* No updates available. */
+ evsched_t *sched =
+ ((server_t *)knot_ns_get_data(nameserver))->sched;
+ if (ret == 0) {
+ log_zone_info("SOA query for zone '%s' answered, no "
+ "transfer needed.\n", zd->conf->name);
+ rcu_read_unlock();
+
+ /* Reinstall timers. */
+ zones_timers_update(zone, zd->conf, sched);
+ return KNOTD_EOK;
+ }
+
+ assert(ret > 0);
+
+ /* Already transferring. */
+ if (pthread_mutex_trylock(&zd->xfr_in.lock) != 0) {
+ /* Unlock zone contents. */
+ dbg_zones("zones: SOA response received, but zone is "
+ "being transferred, refusing to start another "
+ "transfer\n");
+ rcu_read_unlock();
+ return KNOTD_EOK;
+ } else {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
+ }
+
+ /* Prepare XFR client transfer. */
+ knot_ns_xfr_t xfr_req;
+ memset(&xfr_req, 0, sizeof(knot_ns_xfr_t));
+ memcpy(&xfr_req.addr, from, sizeof(sockaddr_t));
+ xfr_req.zone = (void *)zone;
+ xfr_req.send = zones_send_cb;
+
+ /* Select transfer method. */
+ xfr_req.type = zones_transfer_to_use(contents);
+
+ /* Select TSIG key. */
+ /*!< \todo [TSIG] DISABLED */
+ xfr_req.tsig_key = 0;
+ /* CLEANUP */
+// if (zd->xfr_in.tsig_key.name) {
+// xfr_req.tsig_key = &zd->xfr_in.tsig_key;
+// }
+
+ /* Unlock zone contents. */
+ rcu_read_unlock();
+
+ /* Enqueue XFR request. */
+ return xfr_request(((server_t *)knot_ns_get_data(
+ nameserver))->xfr_h, &xfr_req);
+ }
+
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_ns_xfr_type_t zones_transfer_to_use(const knot_zone_contents_t *zone)
+{
+ /*! \todo Implement. */
+ return XFR_TYPE_IIN;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_find_zone_for_xfr(const knot_zone_contents_t *zone,
+ const char **zonefile, const char **zonedb)
+{
+ /* find the zone file name and zone db file name for the zone */
+ conf_t *cnf = conf();
+ node *n = NULL;
+ WALK_LIST(n, cnf->zones) {
+ conf_zone_t *zone_conf = (conf_zone_t *)n;
+ knot_dname_t *zone_name = knot_dname_new_from_str(
+ zone_conf->name, strlen(zone_conf->name), NULL);
+ if (zone_name == NULL) {
+ return KNOTD_ENOMEM;
+ }
+
+ int r = knot_dname_compare(zone_name, knot_node_owner(
+ knot_zone_contents_apex(zone)));
+
+ /* Directly discard dname, won't be needed. */
+ knot_dname_free(&zone_name);
+
+ if (r == 0) {
+ /* found the right zone */
+ *zonefile = zone_conf->file;
+ *zonedb = zone_conf->db;
+ return KNOTD_EOK;
+ }
+ }
+
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_zones("zones: no zone found for the zone received by transfer "
+ "(%s).\n", name);
+ free(name);
+
+ return KNOTD_ENOENT;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static char *zones_find_free_filename(const char *old_name)
+{
+ /* find zone name not present on the disk */
+ int free_name = 0;
+ size_t name_size = strlen(old_name);
+
+ char *new_name = malloc(name_size + 3);
+ if (new_name == NULL) {
+ return NULL;
+ }
+ memcpy(new_name, old_name, name_size);
+ new_name[name_size] = '.';
+ new_name[name_size + 2] = 0;
+
+ dbg_zones_verb("zones: finding free name for the zone file.\n");
+ int c = 48;
+ FILE *file;
+ while (!free_name && c < 58) {
+ new_name[name_size + 1] = c;
+ dbg_zones_verb("zones: trying file name %s\n", new_name);
+ if ((file = fopen(new_name, "r")) != NULL) {
+ fclose(file);
+ ++c;
+ } else {
+ free_name = 1;
+ }
+ }
+
+ if (free_name) {
+ return new_name;
+ } else {
+ free(new_name);
+ return NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_dump_xfr_zone_text(knot_zone_contents_t *zone,
+ const char *zonefile)
+{
+ assert(zone != NULL && zonefile != NULL);
+
+ /*! \todo new_zonefile may be created by another process,
+ * until the zone_dump_text is called. Needs to be opened in
+ * this function for writing.
+ * Use open() for exclusive open and fcntl() for locking.
+ */
+
+ char *new_zonefile = zones_find_free_filename(zonefile);
+
+ if (new_zonefile == NULL) {
+ dbg_zones("zones: failed to find free filename for temporary "
+ "storage of the zone text file.\n");
+ return KNOTD_ERROR; /*! \todo New error code? */
+ }
+
+ int rc = zone_dump_text(zone, new_zonefile);
+
+ if (rc != KNOTD_EOK) {
+ dbg_zones("Failed to save the zone to text zone file '%s'.\n",
+ new_zonefile);
+ free(new_zonefile);
+ return KNOTD_ERROR;
+ }
+
+ /*! \todo this would also need locking as well. */
+ struct stat s;
+ rc = stat(zonefile, &s);
+ if (rc < 0 || remove(zonefile) == 0) {
+ if (rename(new_zonefile, zonefile) != 0) {
+ dbg_zones("Failed to replace old zonefile '%s'' with new"
+ " zone file '%s'.\n", zonefile, new_zonefile);
+ /*! \todo with proper locking, this shouldn't happen,
+ * revise it later on.
+ */
+ zone_dump_text(zone, zonefile);
+ return KNOTD_ERROR;
+ }
+ } else {
+ dbg_zones("zones: failed to replace old zonefile '%s'.\n",
+ zonefile);
+ return KNOTD_ERROR;
+ }
+
+ free(new_zonefile);
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_dump_xfr_zone_binary(knot_zone_contents_t *zone,
+ const char *zonedb,
+ const char *zonefile)
+{
+ assert(zone != NULL && zonedb != NULL);
+
+ /*! \todo new_zonedb may be created by another process,
+ * until the zone_dump_text is called. Needs to be opened in
+ * this function for writing.
+ * Use open() for exclusive open and fcntl() for locking.
+ */
+ char *new_zonedb = zones_find_free_filename(zonedb);
+
+ if (new_zonedb == NULL) {
+ dbg_zones("zones: failed to find free filename for temporary "
+ "storage of the zone binary file '%s'\n",
+ zonedb);
+ return KNOTD_ERROR; /*! \todo New error code? */
+ }
+
+ /*! \todo this would also need locking as well. */
+ int rc = knot_zdump_dump_and_swap(zone, new_zonedb, zonedb, zonefile);
+ free(new_zonedb);
+
+ if (rc != KNOT_EOK) {
+ return KNOTD_ERROR;
+ }
+
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_save_zone(const knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || xfr->data == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ knot_zone_contents_t *zone =
+ (knot_zone_contents_t *)xfr->data;
+
+ const char *zonefile = NULL;
+ const char *zonedb = NULL;
+
+ int ret = zones_find_zone_for_xfr(zone, &zonefile, &zonedb);
+ if (ret != KNOTD_EOK) {
+ return ret;
+ }
+
+ assert(zonefile != NULL && zonedb != NULL);
+
+ /* dump the zone into text zone file */
+ ret = zones_dump_xfr_zone_text(zone, zonefile);
+ if (ret != KNOTD_EOK) {
+ return KNOTD_ERROR;
+ }
+ /* dump the zone into binary db file */
+ ret = ns_dump_xfr_zone_binary(zone, zonedb, zonefile);
+ if (ret != KNOTD_EOK) {
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_ns_conf_hook(const struct conf_t *conf, void *data)
+{
+ knot_nameserver_t *ns = (knot_nameserver_t *)data;
+ dbg_zones_verb("zones: reconfiguring name server.\n");
+
+ knot_zonedb_t *old_db = 0;
+
+ int ret = zones_update_db_from_config(conf, ns, &old_db);
+ if (ret != KNOTD_EOK) {
+ return ret;
+ }
+ /* Wait until all readers finish with reading the zones. */
+ synchronize_rcu();
+
+ dbg_zones_verb("zones: nameserver's zone db: %p, old db: %p\n",
+ ns->zone_db, old_db);
+
+ /* Delete all deprecated zones and delete the old database. */
+ knot_zonedb_deep_free(&old_db);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_check_binary_size(uint8_t **data, size_t *allocated,
+ size_t required)
+{
+ if (required <= *allocated) {
+ return KNOTD_EOK;
+ }
+
+ /* Allocate new memory block. */
+ size_t new_count = required;
+ uint8_t *new_data = malloc(new_count * sizeof(uint8_t));
+ if (new_data == NULL) {
+ return KNOTD_ENOMEM;
+ }
+
+ /* Clear memory block and copy old data. */
+ memset(new_data, 0, new_count * sizeof(uint8_t));
+ memcpy(new_data, *data, *allocated);
+
+ /* Switch pointers and free old pointer. */
+ free(*data);
+ *data = new_data;
+ *allocated = new_count;
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_changeset_rrset_to_binary(uint8_t **data, size_t *size,
+ size_t *allocated,
+ knot_rrset_t *rrset)
+{
+ assert(data != NULL);
+ assert(size != NULL);
+ assert(allocated != NULL);
+
+ /*
+ * In *data, there is the whole changeset in the binary format,
+ * the actual RRSet will be just appended to it
+ */
+
+ uint8_t *binary = NULL;
+ size_t actual_size = 0;
+ int ret = knot_zdump_rrset_serialize(rrset, &binary, &actual_size);
+ if (ret != KNOT_EOK) {
+ return KNOTD_ERROR; /*! \todo Other code? */
+ }
+
+ ret = zones_check_binary_size(data, allocated, *size + actual_size);
+ if (ret != KNOT_EOK) {
+ free(binary);
+ return KNOTD_ERROR;
+ }
+
+ memcpy(*data + *size, binary, actual_size);
+ *size += actual_size;
+ free(binary);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_changesets_to_binary(knot_changesets_t *chgsets)
+{
+ assert(chgsets != NULL);
+ assert(chgsets->allocated >= chgsets->count);
+
+ /*
+ * Converts changesets to the binary format stored in chgsets->data
+ * from the changeset_t structures.
+ */
+ int ret;
+
+ for (int i = 0; i < chgsets->count; ++i) {
+ knot_changeset_t *ch = &chgsets->sets[i];
+ assert(ch->data == NULL);
+ assert(ch->size == 0);
+
+ /* 1) origin SOA */
+ ret = zones_changeset_rrset_to_binary(&ch->data, &ch->size,
+ &ch->allocated, ch->soa_from);
+ if (ret != KNOT_EOK) {
+ free(ch->data);
+ ch->data = NULL;
+ dbg_zones("zones_changeset_rrset_to_binary(): %s\n",
+ knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+
+ int j;
+
+ /* 2) remove RRsets */
+ assert(ch->remove_allocated >= ch->remove_count);
+ for (j = 0; j < ch->remove_count; ++j) {
+ ret = zones_changeset_rrset_to_binary(&ch->data,
+ &ch->size,
+ &ch->allocated,
+ ch->remove[j]);
+ if (ret != KNOT_EOK) {
+ free(ch->data);
+ ch->data = NULL;
+ dbg_zones("zones_changeset_rrset_to_binary(): %s\n",
+ knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+ }
+
+ /* 3) new SOA */
+ ret = zones_changeset_rrset_to_binary(&ch->data, &ch->size,
+ &ch->allocated, ch->soa_to);
+ if (ret != KNOT_EOK) {
+ free(ch->data);
+ ch->data = NULL;
+ dbg_zones("zones_changeset_rrset_to_binary(): %s\n",
+ knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+
+ /* 4) add RRsets */
+ assert(ch->add_allocated >= ch->add_count);
+ for (j = 0; j < ch->add_count; ++j) {
+ ret = zones_changeset_rrset_to_binary(&ch->data,
+ &ch->size,
+ &ch->allocated,
+ ch->add[j]);
+ if (ret != KNOT_EOK) {
+ free(ch->data);
+ ch->data = NULL;
+ dbg_zones("zones_changeset_rrset_to_binary(): %s\n",
+ knot_strerror(ret));
+ return KNOTD_ERROR;
+ }
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_store_changesets(knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || xfr->data == NULL || xfr->zone == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ knot_zone_t *zone = xfr->zone;
+ knot_changesets_t *src = (knot_changesets_t *)xfr->data;
+
+ /*! \todo Convert to binary format. */
+
+ int ret = zones_changesets_to_binary(src);
+ if (ret != KNOTD_EOK) {
+ return ret;
+ }
+
+ /* Fetch zone-specific data. */
+ zonedata_t *zd = (zonedata_t *)zone->data;
+ if (!zd->ixfr_db) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Begin writing to journal. */
+ for (unsigned i = 0; i < src->count; ++i) {
+
+ /* Make key from serials. */
+ knot_changeset_t* chs = src->sets + i;
+ uint64_t k = ixfrdb_key_make(chs->serial_from, chs->serial_to);
+
+ /* Write entry. */
+ int ret = journal_write(zd->ixfr_db, k, (const char*)chs->data,
+ chs->size);
+
+ /* Check for errors. */
+ while (ret != KNOTD_EOK) {
+
+ /* Sync to zonefile may be needed. */
+ if (ret == KNOTD_EAGAIN) {
+
+ /* Cancel sync timer. */
+ event_t *tmr = zd->ixfr_dbsync;
+ if (tmr) {
+ dbg_xfr_verb("xfr: cancelling zonefile "
+ "SYNC timer of '%s'\n",
+ zd->conf->name);
+ evsched_cancel(tmr->parent, tmr);
+ }
+
+ /* Synchronize. */
+ dbg_xfr_verb("xfr: forcing zonefile SYNC "
+ "of '%s'\n",
+ zd->conf->name);
+ ret = zones_zonefile_sync(zone);
+ if (ret != KNOTD_EOK) {
+ continue;
+ }
+
+ /* Reschedule sync timer. */
+ if (tmr) {
+ /* Fetch sync timeout. */
+ conf_read_lock();
+ int timeout = zd->conf->dbsync_timeout;
+ timeout *= 1000; /* Convert to ms. */
+ conf_read_unlock();
+
+ /* Reschedule. */
+ dbg_xfr_verb("xfr: resuming SYNC "
+ "of '%s'\n",
+ zd->conf->name);
+ evsched_schedule(tmr->parent, tmr,
+ timeout);
+
+ }
+
+ /* Attempt to write again. */
+ ret = journal_write(zd->ixfr_db, k,
+ (const char*)chs->data,
+ chs->size);
+ } else {
+ /* Other errors. */
+ return KNOTD_ERROR;
+ }
+ }
+
+ /* Free converted binary data. */
+ free(chs->data);
+ chs->data = 0;
+ chs->size = 0;
+ }
+
+ /* Written changesets to journal. */
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
+ uint32_t serial_to)
+{
+ if (!xfr || !xfr->zone || !knot_zone_contents(xfr->zone)) {
+ dbg_zones_detail("Wrong parameters: xfr=%p,"
+ " xfr->zone = %p\n", xfr, xfr->zone);
+ return KNOTD_EINVAL;
+ }
+
+ knot_changesets_t *chgsets = (knot_changesets_t *)
+ calloc(1, sizeof(knot_changesets_t));
+ CHECK_ALLOC_LOG(chgsets, KNOTD_ENOMEM);
+
+ int ret = ns_serial_compare(serial_to, serial_from);
+ dbg_zones_verb("Compared serials, result: %d\n", ret);
+
+ /* if serial_to is not larger than serial_from, do not load anything */
+ if (ret <= 0) {
+ xfr->data = chgsets;
+ return KNOTD_EOK;
+ }
+
+ dbg_zones("Loading changesets...\n");
+
+ ret = zones_load_changesets(xfr->zone, chgsets,
+ serial_from, serial_to);
+ if (ret != KNOTD_EOK) {
+ dbg_zones_verb("Loading changesets failed: %s\n",
+ knotd_strerror(ret));
+ return ret;
+ }
+
+ xfr->data = chgsets;
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_apply_changesets(knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || xfr->zone == NULL || xfr->data == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ return xfrin_apply_changesets_to_zone(xfr->zone,
+ (knot_changesets_t *)xfr->data);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
+{
+ if (!sch || !zone) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Fetch zone data. */
+ zonedata_t *zd = (zonedata_t *)zone->data;
+ if (!zd) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Cancel REFRESH timer. */
+ if (zd->xfr_in.timer) {
+ evsched_cancel(sch, zd->xfr_in.timer);
+ evsched_event_free(sch, zd->xfr_in.timer);
+ zd->xfr_in.timer = 0;
+ }
+
+ /* Cancel EXPIRE timer. */
+ if (zd->xfr_in.expire) {
+ evsched_cancel(sch, zd->xfr_in.expire);
+ evsched_event_free(sch, zd->xfr_in.expire);
+ zd->xfr_in.expire = 0;
+ }
+
+ /* Remove list of pending NOTIFYs. */
+ pthread_mutex_lock(&zd->lock);
+ notify_ev_t *ev = 0, *evn = 0;
+ WALK_LIST_DELSAFE(ev, evn, zd->notify_pending) {
+ zones_cancel_notify(zd, ev);
+ }
+ pthread_mutex_unlock(&zd->lock);
+
+ /* Check XFR/IN master server. */
+ if (zd->xfr_in.master.ptr) {
+
+ /* Schedule REFRESH timer. */
+ uint32_t refresh_tmr = 0;
+ if (knot_zone_contents(zone)) {
+ refresh_tmr = zones_soa_refresh(zone);
+ } else {
+ refresh_tmr = zd->xfr_in.bootstrap_retry;
+ }
+ zd->xfr_in.timer = evsched_schedule_cb(sch, zones_refresh_ev,
+ zone, refresh_tmr);
+ dbg_zones("zone: REFRESH set to %u\n", refresh_tmr);
+ }
+
+ /* Schedule IXFR database syncing. */
+ /*! \todo Sync timer should not be reset after each xfr. */
+ int sync_timeout = cfzone->dbsync_timeout * 1000; /* Convert to ms. */
+ if (zd->ixfr_dbsync) {
+ evsched_cancel(sch, zd->ixfr_dbsync);
+ evsched_event_free(sch, zd->ixfr_dbsync);
+ zd->ixfr_dbsync = 0;
+ }
+ if (zd->ixfr_db) {
+ zd->ixfr_dbsync = evsched_schedule_cb(sch,
+ zones_zonefile_sync_ev,
+ zone, sync_timeout);
+ }
+
+ /* Do not issue NOTIFY queries if stub. */
+ if (!knot_zone_contents(zone)) {
+ conf_read_unlock();
+ return KNOTD_EOK;
+ }
+
+ /* Schedule NOTIFY to slaves. */
+ conf_remote_t *r = 0;
+ conf_read_lock();
+ WALK_LIST(r, cfzone->acl.notify_out) {
+
+ /* Fetch remote. */
+ conf_iface_t *cfg_if = r->remote;
+
+ /* Create request. */
+ notify_ev_t *ev = malloc(sizeof(notify_ev_t));
+ if (!ev) {
+ free(ev);
+ dbg_zones("notify: out of memory to create "
+ "NOTIFY query for %s\n", cfg_if->name);
+ continue;
+ }
+
+ /* Parse server address. */
+ int ret = sockaddr_set(&ev->addr, cfg_if->family,
+ cfg_if->address,
+ cfg_if->port);
+ if (ret < 1) {
+ free(ev);
+ dbg_zones("notify: NOTIFY slave %s has invalid "
+ "address\n", cfg_if->name);
+ continue;
+ }
+
+ /* Prepare request. */
+ ev->retries = cfzone->notify_retries + 1; /* first + N retries*/
+ ev->msgid = 0;
+ ev->zone = zone;
+ ev->timeout = cfzone->notify_timeout;
+
+ /* Schedule request (30 - 60s random delay). */
+ int tmr_s = 30 + (int)(30.0 * (rand() / (RAND_MAX + 1.0)));
+ pthread_mutex_lock(&zd->lock);
+ ev->timer = evsched_schedule_cb(sch, zones_notify_send, ev,
+ tmr_s * 1000);
+ add_tail(&zd->notify_pending, &ev->n);
+ pthread_mutex_unlock(&zd->lock);
+
+ log_server_info("Scheduled NOTIFY query after %d s to %s:%d\n",
+ tmr_s, cfg_if->address, cfg_if->port);
+ }
+
+ conf_read_unlock();
+
+ return KNOTD_EOK;
+}
+
+int zones_cancel_notify(zonedata_t *zd, notify_ev_t *ev)
+{
+ if (!zd || !ev || !ev->timer) {
+ return KNOTD_EINVAL;
+ }
+
+ /* Wait for event to finish running. */
+#ifdef KNOTD_NOTIFY_DEBUG
+ int pkt_id = ev->msgid; /*< Do not optimize! */
+#endif
+ event_t *tmr = ev->timer;
+ ev->timer = 0;
+ pthread_mutex_unlock(&zd->lock);
+ evsched_cancel(tmr->parent, tmr);
+
+ /* Re-lock and find again (if not deleted). */
+ pthread_mutex_lock(&zd->lock);
+ int match_exists = 0;
+ notify_ev_t *tmpev = 0;
+ WALK_LIST(tmpev, zd->notify_pending) {
+ if (tmpev == ev) {
+ match_exists = 1;
+ break;
+ }
+ }
+
+ /* Event deleted before cancelled. */
+ if (!match_exists) {
+ dbg_notify("notify: NOTIFY event for query ID=%u was "
+ "deleted before cancellation.\n",
+ pkt_id);
+ return KNOTD_EOK;
+
+ }
+
+ /* Free event (won't be scheduled again). */
+ dbg_notify("notify: NOTIFY query ID=%u event cancelled.\n",
+ pkt_id);
+ rem_node(&ev->n);
+ evsched_event_free(tmr->parent, tmr);
+ free(ev);
+ return KNOTD_EOK;
+}
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
new file mode 100644
index 0000000..525a78a
--- /dev/null
+++ b/src/knot/server/zones.h
@@ -0,0 +1,264 @@
+/* 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 zones.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * Contains functions for updating zone database from configuration.
+ *
+ * \addtogroup server
+ * @{
+ */
+
+#ifndef _KNOTD_ZONES_H_
+#define _KNOTD_ZONES_H_
+
+#include <stddef.h>
+
+#include "common/lists.h"
+#include "common/acl.h"
+#include "common/evsched.h"
+#include "libknot/nameserver/name-server.h"
+#include "libknot/zone/zonedb.h"
+#include "knot/conf/conf.h"
+#include "knot/server/notify.h"
+#include "knot/server/server.h"
+#include "knot/server/journal.h"
+#include "libknot/zone/zone.h"
+#include "libknot/updates/xfr-in.h"
+
+/* Constants. */
+#define SOA_QRY_TIMEOUT 10000 /*!< SOA query timeout (ms). */
+#define IXFR_DBSYNC_TIMEOUT (60*1000) /*!< Database sync timeout = 60s. */
+#define AXFR_BOOTSTRAP_RETRY (60*1000) /*!< Interval between AXFR BS retries. */
+
+/*!
+ * \brief Zone-related data.
+ */
+typedef struct zonedata_t
+{
+ /*! \brief Shortcut to zone config entry. */
+ conf_zone_t *conf;
+
+ /*! \brief Shortcut to server instance. */
+ server_t *server;
+
+ /*! \brief Zone data lock for exclusive access. */
+ pthread_mutex_t lock;
+
+ /*! \brief Access control lists. */
+ acl_t *xfr_out; /*!< ACL for xfr-out.*/
+ acl_t *notify_in; /*!< ACL for notify-in.*/
+ acl_t *notify_out; /*!< ACL for notify-out.*/
+
+ /*! \brief XFR-IN scheduler. */
+ struct {
+ acl_t *acl; /*!< ACL for xfr-in.*/
+ sockaddr_t master; /*!< Master server for xfr-in.*/
+ knot_key_t tsig_key; /*!< Master TSIG key. */
+ struct event_t *timer; /*!< Timer for REFRESH/RETRY. */
+ struct event_t *expire; /*!< Timer for REFRESH. */
+ int next_id; /*!< ID of the next awaited SOA resp.*/
+ pthread_mutex_t lock; /*!< Pending XFR/IN lock. */
+ void *wrkr; /*!< Pending XFR/IN worker. */
+ uint32_t bootstrap_retry;/*!< AXFR/IN bootstrap retry. */
+ } xfr_in;
+
+ /*! \brief List of pending NOTIFY events. */
+ list notify_pending;
+
+ /*! \brief Zone IXFR history. */
+ journal_t *ixfr_db;
+ struct event_t *ixfr_dbsync; /*!< Syncing IXFR db to zonefile. */
+ uint32_t zonefile_serial;
+} zonedata_t;
+
+/*!
+ * \brief Update zone database according to configuration.
+ *
+ * Creates a new database, copies references those zones from the old database
+ * which are still in the configuration, loads any new zones required and
+ * replaces the database inside the namserver.
+ *
+ * It also creates a list of deprecated zones that should be deleted once the
+ * function finishes.
+ *
+ * This function uses RCU mechanism to guard the access to the config and
+ * nameserver and to publish the new database in the nameserver.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] ns Nameserver which holds the zone database.
+ * \param[out] db_old Old database, containing only zones which should be
+ * deleted afterwards.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_EINVAL
+ * \retval KNOTD_ERROR
+ */
+int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns,
+ knot_zonedb_t **db_old);
+
+/*!
+ * \brief Sync zone data back to text zonefile.
+ *
+ * In case when SOA serial of the zonefile differs from the SOA serial of the
+ * loaded zone, zonefile needs to be updated.
+ *
+ * \note Current implementation rewrites the zone file.
+ *
+ * \param zone Evaluated zone.
+ *
+ * \retval KNOTD_EOK if successful.
+ * \retval KNOTD_EINVAL on invalid parameter.
+ * \retval KNOTD_ERROR on unspecified error during processing.
+ */
+int zones_zonefile_sync(knot_zone_t *zone);
+
+int zones_xfr_check_zone(knot_ns_xfr_t *xfr, knot_rcode_t *rcode);
+
+/*!
+ * \brief Processes normal response packet.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param from Address of the response sender.
+ * \param packet Parsed response packet.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ * size of the response.
+ *
+ * \retval KNOTD_EOK if a valid response was created.
+ * \retval KNOTD_EINVAL on invalid parameters or packet.
+ * \retval KNOTD_EMALF if an error occured and the response is not valid.
+ */
+int zones_process_response(knot_nameserver_t *nameserver,
+ sockaddr_t *from,
+ knot_packet_t *packet, uint8_t *response_wire,
+ size_t *rsize);
+
+/*!
+ * \brief Decides what type of transfer should be used to update the given zone.
+ *
+ * \param nameserver Name server structure that uses the zone.
+ * \param zone Zone to be updated by the transfer.
+ *
+ * \retval
+ */
+knot_ns_xfr_type_t zones_transfer_to_use(const knot_zone_contents_t *zone);
+
+int zones_save_zone(const knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Name server config hook.
+ *
+ * Routine for dynamic name server reconfiguration.
+ *
+ * \param conf Current configuration.
+ * \param data Instance of the nameserver structure to update.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL
+ * \retval KNOTD_ERROR
+ */
+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.
+ * Journal may be compacted, resulting in flattening changeset history.
+ *
+ * \param zone Zone associated with the changeset.
+ * \param src Changesets.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_EAGAIN if journal needs to be synced with zonefile first.
+ *
+ * \todo Expects the xfr structure to be initialized in some way.
+ */
+int zones_store_changesets(knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Load changesets from journal.
+ *
+ * Changesets will be stored on a permanent storage.
+ * Journal may be compacted, resulting in flattening changeset history.
+ *
+ * In case of KNOTD_ERANGE error, whole zone content should be sent instead,
+ * as the changeset history cannot be recovered.
+ *
+ * \param zone Zone containing a changeset journal.
+ * \param dst Container to be loaded.
+ * \param from Starting SOA serial (oldest).
+ * \param to Ending SOA serial (newest).
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid parameters.
+ * \retval KNOTD_ERANGE when changeset history cannot be reconstructed.
+ *
+ * \todo Expects the xfr structure to be initialized in some way.
+ */
+int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
+ uint32_t serial_to);
+
+/*!
+ * \brief Apply changesets to zone.
+ *
+ * Applies a list of XFR-style changesets to the given zone. Also checks if the
+ * changesets are applicable (i.e. zone is right and has the right serial).
+ *
+ * \param zone Zone to which the changesets should be applied.
+ * \param chsets Changesets to be applied to the zone.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_EINVAL
+ */
+int zones_apply_changesets(knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Update zone timers.
+ *
+ * REFRESH/RETRY/EXPIRE timers are updated according to SOA.
+ *
+ * \param sched Event scheduler.
+ * \param zone Related zone.
+ * \param cfzone Related zone contents. If NULL, configuration is
+ * reused.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_EINVAL
+ * \retval KNOTD_ERROR
+ */
+int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch);
+
+/*!
+ * \brief Cancel pending NOTIFY timer.
+ *
+ * \warning Expects locked zonedata lock.
+ *
+ * \param zd Zone data.
+ * \param ev NOTIFY event.
+ *
+ * \retval KNOTD_EOK
+ * \retval KNOTD_ERROR
+ * \retval KNOTD_EINVAL
+ */
+int zones_cancel_notify(zonedata_t *zd, notify_ev_t *ev);
+
+#endif // _KNOTD_ZONES_H_
+
+/*! @} */
diff --git a/src/knot/stat/gatherer.c b/src/knot/stat/gatherer.c
new file mode 100644
index 0000000..e8048a1
--- /dev/null
+++ b/src/knot/stat/gatherer.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <pthread.h>
+
+#include "knot/stat/stat-common.h"
+#include "common/slab/malloc.h"
+#include "knot/stat/gatherer.h"
+
+gatherer_t *new_gatherer()
+{
+ gatherer_t *ret;
+
+ if ((ret = malloc(sizeof(gatherer_t))) == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ pthread_mutex_init(&ret->mutex_read, NULL);
+
+ /* TODO check success */
+
+ for (int i = 0; i < FREQ_BUFFER_SIZE; i++) {
+ ret->freq_array[i] = 0;
+ ret->flow_array[i] = NULL;
+ }
+
+ ret->qps = 0.0;
+ ret->udp_qps = 0.0;
+ ret->tcp_qps = 0.0;
+
+ /* CLEANUP */
+ /* currently disabled */
+ /* ret->mean_latency = 0.0;
+ ret->udp_mean_latency = 0.0;
+ ret->tcp_mean_latency = 0.0;
+
+ ret->udp_latency = 0;
+ ret->tcp_latency = 0; */
+
+ ret->udp_queries = 0;
+ ret->tcp_queries = 0;
+
+ return ret;
+}
+
+void gatherer_free(gatherer_t *gath)
+{
+ for (int i = 0; i < FREQ_BUFFER_SIZE; i++) {
+ if (gath->flow_array[i] != NULL) {
+ free(gath->flow_array[i]->addr);
+ free(gath->flow_array[i]);
+ }
+ }
+
+ pthread_mutex_destroy(&(gath->mutex_read));
+
+ pthread_cancel(gath->sleeper_thread);
+
+ pthread_join((gath->sleeper_thread), NULL);
+
+ free(gath);
+}
diff --git a/src/knot/stat/gatherer.h b/src/knot/stat/gatherer.h
new file mode 100644
index 0000000..62b3939
--- /dev/null
+++ b/src/knot/stat/gatherer.h
@@ -0,0 +1,110 @@
+/* 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 gatherer.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Contains gatherer structure and its API.
+ *
+ * \addtogroup statistics
+ * @{
+ */
+
+#ifndef _KNOTD_GATHERER_H_
+#define _KNOTD_GATHERER_H_
+
+#include <stdint.h>
+
+/* The bigger this number, the better the performance of hashing. */
+enum fbs { FREQ_BUFFER_SIZE = 100000 };
+
+/*!
+ * \brief Enum storing protocol codes.
+ */
+enum protocol {
+ stat_UDP,
+ stat_TCP
+};
+
+typedef enum protocol protocol_t;
+
+/*!
+ * \brief Structure used for backward mapping from a simple
+ * hash back to string representation.
+ */
+struct flow_data {
+ char *addr; /*!< IP adress in string format (IP4 only at this time). */
+ uint16_t port; /*!< TCP/UDP port number. */
+ protocol_t protocol;
+};
+
+typedef struct flow_data flow_data_t;
+
+/*!
+ * \brief Gatherer structure, used for gathering statistics from
+ * multiple threads.
+ */
+struct gatherer {
+ pthread_mutex_t mutex_read; /*!< Mutex used when reading values. */
+ double qps; /*!< Queries per second. */
+ double udp_qps; /*!< Queries per second - UDP. */
+ double tcp_qps; /*!< Queries per second - TCP. */
+
+ /*!< \note latency currently disabled */
+ /* double mean_latency;
+ double udp_mean_latency;
+ double tcp_mean_latency;
+ unsigned udp_latency;
+ unsigned tcp_latency; */
+
+ unsigned udp_queries; /*!< Total number of UDP queries for SLEEP_TIME. */
+ unsigned tcp_queries; /*!< Total number of TCP queries for SLEEP_TIME. */
+ /*!
+ * \brief this variable should be much bigger, preferably sparse array
+ * with 2**32 elements (for IPv4). It is an array with query
+ * query frequencies.
+ */
+ unsigned freq_array[FREQ_BUFFER_SIZE];
+ /*!
+ * \brief Used for backward mapping.
+ */
+ flow_data_t *flow_array[FREQ_BUFFER_SIZE];
+ /*!
+ * \brief Thread used for computation of statistics.
+ */
+ pthread_t sleeper_thread;
+};
+
+typedef struct gatherer gatherer_t;
+
+/*!
+ * \brief Creates a new gatherer instance.
+ *
+ * \return Pointer to created structure, NULL otherwise.
+ */
+gatherer_t *new_gatherer();
+
+/*!
+ * \brief Frees a gatherer instance.
+ *
+ * \param gatherer Gatherer instance to be freed.
+ */
+void gatherer_free(gatherer_t *gatherer);
+
+#endif /* _KNOTD_STAT_GATHERER_H_ */
+
+/*! @} */
diff --git a/src/knot/stat/stat-common.h b/src/knot/stat/stat-common.h
new file mode 100644
index 0000000..032e32b
--- /dev/null
+++ b/src/knot/stat/stat-common.h
@@ -0,0 +1,46 @@
+/* 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 stat-common.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Common macros for stat.
+ *
+ * \addtogroup statistics
+ * @{
+ */
+
+#ifndef _KNOTD_STAT_COMMON_H_
+#define _KNOTD_STAT_COMMON_H_
+
+#include <stdio.h>
+
+//#define STAT_COMPILE
+#define ST_DEBUG
+
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d\n", \
+ __FILE__, __LINE__)
+
+#ifdef ST_DEBUG
+#define dbg_st(msg...) fprintf(stderr, msg)
+#else
+#define dbg_st(msg...)
+#endif
+
+#endif /* _KNOTD_STAT_COMMON_H_ */
+
+/*! @} */
diff --git a/src/knot/stat/stat.c b/src/knot/stat/stat.c
new file mode 100644
index 0000000..a473085
--- /dev/null
+++ b/src/knot/stat/stat.c
@@ -0,0 +1,270 @@
+/* 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 <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "knot/stat/stat-common.h"
+#include "knot/stat/stat.h"
+#include "knot/stat/gatherer.h"
+
+#ifdef STAT_COMPILE
+
+/* Static local gatherer variable, to be used with all functions. */
+static gatherer_t *local_gath;
+
+/* CLEANUP */
+/*
+static void stat_inc_latency( stat_t *stat, uint increment )
+{
+ if (stat->protocol==stat_UDP) {
+ local_gath->udp_latency+=increment;
+ } else {
+ local_gath->tcp_latency+=increment;
+ }
+}*/
+/*
+static uint stat_last_query_time( stat_t *stat )
+{
+ return (stat->t2).tv_nsec-(stat->t1).tv_nsec;
+}*/
+
+/*!
+ * \brief Increases query count in the local data gatherer.
+ *
+ * \param stat Current stat instance.
+ */
+static void stat_inc_query(stat_t *stat)
+{
+ if (stat->protocol == stat_UDP) {
+ local_gath->udp_queries++;
+ } else {
+ local_gath->tcp_queries++;
+ }
+}
+
+/*!
+ * \brief Calculates very simple hash from IPv4 address and returns index to
+ * array.
+ *
+ * \param s_addr Socket address structure.
+ * \param protocol Used protocol.
+ *
+ * \return uint Calculated index.
+ */
+static uint return_index(struct sockaddr_in *s_addr , protocol_t protocol)
+{
+ /* TODO IPv6 */
+ /* This is the first "hash" I could think of quickly. */
+ uint ret = 0;
+
+ char str[24];
+ inet_ntop(AF_INET, &s_addr->sin_addr, str, 24);
+
+ for (int i = 0; i < strlen(str); i++) {
+ if (str[i] != '.') {
+ ret += str[i];
+ ret *= (i + 1);
+ }
+ }
+
+ ret += s_addr->sin_port * 7;
+ if (protocol == stat_UDP) {
+ ret *= 3;
+ } else {
+ ret *= 7;
+ }
+ ret %= FREQ_BUFFER_SIZE;
+ /* Effectively uses only end of the hash, maybe hash the
+ * resulting number once again to get 0 <= n < 10000. */
+ return ret;
+}
+
+/*!
+ * \brief Adds data to local gatherer structure.
+ *
+ * \param stat Current stat variable.
+ *
+ * \retval 0 on success.
+ * \retval -1 on memory error.
+ */
+static int stat_gatherer_add_data(stat_t *stat)
+{
+ /* TODO IPv6*/
+ uint index = return_index(stat->s_addr, stat->protocol);
+ if (!local_gath->freq_array[index]) {
+ char addr[24];
+ inet_ntop(AF_INET, &stat->s_addr->sin_addr, addr, 24);
+ flow_data_t *tmp;
+ tmp = malloc(sizeof(flow_data_t));
+ if (tmp == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+ tmp->addr = malloc(sizeof(char) * 24);
+ if (tmp->addr == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+ strcpy(tmp->addr, addr);
+ tmp->port = stat->s_addr->sin_port;
+ tmp->protocol = stat->protocol;
+ local_gath->flow_array[index] = tmp;
+ }
+
+ //TODO add a check here, whether hashing fction performs well enough
+
+ local_gath->freq_array[index] += 1;
+
+ return 0;
+}
+
+/*!
+ * \brief Resets logging array.
+ */
+static void stat_reset_gatherer_array()
+{
+ for (int i = 0; i < FREQ_BUFFER_SIZE; i++) {
+ local_gath->freq_array[i] = 0;
+ }
+}
+
+/*!
+ * \brief Sleeps for given time and then runs all the computations,
+ * results of which are stored in local gatherer.
+ */
+static void stat_sleep_compute()
+{
+ while (1) {
+ sleep(SLEEP_TIME);
+
+ for (int i = 0; i < FREQ_BUFFER_SIZE; i++) {
+ if (local_gath->freq_array[i] >
+ ACTIVE_FLOW_THRESHOLD) {
+ dbg_st("too much activity at index %d:"
+ " %d queries adress: %s port %d"
+ " protocol %d\n",
+ i, local_gath->freq_array[i],
+ local_gath->flow_array[i]->addr,
+ local_gath->flow_array[i]->port,
+ local_gath->flow_array[i]->protocol);
+ }
+ }
+
+ pthread_mutex_lock(&(local_gath->mutex_read));
+
+ local_gath->udp_qps = local_gath->udp_queries /
+ (double)SLEEP_TIME;
+ local_gath->tcp_qps = local_gath->tcp_queries /
+ (double)SLEEP_TIME;
+ local_gath->qps = local_gath->udp_qps + local_gath->tcp_qps;
+
+ /* following code needs usage of
+ * gettimeofday, which is currently disabled */
+ /* CLEANUP */
+/* local_gath->udp_mean_latency=((double)local_gath->udp_latency/
+ (double)local_gath->udp_queries);
+ local_gath->tcp_mean_latency=((double)local_gath->tcp_latency/
+ (double)local_gath->tcp_queries);
+ local_gath->mean_latency = (local_gath->udp_mean_latency +
+ local_gath->tcp_mean_latency)/2; */
+
+ local_gath->udp_queries = 0;
+ local_gath->tcp_queries = 0;
+
+ /* same thing as above applies here */
+
+/* local_gath->tcp_latency = 0;
+ local_gath->udp_latency = 0; */
+
+ pthread_mutex_unlock(&(local_gath->mutex_read));
+
+ stat_reset_gatherer_array(local_gath);
+
+ dbg_st("qps_udp: %f\n", local_gath->udp_qps);
+/* dbg_st("mean_lat_udp: %f\n", local_gath->udp_mean_latency); */
+
+ dbg_st("qps_tcp: %f\n", local_gath->tcp_qps);
+/* dbg_st("mean_lat_tcp: %f\n", local_gath->tcp_mean_latency); */
+
+ dbg_st("UDP/TCP ratio %f\n",
+ local_gath->udp_qps / local_gath->tcp_qps);
+ }
+}
+
+stat_t *stat_new()
+{
+ stat_t *ret;
+
+ if ((ret = malloc(sizeof(stat_t))) == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ return ret;
+}
+
+void stat_set_protocol(stat_t *stat, int protocol)
+{
+ stat->protocol = protocol;
+}
+
+void stat_get_first(stat_t *stat , struct sockaddr_in *s_addr)
+{
+ /* CLEANUP */
+// gettimeofday(&stat->t2, NULL);
+ stat->s_addr = s_addr;
+// check if s_addr does not get overwritten
+}
+
+void stat_get_second(stat_t *stat)
+{
+ /* CLEANUP */
+// gettimeofday(&stat->t2, NULL);
+ stat_inc_query(stat);
+// stat_inc_latency(stat, stat_last_query_time(stat));
+ stat_gatherer_add_data(stat);
+}
+
+void stat_free(stat_t *stat)
+{
+ free(stat);
+}
+
+void stat_static_gath_init()
+{
+ local_gath = new_gatherer();
+}
+
+void stat_static_gath_start()
+{
+ pthread_create(&(local_gath->sleeper_thread), NULL,
+ (void *) &stat_sleep_compute, NULL);
+}
+
+void stat_static_gath_free()
+{
+ gatherer_free(local_gath);
+}
+
+#endif /* STAT_COMPILE */
diff --git a/src/knot/stat/stat.h b/src/knot/stat/stat.h
new file mode 100644
index 0000000..a82f130
--- /dev/null
+++ b/src/knot/stat/stat.h
@@ -0,0 +1,157 @@
+/* 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 stat.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Contains statistics structure and its API.
+ *
+ * \addtogroup statistics
+ * @{
+ */
+
+#ifndef _KNOTD_STAT_H_
+#define _KNOTD_STAT_H_
+
+#include <time.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "knot/stat/gatherer.h"
+
+#ifdef STAT_COMPILE
+#define STAT_INIT(x) x = stat_new()
+#else
+#define STAT_INIT(x) x = NULL //UNUSED(x)
+#endif /* STAT_COMPILE */
+
+/* Determines how long until the sleeper thread
+ * wakes up and runs computations.
+ */
+static uint const SLEEP_TIME = 15;
+
+/* Sets threshold for active flow detection, will
+ * probably have to be changed. */
+static uint const ACTIVE_FLOW_THRESHOLD = 10;
+
+/*!
+ * \brief Statistics structure, unique for each UDP/TCP thread.
+ */
+struct stat_stat {
+// struct timespec t1, t2; /* Currently disabled */
+ protocol_t protocol; /*!< Flags. */
+ struct sockaddr_in *s_addr;
+// gatherer_t *gatherer; / * not needed when using static gatherer. */
+};
+
+typedef struct stat_stat stat_t;
+
+/*!
+ * \brief Creates new stat_t structure.
+ *
+ * \return Newly allocated and initialized stat structure, NULL on errror.
+ */
+#ifdef STAT_COMPILE
+stat_t *stat_new();
+#else
+inline stat_t *stat_new()
+{
+ return NULL;
+}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Sets a protocol for stat_t structure. Options are stat_UDP, stat_TCP.
+ *
+ * \param stat Stat_t instance (usually newly created).
+ * \param protocol Protocol to be assigned to stat structure.
+ */
+#ifdef STAT_COMPILE
+void stat_set_protocol(stat_t *stat, int protocol);
+#else
+static inline void stat_set_protocol(stat_t *stat, int protocol) {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Gets the time from a processing function.
+ *
+ * \param stat Current instance of stat_t.
+ * \param s_addr Sockaddr structure to be used later for statistics.
+ */
+#ifdef STAT_COMPILE
+#warning "stat fixme: pass sockaddr* for generic _in and _in6 support"
+void stat_get_first(stat_t *stat, struct sockaddr_in *s_addr);
+#else
+static inline void stat_get_first(stat_t *stat, struct sockaddr *s_addr) {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Gets time from a processing fuction and changes
+ * the corresponding variables.
+ *
+ * \param stat Current stat_t instance.
+ */
+#ifdef STAT_COMPILE
+void stat_get_second(stat_t *stat);
+#else
+static inline void stat_get_second(stat_t *stat) {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Frees stat_t structure.
+ *
+ * \param stat Pointer to stat structure to be deallocated.
+ */
+#ifdef STAT_COMPILE
+void stat_free(stat_t *stat);
+#else
+static inline void stat_free(stat_t *stat) {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Initializes static gatherer.
+ */
+#ifdef STAT_COMPILE
+void stat_static_gath_init();
+#else
+static inline void stat_static_gath_init() {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Starts static gatherer's sleeper thread.
+ */
+#ifdef STAT_COMPILE
+void stat_static_gath_start();
+#else
+static inline void stat_static_gath_start() {}
+#endif /* STAT_COMPILE */
+
+/*!
+ * \brief Frees static gatherer, calls gatherer_free().
+ */
+#ifdef STAT_COMPILE
+void stat_static_gath_free();
+#else
+static inline void stat_static_gath_free() {}
+#endif /* STAT_COMPILE */
+
+#endif /* _KNOTD_STAT_H_ */
+
+/*! @} */
diff --git a/src/knot/zone/zone-dump-text.c b/src/knot/zone/zone-dump-text.c
new file mode 100644
index 0000000..f7899a1
--- /dev/null
+++ b/src/knot/zone/zone-dump-text.c
@@ -0,0 +1,1083 @@
+/*!
+ * \file zone-dump-text.c
+ *
+ * \author modifications (non-buffer implementation, zone-specific functions)
+ * by Jan Kadlec <jan.kadlec@nic.cz>,
+ * conversion functions by NLnet Labs,
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ * b64ntop by ISC.
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <assert.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "libknot/libknot.h"
+#include "libknot/common.h"
+#include "common/skip-list.h"
+#include "common/base32hex.h"
+
+/* TODO max length of alg */
+
+enum uint_max_length {
+ U8_MAX_STR_LEN = 4, U16_MAX_STR_LEN = 6,
+ U32_MAX_STR_LEN = 11, MAX_RR_TYPE_LEN = 20,
+ MAX_NSEC_BIT_STR_LEN = 4096,
+ };
+
+#define APL_NEGATION_MASK 0x80U
+#define APL_LENGTH_MASK (~APL_NEGATION_MASK)
+
+/* RFC 4025 - codes for different types that IPSECKEY can hold. */
+#define IPSECKEY_NOGATEWAY 0
+#define IPSECKEY_IP4 1
+#define IPSECKEY_IP6 2
+#define IPSECKEY_DNAME 3
+
+/* Following copyrights are only valid for b64_ntop function */
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC 1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int b64_ntop(uint8_t const *src, size_t srclength, char *target,
+ size_t targsize) {
+ size_t datalength = 0;
+ uint8_t input[3];
+ uint8_t output[4];
+ size_t i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+ Assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* Taken from RFC 4398, section 2.1. */
+knot_lookup_table_t knot_dns_certificate_types[] = {
+/* 0 Reserved */
+ { 1, "PKIX" }, /* X.509 as per PKIX */
+ { 2, "SPKI" }, /* SPKI cert */
+ { 3, "PGP" }, /* OpenPGP packet */
+ { 4, "IPKIX" }, /* The URL of an X.509 data object */
+ { 5, "ISPKI" }, /* The URL of an SPKI certificate */
+ { 6, "IPGP" }, /* The fingerprint and URL of an OpenPGP packet */
+ { 7, "ACPKIX" }, /* Attribute Certificate */
+ { 8, "IACPKIX" }, /* The URL of an Attribute Certificate */
+ { 253, "URI" }, /* URI private */
+ { 254, "OID" }, /* OID private */
+/* 255 Reserved */
+/* 256-65279 Available for IANA assignment */
+/* 65280-65534 Experimental */
+/* 65535 Reserved */
+ { 0, NULL }
+};
+
+/* Taken from RFC 2535, section 7. */
+knot_lookup_table_t knot_dns_algorithms[] = {
+ { 1, "RSAMD5" }, /* RFC 2537 */
+ { 2, "DH" }, /* RFC 2539 */
+ { 3, "DSA" }, /* RFC 2536 */
+ { 4, "ECC" },
+ { 5, "RSASHA1" }, /* RFC 3110 */
+ { 252, "INDIRECT" },
+ { 253, "PRIVATEDNS" },
+ { 254, "PRIVATEOID" },
+ { 0, NULL }
+};
+
+static int get_bit(uint8_t bits[], size_t index)
+{
+ /*
+ * The bits are counted from left to right, so bit #0 is the
+ * left most bit.
+ */
+ return bits[index / 8] & (1 << (7 - index % 8));
+}
+
+static inline uint8_t * rdata_item_data(knot_rdata_item_t item)
+{
+ return (uint8_t *)(item.raw_data + 1);
+}
+
+static inline uint16_t rdata_item_size(knot_rdata_item_t item)
+{
+ return item.raw_data[0];
+}
+
+char *rdata_dname_to_string(knot_rdata_item_t item)
+{
+ return knot_dname_to_str(item.dname);
+}
+
+char *rdata_dns_name_to_string(knot_rdata_item_t item)
+{
+ return knot_dname_to_str(item.dname);
+}
+
+static char *rdata_txt_data_to_string(const uint8_t *data)
+{
+ uint8_t length = data[0];
+ size_t i;
+
+ if (length == 0) {
+ return NULL;
+ }
+
+ /*
+ * 3 because: opening '"', closing '"', and \0 at the end.
+ * Times 2 because string can be all "double chars".
+ */
+ size_t current_length = sizeof(char) * (length * 2 + 3);
+ char *ret = malloc(current_length);
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ memset(ret, 0, sizeof(char) * (length * 2 + 3));
+
+
+ strcat(ret, "\"");
+
+ for (i = 1; i <= length; i++) {
+ char ch = (char) data[i];
+ if (isprint((int)ch)) {
+ if (ch == '"' || ch == '\\') {
+ strcat(ret, "\"");
+ }
+ /* for the love of god, how to this better,
+ but w/o obscure self-made functions */
+ char tmp_str[2];
+ tmp_str[0] = ch;
+ tmp_str[1] = 0;
+ strcat(ret, tmp_str);
+ } else {
+ strcat(ret, "\\");
+ char tmp_str[2];
+ tmp_str[0] = ch - '0';
+ tmp_str[1] = 0;
+
+ strcat(ret, tmp_str);
+ }
+ }
+ strcat(ret, "\"");
+
+ return ret;
+}
+
+char *rdata_text_to_string(knot_rdata_item_t item)
+{
+ uint16_t size = item.raw_data[0];
+ char *ret = malloc(sizeof(char) * size * 2) ;
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ memset(ret, 0, sizeof(char) * size);
+ const uint8_t *data = (uint8_t *)(item.raw_data + 1);
+ size_t read_count = 0;
+ while (read_count < size) {
+ assert(read_count <= size);
+ char *txt = rdata_txt_data_to_string(data + read_count);
+ if (txt == NULL) {
+ free(ret);
+ return NULL;
+ }
+ read_count += strlen(txt) - 1;
+ /* Create delimiter. */
+ char del[2];
+ del[0] = ' ';
+ del[1] = '\0';
+ strcat(ret, txt);
+ strcat(ret, del);
+ free(txt);
+ }
+
+ return ret;
+}
+
+char *rdata_byte_to_string(knot_rdata_item_t item)
+{
+ assert(item.raw_data[0] == 1);
+ uint8_t data = item.raw_data[1];
+ char *ret = malloc(sizeof(char) * U8_MAX_STR_LEN);
+ snprintf(ret, U8_MAX_STR_LEN, "%d", (char) data);
+ return ret;
+}
+
+char *rdata_short_to_string(knot_rdata_item_t item)
+{
+ uint16_t data = knot_wire_read_u16(rdata_item_data(item));
+ char *ret = malloc(sizeof(char) * U16_MAX_STR_LEN);
+ snprintf(ret, U16_MAX_STR_LEN, "%u", data);
+ /* XXX Use proper macros - see response tests*/
+ /* XXX check return value, return NULL on failure */
+ return ret;
+}
+
+char *rdata_long_to_string(knot_rdata_item_t item)
+{
+ uint32_t data = knot_wire_read_u32(rdata_item_data(item));
+ char *ret = malloc(sizeof(char) * U32_MAX_STR_LEN);
+ /* u should be enough */
+ snprintf(ret, U32_MAX_STR_LEN, "%u", data);
+ return ret;
+}
+
+char *rdata_a_to_string(knot_rdata_item_t item)
+{
+ /* 200 seems like a little too much */
+ char *ret = malloc(sizeof(char) * 200);
+ if (inet_ntop(AF_INET, rdata_item_data(item), ret, 200)) {
+ return ret;
+ } else {
+ return NULL;
+ }
+}
+
+char *rdata_aaaa_to_string(knot_rdata_item_t item)
+{
+ char *ret = malloc(sizeof(char) * 200);
+ if (inet_ntop(AF_INET6, rdata_item_data(item), ret, 200)) {
+ return ret;
+ } else {
+ return NULL;
+ }
+}
+
+char *rdata_rrtype_to_string(knot_rdata_item_t item)
+{
+ uint16_t type = knot_wire_read_u16(rdata_item_data(item));
+ const char *tmp = knot_rrtype_to_string(type);
+ char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN);
+ strncpy(ret, tmp, MAX_RR_TYPE_LEN);
+ return ret;
+}
+
+char *rdata_algorithm_to_string(knot_rdata_item_t item)
+{
+ uint8_t id = *rdata_item_data(item);
+ char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN);
+ knot_lookup_table_t *alg
+ = knot_lookup_by_id(knot_dns_algorithms, id);
+ if (alg) {
+ strncpy(ret, alg->name, MAX_RR_TYPE_LEN);
+ } else {
+ snprintf(ret, U8_MAX_STR_LEN, "%u", id);
+ }
+
+ return ret;
+}
+
+char *rdata_certificate_type_to_string(knot_rdata_item_t item)
+{
+ uint16_t id = knot_wire_read_u16(rdata_item_data(item));
+ char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN);
+ knot_lookup_table_t *type
+ = knot_lookup_by_id(knot_dns_certificate_types, id);
+ if (type) {
+ strncpy(ret, type->name, MAX_RR_TYPE_LEN);
+ } else {
+ snprintf(ret, U16_MAX_STR_LEN, "%u", id);
+ }
+
+ return ret;
+}
+
+char *rdata_period_to_string(knot_rdata_item_t item)
+{
+ /* uint32 but read 16 XXX */
+ uint32_t period = knot_wire_read_u32(rdata_item_data(item));
+ char *ret = malloc(sizeof(char) * U32_MAX_STR_LEN);
+ snprintf(ret, U32_MAX_STR_LEN, "%u", period);
+ return ret;
+}
+
+char *rdata_time_to_string(knot_rdata_item_t item)
+{
+ time_t time = (time_t) knot_wire_read_u32(rdata_item_data(item));
+ struct tm tm_conv;
+ if (gmtime_r(&time, &tm_conv) == 0) {
+ return 0;
+ }
+ char *ret = malloc(sizeof(char) * 15);
+ if (strftime(ret, 15, "%Y%m%d%H%M%S", &tm_conv)) {
+ return ret;
+ } else {
+ free(ret);
+ return 0;
+ }
+}
+
+char *rdata_base32_to_string(knot_rdata_item_t item)
+{
+ int length;
+ size_t size = rdata_item_size(item);
+ if (size == 0) {
+ char *ret = malloc(sizeof(char) * 2);
+ ret[0] = '-';
+ ret[1] = '\0';
+ return ret;
+ }
+
+ size -= 1; // remove length byte from count
+ char *ret = NULL;
+ length = base32hex_encode_alloc((char *)rdata_item_data(item) + 1,
+ size, &ret);
+ if (length > 0) {
+ return ret;
+ } else {
+ free(ret);
+ return NULL;
+ }
+}
+
+char *rdata_base64_to_string(knot_rdata_item_t item)
+{
+ int length;
+ size_t size = rdata_item_size(item);
+ char *ret = malloc((sizeof(char) * 2 * size) + 1 * sizeof(char));
+ length = b64_ntop(rdata_item_data(item), size,
+ ret, (sizeof(char)) * (size * 2 + 1));
+ if (length > 0) {
+ return ret;
+ } else {
+ free(ret);
+ return NULL;
+ }
+}
+
+char *hex_to_string(const uint8_t *data, size_t size)
+{
+ static const char hexdigits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ size_t i;
+
+ char *ret = malloc(sizeof(char) * (size * 2 + 1));
+
+ for (i = 0; i < size * 2; i += 2) {
+ uint8_t octet = *data++;
+ ret[i] = hexdigits [octet >> 4];
+ ret[i + 1] = hexdigits [octet & 0x0f];
+ }
+
+ ret[i] = '\0';
+
+ return ret;
+}
+
+char *rdata_hex_to_string(knot_rdata_item_t item)
+{
+ return hex_to_string(rdata_item_data(item), rdata_item_size(item));
+}
+
+char *rdata_hexlen_to_string(knot_rdata_item_t item)
+{
+ if(rdata_item_size(item) <= 1) {
+ // NSEC3 salt hex can be empty
+ char *ret = malloc(sizeof(char) * 2);
+ ret[0] = '-';
+ ret[1] = '\0';
+ return ret;
+ } else {
+ return hex_to_string(rdata_item_data(item) + 1,
+ rdata_item_size(item) - 1);
+ }
+}
+
+char *rdata_nsap_to_string(knot_rdata_item_t item)
+{
+ char *ret = malloc(sizeof(char) * (rdata_item_size(item) + 3));
+ memcpy(ret, "0x", strlen("0x"));
+ char *converted = hex_to_string(rdata_item_data(item),
+ rdata_item_size(item));
+ strcat(ret, converted);
+ free(converted);
+ return ret;
+}
+
+char *rdata_apl_to_string(knot_rdata_item_t item)
+{
+ uint8_t *data = rdata_item_data(item);
+ uint16_t address_family = knot_wire_read_u16(data);
+ uint8_t prefix = data[2];
+ uint8_t length = data[3];
+ int negated = length & APL_NEGATION_MASK;
+ int af = -1;
+
+ char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN);
+
+ memset(ret, 0, MAX_NSEC_BIT_STR_LEN);
+
+ length &= APL_LENGTH_MASK;
+ switch (address_family) {
+ case 1: af = AF_INET; break;
+ case 2: af = AF_INET6; break;
+ }
+
+ if (af != -1) {
+ char text_address[1000];
+ uint8_t address[128];
+ memset(address, 0, sizeof(address));
+ memcpy(address, data + 4, length);
+ if (inet_ntop(af, address,
+ text_address,
+ sizeof(text_address))) {
+ snprintf(ret, sizeof(text_address) +
+ U32_MAX_STR_LEN * 2,
+ "%s%d:%s/%d",
+ negated ? "!" : "",
+ (int) address_family,
+ text_address,
+ (int) prefix);
+ }
+ }
+
+ return ret;
+
+ /*
+ int result = 0;
+ buffer_type packet;
+
+ buffer_create_from(
+ &packet, rdata_item_data(rdata), rdata_atom_size(rdata));
+
+ if (buffer_available(&packet, 4)) {
+ uint16_t address_family = buffer_read_u16(&packet);
+ uint8_t prefix = buffer_read_u8(&packet);
+ uint8_t length = buffer_read_u8(&packet);
+ int negated = length & APL_NEGATION_MASK;
+ int af = -1;
+
+ length &= APL_LENGTH_MASK;
+ switch (address_family) {
+ case 1: af = AF_INET; break;
+ *case 2: af = AF_INET6; break;
+ }
+ if (af != -1 && buffer_available(&packet, length)) {
+ char text_address[1000];
+ uint8_t address[128];
+ memset(address, 0, sizeof(address));
+ buffer_read(&packet, address, length);
+ if (inet_ntop(af, address, text_address,
+ sizeof(text_address))) {
+ buffer_printf(output, "%s%d:%s/%d",
+ negated ? "!" : "",
+ (int) address_family,
+ text_address,
+ (int) prefix);
+ result = 1;
+ }
+ }
+ }
+ return result;
+ */
+
+
+}
+
+char *rdata_services_to_string(knot_rdata_item_t item)
+{
+ uint8_t *data = rdata_item_data(item);
+ uint8_t protocol_number = data[0];
+ ssize_t bitmap_size = rdata_item_size(item) - 1;
+ uint8_t *bitmap = data + 1;
+ struct protoent *proto = getprotobynumber(protocol_number);
+
+ char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN);
+
+ memset(ret, 0, MAX_NSEC_BIT_STR_LEN);
+
+ if (proto) {
+ int i;
+
+ strcpy(ret, proto->p_name);
+
+ strcat(ret, " ");
+
+ for (i = 0; i < bitmap_size * 8; ++i) {
+ if (get_bit(bitmap, i)) {
+ struct servent *service =
+ getservbyport((int)htons(i),
+ proto->p_name);
+ if (service) {
+ strcat(ret, service->s_name);
+ strcat(ret, " ");
+ } else {
+ char tmp[U32_MAX_STR_LEN];
+ snprintf(tmp, U32_MAX_STR_LEN,
+ "%d ", i);
+ strcat(ret, tmp);
+ }
+ }
+ }
+ }
+
+ return ret;
+
+ /*
+ int result = 0;
+ uint8_t protocol_number = buffer_read_u8(&packet);
+ ssize_t bitmap_size = buffer_remaining(&packet);
+ uint8_t *bitmap = buffer_current(&packet);
+ struct protoent *proto = getprotobynumber(protocol_number);
+
+
+ if (proto) {
+ int i;
+
+ strcpy(ret, proto->p_name);
+
+ for (i = 0; i < bitmap_size * 8; ++i) {
+ if (get_bit(bitmap, i)) {
+ struct servent *service =
+ getservbyport((int)htons(i),
+ proto->p_name);
+ if (service) {
+ buffer_printf(output, " %s",
+ service->s_name);
+ } else {
+ buffer_printf(output, " %d", i);
+ }
+ }
+ }
+ result = 1;
+ }
+ return ret;
+ */
+}
+
+char *rdata_ipsecgateway_to_string(knot_rdata_item_t item,
+ const knot_rrset_t *rrset)
+{
+ const knot_rdata_item_t *gateway_type_item =
+ knot_rdata_item(knot_rrset_rdata(rrset), 1);
+ if (gateway_type_item == NULL) {
+ return NULL;
+ }
+ /* First two bytes store length. */
+ int gateway_type = ((uint8_t *)(gateway_type_item->raw_data))[2];
+ switch(gateway_type) {
+ case IPSECKEY_NOGATEWAY: {
+ char *ret = malloc(sizeof(char) * 4);
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ memset(ret, 0, sizeof(char) * 4);
+ memcpy(ret, ".", 4);
+/* ret[0] = '\"';
+ ret[1] = '.';
+ ret[2] = '\"';
+ ret[3] = '\0'; */
+ return ret;
+ }
+ case IPSECKEY_IP4:
+ return rdata_a_to_string(item);
+ case IPSECKEY_IP6:
+ return rdata_aaaa_to_string(item);
+ case IPSECKEY_DNAME:
+ return rdata_dname_to_string(item);
+ default:
+ return NULL;
+ }
+
+ /* Flow *should* not get here. */
+ return NULL;
+}
+
+char *rdata_nxt_to_string(knot_rdata_item_t item)
+{
+ size_t i;
+ uint8_t *bitmap = rdata_item_data(item);
+ size_t bitmap_size = rdata_item_size(item);
+
+ char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN);
+
+ memset(ret, 0, MAX_NSEC_BIT_STR_LEN);
+
+ for (i = 0; i < bitmap_size * 8; ++i) {
+ if (get_bit(bitmap, i)) {
+ strcat(ret, knot_rrtype_to_string(i));
+ strcat(ret, " ");
+ }
+ }
+
+ return ret;
+}
+
+
+char *rdata_nsec_to_string(knot_rdata_item_t item)
+{
+ /* CLEANUP */
+// int insert_space = 0;
+
+ char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN);
+
+ memset(ret, 0, MAX_NSEC_BIT_STR_LEN);
+
+ uint8_t *data = rdata_item_data(item);
+
+ int increment = 0;
+
+ for (int i = 0; i < rdata_item_size(item); i += increment) {
+ increment = 0;
+ uint8_t window = data[i];
+ increment++;
+
+ uint8_t bitmap_size = data[i + increment];
+ increment++;
+
+ uint8_t *bitmap = malloc(sizeof(uint8_t) * bitmap_size);
+
+ memcpy(bitmap, data + i + increment,
+ bitmap_size);
+
+ increment += bitmap_size;
+
+ for (int j = 0; j < bitmap_size * 8; j++) {
+ if (get_bit(bitmap, j)) {
+ strcat(ret,
+ knot_rrtype_to_string(j +
+ window * 256));
+ strcat(ret, " ");
+ }
+ }
+
+ free(bitmap);
+ }
+
+ return ret;
+
+ /* CLEANUP */
+/* while (buffer_available(&packet, 2)) {
+ uint8_t window = buffer_read_u8(&packet);
+ uint8_t bitmap_size = buffer_read_u8(&packet);
+ uint8_t *bitmap = buffer_current(&packet);
+ int i;
+
+ if (!buffer_available(&packet, bitmap_size)) {
+ buffer_set_position(output, saved_position);
+ return 0;
+ }
+
+ for (i = 0; i < bitmap_size * 8; ++i) {
+ if (get_bit(bitmap, i)) {
+ buffer_printf(output,
+ "%s%s",
+ insert_space ? " " : "",
+ rrtype_to_string(
+ window * 256 + i));
+ insert_space = 1;
+ }
+ }
+ buffer_skip(&packet, bitmap_size);
+ }
+
+ return 1; */
+}
+
+char *rdata_unknown_to_string(knot_rdata_item_t item)
+{
+ uint16_t size = rdata_item_size(item);
+ char *ret =
+ malloc(sizeof(char) * (rdata_item_size(item) +
+ strlen("\\# ") + U16_MAX_STR_LEN));
+ snprintf(ret, strlen("\\# ") + U16_MAX_STR_LEN, "%lu",
+ (unsigned long) size);
+ char *converted = hex_to_string(rdata_item_data(item), size);
+ strcat(ret, converted);
+ free(converted);
+ return ret;
+}
+
+char *rdata_loc_to_string(knot_rdata_item_t item)
+{
+ return rdata_unknown_to_string(item);
+}
+
+typedef char * (*item_to_string_t)(knot_rdata_item_t);
+
+static item_to_string_t item_to_string_table[KNOT_RDATA_ZF_UNKNOWN + 1] = {
+ rdata_dname_to_string,
+ rdata_dns_name_to_string,
+ rdata_text_to_string,
+ rdata_byte_to_string,
+ rdata_short_to_string,
+ rdata_long_to_string,
+ rdata_a_to_string,
+ rdata_aaaa_to_string,
+ rdata_rrtype_to_string,
+ rdata_algorithm_to_string,
+ rdata_certificate_type_to_string,
+ rdata_period_to_string,
+ rdata_time_to_string,
+ rdata_base64_to_string,
+ rdata_base32_to_string,
+ rdata_hex_to_string,
+ rdata_hexlen_to_string,
+ rdata_nsap_to_string,
+ rdata_apl_to_string,
+ NULL, //rdata_ipsecgateway_to_string,
+ rdata_services_to_string,
+ rdata_nxt_to_string,
+ rdata_nsec_to_string,
+ rdata_loc_to_string,
+ rdata_unknown_to_string
+};
+
+char *rdata_item_to_string(knot_rdata_zoneformat_t type,
+ knot_rdata_item_t item)
+{
+ return item_to_string_table[type](item);
+}
+
+/* CLEANUP */
+/*void knot_zone_tree_apply_inorder(knot_zone_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data); */
+
+void rdata_dump_text(const knot_rdata_t *rdata, uint16_t type, FILE *f,
+ const knot_rrset_t *rrset)
+{
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ char *item_str = NULL;
+ for (int i = 0; i < rdata->count; i++) {
+ /* Workaround for IPSec gateway. */
+ if (desc->zoneformat[i] == KNOT_RDATA_ZF_IPSECGATEWAY) {
+ item_str = rdata_ipsecgateway_to_string(rdata->items[i],
+ rrset);
+ } else {
+ item_str = rdata_item_to_string(desc->zoneformat[i],
+ rdata->items[i]);
+ }
+ if (item_str == NULL) {
+ item_str =
+ rdata_item_to_string(KNOT_RDATA_ZF_UNKNOWN,
+ rdata->items[i]);
+ }
+ if (i != rdata->count - 1) {
+ fprintf(f, "%s ", item_str);
+ } else {
+ fprintf(f, "%s", item_str);
+ }
+ free(item_str);
+ }
+ fprintf(f, "\n");
+}
+
+void dump_rrset_header(const knot_rrset_t *rrset, FILE *f)
+{
+ char *name = knot_dname_to_str(rrset->owner);
+ fprintf(f, "%-20s ", name);
+ free(name);
+ fprintf(f, "%-5u ", rrset->ttl);
+ fprintf(f, "%-2s ", knot_rrclass_to_string(rrset->rclass));
+ fprintf(f, "%-5s ", knot_rrtype_to_string(rrset->type));
+}
+
+void rrsig_set_dump_text(knot_rrset_t *rrsig, FILE *f)
+{
+ dump_rrset_header(rrsig, f);
+ knot_rdata_t *tmp = rrsig->rdata;
+
+ while (tmp->next != rrsig->rdata) {
+ rdata_dump_text(tmp, KNOT_RRTYPE_RRSIG, f, rrsig);
+ dump_rrset_header(rrsig, f);
+ tmp = tmp->next;
+ }
+
+ rdata_dump_text(tmp, KNOT_RRTYPE_RRSIG, f, rrsig);
+}
+
+
+void rrset_dump_text(const knot_rrset_t *rrset, FILE *f)
+{
+ dump_rrset_header(rrset, f);
+ knot_rdata_t *tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata) {
+ rdata_dump_text(tmp, rrset->type, f, rrset);
+ dump_rrset_header(rrset, f);
+ tmp = tmp->next;
+ }
+
+ rdata_dump_text(tmp, rrset->type, f, rrset);
+ knot_rrset_t *rrsig_set = rrset->rrsigs;
+ if (rrsig_set != NULL) {
+ rrsig_set_dump_text(rrsig_set, f);
+ }
+}
+
+struct dump_param {
+ FILE *f;
+ const knot_dname_t *origin;
+};
+
+void apex_node_dump_text(knot_node_t *node, FILE *f)
+{
+ knot_rrset_t dummy_rrset;
+ dummy_rrset.type = KNOT_RRTYPE_SOA;
+ knot_rrset_t *tmp_rrset =
+ (knot_rrset_t *)gen_tree_find(node->rrset_tree,
+ &dummy_rrset);
+ assert(tmp_rrset);
+ rrset_dump_text(tmp_rrset, f);
+
+ const knot_rrset_t **rrsets =
+ knot_node_rrsets(node);
+
+ for (int i = 0; i < node->rrset_count; i++) {
+ if (rrsets[i]->type != KNOT_RRTYPE_SOA) {
+ rrset_dump_text(rrsets[i], f);
+ }
+ }
+
+ free(rrsets);
+}
+
+void node_dump_text(knot_node_t *node, void *data)
+{
+ struct dump_param *param;
+ param = (struct dump_param *)data;
+ FILE *f = param->f;
+ const knot_dname_t *origin = param->origin;
+
+ /* pointers should do in this case */
+ if (node->owner == origin) {
+ apex_node_dump_text(node, f);
+ return;
+ }
+
+ const knot_rrset_t **rrsets =
+ knot_node_rrsets(node);
+
+ for (int i = 0; i < node->rrset_count; i++) {
+ rrset_dump_text(rrsets[i], f);
+ }
+
+ free(rrsets);
+}
+
+int zone_dump_text(knot_zone_contents_t *zone, const char *filename)
+{
+ FILE *f = fopen(filename, "w");
+ if (f == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ fprintf(f, ";Dumped using %s v. %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+
+ struct dump_param param;
+ param.f = f;
+ assert(zone->apex != NULL && zone->apex->owner != NULL);
+ param.origin = knot_node_owner(knot_zone_contents_apex(zone));
+ knot_zone_contents_tree_apply_inorder(zone, node_dump_text, &param);
+ knot_zone_contents_nsec3_apply_inorder(zone, node_dump_text, &param);
+ fclose(f);
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/zone/zone-dump-text.h b/src/knot/zone/zone-dump-text.h
new file mode 100644
index 0000000..70dcff4
--- /dev/null
+++ b/src/knot/zone/zone-dump-text.h
@@ -0,0 +1,46 @@
+/* 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-dump-text.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Functions for dumping zone to text file.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_ZONE_DUMP_TEXT_H_
+#define _KNOT_ZONE_DUMP_TEXT_H_
+
+#include "libknot/util/descriptor.h"
+#include "libknot/zone/zone.h"
+
+/*!
+ * \brief Dumps given zone to text (BIND-like) file.
+ *
+ * \param zone Zone to be saved.
+ * \param filename Name of file to be created.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EBADARG if the specified file is not valid for writing.
+ */
+int zone_dump_text(knot_zone_contents_t *zone, const char *filename);
+
+#endif // _KNOT_ZONE_DUMP_TEXT_H_
+
+/*! @} */
diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c
new file mode 100644
index 0000000..afc577d
--- /dev/null
+++ b/src/knot/zone/zone-dump.c
@@ -0,0 +1,2301 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "libknot/common.h"
+#include "knot/zone/zone-dump.h"
+#include "libknot/libknot.h"
+#include "knot/other/debug.h"
+#include "common/skip-list.h"
+#include "common/base32hex.h"
+#include "common/crc.h"
+#include "libknot/util/error.h"
+
+#define ZONECHECKS_VERBOSE
+
+/*! \note Contents of a dump file:
+ * MAGIC(knotxx) db_filename dname_table
+ * NUMBER_OF_NORMAL_NODES NUMBER_OF_NSEC3_NODES
+ * [normal_nodes] [nsec3_nodes]
+ * --------------------------------------------
+ * dname_table is dumped as follows:
+ * NUMBER_OF_DNAMES [dname_wire_length dname_wire label_count dname_labels ID]
+ * node has following format:
+ * owner_id
+ * node_flags node_rrset_count [node_rrsets]
+ * rrset has following format:
+ * rrset_type rrset_class rrset_ttl rrset_rdata_count rrset_rrsig_count
+ * [rrset_rdata] [rrset_rrsigs]
+ * rdata can contain either dname ID,
+ * or raw data stored like this: data_len [data]
+ */
+
+static const uint MAX_CNAME_CYCLE_DEPTH = 15;
+
+/*!
+ *\brief Internal error constants. General errors are added for convenience,
+ * so that code does not have to change if new errors are added.
+ */
+enum zonechecks_errors {
+ ZC_ERR_ALLOC = -40,
+ ZC_ERR_UNKNOWN,
+
+ ZC_ERR_MISSING_SOA,
+
+ ZC_ERR_GENERIC_GENERAL_ERROR, /* isn't there a better name? */
+
+ ZC_ERR_RRSIG_RDATA_TYPE_COVERED,
+ ZC_ERR_RRSIG_RDATA_TTL,
+ ZC_ERR_RRSIG_RDATA_LABELS,
+ ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
+ 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,
+
+ ZC_ERR_NO_NSEC,
+ ZC_ERR_NSEC_RDATA_BITMAP,
+ ZC_ERR_NSEC_RDATA_MULTIPLE,
+ ZC_ERR_NSEC_RDATA_CHAIN,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC,
+
+ ZC_ERR_NSEC_GENERAL_ERROR,
+
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION,
+ ZC_ERR_NSEC3_NOT_FOUND,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT,
+ ZC_ERR_NSEC3_RDATA_TTL,
+ ZC_ERR_NSEC3_RDATA_CHAIN,
+ ZC_ERR_NSEC3_RDATA_BITMAP,
+
+ ZC_ERR_NSEC3_GENERAL_ERROR,
+
+ ZC_ERR_CNAME_CYCLE,
+ ZC_ERR_DNAME_CYCLE,
+ ZC_ERR_CNAME_EXTRA_RECORDS,
+ ZC_ERR_DNAME_EXTRA_RECORDS,
+ ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC,
+ ZC_ERR_CNAME_MULTIPLE,
+ ZC_ERR_DNAME_MULTIPLE,
+
+ ZC_ERR_CNAME_GENERAL_ERROR,
+
+ ZC_ERR_GLUE_NODE,
+ ZC_ERR_GLUE_RECORD,
+
+ ZC_ERR_GLUE_GENERAL_ERROR,
+};
+
+static char *error_messages[(-ZC_ERR_ALLOC) + 1] = {
+ [-ZC_ERR_ALLOC] = "Memory allocation error!\n",
+
+ [-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!\n",
+
+ [-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] =
+ "RRSIG: Type covered rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_TTL] =
+ "RRSIG: TTL rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_LABELS] =
+ "RRSIG: Labels rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER] =
+ "RRSIG: Signer name is different than in DNSKEY!\n",
+ [-ZC_ERR_RRSIG_RDATA_SIGNED_WRONG] =
+ "RRSIG: Key error!\n",
+ [-ZC_ERR_RRSIG_NO_RRSIG] =
+ "RRSIG: No RRSIG!\n",
+ [-ZC_ERR_RRSIG_SIGNED] =
+ "RRSIG: Signed RRSIG!\n",
+ [-ZC_ERR_RRSIG_OWNER] =
+ "RRSIG: Owner name rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_CLASS] =
+ "RRSIG: Class is wrong!\n",
+ [-ZC_ERR_RRSIG_TTL] =
+ "RRSIG: TTL is wrong!\n",
+ [-ZC_ERR_RRSIG_NOT_ALL] =
+ "RRSIG: Not all RRs are signed!\n",
+
+ [-ZC_ERR_NO_NSEC] =
+ "NSEC: Missing NSEC record\n",
+ [-ZC_ERR_NSEC_RDATA_BITMAP] =
+ "NSEC: Wrong NSEC bitmap!\n",
+ [-ZC_ERR_NSEC_RDATA_MULTIPLE] =
+ "NSEC: Multiple NSEC records!\n",
+ [-ZC_ERR_NSEC_RDATA_CHAIN] =
+ "NSEC: NSEC chain is not coherent!\n",
+ [-ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC] =
+ "NSEC: NSEC chain is not cyclic!\n",
+
+ [-ZC_ERR_NSEC3_UNSECURED_DELEGATION] =
+ "NSEC3: Zone contains unsecured delegation!\n",
+ [-ZC_ERR_NSEC3_NOT_FOUND] =
+ "NSEC3: Could not find previous NSEC3 record in the zone!\n",
+ [-ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT] =
+ "NSEC3: Unsecured delegation is not part "
+ "of the Opt-Out span!\n",
+ [-ZC_ERR_NSEC3_RDATA_TTL] =
+ "NSEC3: Original TTL rdata field is wrong!\n",
+ [-ZC_ERR_NSEC3_RDATA_CHAIN] =
+ "NSEC3: NSEC3 chain is not coherent!\n",
+ [-ZC_ERR_NSEC3_RDATA_BITMAP] =
+ "NSEC3: NSEC3 bitmap error!\n",
+
+ [-ZC_ERR_CNAME_CYCLE] =
+ "CNAME: CNAME cycle!\n",
+ [-ZC_ERR_DNAME_CYCLE] =
+ "CNAME: DNAME cycle!\n",
+ [-ZC_ERR_CNAME_EXTRA_RECORDS] =
+ "CNAME: Node with CNAME record has other records!\n",
+ [-ZC_ERR_DNAME_EXTRA_RECORDS] =
+ "DNAME: Node with DNAME record has other records!\n",
+ [-ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC] =
+ "CNAME: Node with CNAME record has other "
+ "records than RRSIG and NSEC/NSEC3!\n",
+ [-ZC_ERR_CNAME_MULTIPLE] = "CNAME: Multiple CNAME records!\n",
+ [-ZC_ERR_DNAME_MULTIPLE] = "DNAME: Multiple DNAME records!\n",
+
+ /* ^
+ | Important errors (to be logged on first occurence and counted) */
+
+
+ /* Below are errors of lesser importance, to be counted unless
+ specified otherwise */
+
+ [-ZC_ERR_GLUE_NODE] =
+ "GLUE: Node with Glue record missing!\n",
+ [-ZC_ERR_GLUE_RECORD] =
+ "GLUE: Record with Glue address missing\n",
+};
+
+/*!
+ * \brief Structure representing handle options.
+ */
+struct handler_options {
+ char log_cname; /*!< Log all CNAME related semantic errors. */
+ char log_glue; /*!< Log all glue related semantic errors. */
+ char log_rrsigs; /*!< Log all RRSIG related semantic errors. */
+ char log_nsec; /*!< Log all NSEC related semantic errors. */
+ char log_nsec3; /*!< Log all NSEC3 related semantic errors. */
+};
+
+/*!
+ * \brief Structure for handling semantic errors.
+ */
+struct err_handler {
+ /* Consider moving error messages here */
+ struct handler_options options; /*!< Handler options. */
+ uint errors[(-ZC_ERR_ALLOC) + 1]; /*!< Array with error messages */
+};
+
+typedef struct err_handler err_handler_t;
+
+/*!
+ * \brief Creates new semantic error handler.
+ *
+ * \param log_cname If true, log all CNAME related events.
+ * \param log_glue If true, log all Glue related events.
+ * \param log_rrsigs If true, log all RRSIGs related events.
+ * \param log_nsec If true, log all NSEC related events.
+ * \param log_nsec3 If true, log all NSEC3 related events.
+ *
+ * \return err_handler_t * Created error handler.
+ */
+static err_handler_t *handler_new(char log_cname, char log_glue,
+ char log_rrsigs, char log_nsec,
+ char log_nsec3)
+{
+ err_handler_t *handler = malloc(sizeof(err_handler_t));
+ CHECK_ALLOC_LOG(handler, NULL);
+
+ /* It should be initialized, but to be safe */
+ memset(handler->errors, 0, sizeof(uint) * (-ZC_ERR_ALLOC + 1));
+
+ handler->options.log_cname = log_cname;
+ handler->options.log_glue = log_glue;
+ handler->options.log_rrsigs = log_rrsigs;
+ handler->options.log_nsec = log_nsec;
+ handler->options.log_nsec3 = log_nsec3;
+
+ return handler;
+}
+
+/*!
+ * \brief Prints error message with node information.
+ *
+ * \note If \a node is NULL, only total number of errors is printed.
+ *
+ * \param handler Error handler.
+ * \param node Node with semantic error in it.
+ * \param error Type of error.
+ */
+static void log_error_from_node(err_handler_t *handler,
+ const knot_node_t *node,
+ uint error)
+{
+ if (node != NULL) {
+ char *name =
+ knot_dname_to_str(knot_node_owner(node));
+ fprintf(stderr, "Semantic warning in node: %s: ", name);
+ fprintf(stderr, "%s", error_messages[-error]);
+ free(name);
+ } else {
+ fprintf(stderr, "Total number of warnings is: %d for error: %s",
+ handler->errors[-error],
+ error_messages[-error]);
+ }
+}
+
+/*!
+ * \brief Called when error has been encountered in node. Will either log error
+ * or print it, depending on handler's options.
+ *
+ * \param handler Error handler.
+ * \param node Node with semantic error in it.
+ * \param error Type of error.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval ZC_ERR_UNKNOWN if unknown error.
+ * \retval ZC_ERR_ALLOC if memory error.
+ */
+static int err_handler_handle_error(err_handler_t *handler,
+ const knot_node_t *node,
+ uint error)
+{
+ assert(handler && node);
+ if ((error != 0) &&
+ (error > ZC_ERR_GLUE_GENERAL_ERROR)) {
+ return ZC_ERR_UNKNOWN;
+ }
+
+ if (error == ZC_ERR_ALLOC) {
+ ERR_ALLOC_FAILED;
+ return ZC_ERR_ALLOC;
+ }
+
+ /* missing SOA can only occur once, so there
+ * needn't to be an option for it */
+
+ if ((error != 0) &&
+ (error < ZC_ERR_GENERIC_GENERAL_ERROR)) {
+ /* The two errors before SOA were handled */
+ log_error_from_node(handler, node, error);
+
+ } else if ((error < ZC_ERR_RRSIG_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_rrsigs))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_RRSIG_GENERAL_ERROR) &&
+ (error < ZC_ERR_NSEC_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_nsec))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_NSEC_GENERAL_ERROR) &&
+ (error < ZC_ERR_NSEC3_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_nsec3))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_NSEC3_GENERAL_ERROR) &&
+ (error < ZC_ERR_CNAME_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_cname))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_CNAME_GENERAL_ERROR) &&
+ (error < ZC_ERR_GLUE_GENERAL_ERROR) &&
+ handler->options.log_glue) {
+
+ log_error_from_node(handler, node, error);
+
+ }
+
+ handler->errors[-error]++;
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief This function prints all errors that occured in zone.
+ *
+ * \param handler Error handler containing found errors.
+ */
+static void err_handler_log_all(err_handler_t *handler)
+{
+ if (handler == NULL) {
+ return;
+ }
+
+ for (int i = ZC_ERR_ALLOC; i < ZC_ERR_GLUE_GENERAL_ERROR; i++) {
+ if (handler->errors[-i] > 0) {
+ log_error_from_node(handler, NULL, i);
+ }
+ }
+}
+
+/*!
+ * \brief Arguments to be used with tree traversal functions. Uses void pointers
+ * to be more versatile.
+ *
+ */
+struct arg {
+ void *arg1; /* FILE *f / zone */
+ void *arg2; /* skip_list_t */
+ void *arg3; /* zone */
+ void *arg4; /* first node */
+ void *arg5; /* last node */
+ void *arg6; /* error handler */
+ void *arg7; /* CRC */
+};
+
+typedef struct arg arg_t;
+
+/*!
+ * \brief Semantic check - CNAME cycles. Uses constant value with maximum
+ * allowed CNAME chain depth.
+ *
+ * \param zone Zone containing the RRSet.
+ * \param rrset RRSet to be tested.
+ *
+ * \retval KNOT_EOK when there is no cycle.
+ * \retval ZC_ERR_CNAME_CYCLE when cycle is present.
+ */
+static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset)
+{
+ const knot_rrset_t *next_rrset = rrset;
+ assert(rrset);
+ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(next_rrset);
+ const knot_node_t *next_node = NULL;
+
+ uint i = 0;
+
+ assert(tmp_rdata);
+
+ const knot_dname_t *next_dname =
+ knot_rdata_cname_name(tmp_rdata);
+
+ assert(next_dname);
+
+ while (i < MAX_CNAME_CYCLE_DEPTH && next_dname != NULL) {
+ next_node = knot_zone_contents_get_node(zone, next_dname);
+ if (next_node == NULL) {
+ next_node =
+ knot_zone_contents_get_nsec3_node(zone, next_dname);
+ }
+
+ if (next_node != NULL) {
+ next_rrset = knot_node_rrset(next_node,
+ KNOT_RRTYPE_CNAME);
+ if (next_rrset != NULL) {
+ next_dname =
+ knot_rdata_cname_name(next_rrset->rdata);
+ } else {
+ next_node = NULL;
+ next_dname = NULL;
+ }
+ } else {
+ next_dname = NULL;
+ }
+ i++;
+ }
+
+ /* even if the length is 0, i will be 1 */
+ if (i >= MAX_CNAME_CYCLE_DEPTH) {
+ return ZC_ERR_CNAME_CYCLE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Return raw data from rdata item structure (without length).
+ *
+ * \param item Item to get rdata from.
+ * \return uint16_t * raw data without length.
+ */
+static inline uint16_t *rdata_item_data(const knot_rdata_item_t *item)
+{
+ return (uint16_t *)(item->raw_data + 1);
+}
+
+/*!
+ * \brief Returns type covered field from RRSIG RRSet's rdata.
+ *
+ * \param rdata RRSIG rdata.
+ * \return uint16_t Type covered.
+ */
+uint16_t type_covered_from_rdata(const knot_rdata_t *rdata)
+{
+ return ntohs(*(uint16_t *) rdata_item_data(&(rdata->items[0])));
+}
+
+/*!
+ * \brief Check whether DNSKEY rdata are valid.
+ *
+ * \param rdata DNSKEY rdata to be checked.
+ *
+ * \retval KNOT_EOK when rdata are OK.
+ * \retval ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER when rdata are not OK.
+ */
+static int check_dnskey_rdata(const knot_rdata_t *rdata)
+{
+ /* check that Zone key bit it set - position 7 in net order */
+ /*! \todo FIXME: endian? I swear I've fixed this already, it was 7 i guesss*/
+ uint16_t mask = 1 << 8; //0b0000000100000000;
+
+ uint16_t flags =
+ knot_wire_read_u16((uint8_t *)rdata_item_data
+ (knot_rdata_item(rdata, 0)));
+
+ if (flags & mask) {
+ return KNOT_EOK;
+ } else {
+ /* This error does not exactly fit, but it's better
+ * than a new one */
+ return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
+ }
+}
+
+/*!
+ * \brief Calculates keytag for RSA/SHA algorithm.
+ *
+ * \param key Key wireformat.
+ * \param keysize Wireformat size.
+ *
+ * \return uint16_t Calculated keytag.
+ */
+static uint16_t keytag_1(uint8_t *key, uint16_t keysize)
+{
+ uint16_t ac = 0;
+ if (keysize > 4) {
+ memmove(&ac, key + keysize - 3, 2);
+ }
+
+ ac = ntohs(ac);
+ return ac;
+}
+
+/*!
+ * \brief Calculates keytag from key wire.
+ *
+ * \param key Key wireformat.
+ * \param keysize Wireformat size.
+ *
+ * \return uint16_t Calculated keytag.
+ */
+static uint16_t keytag(uint8_t *key, uint16_t keysize )
+{
+ uint32_t ac = 0; /* assumed to be 32 bits or larger */
+
+ /* algorithm RSA/SHA */
+ if (key[3] == 1) {
+ return keytag_1(key, keysize);
+ } else {
+ for(int i = 0; i < keysize; i++) {
+ ac += (i & 1) ? key[i] : key[i] << 8;
+ }
+
+ ac += (ac >> 16) & 0xFFFF;
+ return (uint16_t)ac & 0xFFFF;
+ }
+}
+
+/*!
+ * \brief Returns size of raw data item.
+ *
+ * \param item Raw data item.
+ *
+ * \return uint16_t Size of raw data item.
+ */
+static inline uint16_t rdata_item_size(const knot_rdata_item_t *item)
+{
+ return item->raw_data[0];
+}
+
+/*!
+ * \brief Converts DNSKEY rdata to wireformat.
+ *
+ * \param rdata DNSKEY rdata to be converted.
+ * \param wire Created wire.
+ * \param size Size of created wire.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM on memory error.
+ */
+static int dnskey_to_wire(const knot_rdata_t *rdata, uint8_t **wire,
+ uint *size)
+{
+ assert(*wire == NULL);
+ /* flags + algorithm + protocol + keysize */
+ *size = 2 + 1 + 1 + knot_rdata_item(rdata, 3)->raw_data[0];
+ *wire = malloc(sizeof(uint8_t) * *size);
+ CHECK_ALLOC_LOG(*wire, KNOT_ENOMEM);
+
+ /* copy the wire octet by octet */
+
+ /* TODO check if we really have that many items */
+ if (rdata->count < 4) {
+ return KNOT_ERROR;
+ }
+
+ (*wire)[0] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[2];
+ (*wire)[1] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[3];
+
+ (*wire)[2] = ((uint8_t *)(knot_rdata_item(rdata, 1)->raw_data))[2];
+ (*wire)[3] = ((uint8_t *)(knot_rdata_item(rdata, 2)->raw_data))[2];
+
+ memcpy(*wire + 4, knot_rdata_item(rdata, 3)->raw_data + 1,
+ knot_rdata_item(rdata, 3)->raw_data[0]);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Semantic check - RRSIG rdata.
+ *
+ * \param rdata_rrsig RRSIG rdata to be checked.
+ * \param rrset RRSet containing the rdata.
+ * \param dnskey_rrset RRSet containing zone's DNSKEY
+ *
+ * \retval KNOT_EOK if rdata are OK.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_rrsig_rdata(const knot_rdata_t *rdata_rrsig,
+ const knot_rrset_t *rrset,
+ const knot_rrset_t *dnskey_rrset)
+{
+ if (rdata_rrsig == NULL) {
+ return ZC_ERR_RRSIG_NO_RRSIG;
+ }
+
+ if (type_covered_from_rdata(rdata_rrsig) !=
+ knot_rrset_type(rrset)) {
+ /* zoneparser would not let this happen
+ * but to be on the safe side
+ */
+ return ZC_ERR_RRSIG_RDATA_TYPE_COVERED;
+ }
+
+ /* label number at the 2nd index should be same as owner's */
+ uint16_t *raw_data =
+ rdata_item_data(knot_rdata_item(rdata_rrsig, 2));
+
+ uint8_t labels_rdata = ((uint8_t *)raw_data)[0];
+
+ int tmp = knot_dname_label_count(knot_rrset_owner(rrset)) -
+ labels_rdata;
+
+ if (tmp != 0) {
+ /* if name has wildcard, label must not be included */
+ if (!knot_dname_is_wildcard(knot_rrset_owner(rrset))) {
+ return ZC_ERR_RRSIG_RDATA_LABELS;
+ } else {
+ if (abs(tmp) != 1) {
+ return ZC_ERR_RRSIG_RDATA_LABELS;
+ }
+ }
+ }
+
+ /* check original TTL */
+ uint32_t original_ttl =
+ knot_wire_read_u32((uint8_t *)rdata_item_data(
+ knot_rdata_item(rdata_rrsig, 3)));
+
+ if (original_ttl != knot_rrset_ttl(rrset)) {
+ return ZC_ERR_RRSIG_RDATA_TTL;
+ }
+
+ /* signer's name is same as in the zone apex */
+ knot_dname_t *signer_name =
+ knot_rdata_item(rdata_rrsig, 7)->dname;
+
+ /* dnskey is in the apex node */
+ if (knot_dname_compare(signer_name,
+ knot_rrset_owner(dnskey_rrset)) != 0) {
+ return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
+ }
+
+ /* Compare algorithm, key tag and signer's name with DNSKEY rrset
+ * one of the records has to match. Signer name has been checked
+ * before */
+ char match = 0;
+ const knot_rdata_t *tmp_dnskey_rdata =
+ knot_rrset_rdata(dnskey_rrset);
+ do {
+ uint8_t alg =
+ ((uint8_t *)(knot_rdata_item(rdata_rrsig, 1)->raw_data))[2];
+ uint8_t alg_dnskey =
+ ((uint8_t *)(knot_rdata_item(tmp_dnskey_rdata,
+ 2)->raw_data))[2];
+
+ raw_data = rdata_item_data(knot_rdata_item(rdata_rrsig, 6));
+ uint16_t key_tag_rrsig =
+ knot_wire_read_u16((uint8_t *)raw_data);
+
+/* raw_data =
+ rdata_item_data(knot_rdata_item(
+ tmp_dnskey_rdata, 3));
+
+ uint16_t raw_length = rdata_item_size(knot_rdata_item(
+ tmp_dnskey_rdata, 3)); */
+
+ uint8_t *dnskey_wire = NULL;
+ uint dnskey_wire_size = 0;
+
+ int ret = 0;
+ if ((ret = dnskey_to_wire(tmp_dnskey_rdata, &dnskey_wire,
+ &dnskey_wire_size)) != KNOT_EOK) {
+ return ret;
+ }
+
+ uint16_t key_tag_dnskey =
+ keytag(dnskey_wire, dnskey_wire_size);
+
+ free(dnskey_wire);
+
+ match = (alg == alg_dnskey) &&
+ (key_tag_rrsig == key_tag_dnskey) &&
+ !check_dnskey_rdata(tmp_dnskey_rdata);
+
+ } while (!match &&
+ ((tmp_dnskey_rdata =
+ knot_rrset_rdata_next(dnskey_rrset,
+ tmp_dnskey_rdata))
+ != NULL));
+
+ if (!match) {
+ return ZC_ERR_RRSIG_RDATA_SIGNED_WRONG;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Semantic check - RRSet's RRSIG.
+ *
+ * \param rrset RRSet containing RRSIG.
+ * \param dnskey_rrset
+ * \param nsec3 NSEC3 active.
+ *
+ * \retval KNOT_EOK on success.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_rrsig_in_rrset(const knot_rrset_t *rrset,
+ const knot_rrset_t *dnskey_rrset,
+ char nsec3)
+{
+ assert(dnskey_rrset && rrset);
+
+ const knot_rrset_t *rrsigs = knot_rrset_rrsigs(rrset);
+
+ if (rrsigs == NULL) {
+ return ZC_ERR_RRSIG_NO_RRSIG;
+ }
+
+ /* signed rrsig - nonsense */
+ if (knot_rrset_rrsigs(rrsigs) != NULL) {
+ return ZC_ERR_RRSIG_SIGNED;
+ }
+
+ /* Different owner, class, ttl */
+
+ if (knot_dname_compare(knot_rrset_owner(rrset),
+ knot_rrset_owner(rrsigs)) != 0) {
+ return ZC_ERR_RRSIG_OWNER;
+ }
+
+ if (knot_rrset_class(rrset) != knot_rrset_class(rrsigs)) {
+ return ZC_ERR_RRSIG_CLASS;
+ }
+
+ if (knot_rrset_ttl(rrset) != knot_rrset_ttl(rrset)) {
+ return ZC_ERR_RRSIG_TTL;
+ }
+
+ /* Check whether all rrsets have their rrsigs */
+ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset);
+ const knot_rdata_t *tmp_rrsig_rdata = knot_rrset_rdata(rrsigs);
+
+ assert(tmp_rdata);
+ assert(tmp_rrsig_rdata);
+ int ret = 0;
+ char all_signed = tmp_rdata && tmp_rrsig_rdata;
+ do {
+ if ((ret = check_rrsig_rdata(tmp_rrsig_rdata,
+ rrset,
+ dnskey_rrset)) != 0) {
+ return ret;
+ }
+
+ all_signed = tmp_rdata && tmp_rrsig_rdata;
+ } while (((tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata))
+ != NULL) &&
+ ((tmp_rrsig_rdata =
+ knot_rrset_rdata_next(rrsigs, tmp_rrsig_rdata))
+ != NULL));
+
+ if (!all_signed) {
+ return ZC_ERR_RRSIG_NOT_ALL;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Returns bit on index from array in network order. Taken from NSD.
+ *
+ * \param bits Array in network order.
+ * \param index Index to return from array.
+ *
+ * \return int Bit on given index.
+ */
+static int get_bit(uint8_t *bits, size_t index)
+{
+ /*
+ * The bits are counted from left to right, so bit #0 is the
+ * leftmost bit.
+ */
+ return bits[index / 8] & (1 << (7 - index % 8));
+}
+
+/*!
+ * \brief Converts NSEC bitmap to array of integers. (Inspired by NSD code)
+ *
+ * \param item Item containing the bitmap.
+ * \param array Array to be created.
+ * \param count Count of items in array.
+ *
+ * \retval KNOT_OK on success.
+ * \retval KNOT_NOMEM on memory error.
+ */
+static int rdata_nsec_to_type_array(const knot_rdata_item_t *item,
+ uint16_t **array,
+ uint *count)
+{
+ assert(*array == NULL);
+
+ uint8_t *data = (uint8_t *)rdata_item_data(item);
+
+ int increment = 0;
+ *count = 0;
+
+ for (int i = 0; i < rdata_item_size(item); i += increment) {
+ increment = 0;
+ uint8_t window = data[i];
+ increment++;
+
+ uint8_t bitmap_size = data[i + increment];
+ increment++;
+
+ uint8_t *bitmap =
+ malloc(sizeof(uint8_t) * (bitmap_size));
+ if (bitmap == NULL) {
+ ERR_ALLOC_FAILED;
+ free(*array);
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(bitmap, data + i + increment,
+ bitmap_size);
+
+ increment += bitmap_size;
+
+ for (int j = 0; j < bitmap_size * 8; j++) {
+ if (get_bit(bitmap, j)) {
+ (*count)++;
+ void *tmp = realloc(*array,
+ sizeof(uint16_t) *
+ *count);
+ if (tmp == NULL) {
+ ERR_ALLOC_FAILED;
+ free(bitmap);
+ free(*array);
+ return KNOT_ENOMEM;
+ }
+ *array = tmp;
+ (*array)[*count - 1] = j + window * 256;
+ }
+ }
+ free(bitmap);
+ }
+
+ return KNOT_EOK;
+}
+
+/* should write error, not return values !!! */
+
+/*!
+ * \brief Semantic check - check node's NSEC node.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param handler Error handler
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_nsec3_node_in_zone(knot_zone_contents_t *zone, knot_node_t *node,
+ err_handler_t *handler)
+{
+ assert(handler);
+ const knot_node_t *nsec3_node = knot_node_nsec3_node(node, 0);
+
+ if (nsec3_node == NULL) {
+ /* I know it's probably not what RFCs say, but it will have to
+ * do for now. */
+ if (knot_node_rrset(node, KNOT_RRTYPE_DS) != NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION);
+ } else {
+ /* Unsecured delegation, check whether it is part of
+ * opt-out span */
+ const knot_node_t *nsec3_previous;
+ const knot_node_t *nsec3_node;
+
+ if (knot_zone_contents_find_nsec3_for_name(zone,
+ knot_node_owner(node),
+ &nsec3_node,
+ &nsec3_previous, 0) != 0) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_NOT_FOUND);
+ }
+
+ if (nsec3_node == NULL) {
+ /* Probably should not ever happen */
+ return ZC_ERR_NSEC3_NOT_FOUND;
+ }
+
+ assert(nsec3_previous);
+
+ const knot_rrset_t *previous_rrset =
+ knot_node_rrset(nsec3_previous,
+ KNOT_RRTYPE_NSEC3);
+
+ assert(previous_rrset);
+
+ /* check for Opt-Out flag */
+ uint8_t flags =
+ ((uint8_t *)(previous_rrset->rdata->items[1].raw_data))[2];
+
+ uint8_t opt_out_mask = 1;
+
+ if (!(flags & opt_out_mask)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT);
+ }
+ }
+ }
+
+ const knot_rrset_t *nsec3_rrset =
+ knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
+
+ assert(nsec3_rrset);
+
+ const knot_rrset_t *soa_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ assert(soa_rrset);
+
+ uint32_t minimum_ttl =
+ knot_wire_read_u32((uint8_t *)
+ rdata_item_data(
+ knot_rdata_item(
+ knot_rrset_rdata(
+ knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA)), 6)));
+ /* Are those getters even worth this?
+ * Now I have no idea what this code does. */
+
+ if (knot_rrset_ttl(nsec3_rrset) != minimum_ttl) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_TTL);
+ }
+
+ /* check that next dname is in the zone */
+ uint8_t *next_dname_decoded = NULL;
+ size_t real_size = 0;
+
+ if (((real_size = base32hex_encode_alloc(((char *)
+ rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
+ rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
+ (char **)&next_dname_decoded)) <= 0) ||
+ (next_dname_decoded == NULL)) {
+ fprintf(stderr, "Could not encode base32 string!\n");
+ return KNOT_ERROR;
+ }
+
+ /* This is why we allocate maximum length of decoded string + 1 */
+ memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
+ next_dname_decoded[0] = real_size;
+
+ /* Local allocation, will be discarded. */
+ knot_dname_t *next_dname =
+ knot_dname_new_from_wire(next_dname_decoded,
+ real_size + 1, NULL);
+ CHECK_ALLOC_LOG(next_dname, KNOT_ENOMEM);
+
+ free(next_dname_decoded);
+
+ if (knot_dname_cat(next_dname,
+ knot_node_owner(knot_zone_contents_apex(zone))) == NULL) {
+ fprintf(stderr, "Could not concatenate dnames!\n");
+ return KNOT_ERROR;
+
+ }
+
+ if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Directly discard. */
+ knot_dname_free(&next_dname);
+
+ /* This is probably not sufficient, but again, it is covered in
+ * zone load time */
+
+ uint count;
+ uint16_t *array = NULL;
+ if (rdata_nsec_to_type_array(
+ knot_rdata_item(
+ knot_rrset_rdata(nsec3_rrset), 5),
+ &array, &count) != 0) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_ALLOC);
+ return KNOT_ERROR;
+ }
+
+ uint16_t type = 0;
+ for (int j = 0; j < count; j++) {
+ /* test for each type's presence */
+ type = array[j];
+ if (type == KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+ if (knot_node_rrset(node,
+ type) == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_BITMAP);
+ break;
+/* char *name =
+ knot_dname_to_str(
+ log_zone_error("Node %s does "
+ "not contain RRSet of type %s "
+ "but NSEC bitmap says "
+ "it does!\n", name,
+ knot_rrtype_to_string(type));
+ free(name); */
+ }
+ }
+
+ free(array);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Run semantic checks for node without DNSSEC-related types.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param do_checks Level of checks to be done.
+ * \param handler Error handler.
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int semantic_checks_plain(knot_zone_contents_t *zone,
+ knot_node_t *node,
+ char do_checks,
+ err_handler_t *handler)
+{
+ assert(handler);
+ const knot_rrset_t *cname_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_CNAME);
+ if (cname_rrset != NULL) {
+ if (check_cname_cycles_in_zone(zone, cname_rrset) !=
+ KNOT_EOK) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_CYCLE);
+ }
+
+ /* No DNSSEC and yet there is more than one rrset in node */
+ if (do_checks == 1 &&
+ knot_node_rrset_count(node) != 1) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_EXTRA_RECORDS);
+ } else if (knot_node_rrset_count(node) != 1) {
+ /* With DNSSEC node can contain RRSIG or NSEC */
+ if (!(knot_node_rrset(node, KNOT_RRTYPE_RRSIG) ||
+ knot_node_rrset(node, KNOT_RRTYPE_NSEC)) ||
+ knot_node_rrset_count(node) > 3) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC);
+ }
+ }
+
+ if (knot_rrset_rdata(cname_rrset)->next !=
+ knot_rrset_rdata(cname_rrset)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_MULTIPLE);
+ }
+ }
+
+ const knot_rrset_t *dname_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_DNAME);
+ if (dname_rrset != NULL) {
+ if (check_cname_cycles_in_zone(zone, dname_rrset) !=
+ KNOT_EOK) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_CYCLE);
+ }
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_EXTRA_RECORDS);
+ }
+
+ if (knot_rrset_rdata(dname_rrset)->next !=
+ knot_rrset_rdata(dname_rrset)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_MULTIPLE);
+ }
+ }
+
+ /* check for glue records at zone cuts */
+ if (knot_node_is_deleg_point(node)) {
+ const knot_rrset_t *ns_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_NS);
+ assert(ns_rrset);
+ //FIXME this should be an error as well ! (i guess)
+
+ const knot_dname_t *ns_dname =
+ knot_rdata_get_item(knot_rrset_rdata
+ (ns_rrset), 0)->dname;
+
+ assert(ns_dname);
+
+ const knot_node_t *glue_node =
+ knot_zone_contents_find_node(zone, ns_dname);
+
+ if (knot_dname_is_subdomain(ns_dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ if (glue_node == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_GLUE_NODE);
+ } else {
+ if ((knot_node_rrset(glue_node,
+ KNOT_RRTYPE_A) == NULL) &&
+ (knot_node_rrset(glue_node,
+ KNOT_RRTYPE_AAAA) == NULL)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_GLUE_RECORD);
+ }
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Run semantic checks for node without DNSSEC-related types.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param first_node First node in canonical order.
+ * \param last_node Last node in canonical order.
+ * \param handler Error handler.
+ * \param nsec3 NSEC3 used.
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int semantic_checks_dnssec(knot_zone_contents_t *zone,
+ knot_node_t *node,
+ knot_node_t *first_node,
+ knot_node_t **last_node,
+ err_handler_t *handler,
+ char nsec3)
+{
+ assert(handler);
+ assert(node);
+ char auth = !knot_node_is_non_auth(node);
+ char deleg = knot_node_is_deleg_point(node);
+ uint rrset_count = knot_node_rrset_count(node);
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ const knot_rrset_t *dnskey_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_DNSKEY);
+
+ int ret = 0;
+
+ for (int i = 0; i < rrset_count; i++) {
+ const knot_rrset_t *rrset = rrsets[i];
+ if (auth && !deleg &&
+ (ret = check_rrsig_in_rrset(rrset, dnskey_rrset,
+ nsec3)) != 0) {
+ /* CLEANUP */
+/* log_zone_error("RRSIG %d node %s\n", ret,
+ knot_dname_to_str(node->owner));*/
+
+ err_handler_handle_error(handler, node, ret);
+ }
+
+ if (!nsec3 && auth) {
+ /* check for NSEC record */
+ const knot_rrset_t *nsec_rrset =
+ knot_node_rrset(node,
+ KNOT_RRTYPE_NSEC);
+
+ if (nsec_rrset == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NO_NSEC);
+ /* CLEANUP */
+/* char *name =
+ knot_dname_to_str(node->owner);
+ log_zone_error("Missing NSEC in node: "
+ "%s\n", name);
+ free(name);
+ return; */
+ } else {
+
+ /* check NSEC/NSEC3 bitmap */
+
+ uint count;
+
+ uint16_t *array = NULL;
+
+ if (rdata_nsec_to_type_array(
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset),
+ 1),
+ &array, &count) != 0) {
+ err_handler_handle_error(handler,
+ NULL,
+ ZC_ERR_ALLOC);
+ return ZC_ERR_ALLOC; /* ... */
+ /*return; */
+ }
+
+ uint16_t type = 0;
+ for (int j = 0; j < count; j++) {
+ /* test for each type's presence */
+ type = array[j];
+ if (type == KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+ if (knot_node_rrset(node,
+ type) == NULL) {
+ err_handler_handle_error(
+ handler,
+ node,
+ ZC_ERR_NSEC_RDATA_BITMAP);
+ /* CLEANUP */
+ /* char *name =
+ knot_dname_to_str(
+ knot_node_owner(node));
+
+ log_zone_error("Node %s does "
+ "not contain RRSet of type %s "
+ "but NSEC bitmap says "
+ "it does!\n", name,
+ knot_rrtype_to_string(type));
+
+ free(name); */
+ }
+ }
+ free(array);
+ }
+
+ /* Test that only one record is in the
+ * NSEC RRSet */
+
+ if ((nsec_rrset != NULL) &&
+ knot_rrset_rdata(nsec_rrset)->next !=
+ knot_rrset_rdata(nsec_rrset)) {
+ err_handler_handle_error(handler,
+ node,
+ ZC_ERR_NSEC_RDATA_MULTIPLE);
+ /* CLEANUP */
+/* char *name =
+ knot_dname_to_str(
+ knot_node_owner(node));
+ log_zone_error("Node %s contains more "
+ "than one NSEC "
+ "record!\n", name);
+ knot_rrset_dump(nsec_rrset, 0);
+ free(name); */
+ }
+
+ /*
+ * Test that NSEC chain is coherent.
+ * We have already checked that every
+ * authoritative node contains NSEC record
+ * so checking should only be matter of testing
+ * the next link in each node.
+ */
+
+ if (nsec_rrset != NULL) {
+ knot_dname_t *next_domain =
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset),
+ 0)->dname;
+
+ assert(next_domain);
+
+ if (knot_zone_contents_find_node(zone, next_domain) ==
+ NULL) {
+ err_handler_handle_error(handler,
+ node,
+ ZC_ERR_NSEC_RDATA_CHAIN);
+ /* CLEANUP */
+/* log_zone_error("NSEC chain is not "
+ "coherent!\n"); */
+ }
+
+ if (knot_dname_compare(next_domain,
+ knot_node_owner(knot_zone_contents_apex(zone)))
+ == 0) {
+ /* saving the last node */
+ *last_node = node;
+ }
+
+ }
+ } else if (nsec3 && (auth || deleg)) { /* nsec3 */
+ int ret = check_nsec3_node_in_zone(zone, node,
+ handler);
+ if (ret != KNOT_EOK) {
+ free(rrsets);
+ return ret;
+ }
+ }
+ }
+ free(rrsets);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Function called by zone traversal function. Used to call
+ * knot_zone_save_enclosers.
+ *
+ * \param node Node to be searched.
+ * \param data Arguments.
+ */
+static void do_checks_in_tree(knot_node_t *node, void *data)
+{
+ assert(data != NULL);
+ arg_t *args = (arg_t *)data;
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ short count = knot_node_rrset_count(node);
+
+ assert(count == 0 || rrsets != NULL);
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)args->arg1;
+
+ assert(zone);
+
+
+/* for (int i = 0; i < count; ++i) {
+ assert(rrsets[i] != NULL);
+ knot_zone_save_enclosers_rrset(rrsets[i],
+ zone,
+ (skip_list_t *)args->arg2);
+ } */
+
+ knot_node_t *first_node = (knot_node_t *)args->arg4;
+ knot_node_t **last_node = (knot_node_t **)args->arg5;
+
+ err_handler_t *handler = (err_handler_t *)args->arg6;
+
+ char do_checks = *((char *)(args->arg3));
+
+ if (do_checks) {
+ semantic_checks_plain(zone, node, do_checks, handler);
+ }
+
+ if (do_checks > 1) {
+ semantic_checks_dnssec(zone, node, first_node, last_node,
+ handler, do_checks == 3);
+ }
+
+ free(rrsets);
+}
+
+/*!
+ * \brief Helper function - wraps its arguments into arg_t structure and
+ * calls function that does the actual work.
+ *
+ * \param zone Zone to be searched / checked
+ * \param list Skip list of closests enclosers.
+ * \param do_checks Level of semantic checks.
+ * \param handler Semantic error handler.
+ * \param last_node Last checked node, which is part of NSEC(3) chain.
+ */
+void zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks,
+ err_handler_t *handler,
+ knot_node_t **last_node)
+{
+ if (!do_checks) {
+ return;
+ }
+
+ arg_t arguments;
+ arguments.arg1 = zone;
+ arguments.arg3 = &do_checks;
+ arguments.arg4 = NULL;
+ arguments.arg5 = last_node;
+ arguments.arg6 = handler;
+
+ knot_zone_contents_tree_apply_inorder(zone,
+ do_checks_in_tree,
+ (void *)&arguments);
+}
+
+static inline int fwrite_to_file_crc(const void *src,
+ size_t size, size_t n, FILE *f,
+ crc_t *crc)
+{
+ size_t rc = fwrite(src, size, n, f);
+ if (rc != n) {
+ fprintf(stderr, "fwrite: invalid write %zu (expected %zu)\n", rc,
+ n);
+ }
+ /* \todo this seems to be wrong, if you fwrite less than n items, you probably should not continue */
+
+ if (size * n > 0) {
+ *crc =
+ crc_update(*crc, (unsigned char *)src,
+ size * n);
+ }
+
+ /* \todo the rc return is certainly wrong as it is used in the caller function */
+// return rc == n;
+ return (int)rc;
+
+}
+
+static inline int fwrite_to_stream(const void *src,
+ size_t size, size_t n,
+ uint8_t **stream,
+ size_t *stream_size)
+{
+ /* Resize the stream */
+ void *tmp = realloc(*stream,
+ (*stream_size + (size * n)) * sizeof(uint8_t));
+ if (tmp != NULL) {
+ *stream = tmp;
+ memcpy(*stream + *stream_size, src,
+ size * n);
+ *stream_size += (size * n) * sizeof(uint8_t);
+ return KNOT_EOK;
+ } else {
+ free(*stream);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+static int fwrite_wrapper(const void *src,
+ size_t size, size_t n, FILE *fp,
+ uint8_t **stream, size_t *stream_size, crc_t *crc)
+{
+ if (fp == NULL) {
+ assert(stream && stream_size);
+ assert(crc == NULL);
+ return fwrite_to_stream(src, size, n, stream, stream_size);
+ } else {
+ assert(stream == NULL && stream_size == NULL);
+ return fwrite_to_file_crc(src, size, n, fp, crc);
+ }
+}
+
+/*!
+ * \brief Dumps dname labels in binary format to given file.
+ *
+ * \param dname Dname whose labels are to be dumped.
+ * \param f Output file.
+ */
+static void knot_labels_dump_binary(const knot_dname_t *dname, FILE *f,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ dbg_zdump("label count: %d\n", dname->label_count);
+ uint16_t label_count = dname->label_count;
+ /* \todo check the return value */
+ fwrite_wrapper(&label_count, sizeof(label_count), 1, f, stream,
+ stream_size, crc);
+ /* \todo check the return value */
+ fwrite_wrapper(dname->labels, sizeof(uint8_t), dname->label_count, f,
+ stream, stream_size, crc);
+}
+
+/*!
+ * \brief Dumps dname in binary format to given file.
+ *
+ * \param dname Dname to be dumped.
+ * \param f Output file.
+ */
+static void knot_dname_dump_binary(const knot_dname_t *dname, FILE *f,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ uint32_t dname_size = dname->size;
+ /* \todo check the return value */
+ fwrite_wrapper(&dname_size, sizeof(dname_size), 1, f, stream,
+ stream_size, crc);
+ /* \todo check the return value */
+ fwrite_wrapper(dname->name, sizeof(uint8_t), dname->size, f,
+ stream, stream_size, crc);
+ dbg_zdump("dname size: %d\n", dname->size);
+ knot_labels_dump_binary(dname, f, stream, stream_size, crc);
+}
+
+/*!< \todo some global variable indicating error! */
+static void dump_dname_with_id(const knot_dname_t *dname, FILE *f,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ uint32_t id = dname->id;
+ /* \todo check the return value */
+ fwrite_wrapper(&id, sizeof(id), 1, f, stream, stream_size, crc);
+ knot_dname_dump_binary(dname, f, stream, stream_size, crc);
+/* if (!fwrite_wrapper_safe(&dname->id, sizeof(dname->id), 1, f)) {
+ return KNOT_ERROR;
+ } */
+}
+
+/*!
+ * \brief Dumps given rdata in binary format to given file.
+ *
+ * \param rdata Rdata to be dumped.
+ * \param type Type of rdata.
+ * \param data Arguments to be propagated.
+ */
+static void knot_rdata_dump_binary(knot_rdata_t *rdata,
+ uint32_t type, void *data, int use_ids,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ FILE *f = (FILE *)((arg_t *)data)->arg1;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+
+ dbg_zdump("Dumping type: %d\n", type);
+
+ if (desc->fixed_items) {
+ assert(desc->length == rdata->count);
+ }
+
+ /* Write rdata count. */
+ /* \todo check the return value */
+ fwrite_wrapper(&(rdata->count),
+ sizeof(rdata->count), 1, f, stream, stream_size, crc);
+
+ for (int i = 0; i < rdata->count; i++) {
+ if (&(rdata->items[i]) == NULL) {
+ dbg_zdump("Item n. %d is not set!\n", i);
+ continue;
+ }
+ dbg_zdump("Item n: %d\n", i);
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ) {
+ /* some temp variables - this is way too long */
+ assert(rdata->items[i].dname != NULL);
+ knot_dname_t *wildcard = NULL;
+
+ if (rdata->items[i].dname->node != NULL &&
+ rdata->items[i].dname->node->owner !=
+ rdata->items[i].dname) {
+ wildcard = rdata->items[i].dname->node->owner;
+ }
+
+ if (use_ids) {
+ /* Write ID. */
+ dbg_zload("%s \n",
+ knot_dname_to_str(rdata->items[i].dname));
+ assert(rdata->items[i].dname->id != 0);
+
+ uint32_t id = rdata->items[i].dname->id;
+ /* \todo check the return value */
+ fwrite_wrapper(&id,
+ sizeof(id), 1, f, stream, stream_size,
+ crc);
+ } else {
+// assert(rdata->items[i].dname->id != 0);
+ dump_dname_with_id(rdata->items[i].dname,
+ f, stream,
+ stream_size, crc);
+ }
+
+ /* Write in the zone bit */
+ if (rdata->items[i].dname->node != NULL && !wildcard) {
+ /* \todo check the return value */
+ fwrite_wrapper((uint8_t *)"\1",
+ sizeof(uint8_t), 1, f, stream,
+ stream_size, crc);
+ } else {
+ /* \todo check the return value */
+ fwrite_wrapper((uint8_t *)"\0", sizeof(uint8_t),
+ 1, f, stream, stream_size, crc);
+ }
+
+ if (use_ids && wildcard) {
+ /* \todo check the return value */
+ fwrite_wrapper((uint8_t *)"\1",
+ sizeof(uint8_t), 1, f, stream,
+ stream_size, crc);
+ uint32_t wildcard_id = wildcard->id;
+ /* \todo check the return value */
+ fwrite_wrapper(&wildcard_id,
+ sizeof(wildcard_id), 1, f, stream,
+ stream_size, crc);
+ } else {
+ /* \todo check the return value */
+ fwrite_wrapper((uint8_t *)"\0", sizeof(uint8_t),
+ 1, f, stream,
+ stream_size, crc);
+ }
+
+ } else {
+ dbg_zdump("Writing raw data. Item nr.: %d\n",
+ i);
+ assert(rdata->items[i].raw_data != NULL);
+ /* \todo check the return value */
+ fwrite_wrapper(rdata->items[i].raw_data,
+ sizeof(uint8_t),
+ rdata->items[i].raw_data[0] + 2, f,
+ stream, stream_size, crc);
+
+ dbg_zdump("Written %d long raw data\n",
+ rdata->items[i].raw_data[0]);
+ }
+ }
+}
+
+/*!
+ * \brief Dumps RRSIG in binary format to given file.
+ *
+ * \param rrsig RRSIG to be dumped.
+ * \param data Arguments to be propagated.
+ */
+static void knot_rrsig_set_dump_binary(knot_rrset_t *rrsig, arg_t *data,
+ int use_ids,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ dbg_zdump("Dumping rrset \\w owner: %s\n",
+ knot_dname_to_str(rrsig->owner));
+ assert(rrsig->type == KNOT_RRTYPE_RRSIG);
+ assert(rrsig->rdata);
+ FILE *f = (FILE *)((arg_t *)data)->arg1;
+ /* \todo check the return value */
+ fwrite_wrapper(&rrsig->type, sizeof(rrsig->type), 1, f,
+ stream, stream_size, crc);
+ fwrite_wrapper(&rrsig->rclass, sizeof(rrsig->rclass), 1, f,
+ stream, stream_size, crc);
+ fwrite_wrapper(&rrsig->ttl, sizeof(rrsig->ttl), 1, f,
+ stream, stream_size, crc);
+
+ uint32_t rdata_count = 1;
+ /* Calculate rrset rdata count. */
+ knot_rdata_t *tmp_rdata = rrsig->rdata;
+ while(tmp_rdata->next != rrsig->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ rdata_count++;
+ }
+
+ fwrite_wrapper(&rdata_count, sizeof(rdata_count), 1, f,
+ stream, stream_size, crc);
+
+ tmp_rdata = rrsig->rdata;
+ while (tmp_rdata->next != rrsig->rdata) {
+ knot_rdata_dump_binary(tmp_rdata, KNOT_RRTYPE_RRSIG, data,
+ use_ids, stream, stream_size, crc);
+ tmp_rdata = tmp_rdata->next;
+ }
+ knot_rdata_dump_binary(tmp_rdata, KNOT_RRTYPE_RRSIG, data, use_ids,
+ stream, stream_size, crc);
+}
+
+/*!
+ * \brief Dumps RRSet in binary format to given file.
+ *
+ * \param rrset RRSSet to be dumped.
+ * \param data Arguments to be propagated.
+ */
+static void knot_rrset_dump_binary(const knot_rrset_t *rrset, void *data,
+ int use_ids,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ FILE *f = (FILE *)((arg_t *)data)->arg1;
+
+ if (!use_ids) {
+ dump_dname_with_id(rrset->owner, f, stream, stream_size, crc);
+ }
+
+ /* \todo check the return value */
+ fwrite_wrapper(&rrset->type, sizeof(rrset->type), 1, f,
+ stream, stream_size, crc);
+ fwrite_wrapper(&rrset->rclass, sizeof(rrset->rclass), 1, f,
+ stream, stream_size, crc);
+ fwrite_wrapper(&rrset->ttl, sizeof(rrset->ttl), 1, f,
+ stream, stream_size, crc);
+
+ uint32_t rdata_count = 1;
+ uint8_t has_rrsig = rrset->rrsigs != NULL;
+
+ /* Calculate rrset rdata count. */
+ knot_rdata_t *tmp_rdata = rrset->rdata;
+ while(tmp_rdata->next != rrset->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ rdata_count++;
+ }
+
+ fwrite_wrapper(&rdata_count, sizeof(rdata_count), 1, f,
+ stream, stream_size, crc);
+ fwrite_wrapper(&has_rrsig, sizeof(has_rrsig), 1, f,
+ stream, stream_size, crc);
+
+ tmp_rdata = rrset->rdata;
+
+ while (tmp_rdata->next != rrset->rdata) {
+ knot_rdata_dump_binary(tmp_rdata, rrset->type, data, use_ids,
+ stream, stream_size, crc);
+ tmp_rdata = tmp_rdata->next;
+ }
+ knot_rdata_dump_binary(tmp_rdata, rrset->type, data, use_ids,
+ stream, stream_size, crc);
+
+ /* This is now obsolete, although I'd rather not use recursion - that
+ * would probably not work */
+
+ if (rrset->rrsigs != NULL) {
+ knot_rrsig_set_dump_binary(rrset->rrsigs, data, use_ids,
+ stream, stream_size, crc);
+ }
+}
+
+/*!
+ * \brief Dumps all RRSets in node to file in binary format.
+ *
+ * \param node Node to dumped.
+ * \param data Arguments to be propagated.
+ */
+static void knot_node_dump_binary(knot_node_t *node, void *data,
+ uint8_t **stream, size_t *stream_size,
+ crc_t *crc)
+{
+ arg_t *args = (arg_t *)data;
+ FILE *f = (FILE *)args->arg1;
+
+// node_count++;
+ /* first write dname */
+ assert(node->owner != NULL);
+
+ /* Write owner ID. */
+ dbg_zdump("Dumping node owned by %s\n",
+ knot_dname_to_str(node->owner));
+ assert(node->owner->id != 0);
+ uint32_t owner_id = node->owner->id;
+ /* \todo check the return value */
+ fwrite_wrapper(&owner_id, sizeof(owner_id), 1, f, stream, stream_size,
+ crc);
+
+ if (knot_node_parent(node, 0) != NULL) {
+ uint32_t parent_id = knot_dname_id(
+ knot_node_owner(knot_node_parent(node, 0)));
+ fwrite_wrapper(&parent_id, sizeof(parent_id), 1, f,
+ stream, stream_size, crc);
+ } else {
+ uint32_t parent_id = 0;
+ fwrite_wrapper(&parent_id, sizeof(parent_id), 1, f,
+ stream, stream_size, crc);
+ }
+
+ fwrite_wrapper(&(node->flags), sizeof(node->flags), 1, f,
+ stream, stream_size, crc);
+
+ dbg_zdump("Written flags: %u\n", node->flags);
+
+ if (knot_node_nsec3_node(node, 0) != NULL) {
+ uint32_t nsec3_id =
+ knot_node_owner(knot_node_nsec3_node(node, 0))->id;
+ fwrite_wrapper(&nsec3_id, sizeof(nsec3_id), 1, f,
+ stream, stream_size, crc);
+ dbg_zdump("Written nsec3 node id: %u\n",
+ knot_node_owner(knot_node_nsec3_node(node, 0))->id);
+ } else {
+ uint32_t nsec3_id = 0;
+ fwrite_wrapper(&nsec3_id, sizeof(nsec3_id), 1, f,
+ stream, stream_size, crc);
+ }
+
+ /* Now we need (or do we?) count of rrsets to be read
+ * but that number is yet unknown */
+
+ uint16_t rrset_count = node->rrset_count;
+ fwrite_wrapper(&rrset_count, sizeof(rrset_count), 1, f,
+ stream, stream_size, crc);
+
+ /* CLEANUP */
+// const skip_node_t *skip_node = skip_first(node->rrsets);
+
+ const knot_rrset_t **node_rrsets = knot_node_rrsets(node);
+ for (int i = 0; i < rrset_count; i++)
+ {
+ knot_rrset_dump_binary(node_rrsets[i], data, 1,
+ stream, stream_size, crc);
+ }
+
+ /* CLEANUP */
+// if (skip_node == NULL) {
+// /* we can return, count is set to 0 */
+// return;
+// }
+
+// knot_rrset_t *tmp;
+
+// do {
+// tmp = (knot_rrset_t *)skip_node->value;
+// knot_rrset_dump_binary(tmp, data, 1);
+// } while ((skip_node = skip_next(skip_node)) != NULL);
+
+ free(node_rrsets);
+
+ dbg_zdump("Position after all rrsets: %ld\n", ftell(f));
+ dbg_zdump("Writing here: %ld\n", ftell(f));
+ dbg_zdump("Function ends with: %ld\n\n", ftell(f));
+}
+
+/*!
+ * \brief Checks if zone uses DNSSEC and/or NSEC3
+ *
+ * \param zone Zone to be checked.
+ *
+ * \retval 0 if zone is not secured.
+ * \retval 2 if zone uses NSEC3
+ * \retval 1 if zone uses NSEC
+ */
+static int zone_is_secure(knot_zone_contents_t *zone)
+{
+ if (knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_DNSKEY) == NULL) {
+ return 0;
+ } else {
+ if (knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_NSEC3PARAM) != NULL) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+/*!
+ * \brief Checks if last node in NSEC/NSEC3 chain points to first node in the
+ * chain and prints possible errors.
+ *
+ * \param handler Semantic error handler.
+ * \param zone Current zone.
+ * \param last_node Last node in NSEC/NSEC3 chain.
+ * \param do_checks Level of semantic checks.
+ */
+static void log_cyclic_errors_in_zone(err_handler_t *handler,
+ knot_zone_contents_t *zone,
+ knot_node_t *last_node,
+ const knot_node_t *first_nsec3_node,
+ const knot_node_t *last_nsec3_node,
+ char do_checks)
+{
+ if (do_checks == 3) {
+ /* Each NSEC3 node should only contain one RRSET. */
+ assert(last_nsec3_node && first_nsec3_node);
+ const knot_rrset_t *nsec3_rrset =
+ knot_node_rrset(last_nsec3_node,
+ KNOT_RRTYPE_NSEC3);
+ if (nsec3_rrset == NULL) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* check that next dname is in the zone */
+ uint8_t *next_dname_decoded = NULL;
+ size_t real_size = 0;
+
+ if (((real_size = base32hex_encode_alloc(((char *)
+ rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
+ rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
+ (char **)&next_dname_decoded)) <= 0) ||
+ (next_dname_decoded == NULL)) {
+ fprintf(stderr, "Could not encode base32 string!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* This is why allocate maximum length of decoded string + 1 */
+ memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
+ next_dname_decoded[0] = real_size;
+
+ /* Local allocation, will be discarded. */
+ knot_dname_t *next_dname =
+ knot_dname_new_from_wire(next_dname_decoded,
+ real_size + 1, NULL);
+ if (next_dname == NULL) {
+ fprintf(stderr, "Could not allocate dname!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_ALLOC);
+ return;
+ }
+
+ free(next_dname_decoded);
+
+ /*! \todo Free result and dname! */
+ if (knot_dname_cat(next_dname,
+ knot_node_owner(knot_zone_contents_apex(zone))) ==
+ NULL) {
+ fprintf(stderr, "Could not concatenate dnames!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* Check it points somewhere first. */
+ if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Compare with the actual first NSEC3 node. */
+ if (knot_dname_compare(first_nsec3_node->owner,
+ next_dname) != 0) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Directly discard. */
+ knot_dname_free(&next_dname);
+
+ } else if (do_checks == 2 ) {
+ if (last_node == NULL) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ return;
+ } else {
+ const knot_rrset_t *nsec_rrset =
+ knot_node_rrset(last_node,
+ KNOT_RRTYPE_NSEC);
+
+ if (nsec_rrset == NULL) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ return;
+ }
+
+ const knot_dname_t *next_dname =
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset), 0)->dname;
+ 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) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ }
+ }
+ }
+}
+
+/*!
+ * \brief Safe wrapper around fwrite.
+ *
+ * \param dst Destination pointer.
+ * \param size Size of element to be written.
+ * \param n Number of elements to be written.
+ * \param fp File to write to.
+ *
+ * \retval > 0 if succesfull.
+ * \retval 0 if failed.
+ */
+//static inline int fwrite_wrapper_safe(const void *src,
+// size_t size, size_t n, FILE *fp)
+//{
+// int rc = fwrite_wrapper(src, size, n, fp);
+// if (rc != n) {
+// fprintf(stderr, "fwrite_wrapper: invalid write %d (expected %zu)\n", rc,
+// n);
+// }
+
+// return rc == n;
+//}
+
+static void dump_dname_from_tree(knot_dname_t *dname,
+ void *data)
+{
+ arg_t *arg = (arg_t *)data;
+ FILE *f = (FILE *)arg->arg1;
+ crc_t *crc = (crc_t*)arg->arg2;
+ dump_dname_with_id(dname, f, NULL, NULL, crc);
+}
+
+static int knot_dump_dname_table(const knot_dname_table_t *dname_table,
+ FILE *f, crc_t *crc)
+{
+ arg_t arg;
+ arg.arg1 = f;
+ arg.arg2 = crc;
+ /* Go through the tree and dump each dname along with its ID. */
+ knot_dname_table_tree_inorder_apply(dname_table,
+ dump_dname_from_tree, &arg);
+ /* CLEANUP */
+// TREE_FORWARD_APPLY(dname_table->tree, dname_table_node, avl,
+// dump_dname_from_tree, (void *)f);
+
+ return KNOT_EOK;
+}
+
+static void save_node_from_tree(knot_node_t *node, void *data)
+{
+ arg_t *arg = (arg_t *)data;
+ /* Increment node count */
+ (*((uint32_t *)(arg->arg1)))++;
+ /* Save the first node only */
+ if (arg->arg2 == NULL) {
+ arg->arg2 = (void *)node;
+ }
+ arg->arg3 = (void *)node;
+}
+
+static void dump_node_to_file(knot_node_t *node, void *data)
+{
+ arg_t *arg = (arg_t *)data;
+ knot_node_dump_binary(node, data, NULL, NULL, (crc_t *)arg->arg7);
+}
+
+int knot_zdump_binary(knot_zone_contents_t *zone, const char *filename,
+ int do_checks, const char *sfilename)
+{
+ /* Open .new file. */
+ char new_path[strlen(filename) + strlen(".new") + 1];
+ memcpy(new_path, filename, strlen(filename) + 1);
+ strcat(new_path, ".new");
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ int fd = open(new_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+ if (fd == -1) {
+ return KNOT_EBADARG;
+ }
+
+ FILE *f = fdopen(fd, "wb");
+ assert(f);
+
+ /* CLEANUP */
+// skip_list_t *encloser_list = skip_create_list(compare_pointers);
+ arg_t arguments;
+ /* Memory to be derefenced in the save_node_from_tree function. */
+ uint32_t node_count = 0;
+ arguments.arg1 = &node_count;
+ arguments.arg2 = NULL;
+
+ /* Count number of normal nodes. */
+ knot_zone_contents_tree_apply_inorder(zone, save_node_from_tree, &arguments);
+ /* arg1 is now count of normal nodes */
+ uint32_t normal_node_count = *((uint32_t *)arguments.arg1);
+
+ node_count = 0;
+ arguments.arg1 = &node_count;
+ arguments.arg2 = NULL;
+
+ /* Count number of NSEC3 nodes. */
+ knot_zone_contents_nsec3_apply_inorder(zone, save_node_from_tree, &arguments);
+ uint32_t nsec3_node_count = *((uint32_t *)arguments.arg1);
+ /* arg2 is the first NSEC3 node - used in sem checks. */
+ /* arg3 is the last NSEC3 node - used in sem checks. */
+ const knot_node_t *first_nsec3_node = (knot_node_t *)arguments.arg2;
+ const knot_node_t *last_nsec3_node = (knot_node_t *)arguments.arg3;
+
+ if (do_checks && zone_is_secure(zone)) {
+ do_checks += zone_is_secure(zone);
+ }
+
+ err_handler_t *handler = NULL;
+
+ if (do_checks) {
+ handler = handler_new(1, 0, 1, 1, 1);
+ if (handler == NULL) {
+ /* disable checks and we can continue */
+ do_checks = 0;
+ } else { /* Do check for SOA right now */
+ if (knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA) == NULL) {
+ err_handler_handle_error(handler,
+ knot_zone_contents_apex(zone),
+ ZC_ERR_MISSING_SOA);
+ }
+ }
+
+ knot_node_t *last_node = NULL;
+
+ zone_do_sem_checks(zone, do_checks, handler, &last_node);
+ log_cyclic_errors_in_zone(handler, zone, last_node,
+ first_nsec3_node, last_nsec3_node,
+ do_checks);
+ err_handler_log_all(handler);
+ free(handler);
+ }
+
+ crc_t crc = crc_init();
+
+ /* Start writing header - magic bytes. */
+ static const uint8_t MAGIC[MAGIC_LENGTH] = MAGIC_BYTES;
+ fwrite_wrapper(&MAGIC, sizeof(uint8_t), MAGIC_LENGTH, f, NULL, NULL,
+ &crc);
+
+ /* Write source file length. */
+ uint32_t sflen = 0;
+ if (sfilename) {
+ sflen = strlen(sfilename) + 1;
+ }
+ fwrite_wrapper(&sflen, sizeof(uint32_t), 1, f, NULL, NULL, &crc);
+
+ /* Write source file. */
+ fwrite_wrapper(sfilename, sflen, 1, f, NULL, NULL, &crc);
+
+ /* Notice: End of header,
+ */
+
+ /* Start writing compiled data. */
+ fwrite_wrapper(&normal_node_count, sizeof(normal_node_count), 1, f,
+ NULL, NULL, &crc);
+ fwrite_wrapper(&nsec3_node_count, sizeof(nsec3_node_count), 1, f,
+ NULL, NULL, &crc);
+ uint32_t auth_node_count = zone->node_count;
+ fwrite_wrapper(&auth_node_count,
+ sizeof(auth_node_count), 1, f, NULL, NULL, &crc);
+
+ /* Write total number of dnames */
+ assert(zone->dname_table);
+ uint32_t total_dnames = zone->dname_table->id_counter;
+ fwrite_wrapper(&total_dnames,
+ sizeof(total_dnames), 1, f, NULL, NULL, &crc);
+
+ /* Write dname table. */
+ if (knot_dump_dname_table(zone->dname_table, f, &crc)
+ != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ arguments.arg1 = (void *)f;
+ arguments.arg3 = zone;
+ arguments.arg7 = &crc;
+
+ /* TODO is there a way how to stop the traversal upon error? */
+ knot_zone_contents_tree_apply_inorder(zone, dump_node_to_file,
+ (void *)&arguments);
+
+ knot_zone_contents_nsec3_apply_inorder(zone, dump_node_to_file,
+ (void *)&arguments);
+ fclose(f);
+
+ crc = crc_finalize(crc);
+ /* Write CRC to separate .crc file. */
+ /*!< \todo There is now function doing this. */
+ char *crc_path =
+ malloc(sizeof(char) * (strlen(filename) + strlen(".crc") + 1));
+ if (unlikely(!crc_path)) {
+ close(fd);
+ return KNOT_ENOMEM;
+ }
+ memset(crc_path, 0,
+ sizeof(char) * (strlen(filename) + strlen(".crc") + 1));
+ memcpy(crc_path, filename, sizeof(char) * strlen(filename));
+
+ crc_path = strcat(crc_path, ".crc");
+ FILE *f_crc = fopen(crc_path, "w");
+ if (unlikely(!f_crc)) {
+ dbg_zload("knot_zload_open: failed to open '%s'\n",
+ crc_path);
+ close(fd);
+ free(crc_path);
+ return ENOENT;
+ }
+ free(crc_path);
+
+ fprintf(f_crc, "%lu\n", (unsigned long)crc);
+ fclose(f_crc);
+
+ close(fd);
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ fd = open(filename, O_WRONLY | O_CREAT, mode);
+ if (fd == -1) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ fprintf(stderr, "Could not open destination file! Use '%s' "
+ "file instead.\n", new_path);
+ return KNOT_ERROR;
+ }
+
+ /* Try to obtain exclusive lock for originally given file. */
+ if (fcntl(fd, F_SETLK, knot_file_lock(F_WRLCK, SEEK_SET)) == -1) {
+ fprintf(stderr, "Could not lock destination file for write! "
+ "Use '%s' file instead.\n", new_path);
+ close(fd);
+ return KNOT_ERROR;
+ }
+
+ /* Move .new file to original file. */
+ if (rename(new_path, filename) != 0) {
+ fprintf(stderr, "Could not move to originally given file! "
+ "Use '%s' file instead.\n", new_path);
+ close(fd);
+ return KNOT_ERROR;
+ }
+
+ /* Release the lock. */
+ if (fcntl(fd, F_SETLK, knot_file_lock(F_UNLCK, SEEK_SET)) == -1) {
+ fprintf(stderr, "Could not unlock destination file!\n");
+ return KNOT_ERROR;
+ }
+
+ close(fd);
+
+ return KNOT_EOK;
+}
+
+int knot_zdump_rrset_serialize(const knot_rrset_t *rrset, uint8_t **stream,
+ size_t *size)
+{
+ if (stream == NULL || *stream != NULL || rrset == NULL ||
+ size == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *size = 0;
+ arg_t arguments;
+ memset(&arguments, 0, sizeof(arg_t));
+
+ knot_rrset_dump_binary(rrset, &arguments, 0, stream, size, NULL);
+
+ return KNOT_EOK;
+}
+
+static char *knot_zdump_crc_file(const char* filename)
+{
+ char *crc_path =
+ malloc(sizeof(char) * (strlen(filename) +
+ strlen(".crc") + 1));
+ CHECK_ALLOC_LOG(crc_path, NULL);
+ memset(crc_path, 0,
+ sizeof(char) * (strlen(filename) +
+ strlen(".crc") + 1));
+ memcpy(crc_path, filename,
+ sizeof(char) * strlen(filename));
+ crc_path = strcat(crc_path, ".crc");
+ return crc_path;
+}
+
+int knot_zdump_dump_and_swap(knot_zone_contents_t *zone,
+ const char *temp_zonedb,
+ const char *destination_zonedb,
+ const char *sfilename)
+{
+ int rc = knot_zdump_binary(zone, temp_zonedb, 0, sfilename);
+
+ if (rc != KNOT_EOK) {
+ dbg_zdump("Failed to save the zone to binary zone db %s."
+ "\n", temp_zonedb);
+ return KNOT_ERROR;
+ }
+
+ /*! \todo this would also need locking as well. */
+ rc = remove(destination_zonedb);
+ if (rc == 0 || (rc != 0 && errno == ENOENT)) {
+
+ /* Delete old CRC file. */
+ char *destination_zonedb_crc =
+ knot_zdump_crc_file(destination_zonedb);
+ if (destination_zonedb_crc == NULL) {
+ return KNOT_ENOMEM;
+ }
+ remove(destination_zonedb_crc);
+
+ /* Move CRC file. */
+ char *temp_zonedb_crc =
+ knot_zdump_crc_file(temp_zonedb);
+ if (temp_zonedb_crc == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (rename(temp_zonedb_crc, destination_zonedb_crc) != 0) {
+ dbg_zdump("Failed to replace old zonedb CRC %s "
+ "with new CRC zone file %s.\n",
+ destination_zonedb_crc,
+ temp_zonedb_crc);
+ return KNOT_ERROR;
+ }
+ free(temp_zonedb_crc);
+ free(destination_zonedb_crc);
+
+ /* Rename zonedb. */
+ if (rename(temp_zonedb, destination_zonedb) != 0) {
+ dbg_zdump("Failed to replace old zonedb %s "
+ "with new zone file %s.\n",
+ temp_zonedb,
+ destination_zonedb);
+ /*! \todo with proper locking, this shouldn't happen,
+ * revise it later on.
+ */
+ return KNOT_ERROR;
+ }
+ } else {
+ dbg_zdump("Failed to replace old zonedb '%s'', %s.\n",
+ destination_zonedb, strerror(errno));
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/zone/zone-dump.h b/src/knot/zone/zone-dump.h
new file mode 100644
index 0000000..daf6c18
--- /dev/null
+++ b/src/knot/zone/zone-dump.h
@@ -0,0 +1,94 @@
+/* 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-dump.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Functions for dumping zone to binary file.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_ZONEDUMP_H_
+#define _KNOT_ZONEDUMP_H_
+
+#include "common/crc.h"
+#include "libknot/zone/zone.h"
+
+/*!
+ * \brief Zone loader enums.
+ */
+enum {
+ MAGIC_LENGTH = 7 /*!< Compiled zone magic length. */
+};
+
+/*! \brief Magic identifier: { "knot", maj_ver, min_ver, revision } */
+#define MAGIC_BYTES {'k', 'n', 'o', 't', '0', '8', '0'}
+
+/*!
+ * \brief Dumps given zone to binary file.
+ *
+ * \param zone Zone to be saved.
+ * \param filename Name of file to be created.
+ * \param do_checks Set to 1 to enable checking the zone for semantic errors.
+ * \param sfilename Source filename of the text zone file.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EBADARG if the file cannot be opened for writing.
+ */
+int knot_zdump_binary(knot_zone_contents_t *zone, const char *filename,
+ int do_checks, const char *sfilename);
+
+/*!
+ * \brief Serializes RRSet into binary stream. Expects NULL pointer, memory
+ * is handled inside function.
+ *
+ * \param rrset RRSet to be serialized.
+ * \param stream Stream containing serialized RRSet.
+ * \param size Length of created stream.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EBADARG if wrong arguments are supplied.
+ * \retval KNOT_ENOMEM on memory error.
+ */
+int knot_zdump_rrset_serialize(const knot_rrset_t *rrset, uint8_t **stream,
+ size_t *size);
+
+/*!
+ * \brief Serializes RRSet into binary stream. Expects NULL pointer, memory
+ * is handled inside function.
+ *
+ * \param rrset RRSet to be serialized.
+ * \param stream Stream containing serialized RRSet.
+ * \param size Length of created stream.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EBADARG if wrong arguments are supplied.
+ * \retval KNOT_ENOMEM on memory error.
+ */
+int knot_zdump_rrset_serialize(const knot_rrset_t *rrset, uint8_t **stream,
+ size_t *size);
+
+int knot_zdump_dump_and_swap(knot_zone_contents_t *zone,
+ const char *temp_zonedb,
+ const char *destination_zonedb,
+ const char *sfilename);
+
+#endif /* _KNOT_ZONEDUMP_H_ */
+
+/*! @} */
diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c
new file mode 100644
index 0000000..9ab0e8d
--- /dev/null
+++ b/src/knot/zone/zone-load.c
@@ -0,0 +1,1209 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "common/crc.h"
+#include "libknot/common.h"
+#include "knot/other/debug.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zone-dump.h"
+#include "libknot/libknot.h"
+
+/*!
+ * \brief Compares two time_t values.
+ *
+ * \param x First time_t value to be compared.
+ * \param y Second time_t value to be compared.
+ *
+ * \retval 0 when times are the some.
+ * \retval 1 when y < x.
+ * \retval -1 when x > y.
+ */
+static int timet_cmp(time_t x, time_t y)
+{
+ /* Calculate difference in the scale of seconds. */
+ long diff = x - y;
+
+ /* X and Y are equal. */
+ if (diff == 0) {
+ return 0;
+ }
+
+ /* X is newer. */
+ if (diff > 0) {
+ return 1;
+ }
+
+ /* Y is newer. */
+ return -1;
+}
+
+/*!
+ * \brief Safe wrapper around fread.
+ *
+ * \param dst Destination pointer.
+ * \param size Size of element to be read.
+ * \param n Number of elements to be read.
+ * \param fp File to read from.
+ *
+ * \retval > 0 if succesfull.
+ * \retval 0 if failed.
+ */
+static inline int fread_safe_from_file(void *dst,
+ size_t size, size_t n, FILE *fp)
+{
+ int rc = fread(dst, size, n, fp);
+ if (rc != n) {
+ fprintf(stderr, "fread: invalid read %d (expected %zu)\n", rc,
+ n);
+ }
+
+ return rc == n;
+}
+
+static uint8_t *knot_zload_stream = NULL;
+static size_t knot_zload_stream_remaining = 0;
+static size_t knot_zload_stream_size = 0;
+
+static inline int read_from_stream(void *dst,
+ size_t size, size_t n, FILE *fp)
+{
+ if (knot_zload_stream_remaining < (size * n)) {
+ return 0;
+ }
+
+ memcpy(dst,
+ knot_zload_stream +
+ (knot_zload_stream_size - knot_zload_stream_remaining),
+ size * n);
+ knot_zload_stream_remaining -= size * n;
+
+ return 1;
+}
+
+static int (*fread_wrapper)(void *dst, size_t size, size_t n, FILE *fp);
+
+/*! \note Contents of dump file:
+ * MAGIC(knotxx) NUMBER_OF_NORMAL_NODES NUMBER_OF_NSEC3_NODES
+ * [normal_nodes] [nsec3_nodes]
+ * node has following format:
+ * owner_size owner_wire owner_label_size owner_labels owner_id
+ * node_flags node_rrset_count [node_rrsets]
+ * rrset has following format:
+ * rrset_type rrset_class rrset_ttl rrset_rdata_count rrset_rrsig_count
+ * [rrset_rdata] [rrset_rrsigs]
+ * rdata can either contain full dnames (that is with labels but without ID)
+ * or dname ID, if dname is in the zone
+ * or raw data stored like this: data_len [data]
+ */
+
+enum { DNAME_MAX_WIRE_LENGTH = 256 };
+
+/*!
+ * \brief Helper function. Frees rdata items and temporary array of items.
+ *
+ * \param rdata Rdata to be freed.
+ * \param items Items to be freed.
+ * \param count Current count of rdata items.
+ * \param type RRSet type.
+ */
+static void load_rdata_purge(knot_rdata_t *rdata,
+ knot_rdata_item_t *items,
+ int count,
+ knot_rrtype_descriptor_t *desc,
+ uint16_t type)
+{
+ /* Increase refcount manually, as the set_items() doesn't see the dname
+ * type and thus is unable to increment refcounter.
+ */
+ for (int i = 0; i < count; ++i) {
+ switch(desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ knot_dname_retain(items[i].dname);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Copy items to rdata and free the temporary rdata. */
+ knot_rdata_set_items(rdata, items, count);
+ knot_rdata_deep_free(&rdata, type, 1);
+ free(items);
+}
+
+static knot_dname_t *read_dname_with_id(FILE *f)
+{
+ knot_dname_t *ret = knot_dname_new();
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ /* Read ID. */
+ uint32_t dname_id = 0;
+ if (!fread_wrapper(&dname_id, sizeof(dname_id), 1, f)) {
+ knot_dname_release(ret);
+ return NULL;
+ }
+
+ ret->id = dname_id;
+ dbg_zload("loaded: dname id: %u\n", dname_id);
+
+ /* Read size of dname. */
+ uint32_t dname_size = 0;
+ if (!fread_wrapper(&dname_size, sizeof(dname_size), 1, f)) {
+ knot_dname_release(ret);
+ return NULL;
+ }
+ ret->size = dname_size;
+ dbg_zload("loaded: dname length: %u\n", ret->size);
+
+ assert(ret->size <= DNAME_MAX_WIRE_LENGTH);
+
+ /* Read wireformat of dname. */
+ ret->name = malloc(sizeof(uint8_t) * ret->size);
+ if (ret->name == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_release(ret);
+ return NULL;
+ }
+
+ if (!fread_wrapper(ret->name, sizeof(uint8_t), ret->size, f)) {
+ knot_dname_release(ret);
+ return NULL;
+ }
+
+ /* Read labels. */
+ uint16_t label_count = 0;
+ if (!fread_wrapper(&label_count, sizeof(label_count), 1, f)) {
+ knot_dname_release(ret);
+ return NULL;
+ }
+
+ ret->label_count = label_count;
+
+ ret->labels = malloc(sizeof(uint8_t) * ret->label_count);
+ if (ret->labels == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_release(ret);
+ return NULL;
+ }
+
+ if (!fread_wrapper(ret->labels, sizeof(uint8_t), ret->label_count, f)) {
+ free(ret->name);
+ free(ret);
+ return NULL;
+ }
+
+ dbg_zload("loaded: %s (id: %d)\n", knot_dname_to_str(ret),
+ ret->id);
+
+ return ret;
+}
+
+/*!
+ * \brief Load rdata in binary format from file.
+ *
+ * \param type Type of RRSet containing read rdata.
+ * \param f File to read binary data from.
+ *
+ * \return Pointer to read and created rdata on success, NULL otherwise.
+ */
+static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
+ knot_dname_t **id_array,
+ int use_ids)
+{
+ knot_rdata_t *rdata = knot_rdata_new();
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+
+
+ /* First, should read rdata count. */
+
+ uint32_t rdata_count = 0;
+
+ if(!fread_wrapper(&rdata_count, sizeof(rdata_count), 1, f)) {
+ return NULL;
+ }
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * rdata_count);
+
+ if (desc->fixed_items) {
+ assert(desc->length == rdata_count);
+ }
+
+ uint16_t raw_data_length;
+
+ dbg_zload("Reading %d items\n", rdata_count);
+
+ dbg_zload("current type: %s\n", knot_rrtype_to_string(type));
+
+ for (int i = 0; i < rdata_count; i++) {
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ) {
+
+ /* TODO maybe this does not need to be stored this big*/
+
+ uint32_t dname_id = 0;
+ uint8_t has_wildcard = 0;
+ uint8_t in_the_zone = 0;
+
+ if (use_ids) {
+ if(!fread_wrapper(&dname_id, sizeof(dname_id), 1, f)) {
+ load_rdata_purge(rdata, items, i, desc, type);
+ return NULL;
+ }
+
+ /* Store reference do dname. */
+ knot_dname_retain(id_array[dname_id]);
+ items[i].dname = id_array[dname_id];
+ } else {
+ items[i].dname = read_dname_with_id(f);
+ }
+
+ if(!fread_wrapper(&in_the_zone, sizeof(in_the_zone),
+ 1, f)) {
+ load_rdata_purge(rdata, items, i, desc, type);
+ return NULL;
+ }
+
+ if(!fread_wrapper(&has_wildcard, sizeof(uint8_t),
+ 1, f)) {
+ load_rdata_purge(rdata, items, i, desc, type);
+ return NULL;
+ }
+
+ if (use_ids && has_wildcard) {
+ if(!fread_wrapper(&dname_id, sizeof(dname_id),
+ 1, f)) {
+ load_rdata_purge(rdata, items,
+ i, desc, type);
+ return NULL;
+ }
+ items[i].dname->node =
+ id_array[dname_id]->node;
+ } else if (use_ids && !in_the_zone) { /* destroy the node */
+ if (id_array[dname_id]->node != NULL) {
+ knot_node_free(&id_array[dname_id]->
+ node, 0, 0);
+ }
+ /* Also sets node to NULL! */
+ }
+ assert(items[i].dname);
+ } else {
+ if (!fread_wrapper(&raw_data_length,
+ sizeof(raw_data_length), 1, f)) {
+ load_rdata_purge(rdata, items, i, desc, type);
+ return NULL;
+ }
+
+ items[i].raw_data =
+ malloc(sizeof(uint8_t) * (raw_data_length + 2));
+ items[i].raw_data[0] = raw_data_length;
+
+ if (!fread_wrapper(items[i].raw_data + 1, sizeof(uint8_t),
+ raw_data_length, f)) {
+ load_rdata_purge(rdata, items, i + 1, desc, type);
+ return NULL;
+ }
+ dbg_zload("read raw_data len %d\n", raw_data_length);
+ }
+ }
+
+ /* Each item has refcount already incremented for saving in rdata. */
+ if (knot_rdata_set_items(rdata, items, rdata_count) != 0) {
+ fprintf(stderr, "zoneload: Could not set items "
+ "when loading rdata.\n");
+ }
+
+ free(items);
+
+ dbg_zload("knot_load_rdata: all %d items read\n",
+ desc->length);
+
+ assert(rdata->count == rdata_count);
+
+ rdata->count = rdata_count;
+
+ return rdata;
+}
+
+/*!
+ * \brief Loads RRSIG from binary file.
+ *
+ * \param f File to read from.
+ *
+ * \return pointer to created and read RRSIG on success, NULL otherwise.
+ */
+static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array,
+ int use_ids)
+{
+ knot_rrset_t *rrsig;
+
+ uint16_t rrset_type;
+ uint16_t rrset_class;
+ uint32_t rrset_ttl;
+
+ uint32_t rdata_count;
+
+ if (!fread_wrapper(&rrset_type, sizeof(rrset_type), 1, f)) {
+ return NULL;
+ }
+
+ if (rrset_type != KNOT_RRTYPE_RRSIG) {
+ fprintf(stderr, "!! Error: rrsig has wrong type\n");
+ return NULL;
+ }
+ dbg_zload("rrset type: %d\n", rrset_type);
+ if (!fread_wrapper(&rrset_class, sizeof(rrset_class), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("rrset class %d\n", rrset_class);
+
+ if (!fread_wrapper(&rrset_ttl, sizeof(rrset_ttl), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("rrset ttl %d\n", rrset_ttl);
+
+ if (!fread_wrapper(&rdata_count, sizeof(rdata_count), 1, f)) {
+ return NULL;
+ }
+
+ rrsig = knot_rrset_new(NULL, rrset_type, rrset_class, rrset_ttl);
+
+ knot_rdata_t *tmp_rdata;
+
+ dbg_zload("loading %d rdata entries\n", rdata_count);
+
+ for (int i = 0; i < rdata_count; i++) {
+ tmp_rdata = knot_load_rdata(KNOT_RRTYPE_RRSIG, f,
+ id_array, use_ids);
+ if (tmp_rdata) {
+ knot_rrset_add_rdata(rrsig, tmp_rdata);
+ } else {
+ knot_rrset_deep_free(&rrsig, 0, 1, 1);
+ return NULL;
+ }
+ }
+
+ return rrsig;
+}
+
+/*!
+ * \brief Loads RRSet from binary file.
+ *
+ * \param f File to read from.
+ *
+ * \return pointer to created and read RRSet on success, NULL otherwise.
+ */
+static knot_rrset_t *knot_load_rrset(FILE *f, knot_dname_t **id_array,
+ int use_ids)
+{
+ knot_rrset_t *rrset = NULL;
+
+ uint16_t rrset_type = 0;
+ uint16_t rrset_class = 0;
+ uint32_t rrset_ttl = 0;
+
+ uint32_t rdata_count = 0;
+ uint8_t rrsig_count = 0;
+
+ knot_dname_t *owner = NULL;
+
+ if (!use_ids) {
+ dbg_zload("Loading owner of new RRSet from wire.\n");
+ owner = read_dname_with_id(f);
+ }
+
+ if (!fread_wrapper(&rrset_type, sizeof(rrset_type), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("Zone load: rrset load: type: %u\n", rrset_type);
+ if (!fread_wrapper(&rrset_class, sizeof(rrset_class), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("Zone load: rrset class: type: %u\n", rrset_class);
+ if (!fread_wrapper(&rrset_ttl, sizeof(rrset_ttl), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("Zone load: rrset ttl: type: %u\n", rrset_ttl);
+ if (!fread_wrapper(&rdata_count, sizeof(rdata_count), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("Zone load: rrset load: rdata count: %u\n", rdata_count);
+ if (!fread_wrapper(&rrsig_count, sizeof(rrsig_count), 1, f)) {
+ return NULL;
+ }
+ dbg_zload("Zone load: rrset load: type: %u\n", rrset_type);
+
+ dbg_zload("Loading RRSet owned by: %s\n",
+ knot_dname_to_str(owner));
+
+ rrset = knot_rrset_new(owner, rrset_type, rrset_class, rrset_ttl);
+
+ if (!use_ids) {
+ /* Directly release if allocated locally. */
+ knot_dname_release(owner);
+ owner = 0;
+ }
+
+ dbg_zload("RRSet type: %d\n", rrset->type);
+
+ knot_rdata_t *tmp_rdata = NULL;
+
+ for (int i = 0; i < rdata_count; i++) {
+ tmp_rdata = knot_load_rdata(rrset->type, f,
+ id_array, use_ids);
+ if (tmp_rdata) {
+ knot_rrset_add_rdata(rrset, tmp_rdata);
+ } else {
+ knot_rrset_deep_free(&rrset, 0, 1, 1);
+ return NULL;
+ }
+ }
+
+ knot_rrset_t *tmp_rrsig = NULL;
+
+ dbg_zload("Reading: %d RRSIGs\n", rrsig_count);
+ if (rrsig_count) {
+ tmp_rrsig = knot_load_rrsig(f, id_array, use_ids);
+ if (!use_ids) {
+ knot_rrset_set_owner(tmp_rrsig, rrset->owner);
+ }
+ }
+
+ knot_rrset_set_rrsigs(rrset, tmp_rrsig);
+
+ dbg_zload("Finished loading RRSet %p\n", rrset);
+
+ return rrset;
+}
+
+/*!
+ * \brief Loads node from binary file.
+ *
+ * \param f File to read from.
+ *
+ * \return Pointer to created and read node on success, NULL otherwise.
+ */
+static knot_node_t *knot_load_node(FILE *f, knot_dname_t **id_array)
+{
+ uint8_t flags = 0;
+ knot_node_t *node = NULL;
+ uint32_t parent_id = 0;
+ uint32_t nsec3_node_id = 0;
+ uint16_t rrset_count = 0;
+ uint32_t dname_id = 0;
+
+ /* At the beginning of node - just dname_id !!!.*/
+ if (!fread_wrapper(&dname_id, sizeof(dname_id), 1, f)) {
+ return NULL;
+ }
+
+ if (!fread_wrapper(&parent_id, sizeof(parent_id), 1, f)) {
+ return NULL;
+ }
+
+ if (!fread_wrapper(&flags, sizeof(flags), 1, f)) {
+ return NULL;
+ }
+
+ if (!fread_wrapper(&nsec3_node_id, sizeof(nsec3_node_id), 1, f)) {
+ return NULL;
+ }
+
+ if (!fread_wrapper(&rrset_count, sizeof(rrset_count), 1, f)) {
+ return NULL;
+ }
+ knot_dname_t *owner = id_array[dname_id];
+
+ dbg_zload("Node owner id: %d\n", dname_id);
+ dbg_zload("Node owned by: %s\n", knot_dname_to_str(owner));
+ dbg_zload("Number of RRSets in a node: %d\n", rrset_count);
+
+ node = owner->node;
+
+ if (node == NULL) {
+ fprintf(stderr, "zone: Could not create node.\n");
+ return NULL;
+ }
+ /* XXX can it be 0, ever? I think not. */
+ if (nsec3_node_id != 0) {
+ knot_node_set_nsec3_node(node, id_array[nsec3_node_id]->node);
+ /* CLEANUP */
+// node->nsec3_node = id_array[nsec3_node_id]->node;
+ } else {
+ knot_node_set_nsec3_node(node, NULL);
+ /* CLEANUP */
+// node->nsec3_node = NULL;
+ }
+
+ /* Retain new owner while releasing replaced owner. */
+ knot_node_set_owner(node, owner);
+ node->flags = flags;
+
+ //XXX will have to be set already...canonical order should do it
+
+ if (parent_id != 0) {
+ knot_node_set_parent(node, id_array[parent_id]->node);
+ assert(knot_node_parent(node, 0) != NULL);
+ } else {
+ knot_node_set_parent(node, NULL);
+ }
+
+ knot_rrset_t *tmp_rrset;
+
+ for (int i = 0; i < rrset_count; i++) {
+ if ((tmp_rrset = knot_load_rrset(f, id_array, 1)) == NULL) {
+ knot_node_free(&node, 1, 0);
+ //TODO what else to free?
+ fprintf(stderr, "zone: Could not load rrset.\n");
+ return NULL;
+ }
+ /* Retain new owner while releasing replaced owner. */
+ knot_rrset_set_owner(tmp_rrset, node->owner);
+ if (tmp_rrset->rrsigs != NULL) {
+ knot_rrset_set_owner(tmp_rrset->rrsigs, node->owner);
+ }
+ if (knot_node_add_rrset(node, tmp_rrset, 0) < 0) {
+ fprintf(stderr, "zone: Could not add rrset.\n");
+ return NULL;
+ }
+ }
+ assert(node != NULL);
+ dbg_zload("Node loaded: %p\n", node);
+ return node;
+}
+
+/*!
+ * \brief Finds and sets wildcard child for given node's owner.
+ *
+ * \param zone Current zone.
+ * \param node Node to be used.
+ * \param nsec3 Is NSEC3 node.
+ */
+static void find_and_set_wildcard_child(knot_zone_contents_t *zone,
+ knot_node_t *node, int nsec3)
+{
+ knot_dname_t *chopped = knot_dname_left_chop(node->owner);
+ assert(chopped);
+ knot_node_t *wildcard_parent;
+ if (!nsec3) {
+ wildcard_parent =
+ knot_zone_contents_get_node(zone, chopped);
+ } else {
+ wildcard_parent =
+ knot_zone_contents_get_nsec3_node(zone, chopped);
+ }
+
+ /* Directly discard. */
+ knot_dname_free(&chopped);
+
+ assert(wildcard_parent); /* it *has* to be there */
+
+ knot_node_set_wildcard_child(wildcard_parent, node);
+}
+
+/*!
+ * \brief Checks if magic string at the beginning of the file is the same
+ * as defined.
+ *
+ * \param f File to read magic string from.
+ * \param MAGIC Magic string.
+ * \param MAGIC_LENGTH Length of magic string.
+ *
+ * \retval 1 if magic is the same.
+ * \retval 0 otherwise.
+ */
+static int knot_check_magic(FILE *f, const uint8_t* MAGIC, uint MAGIC_LENGTH)
+{
+ uint8_t tmp_magic[MAGIC_LENGTH];
+
+ if (!fread_wrapper(&tmp_magic, sizeof(uint8_t), MAGIC_LENGTH, f)) {
+ return 0;
+ }
+
+ for (int i = 0; i < MAGIC_LENGTH; i++) {
+ if (tmp_magic[i] != MAGIC[i]) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static unsigned long calculate_crc(FILE *f)
+{
+ crc_t crc = crc_init();
+ /* Get file size. */
+ fseek(f, 0L, SEEK_END);
+ size_t file_size = ftell(f);
+ fseek(f, 0L, SEEK_SET);
+
+ const size_t chunk_size = 1024;
+ /* read chunks of 1 kB */
+ size_t read_bytes = 0;
+ /* Prealocate chunk */
+ unsigned char *chunk = malloc(sizeof(unsigned char) * chunk_size);
+ CHECK_ALLOC_LOG(chunk, 0);
+ while ((file_size - read_bytes) > chunk_size) {
+ if (!fread_wrapper(chunk, sizeof(unsigned char), chunk_size, f)) {
+ free(chunk);
+ return 0;
+ }
+ crc = crc_update(crc, chunk,
+ sizeof(unsigned char) * chunk_size);
+ read_bytes += chunk_size;
+ }
+
+ /* Read the rest of the file */
+ if (!fread_wrapper(chunk, sizeof(unsigned char), file_size - read_bytes,
+ f)) {
+ free(chunk);
+ return 0;
+ }
+
+ crc = crc_update(crc, chunk,
+ sizeof(unsigned char) * (file_size - read_bytes));
+ free(chunk);
+
+ fseek(f, 0L, SEEK_SET);
+ return (unsigned long)crc_finalize(crc);
+}
+
+int knot_zload_open(zloader_t **dst, const char *filename)
+{
+ *dst = 0;
+ if (!dst || !filename) {
+ return KNOT_EBADARG;
+ }
+
+ fread_wrapper = fread_safe_from_file;
+
+ /* Open file for binary read. */
+ FILE *f = fopen(filename, "rb");
+ if (unlikely(!f)) {
+ dbg_zload("knot_zload_open: failed to open '%s'\n",
+ filename);
+ return KNOT_EFEWDATA; // No such file or directory (POSIX.1)
+ }
+
+ /* Calculate CRC and compare with filename.crc file */
+ unsigned long crc_calculated = calculate_crc(f);
+
+ /* Read CRC from filename.crc file */
+ char *crc_path =
+ malloc(sizeof(char) * (strlen(filename) + strlen(".crc") + 1));
+ if (unlikely(!crc_path)) {
+ fclose(f);
+ return KNOT_ENOMEM;
+ }
+ memset(crc_path, 0,
+ sizeof(char) * (strlen(filename) + strlen(".crc") + 1));
+
+ memcpy(crc_path, filename, sizeof(char) * strlen(filename));
+
+ crc_path = strcat(crc_path, ".crc");
+ FILE *f_crc = fopen(crc_path, "r");
+ if (unlikely(!f_crc)) {
+ dbg_zload("knot_zload_open: failed to open '%s'\n",
+ crc_path);
+ fclose(f);
+ free(crc_path);
+ return KNOT_ECRC;
+ }
+
+ unsigned long crc_from_file = 0;
+ if (fscanf(f_crc, "%lu\n", &crc_from_file) != 1) {
+ dbg_zload("knot_zload_open: could not read "
+ "CRC from file '%s'\n",
+ crc_path);
+ fclose(f_crc);
+ fclose(f);
+ free(crc_path);
+ return KNOT_ERROR;
+ }
+ free(crc_path);
+ fclose(f_crc);
+
+ /* Compare calculated and read CRCs. */
+ if (crc_from_file != crc_calculated) {
+ dbg_zload("knot_zload_open: CRC failed for "
+ "file '%s'\n",
+ filename);
+ fclose(f);
+ return KNOT_ECRC;
+ }
+
+ /* Check magic sequence. */
+ static const uint8_t MAGIC[MAGIC_LENGTH] = MAGIC_BYTES;
+ if (!knot_check_magic(f, MAGIC, MAGIC_LENGTH)) {
+ fclose(f);
+ dbg_zload("knot_zload_open: magic bytes "
+ "in don't match '%*s' "
+ "(%s)\n",
+ (int)MAGIC_LENGTH, (const char*)MAGIC, filename);
+ return KNOT_EMALF; // Illegal byte sequence (POSIX.1, C99)
+ }
+
+ /* Read source file length. */
+ uint32_t sflen = 0;
+ if (!fread_wrapper(&sflen, 1, sizeof(uint32_t), f)) {
+ dbg_zload("knot_zload_open: failed to read "
+ "sfile length\n");
+ fclose(f);
+ return KNOT_ERROR;
+ }
+
+ /* Read source file. */
+ char *sfile = malloc(sflen);
+ if (!sfile) {
+ dbg_zload("knot_zload_open: invalid sfile "
+ "length %u\n", sflen);
+ fclose(f);
+ return KNOT_ENOMEM;
+ }
+ if (!fread_wrapper(sfile, 1, sflen, f)) {
+ dbg_zload("knot_zload_open: failed to read %uB "
+ "source file\n",
+ sflen);
+ free(sfile);
+ fclose(f);
+ return KNOT_ERROR;
+ }
+
+ /* Allocate new loader. */
+ zloader_t *zl = malloc(sizeof(zloader_t));
+ if (!zl) {
+ free(sfile);
+ fclose(f);
+ return KNOT_ENOMEM;
+ }
+
+ dbg_zload("knot_zload_open: opened '%s' as fp %p "
+ "(source is '%s')\n",
+ filename, f, sfile);
+ zl->filename = strdup(filename);
+ zl->source = sfile;
+ zl->fp = f;
+ *dst = zl;
+
+ return KNOT_EOK;
+}
+
+static void cleanup_id_array(knot_dname_t **id_array,
+ const uint from, const uint to)
+{
+ for (uint i = from; i < to; i++) {
+ knot_dname_release(id_array[i]);
+ }
+
+ free(id_array);
+}
+
+//static knot_dname_table_t *create_dname_table(FILE *f, uint max_id)
+//{
+// if (f == NULL ) {
+// return NULL;
+// }
+
+// if (!fread_wrapper(&max_id, sizeof(max_id), 1, f)) {
+// return NULL;
+// }
+
+// knot_dname_table_t *dname_table = knot_dname_table_new();
+// if (dname_table == NULL) {
+// return NULL;
+// }
+
+// /* Create nodes containing dnames. */
+// for (uint i = 1; i < max_id; i++) {
+// knot_dname_t *loaded_dname = read_dname_with_id(f);
+// if (loaded_dname == NULL) {
+// knot_dname_table_deep_free(&dname_table);
+// return NULL;
+// }
+// if (knot_dname_table_add_dname(dname_table,
+// loaded_dname) != KNOT_EOK) {
+
+// }
+// }
+
+// return dname_table;
+//}
+
+static knot_dname_table_t *create_dname_table_from_array(
+ knot_dname_t **array, uint max_id)
+{
+ if (array == NULL) {
+ /* should I set errno or what ... ? */
+ dbg_zload("No array passed\n");
+ return NULL;
+ }
+
+ assert(array[0] == NULL);
+
+ knot_dname_table_t *ret = knot_dname_table_new();
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ /* Table will have max_id entries */
+ for (uint i = 1; i < max_id; i++) {
+ assert(array[i]);
+ if (knot_dname_table_add_dname(ret,
+ array[i]) != KNOT_EOK) {
+ dbg_zload("Could not add: %s\n",
+ knot_dname_to_str(array[i]));
+ knot_dname_table_deep_free(&ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+static knot_dname_t **create_dname_array(FILE *f, uint max_id)
+{
+ if (f == NULL) {
+ return NULL;
+ }
+
+ knot_dname_t **array =
+ malloc(sizeof(knot_dname_t *) * ( max_id + 1));
+ memset(array, 0, sizeof(knot_dname_t *) * (max_id + 1));
+ if (array == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ for (uint i = 0; i < max_id - 1; i++) {
+ knot_dname_t *read_dname = read_dname_with_id(f);
+ if (read_dname == NULL) {
+ cleanup_id_array(array, 0, i);
+ return NULL;
+ }
+
+ if (read_dname->id < max_id) {
+
+ /* Create new node from dname. */
+ read_dname->node = knot_node_new(read_dname, NULL, 0);
+
+ if (read_dname->node == NULL) {
+ ERR_ALLOC_FAILED;
+
+ /* Release read dname. */
+ knot_dname_release(read_dname);
+ cleanup_id_array(array, 0, i);
+ return NULL;
+ }
+
+ /* Store reference to dname in array. */
+ array[read_dname->id] = read_dname;
+ } else {
+ /* Release read dname. */
+ knot_dname_release(read_dname);
+ cleanup_id_array(array, 0, i);
+ return NULL;
+ }
+
+ }
+
+ return array;
+}
+
+knot_zone_t *knot_zload_load(zloader_t *loader)
+{
+ dbg_zload("Loading zone, loader: %p\n", loader);
+ if (!loader) {
+ dbg_zload("NULL loader!\n");
+ return NULL;
+ }
+
+ fread_wrapper = fread_safe_from_file;
+
+ FILE *f = loader->fp;
+
+ knot_node_t *tmp_node;
+
+ /* Load the dname table. */
+ /* CLEANUP */
+// const knot_dname_table_t *dname_table =
+// create_dname_table(f, total_dnames);
+// if (dname_table == NULL) {
+// return NULL;
+// }
+
+ uint32_t node_count;
+ uint32_t nsec3_node_count;
+ uint32_t auth_node_count;
+
+ if (!fread_wrapper(&node_count, sizeof(node_count), 1, f)) {
+ dbg_zload("wrong read!\n");
+ return NULL;
+ }
+
+ if (!fread_wrapper(&nsec3_node_count, sizeof(nsec3_node_count), 1, f)) {
+ dbg_zload("wrong read!\n");
+ return NULL;
+ }
+ if (!fread_wrapper(&auth_node_count,
+ sizeof(auth_node_count), 1, f)) {
+ dbg_zload("wrong read!\n");
+ return NULL;
+ }
+ dbg_zload("authoritative nodes: %u\n", auth_node_count);
+
+ dbg_zload("loading %u nodes\n", node_count);
+
+ uint32_t total_dnames = 0;
+ /* First, read number of dnames in dname table. */
+ if (!fread_wrapper(&total_dnames, sizeof(total_dnames), 1, f)) {
+ return NULL;
+ }
+
+ dbg_zload("total dname count: %d\n", total_dnames);
+
+ /* Create id array. */
+ knot_dname_t **id_array = create_dname_array(f, total_dnames);
+ if (id_array == NULL) {
+ return NULL;
+ }
+
+ knot_dname_table_t *dname_table =
+ create_dname_table_from_array(id_array, total_dnames);
+ if (dname_table == NULL) {
+ ERR_ALLOC_FAILED;
+ cleanup_id_array(id_array, 1, total_dnames);
+ return NULL;
+ }
+
+ knot_node_t *apex = knot_load_node(f, id_array);
+
+ if (!apex) {
+ fprintf(stderr, "zone: Could not load apex node (in %s)\n",
+ loader->filename);
+ cleanup_id_array(id_array, 1,
+ node_count + nsec3_node_count + 1);
+ return NULL;
+ }
+
+ dbg_zload("Apex node loaded: %p\n", apex);
+
+ knot_zone_t *zone = knot_zone_new(apex, auth_node_count, 0);
+ if (zone == NULL) {
+ cleanup_id_array(id_array, 1,
+ node_count + nsec3_node_count + 1);
+ dbg_zload("Failed to create new zone from apex!\n");
+ return NULL;
+ }
+
+ knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+ assert(contents);
+
+ /* Assign dname table to the new zone. */
+ contents->dname_table = dname_table;
+
+ /* CLEANUP */
+// apex->prev = NULL;
+ knot_node_set_previous(apex, NULL);
+
+ knot_node_t *last_node = 0;
+
+ last_node = apex;
+
+ for (uint i = 1; i < node_count; i++) {
+ tmp_node = knot_load_node(f, id_array);
+
+ if (tmp_node != NULL) {
+ if (knot_zone_contents_add_node(contents, tmp_node,
+ 0, 0, 0) != 0) {
+ fprintf(stderr, "!! cannot add node\n");
+ continue;
+ }
+ if (knot_dname_is_wildcard(tmp_node->owner)) {
+ find_and_set_wildcard_child(contents,
+ tmp_node, 0);
+ }
+
+ knot_node_set_previous(tmp_node, last_node);
+ /* CLEANUP */
+// tmp_node->prev = last_node;
+
+ if (tmp_node->rrset_count &&
+ (knot_node_is_deleg_point(tmp_node) ||
+ !knot_node_is_non_auth(tmp_node))) {
+ last_node = tmp_node;
+ }
+
+ } else {
+ fprintf(stderr, "zone: Node error (in %s).\n",
+ loader->filename);
+ }
+ }
+
+ assert(knot_node_previous(knot_zone_contents_apex(contents), 0)
+ == NULL);
+
+ knot_node_set_previous(knot_zone_contents_get_apex(contents),
+ last_node);
+
+ dbg_zload("loading %u nsec3 nodes\n", nsec3_node_count);
+
+ knot_node_t *nsec3_first = NULL;
+
+ if (nsec3_node_count > 0) {
+ nsec3_first = knot_load_node(f, id_array);
+
+ assert(nsec3_first != NULL);
+
+ if (knot_zone_contents_add_nsec3_node(contents, nsec3_first, 0, 0, 0)
+ != 0) {
+ fprintf(stderr, "!! cannot add first nsec3 node, "
+ "exiting.\n");
+ knot_zone_deep_free(&zone, 0);
+ free(id_array);
+ /* TODO this will leak dnames from id_array that were
+ * not assigned. */
+ return NULL;
+ }
+
+ knot_node_set_previous(nsec3_first, NULL);
+ /* CLEANUP */
+// nsec3_first->prev = NULL;
+
+ last_node = nsec3_first;
+ }
+
+ for (uint i = 1; i < nsec3_node_count; i++) {
+ tmp_node = knot_load_node(f, id_array);
+
+ if (tmp_node != NULL) {
+ if (knot_zone_contents_add_nsec3_node(contents,
+ tmp_node, 0, 0, 0) != 0) {
+ fprintf(stderr, "!! cannot add nsec3 node\n");
+ continue;
+ }
+
+ knot_node_set_previous(tmp_node, last_node);
+ /* CLEANUP */
+// tmp_node->prev = last_node;
+
+ last_node = tmp_node;
+ } else {
+ fprintf(stderr, "zone: Node error (in %s).\n",
+ loader->filename);
+ }
+ }
+
+ if (nsec3_node_count) {
+ assert(knot_node_previous(nsec3_first, 0) == NULL);
+ knot_node_set_previous(nsec3_first, last_node);
+ /* CLEANUP */
+// nsec3_first->prev = last_node;
+ }
+
+ /* ID array is now useless */
+ for (uint i = 1; i < total_dnames; i++) {
+ /* Added to table, may discard now. */
+ knot_dname_release(id_array[i]);
+ }
+ free(id_array);
+
+ dbg_zload("zone loaded, returning: %p\n", zone);
+ return zone;
+}
+
+int knot_zload_needs_update(zloader_t *loader)
+{
+ if (!loader) {
+ return 1;
+ }
+
+ /* Check if the source still exists. */
+ struct stat st_src;
+ if (stat(loader->source, &st_src) != 0) {
+ return 1;
+ }
+
+ /* Check if the compiled file still exists. */
+ struct stat st_bin;
+ if (stat(loader->filename, &st_bin) != 0) {
+ return 1;
+ }
+
+ /* Compare the mtime of the source and file. */
+ /*! \todo Inspect types on Linux. */
+ if (timet_cmp(st_bin.st_mtime, st_src.st_mtime) < 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void knot_zload_close(zloader_t *loader)
+{
+ if (!loader) {
+ return;
+ }
+
+ free(loader->filename);
+ free(loader->source);
+ fclose(loader->fp);
+ free(loader);
+}
+
+int knot_zload_rrset_deserialize(knot_rrset_t **rrset,
+ uint8_t *stream, size_t *size)
+{
+ if (stream == NULL || size == 0) {
+ return KNOT_EBADARG;
+ }
+
+ fread_wrapper = read_from_stream;
+
+ knot_zload_stream = stream;
+ knot_zload_stream_remaining = knot_zload_stream_size = *size;
+
+ knot_rrset_t *ret = knot_load_rrset(NULL, NULL, 0);
+
+ if (ret == NULL) {
+ knot_zload_stream = NULL;
+ knot_zload_stream_remaining = 0;
+ knot_zload_stream_size = 0;
+ return KNOT_EMALF;
+ }
+
+ *size = knot_zload_stream_remaining;
+ *rrset = ret;
+
+ knot_zload_stream = NULL;
+ knot_zload_stream_remaining = 0;
+ knot_zload_stream_size = 0;
+
+ return KNOT_EOK;
+}
+
diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h
new file mode 100644
index 0000000..2fe318f
--- /dev/null
+++ b/src/knot/zone/zone-load.h
@@ -0,0 +1,104 @@
+/* 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-load.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Loader of previously parsed zone
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_ZONELOAD_H_
+#define _KNOT_ZONELOAD_H_
+
+#include <stdio.h>
+
+#include "libknot/zone/zone.h"
+
+/*!
+ * \brief Zone loader structure.
+ */
+typedef struct zloader_t
+{
+ char *filename; /*!< Compiled zone filename. */
+ char *source; /*!< Zone source file. */
+ FILE *fp; /*!< Open filepointer to compiled zone. */
+
+} zloader_t;
+
+/*!
+ * \brief Initializes zone loader from file..
+ *
+ * \param filename File containing the compiled zone.
+ * \param loader Will create new loader in *loader.
+ *
+ * \retval Initialized loader on success.
+ * \retval NULL on error.
+ */
+int knot_zload_open(zloader_t **loader, const char *filename);
+
+/*!
+ * \brief Loads zone from a compiled and serialized zone file.
+ *
+ * \param loader Zone loader instance.
+ *
+ * \retval Loaded zone on success.
+ * \retval NULL otherwise.
+ */
+knot_zone_t *knot_zload_load(zloader_t *loader);
+
+/*!
+ * \brief Checks whether the compiled zone needs a recompilation.
+ *
+ * \param loader Zone loader instance.
+ *
+ * \retval 1 is if needs to be recompiled.
+ * \retval 0 if it is up to date.
+ */
+int knot_zload_needs_update(zloader_t *loader);
+
+
+/*!
+ * \brief Free zone loader.
+ *
+ * \param loader Zone loader instance.
+ */
+void knot_zload_close(zloader_t *loader);
+
+/*!
+ * \brief Loads RRSet serialized by knot_zdump_rrset_serialize().
+ *
+ * \param stream Stream containing serialized RRSet.
+ * \param size Size of stream. This variable will contain remaining length of
+ * stream, once the function has ended.
+ * \param rrset Place for created RRSet.
+ *
+ * \note If RRSet contains RRSIGs, their owners are not copies, but only links
+ * to the owner of RRSet. All RDATA dnames are copied.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EBADAG on wrong arguments.
+ * \retval KNOT_EMALF when stream is malformed.
+ */
+int knot_zload_rrset_deserialize(knot_rrset_t **rrset,
+ uint8_t *stream, size_t *size);
+
+#endif /* _KNOTD_ZONELOAD_H_ */
+
+/*! @} */
diff --git a/src/knotc.8 b/src/knotc.8
new file mode 100644
index 0000000..f2e7e40
--- /dev/null
+++ b/src/knotc.8
@@ -0,0 +1,63 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
+.TH KNOT "1" "November 2011" "Knot DNS, version 0.8" "User Commands"
+.SH NAME
+Knot \- manual page for Knot DNS, version 0.8
+.SH SYNOPSIS
+.B knotc
+[\fIparameters\fR] \fIstart|stop|restart|reload|running|compile\fR
+.SH DESCRIPTION
+.SS "Parameters:"
+.HP
+\fB\-c\fR [file], \fB\-\-config\fR=\fI[file]\fR Select configuration file.
+.TP
+\fB\-j\fR [num], \fB\-\-jobs\fR=\fI[num]\fR
+Number of parallel tasks to run (only for 'compile').
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+Force operation \- override some checks.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Verbose mode \- additional runtime information.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Print knot server version.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR
+Wait for the server to finish start/stop operations.
+.TP
+\fB\-i\fR, \fB\-\-interactive\fR
+Interactive mode (do not daemonize).
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help and usage.
+.SS "Actions:"
+.TP
+start
+Start knot server zone (no\-op if running).
+.TP
+stop
+Stop knot server (no\-op if not running).
+.TP
+restart
+Stops and then starts knot server.
+.TP
+reload
+Reload knot configuration and compiled zones.
+.TP
+running
+check if server is running.
+.TP
+compile
+Compile zone file.
+.SH "SEE ALSO"
+The full documentation for
+.B Knot
+is maintained as a Texinfo manual. If the
+.B info
+and
+.B Knot
+programs are properly installed at your site, the command
+.IP
+.B info Knot
+.PP
+should give you access to the complete manual.
diff --git a/src/knotd.8 b/src/knotd.8
new file mode 100644
index 0000000..3275e9b
--- /dev/null
+++ b/src/knotd.8
@@ -0,0 +1,35 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.4.
+.TH KNOT "1" "November 2011" "Knot DNS, version 0.8" "User Commands"
+.SH NAME
+Knot \- manual page for Knot DNS, version 0.8
+.SH SYNOPSIS
+.B knotd
+[\fIparameters\fR]
+.SH DESCRIPTION
+.SS "Parameters:"
+.HP
+\fB\-c\fR, \fB\-\-config\fR [file] Select configuration file.
+.TP
+\fB\-d\fR, \fB\-\-daemonize\fR
+Run server as a daemon.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Verbose mode \- additional runtime information.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Print version of the server.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print help and usage.
+.SH "SEE ALSO"
+The full documentation for
+.B Knot
+is maintained as a Texinfo manual. If the
+.B info
+and
+.B Knot
+programs are properly installed at your site, the command
+.IP
+.B info Knot
+.PP
+should give you access to the complete manual.
diff --git a/src/libknot/common.h b/src/libknot/common.h
new file mode 100644
index 0000000..9b2d8ae
--- /dev/null
+++ b/src/libknot/common.h
@@ -0,0 +1,105 @@
+/*!
+ * \file common.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Common macros, includes and utilities.
+ *
+ * \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/>.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBLDNS
+#define TEST_WITH_LDNS
+#endif
+
+#ifndef _KNOT_COMMON_H_
+#define _KNOT_COMMON_H_
+
+#define KNOT_NAME "lib" PACKAGE_NAME // Project name
+#define KNOT_VER PACKAGE_VERSION // 0xMMIIRR (MAJOR,MINOR,REVISION)
+
+#ifndef UINT_DEFINED
+typedef unsigned int uint; /*!< \brief Unsigned. */
+#define UINT_DEFINED
+#endif
+
+/*! \brief If defined, zone structures will use hash table for lookup. */
+#define USE_HASH_TABLE
+
+/*! \brief Eliminate compiler warning with unused parameters. */
+#define UNUSED(param) (void)(param)
+
+/*! \brief Type-safe minimum macro. */
+#define MIN(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; })
+
+/*! \brief Type-safe maximum macro. */
+#define MAX(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
+
+/* Optimisation macros. */
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+/* Optimisation macros. */
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+/*! \todo Refactor theese. We should have an allocator function handling this.*/
+#ifndef ERR_ALLOC_FAILED
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d (%s ver.%s)\n", \
+ __FILE__, __LINE__, KNOT_NAME, KNOT_VER)
+#endif
+
+#ifndef CHECK_ALLOC_LOG
+#define CHECK_ALLOC_LOG(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ ERR_ALLOC_FAILED; \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#ifndef CHECK_ALLOC
+#define CHECK_ALLOC(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#endif /* _KNOT_COMMON_H_ */
+
+/*! @} */
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
new file mode 100644
index 0000000..4249763
--- /dev/null
+++ b/src/libknot/consts.h
@@ -0,0 +1,108 @@
+/*!
+ * \file consts.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Contains some DNS-related constants.
+ *
+ * \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_CONSTS_H_
+#define _KNOT_CONSTS_H_
+
+#include <stdint.h>
+#include "util/descriptor.h"
+
+/*
+ * OPCODEs
+ */
+typedef enum knot_opcode {
+ KNOT_OPCODE_QUERY = 0, /* a standard query (QUERY) */
+ KNOT_OPCODE_IQUERY = 1, /* an inverse query (IQUERY) */
+ KNOT_OPCODE_STATUS = 2, /* a server status request (STATUS) */
+ KNOT_OPCODE_NOTIFY = 4, /* NOTIFY */
+ KNOT_OPCODE_UPDATE = 5, /* Dynamic update */
+ KNOT_OPCODE_OFFSET = 14
+} knot_opcode_t;
+
+/*!
+ * \brief Query types (internal use only).
+ *
+ * This type encompasses the different query types distinguished by both the
+ * OPCODE and the QTYPE.
+ */
+typedef enum knot_packet_type {
+ KNOT_QUERY_NORMAL, /*!< Normal query. */
+ KNOT_QUERY_AXFR, /*!< Request for AXFR transfer. */
+ KNOT_QUERY_IXFR, /*!< Request for IXFR transfer. */
+ KNOT_QUERY_NOTIFY, /*!< NOTIFY query. */
+ KNOT_QUERY_UPDATE, /*!< Dynamic update. */
+ KNOT_RESPONSE_NORMAL, /*!< Normal response. */
+ KNOT_RESPONSE_AXFR, /*!< AXFR transfer response. */
+ KNOT_RESPONSE_IXFR, /*!< IXFR transfer response. */
+ KNOT_RESPONSE_NOTIFY /*!< NOTIFY response. */
+} knot_packet_type_t;
+
+/*
+ * RCODEs
+ */
+typedef enum knot_rcode {
+ KNOT_RCODE_NOERROR = 0, /* No error condition */
+ KNOT_RCODE_FORMERR = 1, /* Format error */
+ KNOT_RCODE_SERVFAIL = 2, /* Server failure */
+ KNOT_RCODE_NXDOMAIN = 3, /* Name Error */
+ KNOT_RCODE_NOTIMPL = 4, /* Not implemented */
+ KNOT_RCODE_REFUSED = 5, /* Refused */
+ KNOT_RCODE_YXDOMAIN = 6, /* name should not exist */
+ KNOT_RCODE_YXRRSET = 7, /* rrset should not exist */
+ KNOT_RCODE_NXRRSET = 8, /* rrset does not exist */
+ KNOT_RCODE_NOTAUTH = 9, /* server not authoritative */
+ KNOT_RCODE_NOTZONE = 10, /* name not inside zone */
+} knot_rcode_t;
+
+typedef enum knot_tsig_rcode {
+ KNOT_TSIG_RCODE_BADSIG = 16,
+ KNOT_TSIG_RCODE_BADKEY = 17,
+ KNOT_TSIG_RCODE_BADTIME = 18
+} knot_tsig_rcode_t;
+
+/*
+ * CLASSes
+ */
+//typedef enum knot_class {
+// KNOT_CLASS_IN = 1, /* Class IN */
+// KNOT_CLASS_CS = 2, /* Class CS */
+// KNOT_CLASS_CH = 3, /* Class CHAOS */
+// KNOT_CLASS_HS = 4, /* Class HS */
+// KNOT_CLASS_NONE = 254, /* Class NONE rfc2136 */
+// KNOT_CLASS_ANY = 255 /* Class ANY */
+//} knot_class_t;
+
+/*
+ * Other
+ */
+typedef enum knot_const {
+ KNOT_MAX_DNAME_LENGTH = 255,
+ KNOT_MAX_DNAME_LABELS = 127 // 1-char labels
+} knot_const_t;
+
+#endif /* _KNOT_CONSTS_H_ */
+
+/*! @} */
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
new file mode 100644
index 0000000..869b342
--- /dev/null
+++ b/src/libknot/dname.c
@@ -0,0 +1,1070 @@
+/* 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h> // tolower()
+
+#include "common.h"
+#include "util/error.h"
+#include "dname.h"
+#include "consts.h"
+#include "util/tolower.h"
+#include "util/debug.h"
+#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()
+{
+ 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 (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
+}
+
+/*----------------------------------------------------------------------------*/
+/* Non-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)
+{
+ if (size > KNOT_MAX_DNAME_LENGTH) {
+ return -1;
+ }
+
+ uint wire_size;
+ int root = (*name == '.' && size == 1);
+ // root => different size
+ if (root) {
+ wire_size = 1;
+ } else {
+ wire_size = size + 1;
+ }
+
+ uint8_t *wire;
+ uint8_t labels[KNOT_MAX_DNAME_LABELS];
+ short label_count = 0;
+
+ // signed / unsigned issues??
+ wire = (uint8_t *)malloc(wire_size * sizeof(uint8_t));
+ if (wire == NULL) {
+ return -1;
+ }
+
+ dbg_dname("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);
+ }
+
+ 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 == '.') {
+ dbg_dname("Position %zd (%p): "
+ "label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
+ *label_start = label_length;
+ labels[label_count++] = label_start - wire;
+ label_start = w;
+ label_length = 0;
+ } else {
+ assert(w - wire < wire_size);
+ dbg_dname("Position %zd (%p): character: %c\n",
+ w - wire, w, *ch);
+ *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("Position %zd (%p): character: (null)\n",
+ w - wire, w);
+ *w = 0;
+ } else { // otherwise we did not save the last label length
+ dbg_dname("Position %zd (%p): "
+ "label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
+ *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') {
+ labels[label_count++] = pos - name;
+ pos += *pos + 1;
+ }
+
+ // TODO: how to check if the domain name has right format?
+ if (pos - name > size || *pos != '\0') {
+ dbg_dname("Wrong wire format of domain name!\n");
+ dbg_dname("Position: %d, character: %d, expected"
+ " size: %d\n", pos - name, *pos, size);
+ 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(
+ char *name1 = knot_dname_to_str(d1);
+ char *name2 = knot_dname_to_str(d2);
+
+ dbg_dname("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("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("Label counts: %d and %d\n", l1, l2);
+ assert(l1 >= 0);
+ assert(l2 >= 0);
+
+ // compare labels from last to first
+ while (l1 > 0 && l2 > 0) {
+ dbg_dname("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname(" at offsets: %d and %d\n",
+ d1->labels[l1 - 1], d2->labels[l2 - 1]);
+ 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;
+ }
+
+ if (l1 > 0 && l2 == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*! \brief Destructor for reference counter. */
+static void knot_dname_dtor(struct ref_t *p)
+{
+ knot_dname_t *dname = (knot_dname_t *)p;
+ knot_dname_free(&dname);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new()
+{
+ knot_dname_t *dname = knot_dname_alloc();
+ dname->name = NULL;
+ dname->size = 0;
+ dname->node = NULL;
+ dname->labels = NULL;
+ dname->label_count = -1;
+ dname->id = 0;
+
+ /* Initialize reference counting. */
+ ref_init(&dname->ref, knot_dname_dtor);
+
+ /* Set reference counter to 1, caller should release it after use. */
+ knot_dname_retain(dname);
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new_from_str(const char *name, uint size,
+ struct knot_node *node)
+{
+ if (name == NULL || size == 0) {
+ return NULL;
+ }
+
+// knot_dname_t *dname = knot_dname_alloc();
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ knot_dname_str_to_wire(name, size, dname);
+ dbg_dname("Created dname with size: %d\n", dname->size);
+ dbg_dname("Label offsets: ");
+ for (int i = 0; i < dname->label_count; ++i) {
+ dbg_dname("%d, ", dname->labels[i]);
+ }
+ dbg_dname("\n");
+
+ if (dname->size <= 0) {
+ fprintf(stderr, "Could not parse domain name "
+ "from string: '%.*s'\n", size, name);
+ }
+ assert(dname->name != NULL);
+
+ dname->node = node;
+ dname->id = 0;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//int knot_dname_from_wire(knot_dname_t *dname, const uint8_t *name,
+// uint size)
+//{
+// int i = 0;
+// uint8_t labels[KNOT_MAX_DNAME_LABELS];
+// int label_i = 0;
+
+// while (name[i] != 0) {
+// labels[label_i++] = i;
+// uint8_t label_length = name[i];
+// if (i + label_length >= size) {
+// return -2;
+// }
+// for (int j = 1; j <= label_length; ++j) {
+// }
+// }
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
+ struct knot_node *node)
+{
+ if (name == NULL) { /* && size != 0) { !OS: Nerozumjaju */
+ dbg_dname("No name given!\n");
+ return NULL;
+ }
+
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ 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) {
+ knot_dname_free(&dname);
+ return NULL;
+ }
+
+ dname->node = node;
+ dname->id = 0;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
+ size_t *pos, size_t size,
+ knot_node_t *node)
+{
+ 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) {
+ labels[l] = i;
+ dbg_dname("Next label (%d.) position: %zu\n", l, i);
+
+ if (knot_wire_is_pointer(wire + p)) {
+ // pointer.
+// printf("Pointer.\n");
+ p = knot_wire_get_pointer(wire + p);
+ 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);
+// printf("Label, length: %u.\n", 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;
+ }
+
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
+ if (dname->name == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&dname);
+ return NULL;
+ }
+
+ memcpy(dname->name, name, i + 1);
+ dname->size = i + 1;
+
+ dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
+ if (dname->labels == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&dname);
+ return NULL;
+ }
+ memcpy(dname->labels, labels, l + 1);
+
+ dname->label_count = l;
+
+ dname->node = node;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_from_wire(const uint8_t *name, uint size,
+ struct knot_node *node, knot_dname_t *target)
+{
+ if (name == NULL || target == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ memcpy(target->name, name, size);
+ target->size = size;
+ target->node = node;
+ target->id = 0;
+
+ return knot_dname_find_labels(target, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname)
+{
+ return knot_dname_new_from_wire(dname->name, dname->size,
+ dname->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+char *knot_dname_to_str(const knot_dname_t *dname)
+{
+ if (!dname || dname->size == 0) {
+ return 0;
+ }
+
+ char *name;
+
+ // root => special treatment
+ if (dname->size == 1) {
+ assert(dname->name[0] == 0);
+ name = (char *)malloc(2 * sizeof(char));
+ name[0] = '.';
+ name[1] = '\0';
+ return name;
+ }
+
+ name = (char *)malloc(dname->size * sizeof(char));
+ if (name == NULL) {
+ return NULL;
+ }
+
+ uint8_t *w = dname->name;
+ char *ch = name;
+ int i = 0;
+
+ do {
+ assert(*w != 0);
+ int label_size = *(w++);
+ // copy the label
+ memcpy(ch, w, label_size);
+ i += label_size;
+ ch += label_size;
+ w += label_size;
+ if (w - dname->name < dname->size) { // another label following
+ *(ch++) = '.';
+ ++i;
+ }
+ } while (i < dname->size - 1);
+
+ *ch = 0;
+ assert(ch - name == dname->size - 1);
+
+ return name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_to_lower(knot_dname_t *dname)
+{
+ if (dname == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < dname->size; ++i) {
+ dname->name[i] = knot_tolower(dname->name[i]);
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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_EBADARG;
+ }
+
+ for (int i = 0; i < dname->size; ++i) {
+ name[i] = knot_tolower(dname->name[i]);
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const uint8_t *knot_dname_name(const knot_dname_t *dname)
+{
+ return dname->name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint knot_dname_size(const knot_dname_t *dname)
+{
+ return dname->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_dname_id(const knot_dname_t *dname)
+{
+ return dname->id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_dname_size_part(const knot_dname_t *dname, int labels)
+{
+ assert(labels < dname->label_count);
+ assert(dname->labels != NULL);
+ return (dname->labels[labels]);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const struct knot_node *knot_dname_node(const knot_dname_t *dname,
+ int check_version)
+
+{
+ if (check_version) {
+ return knot_node_current(dname->node);
+ } else {
+ return dname->node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+struct knot_node *knot_dname_get_node(knot_dname_t *dname,
+ int check_version)
+{
+ if (check_version) {
+ return knot_node_get_current(dname->node);
+ } else {
+ return dname->node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_set_node(knot_dname_t *dname, knot_node_t *node)
+{
+ dname->node = node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_update_node(knot_dname_t *dname)
+{
+ knot_node_update_ref(&dname->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_fqdn(const knot_dname_t *dname)
+{
+ return (dname->name[dname->size - 1] == '\0');
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname)
+{
+ knot_dname_t *parent = knot_dname_new();
+ if (parent == NULL) {
+ return NULL;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ memcpy(parent->name, &dname->name[dname->name[0] + 1], parent->size);
+
+ short first_label_length = dname->labels[1];
+
+ for (int i = 0; i < dname->label_count - 1; ++i) {
+ parent->labels[i] = dname->labels[i + 1] - first_label_length;
+ }
+ parent->label_count = dname->label_count - 1;
+
+ return parent;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_left_chop_no_copy(knot_dname_t *dname)
+{
+ // copy the name
+ short first_label_length = dname->labels[1];
+
+ if (dname->label_count > 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;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_subdomain(const knot_dname_t *sub,
+ const knot_dname_t *domain)
+{
+dbg_dname_exec(
+ char *name1 = knot_dname_to_str(sub);
+ char *name2 = knot_dname_to_str(domain);
+
+ dbg_dname("Checking if %s is subdomain of %s\n",
+ name1, name2);
+ free(name1);
+ free(name2);
+);
+
+ if (sub == domain) {
+ return 0;
+ }
+
+ // 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;
+ }
+
+ int l1 = sub->label_count;
+ int l2 = domain->label_count;
+
+ dbg_dname("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
+ }
+
+ // compare labels from last to first
+ while (l1 > 0 && l2 > 0) {
+ dbg_dname("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname(" at offsets: %d and %d\n",
+ sub->labels[l1 - 1], domain->labels[l2 - 1]);
+ // 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);
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_wildcard(const knot_dname_t *dname)
+{
+ return (dname->size >= 2
+ && dname->name[0] == 1
+ && dname->name[1] == '*');
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_matched_labels(const knot_dname_t *dname1,
+ const knot_dname_t *dname2)
+{
+ int l1 = dname1->label_count;
+ int l2 = dname2->label_count;
+
+ // 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;
+ }
+ }
+
+ return matched;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_label_count(const knot_dname_t *dname)
+{
+ return dname->label_count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_dname_label_size(const knot_dname_t *dname, int i)
+{
+// printf("Returning size of %d. label starting on %d\n",
+// i, dname->labels[i]);
+// printf("Label count: %d, size of %d. label: %d, size of %d.label: %d\n",
+// dname->label_count, i, dname->labels[i], i + 1,
+// dname->labels[i+1]);
+// printf("Size from the name: %u\n", dname->name[dname->labels[i]]);
+// printf("Size from label offsets: %u\n",
+// dname->labels[i + 1] - dname->labels[i]);
+
+ assert(i >= 0);
+ assert(dname->size == 1 || i + 1 == dname->label_count
+ || dname->labels[i + 1] - dname->labels[i] - 1
+ == dname->name[dname->labels[i]]);
+ return dname->name[dname->labels[i]];
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname,
+ int size,
+ const knot_dname_t *suffix)
+{
+dbg_dname_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_dname("Replacing suffix of name %s, size %d with ", name,
+ size);
+ free(name);
+ name = knot_dname_to_str(suffix);
+ dbg_dname("%s (size %d)\n", name, suffix->size);
+ free(name);
+);
+ knot_dname_t *res = knot_dname_new();
+ CHECK_ALLOC(res, NULL);
+
+ res->size = dname->size - size + suffix->size;
+
+ dbg_dname("Allocating %d bytes...\n", res->size);
+ res->name = (uint8_t *)malloc(res->size);
+ if (res->name == NULL) {
+ knot_dname_free(&res);
+ return NULL;
+ }
+
+ dbg_dname_hex((char *)res->name, res->size);
+
+ dbg_dname("Copying %d bytes from the original name.\n",
+ dname->size - size);
+ memcpy(res->name, dname->name, dname->size - size);
+ dbg_dname_hex((char *)res->name, res->size);
+
+ dbg_dname("Copying %d bytes from the suffix.\n", suffix->size);
+ memcpy(res->name + dname->size - size, suffix->name, suffix->size);
+
+ dbg_dname_hex((char *)res->name, res->size);
+
+ knot_dname_find_labels(res, 1);
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_free(knot_dname_t **dname)
+{
+ if (dname == NULL || *dname == NULL) {
+ return;
+ }
+
+// char *name = knot_dname_to_str((*dname));
+
+// printf("freeing in dname: %s %p\n", name, *dname);
+
+// free(name);
+
+
+ if ((*dname)->name != NULL) {
+ free((*dname)->name);
+ }
+
+ if((*dname)->labels != NULL) {
+ free((*dname)->labels);
+ }
+
+
+// slab_free(*dname);
+ free(*dname);
+ *dname = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return knot_dname_cmp(d1, d2, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_compare_cs(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return knot_dname_cmp(d1, d2, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
+{
+ 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("1: copying %d bytes from adress %p to %p\n",
+ d1->size, d1->name, new_dname);
+
+ memcpy(new_dname, d1->name, d1->size);
+
+ dbg_dname("2: copying %d bytes from adress %p to %p\n",
+ d2->size, d2->name, new_dname + d1->size);
+
+ memcpy(new_dname + d1->size, d2->name, d2->size);
+
+ // 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;
+ }
+
+ 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);
+
+ d1->size += d2->size;
+
+ return d1;
+}
+
+void knot_dname_set_id(knot_dname_t *dname, unsigned int id)
+{
+ dname->id = id;
+}
+
+unsigned int knot_dname_get_id(const knot_dname_t *dname)
+{
+ if (dname != NULL) {
+ return dname->id;
+ } else {
+ return 0; /* 0 should never be used and is reserved for err. */
+ }
+}
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
new file mode 100644
index 0000000..c0e3f35
--- /dev/null
+++ b/src/libknot/dname.h
@@ -0,0 +1,428 @@
+/*!
+ * \file dname.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Domain name structure and API for manipulating it.
+ *
+ * \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_DNAME_H_
+#define _KNOT_DNAME_H_
+
+#include <stdint.h>
+#include <string.h>
+#include "common/ref.h"
+
+struct knot_node;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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 {
+ ref_t ref; /*!< Reference counting. */
+ uint8_t *name; /*!< Wire format of the domain name. */
+ /*!
+ * \brief Size of the domain name in octets.
+ * \todo Is this needed? Every dname should end with \0 or pointer.
+ */
+ unsigned int size;
+ uint8_t *labels;
+ unsigned short label_count;
+ struct knot_node *node; /*!< Zone node the domain name belongs to. */
+ unsigned int id; /*!< ID of domain name used in zone dumping. */
+};
+
+typedef struct knot_dname knot_dname_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates empty dname structure (no name, no owner node).
+ *
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ * it after use.
+ *
+ * \return Newly allocated and initialized dname structure.
+ *
+ * \todo Possibly useless.
+ */
+knot_dname_t *knot_dname_new();
+
+/*!
+ * \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.
+ *
+ * \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.
+ *
+ * \return Newly allocated and initialized dname structure representing the
+ * given domain name.
+ */
+knot_dname_t *knot_dname_new_from_str(const char *name, unsigned int size,
+ struct knot_node *node);
+
+/*!
+ * \brief Creates a dname structure from domain name given in wire format.
+ *
+ * \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 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.
+ */
+knot_dname_t *knot_dname_new_from_wire(const uint8_t *name,
+ unsigned int size,
+ struct knot_node *node);
+
+knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
+ size_t *pos, size_t size,
+ struct knot_node *node);
+
+/*!
+ * \brief Initializes domain name by the name given in wire format.
+ *
+ * \note The name is copied into the structure.
+ * \note If there is any name in the structure, it will be replaced.
+ * \note If the given name is not a FQDN, the result will be neither.
+ *
+ * \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.
+ * \param target Domain name structure to initialize.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM if allocation of labels info failed.
+ * \retval KNOT_EBADARG if name or target is null.
+ *
+ * \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.
+ */
+int knot_dname_from_wire(const uint8_t *name, unsigned int size,
+ struct knot_node *node, knot_dname_t *target);
+
+/*!
+ * \brief Duplicates the given domain name.
+ *
+ * \note Copied dname referense count is reset to 1, caller is responsible
+ * for releasing it after use.
+ *
+ * \param dname Domain name to be copied.
+ *
+ * \return New domain name which is an exact copy of \a dname.
+ */
+knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname);
+
+/*!
+ * \brief Converts the given domain name to string representation.
+ *
+ * \note Allocates new memory, remember to free it.
+ *
+ * \param dname 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);
+
+/*!
+ * \brief Returns the domain name in wire format.
+ *
+ * \param dname Domain name.
+ *
+ * \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.
+ *
+ * \param dname Domain name to get the size of.
+ *
+ * \return Size of the domain name in wire format in octets.
+ */
+unsigned int knot_dname_size(const knot_dname_t *dname);
+
+unsigned int knot_dname_id(const knot_dname_t *dname);
+
+/*!
+ * \brief Returns size of a part of domain name.
+ *
+ * \param dname Domain name.
+ * \param labels Count of labels to get the size of (counted from left).
+ *
+ * \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.
+ *
+ * \return Zone node the domain name belongs to or NULL if none.
+ */
+const struct knot_node *knot_dname_node(const knot_dname_t *dname,
+ int check_version);
+
+struct knot_node *knot_dname_get_node(knot_dname_t *dname,
+ int check_version);
+
+void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
+
+void knot_dname_update_node(knot_dname_t *dname);
+
+void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
+
+/*!
+ * \brief Checks if the given domain name is a fully-qualified domain name.
+ *
+ * \param dname Domain name to check.
+ *
+ * \retval <> 0 if \a dname is a FQDN.
+ * \retval 0 otherwise.
+ */
+int knot_dname_is_fqdn(const knot_dname_t *dname);
+
+/*!
+ * \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.
+ *
+ * \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 dname Domain name to remove the first label from.
+ */
+void knot_dname_left_chop_no_copy(knot_dname_t *dname);
+
+/*!
+ * \brief Checks if one domain name is a subdomain of other.
+ *
+ * \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.
+ */
+int knot_dname_is_subdomain(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.
+ *
+ * \retval <> 0 if \a dname is a wildcard domain name.
+ * \retval 0 otherwise.
+ */
+int knot_dname_is_wildcard(const knot_dname_t *dname);
+
+/*!
+ * \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.
+ *
+ * \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);
+
+/*!
+ * \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 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);
+
+/*!
+ * \brief Destroys the given domain name.
+ *
+ * Frees also the data within the struct. This is somewhat different behaviour
+ * than that of RDATA and RRSet structures which do not deallocate their
+ * contents.
+ *
+ * Sets the given pointer to NULL.
+ *
+ * \param dname Domain name to be destroyed.
+ */
+void knot_dname_free(knot_dname_t **dname);
+
+/*!
+ * \brief Compares two domain names (case insensitive).
+ *
+ * \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.
+ */
+int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Compares two domain names (case sensitive).
+ *
+ * \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.
+ */
+int knot_dname_compare_cs(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).
+ */
+knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2);
+
+void knot_dname_set_id(knot_dname_t *dname, unsigned int id);
+
+unsigned int knot_dname_get_id(const knot_dname_t *dname);
+
+/*!
+ * \brief Increment reference counter for dname.
+ *
+ * Function makes shallow copy (reference).
+ *
+ * \param dname Referenced dname.
+ */
+static inline void knot_dname_retain(knot_dname_t *dname) {
+ if (dname) {
+ ref_retain(&dname->ref);
+// char *name = knot_dname_to_str(dname);
+// printf("retain: %s %p %d\n", name, dname, dname->ref.count);
+// free(name);
+
+ }
+}
+
+/*#define knot_dname_retain(d) \
+ knot_dname_retain_((d));\
+ if ((d))\
+ printf("dname_retain: %s() at %s:%d, %p refcount=%zu\n",\
+ __func__, __FILE__, __LINE__, d, (d)->ref.count) */
+
+/*!
+ * \brief Decrement reference counter for dname.
+ *
+ * \param dname Referenced dname.
+ */
+static inline void knot_dname_release(knot_dname_t *dname) {
+ if (dname) {
+// char *name = knot_dname_to_str(dname);
+// printf("releasing: %p %s %d\n", dname, name, dname->ref.count - 1);
+// free(name);
+ ref_release(&dname->ref);
+ }
+}
+
+/*#define knot_dname_release(d) \
+ if ((d))\
+ printf("dname_release: %s() at %s:%d, %p refcount=%zu\n",\
+ __func__, __FILE__, __LINE__, d, (d)->ref.count-1);\
+ knot_dname_release_((d)) */
+
+#endif /* _KNOT_DNAME_H_ */
+
+/*! @} */
diff --git a/src/libknot/edns.c b/src/libknot/edns.c
new file mode 100644
index 0000000..05ebd7b
--- /dev/null
+++ b/src/libknot/edns.c
@@ -0,0 +1,416 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "edns.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/debug.h"
+#include "util/error.h"
+
+/*! \brief Various EDNS constatns. */
+enum knot_edns_consts {
+ /*! \brief Mask for the DO bit. */
+ KNOT_EDNS_DO_MASK = (uint16_t)0x8000,
+ /*! \brief Step for allocation of space for option entries. */
+ KNOT_EDNS_OPTION_STEP = 1
+};
+
+/*! \brief Minimum size of EDNS OPT RR in wire format. */
+static const short KNOT_EDNS_MIN_SIZE = 11;
+
+/*----------------------------------------------------------------------------*/
+
+knot_opt_rr_t *knot_edns_new()
+{
+ knot_opt_rr_t *opt_rr = (knot_opt_rr_t *)malloc(
+ sizeof(knot_opt_rr_t));
+ CHECK_ALLOC_LOG(opt_rr, NULL);
+ opt_rr->size = KNOT_EDNS_MIN_SIZE;
+ opt_rr->option_count = 0;
+ opt_rr->options_max = 0;
+
+ opt_rr->ext_rcode = 0;
+ opt_rr->flags = 0;
+ opt_rr->version = 0;
+
+ return opt_rr;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
+ size_t max_size)
+{
+ const uint8_t *pos = wire;
+ int parsed = 0;
+
+ if (pos == NULL || max_size == 0 || opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (max_size < KNOT_EDNS_MIN_SIZE) {
+ dbg_edns("Not enough data to parse OPT RR header.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ // owner of EDNS OPT RR must be root (0)
+ if (*pos != 0) {
+ dbg_edns("EDNS packet malformed (expected root "
+ "domain as owner).\n");
+ return KNOT_EMALF;
+ }
+ pos += 1;
+
+ // check the type of the record (must be OPT)
+ if (knot_wire_read_u16(pos) != KNOT_RRTYPE_OPT) {
+ dbg_edns("EDNS packet malformed (expected OPT type"
+ ".\n");
+ return KNOT_EMALF;
+ }
+ pos += 2;
+
+ opt_rr->payload = knot_wire_read_u16(pos);
+ dbg_edns("Parsed payload: %u\n", opt_rr->payload);
+
+ pos += 2;
+ opt_rr->ext_rcode = *(pos++);
+ opt_rr->version = *(pos++);
+ opt_rr->flags = knot_wire_read_u16(pos);
+ pos += 2;
+
+ parsed = KNOT_EDNS_MIN_SIZE;
+
+ // ignore RDATA, but move pos behind them
+ uint16_t rdlength = knot_wire_read_u16(pos);
+ pos += 2;
+
+ if (max_size - parsed < rdlength) {
+ dbg_edns("Not enough data to parse OPT RR.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ while (parsed < rdlength + KNOT_EDNS_MIN_SIZE) {
+ if (max_size - parsed < 4) {
+ dbg_edns("Not enough data to parse OPT RR"
+ " OPTION header.\n");
+ return KNOT_EFEWDATA;
+ }
+ uint16_t code = knot_wire_read_u16(pos);
+ pos += 2;
+ uint16_t length = knot_wire_read_u16(pos);
+ pos += 2;
+ dbg_edns("EDNS OPTION: Code: %u, Length: %u\n",
+ code, length);
+ if (max_size - parsed - 4 < length) {
+ dbg_edns("Not enough data to parse OPT RR"
+ " OPTION data.\n");
+ return KNOT_EFEWDATA;
+ }
+ int ret;
+ if ((ret =
+ knot_edns_add_option(opt_rr, code, length, pos)) != 0) {
+ dbg_edns("Error parsing OPT option field.\n");
+ return ret;
+ }
+ pos += length;
+ parsed += length + 4;
+ }
+
+ return parsed;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
+ const knot_rrset_t *rrset)
+{
+ if (opt_rr == NULL || rrset == NULL
+ || knot_rrset_type(rrset) != KNOT_RRTYPE_OPT) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_edns("Parsing payload.\n");
+ opt_rr->payload = knot_rrset_class(rrset);
+
+ // the TTL has switched bytes
+ uint32_t ttl;
+ dbg_edns("TTL: %u\n", knot_rrset_ttl(rrset));
+ knot_wire_write_u32((uint8_t *)&ttl, knot_rrset_ttl(rrset));
+ // first byte of TTL is extended RCODE
+ dbg_edns("TTL: %u\n", ttl);
+ memcpy(&opt_rr->ext_rcode, &ttl, 1);
+ dbg_edns("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode);
+ // second is the version
+ memcpy(&opt_rr->version, (const uint8_t *)(&ttl) + 1, 1);
+ dbg_edns("Parsed version: %u.\n", opt_rr->version);
+ // third and fourth are flags
+ opt_rr->flags = knot_wire_read_u16((const uint8_t *)(&ttl) + 2);
+ dbg_edns("Parsed flags: %u.\n", opt_rr->flags);
+ // size of the header, options are counted elsewhere
+ opt_rr->size = 11;
+
+ int rc = 0;
+ dbg_edns("Parsing options.\n");
+ const knot_rdata_t *rdata = knot_rrset_rdata(rrset);
+
+ // in OPT RR, all RDATA are in one RDATA item stored as BINARY data,
+ // i.e. preceded by their length
+ if (rdata != NULL) {
+ assert(knot_rdata_item_count(rdata) == 1);
+ const uint8_t *raw = (const uint8_t *)
+ knot_rdata_item(rdata, 0)->raw_data;
+ uint16_t size = knot_wire_read_u16(raw);
+ int pos = 2;
+ assert(size > 0);
+ while (pos - 2 < size) {
+ // ensure there is enough data to parse the OPTION CODE
+ // and OPTION LENGTH
+ if (size - pos + 2 < 4) {
+ return KNOT_EMALF;
+ }
+ uint16_t opt_code = knot_wire_read_u16(raw + pos);
+ uint16_t opt_size = knot_wire_read_u16(raw + pos + 2);
+
+ // there should be enough data for parsing the OPTION
+ // data
+ if (size - pos - 2 < opt_size) {
+ return KNOT_EMALF;
+ }
+ rc = knot_edns_add_option(opt_rr, opt_code, opt_size,
+ raw + pos + 4);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ pos += 4 + opt_size;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_edns_get_payload(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->payload;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_payload(knot_opt_rr_t *opt_rr,
+ uint16_t payload)
+{
+ assert(opt_rr != NULL);
+ opt_rr->payload = payload;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_edns_get_ext_rcode(const knot_opt_rr_t *opt_rr)
+{
+ return opt_rr->ext_rcode;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_ext_rcode(knot_opt_rr_t *opt_rr,
+ uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ opt_rr->ext_rcode = ext_rcode;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_edns_get_version(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_version(knot_opt_rr_t *opt_rr,
+ uint8_t version)
+{
+ assert(opt_rr != NULL);
+ opt_rr->version = version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_edns_get_flags(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->flags;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_do(const knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_edns("Flags: %u\n", opt_rr->flags);
+ return (opt_rr->flags & KNOT_EDNS_DO_MASK);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_do(knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return;
+ }
+
+ opt_rr->flags |= KNOT_EDNS_DO_MASK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code,
+ uint16_t length, const uint8_t *data)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (opt_rr->option_count == opt_rr->options_max) {
+ knot_opt_option_t *options_new =
+ (knot_opt_option_t *)calloc(
+ (opt_rr->options_max + KNOT_EDNS_OPTION_STEP),
+ sizeof(knot_opt_option_t));
+ CHECK_ALLOC_LOG(options_new, KNOT_ENOMEM);
+ memcpy(options_new, opt_rr->options, opt_rr->option_count);
+ opt_rr->options = options_new;
+ opt_rr->options_max += KNOT_EDNS_OPTION_STEP;
+ }
+
+ dbg_edns("Adding option.\n");
+ dbg_edns("Code: %u.\n", code);
+ dbg_edns("Length: %u.\n", length);
+ dbg_edns("Data: %p.\n", data);
+
+ opt_rr->options[opt_rr->option_count].data = (uint8_t *)malloc(length);
+ CHECK_ALLOC_LOG(opt_rr->options[opt_rr->option_count].data, KNOT_ENOMEM);
+ memcpy(opt_rr->options[opt_rr->option_count].data, data, length);
+
+ opt_rr->options[opt_rr->option_count].code = code;
+ opt_rr->options[opt_rr->option_count].length = length;
+
+ ++opt_rr->option_count;
+ opt_rr->size += 4 + length;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_has_option(const knot_opt_rr_t *opt_rr, uint16_t code)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int i = 0;
+ while (i < opt_rr->option_count && opt_rr->options[i].code != code) {
+ ++i;
+ }
+
+ assert(i >= opt_rr->option_count || opt_rr->options[i].code == code);
+
+ return (i < opt_rr->option_count);
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire,
+ size_t max_size)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(KNOT_EDNS_MIN_SIZE <= max_size);
+
+ if (max_size < opt_rr->size) {
+ dbg_edns("Not enough place for OPT RR wire format.\n");
+ return KNOT_ESPACE;
+ }
+
+ uint8_t *pos = wire;
+ *(pos++) = 0;
+ knot_wire_write_u16(pos, KNOT_RRTYPE_OPT);
+ pos += 2;
+ knot_wire_write_u16(pos, opt_rr->payload);
+ pos += 2;
+ *(pos++) = opt_rr->ext_rcode;
+ *(pos++) = opt_rr->version;
+ knot_wire_write_u16(pos, opt_rr->flags);
+ pos += 2;
+
+ uint8_t *rdlen = pos;
+ uint16_t len = 0;
+ pos += 2;
+
+ // OPTIONs
+ for (int i = 0; i < opt_rr->option_count; ++i) {
+ knot_wire_write_u16(pos, opt_rr->options[i].code);
+ pos += 2;
+ knot_wire_write_u16(pos, opt_rr->options[i].length);
+ pos += 2;
+ memcpy(pos, opt_rr->options[i].data, opt_rr->options[i].length);
+ pos += opt_rr->options[i].length;
+ len += 4 + opt_rr->options[i].length;
+ }
+
+ knot_wire_write_u16(rdlen, len);
+
+ return opt_rr->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_edns_size(knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return opt_rr->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_free(knot_opt_rr_t **opt_rr)
+{
+ if (opt_rr == NULL || *opt_rr == NULL) {
+ return;
+ }
+
+ if ((*opt_rr)->option_count > 0) {
+ free((*opt_rr)->options);
+ }
+ free(*opt_rr);
+ *opt_rr = NULL;
+}
diff --git a/src/libknot/edns.h b/src/libknot/edns.h
new file mode 100644
index 0000000..010d155
--- /dev/null
+++ b/src/libknot/edns.h
@@ -0,0 +1,273 @@
+/*!
+ * \file edns.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for manipulating and parsing EDNS OPT pseudo-RR.
+ *
+ * \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_EDNS_H_
+#define _KNOT_EDNS_H_
+
+#include <stdint.h>
+
+#include "util/utils.h"
+#include "rrset.h"
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Structure representing one OPT RR Option. */
+struct knot_opt_option {
+ uint16_t code;
+ uint16_t length;
+ uint8_t *data;
+};
+
+/*! \brief Structure representing one OPT RR Option. */
+typedef struct knot_opt_option knot_opt_option_t;
+
+/*!
+ * \brief Structure for holding EDNS parameters.
+ *
+ * \todo NSID
+ */
+struct knot_opt_rr {
+ uint16_t payload; /*!< UDP payload. */
+ uint8_t ext_rcode; /*!< Extended RCODE. */
+
+ /*!
+ * \brief Supported version of EDNS.
+ *
+ * Set to EDNS_NOT_SUPPORTED if not supported.
+ */
+ uint8_t version;
+
+ uint16_t flags; /*!< EDNS flags. */
+ knot_opt_option_t *options; /*!< EDNS options. */
+ short option_count; /*!< Count of EDNS options in this OPT RR.*/
+ short options_max; /*!< Maximum count of options. */
+ short size; /*!< Total size of the OPT RR in wire format. */
+};
+
+/*! \brief Structure for holding EDNS parameters. */
+typedef struct knot_opt_rr knot_opt_rr_t;
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Constants for supported versions of EDNS. */
+enum knot_edns_versions {
+ EDNS_VERSION_0 = (uint8_t)0, /*!< EDNS version 0. */
+ EDNS_NOT_SUPPORTED = (uint8_t)255 /*!< EDNS not supported. */
+};
+
+/*! \brief Constants for EDNS option codes. */
+enum knot_edns_option_codes {
+ EDNS_OPTION_NSID = (uint16_t)3 /*!< NSID option code. */
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates new empty OPT RR structure for holding EDNS parameters.
+ *
+ * \return New empty knot_opt_rr_t structure, or NULL if not successful.
+ */
+knot_opt_rr_t *knot_edns_new();
+
+/*!
+ * \brief Initializes OPT RR structure from given OPT RR in wire format.
+ *
+ * \param opt_rr OPT RR structure to initialize.
+ * \param wire Wire format of the OPT RR to parse.
+ * \param max_size Maximum size of the wire format in bytes (may be more
+ * than acutal size of the OPT RR).
+ *
+ * \return Size of the parserd OPT RR in bytes if successful (always > 0).
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_EMALF
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
+ size_t max_size);
+
+int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
+ const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the UDP payload stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the payload from.
+ *
+ * \return UDP payload in bytes.
+ */
+uint16_t knot_edns_get_payload(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the UDP payload field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the payload to.
+ * \param payload UDP payload in bytes.
+ */
+void knot_edns_set_payload(knot_opt_rr_t *opt_rr, uint16_t payload);
+
+/*!
+ * \brief Returns the Extended RCODE stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the Extended RCODE from.
+ *
+ * \return Extended RCODE.
+ */
+uint8_t knot_edns_get_ext_rcode(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the Extended RCODE field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the Extended RCODE to.
+ * \param ext_rcode Extended RCODE to set.
+ */
+void knot_edns_set_ext_rcode(knot_opt_rr_t *opt_rr, uint8_t ext_rcode);
+
+/*!
+ * \brief Returns the EDNS version stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the EDNS version from.
+ *
+ * \return EDNS version.
+ */
+uint8_t knot_edns_get_version(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the EDNS version field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the EDNS version to.
+ * \param version EDNS version to set.
+ */
+void knot_edns_set_version(knot_opt_rr_t *opt_rr, uint8_t version);
+
+/*!
+ * \brief Returns the flags stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the flags from.
+ *
+ * \return EDNS flags.
+ */
+uint16_t knot_edns_get_flags(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Returns the state of the DO bit in the OPT RR flags.
+ *
+ * \param opt_rr OPT RR structure to get the DO bit from.
+ *
+ * \return <> 0 if the DO bit is set.
+ * \return 0 if the DO bit is not set.
+ */
+int knot_edns_do(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the DO bit in the OPT RR.
+ *
+ * \param opt_rr OPT RR structure to set the DO bit in.
+ */
+void knot_edns_set_do(knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Adds EDNS Option to the OPT RR.
+ *
+ * \param opt_rr OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param length Option data length in bytes.
+ * \param data Option data.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code,
+ uint16_t length, const uint8_t *data);
+
+/*!
+ * \brief Checks if the OPT RR contains Option with the specified code.
+ *
+ * \param opt_rr OPT RR structure to check for the Option in.
+ * \param code Option code to check for.
+ *
+ * \retval <> 0 if the OPT RR contains Option with Option code \a code.
+ * \retval 0 otherwise.
+ */
+int knot_edns_has_option(const knot_opt_rr_t *opt_rr, uint16_t code);
+
+/*!
+ * \brief Converts the given OPT RR into wire format.
+ *
+ * \param opt_rr OPT RR structure to convert into wire format.
+ * \param wire Place to put the wire format to.
+ * \param max_size Maximum space available for the wire format in bytes.
+ *
+ * \return Size of the wire format in bytes if successful.
+ * \retval KNOT_ESPACE
+ */
+short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire,
+ size_t max_size);
+
+/*!
+ * \brief Returns size of the OPT RR in wire format.
+ *
+ * \param opt_rr OPT RR to get the size of.
+ *
+ * \return Size of the OPT RR in bytes.
+ */
+short knot_edns_size(knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Properly destroys the OPT RR structure.
+ *
+ * \note Also sets the given pointer to NULL.
+ */
+void knot_edns_free(knot_opt_rr_t **opt_rr);
+
+#endif /* _KNOT_EDNS_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c
new file mode 100644
index 0000000..c5d1c4f
--- /dev/null
+++ b/src/libknot/hash/cuckoo-hash-table.c
@@ -0,0 +1,1688 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h> /* defines uint32_t etc */
+#include <assert.h>
+#include <pthread.h>
+#include <math.h>
+
+#include <urcu.h>
+
+#include "util/utils.h"
+#include "common.h"
+#include "util/debug.h"
+#include "hash/cuckoo-hash-table.h"
+#include "hash/hash-functions.h"
+#include "common/dynamic-array.h"
+
+/*----------------------------------------------------------------------------*/
+/* Macros and inline functions */
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Default size table holding information about used hash table cells
+ * when hashing.
+ */
+#define RELOCATIONS_DEFAULT 200
+
+/*!
+ * \brief Maximum size table holding information about used hash table cells
+ * when hashing (just for debug issues).
+ */
+#define RELOCATIONS_MAX 1000
+
+/*!
+ * \brief Macro for hashing the given key using the universal system.
+ *
+ * \param system Universal system to use for the hashing.
+ * \param key Key to hash.
+ * \param length Size of the key in bytes.
+ * \param exp Exponent of the hash table size (the size is a power of 2).
+ * \param table Hash table index.
+ * \param gen Universal system generation.
+ *
+ * \return Hashed key.
+ */
+#define HASH(system, key, length, exp, gen, table) \
+ us_hash(system, fnv_32_buf(key, length, FNV1_32_INIT), exp, table, gen)
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 2
+ * tables are used.
+ */
+static const float SIZE_RATIO_2 = 2;
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 3
+ * tables are used.
+ */
+static const float SIZE_RATIO_3 = 1.15;
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 4
+ * tables are used.
+ */
+static const float SIZE_RATIO_4 = 1.08;
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Flag marking the generation of hash table or its item to be 1. */
+static const uint8_t FLAG_GENERATION1 = 0x1; // 00000001
+/*! \brief Flag marking the generation of hash table or its item to be 2. */
+static const uint8_t FLAG_GENERATION2 = 0x2; // 00000010
+/*! \brief Flag marking both generations. */
+static const uint8_t FLAG_GENERATION_BOTH = 0x3; // 00000011
+
+/*! \brief Flag used to mark the table when it's being rehashed. */
+static const uint8_t FLAG_REHASH = 0x4; // 00000100
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Clears the table / item flags. */
+static inline void CLEAR_FLAGS(uint8_t *flags)
+{
+ *flags = (uint8_t)0x0;
+}
+
+/*! \brief Returns the generation stored in the flags. */
+static inline uint8_t GET_GENERATION(uint8_t flags)
+{
+ return (flags & FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Checks if the generation stored in both flags are the same. */
+static inline int EQUAL_GENERATIONS(uint8_t flags1, uint8_t flags2)
+{
+ return (GET_GENERATION(flags1) == GET_GENERATION(flags2));
+}
+
+/*! \brief Checks if the generation stored in the flags is 1. */
+static inline int IS_GENERATION1(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION1) != 0);
+}
+
+/*! \brief Sets the generation stored in the flags to 1. */
+static inline void SET_GENERATION1(uint8_t *flags)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION2) | FLAG_GENERATION1;
+}
+
+/*! \brief Checks if the generation stored in the flags is 2. */
+static inline int IS_GENERATION2(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION2) != 0);
+}
+
+/*! \brief Sets the generation stored in the flags to 2. */
+static inline void SET_GENERATION2(uint8_t *flags)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION1) | FLAG_GENERATION2;
+}
+
+/*! \brief Sets the generation stored in the flags to the given generation. */
+static inline void SET_GENERATION(uint8_t *flags, uint8_t generation)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION_BOTH) | generation;
+}
+
+/*! \brief Sets the generation stored in the flags to the next one (cyclic). */
+static inline uint8_t SET_NEXT_GENERATION(uint8_t *flags)
+{
+ return ((*flags) ^= FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Returns the next generation to the one stored in flags (cyclic). */
+static inline uint8_t NEXT_GENERATION(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION_BOTH) ^ FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Sets the rehashing flag to the flags. */
+static inline void SET_REHASHING_ON(uint8_t *flags)
+{
+ *flags = (*flags | FLAG_REHASH);
+}
+
+/*! \brief Removes the rehashing flag from the flags. */
+static inline void SET_REHASHING_OFF(uint8_t *flags)
+{
+ *flags = (*flags & ~FLAG_REHASH);
+}
+
+/*! \brief Checks if the rehashing flag is set in the flags. */
+static inline int IS_REHASHING(uint8_t flags)
+{
+ return ((flags & FLAG_REHASH) != 0);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the exponent of the nearest larger power of two.
+ */
+static uint get_larger_exp(uint n)
+{
+ uint res = 0;
+ while (hashsize(++res) < n) {}
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Counts the ideal table count and the exponent of those tables' sizes.
+ *
+ * Only 3 or 4 hash tables are considered. The setup in which less items are
+ * wasted is recommended.
+ *
+ * \param items Number of items to hash.
+ * \param table_count Recommended number of tables will be saved here.
+ *
+ * \return Exponent of the tables' sizes.
+ */
+static uint get_table_exp_and_count(uint items, uint *table_count)
+{
+ // considering only 3 or 4 tables
+ int exp3 = get_larger_exp((items * SIZE_RATIO_3) / 3);
+ int exp4 = get_larger_exp(items * SIZE_RATIO_4) - 2;
+
+ if (exp4 < 0) {
+ exp4 = 1;
+ }
+
+ dbg_ck("Determining ideal table size...\n");
+ dbg_ck("\tNumber of items: %u\n", items);
+ dbg_ck("\tThree tables: size of one table: %u, total size: %u\n",
+ hashsize(exp3), 3 * hashsize(exp3));
+ dbg_ck("\tFour tables: size of one table: %u, total size: %u\n",
+ hashsize(exp4), 4 * hashsize(exp4));
+
+ // we need exponent at least 1 (this is quite ugly..)
+ if (exp3 == 0) {
+ exp3 = 1;
+ }
+ if (exp4 == 0) {
+ exp4 = 1;
+ }
+
+ if (exp3 >= 32 || exp4 >= 32) {
+ return 0;
+ }
+
+ if (((hashsize(exp3) * 3) - (items)) < ((hashsize(exp4) * 4) - items)) {
+ *table_count = 3;
+ return exp3;
+ } else {
+ *table_count = 4;
+ return exp4;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Counts the maximum effective item count based on size of the tables.
+ *
+ * For 3 tables, the effective utilization should be around 91%.
+ * For 4 tables it is 97%.
+ *
+ * See Fotakis, Dimitris, et al. - Space Efficient Hash Tables with Worst Case
+ * Constant Access Time. CiteSeerX. 2003
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.5337
+ */
+static uint get_max_table_items(uint table_count, int table_exponent)
+{
+ assert(table_count == 3 || table_count == 4);
+
+ float coef;
+
+ if (table_count == 3) {
+ coef = 0.91;
+ } else {
+ coef = 0.97;
+ }
+
+ return (uint)floor((table_count * hashsize(table_exponent)) * coef);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_is_full(const ck_hash_table_t *table)
+{
+ return (table->items >= get_max_table_items(table->table_count,
+ table->table_size_exp));
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_stash_is_full(const ck_hash_table_t *table)
+{
+ return (table->items_in_stash >= STASH_SIZE_MAX);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Clears the given item by assigning a NULL pointer to it.
+ */
+static inline void ck_clear_item(ck_hash_table_item_t **item)
+{
+ *item = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Insert given contents to the hash table item.
+ */
+static void ck_fill_item(const char *key, size_t key_length, void *value,
+ uint generation, ck_hash_table_item_t *item)
+{
+ // must allocate new space for key and value, otherwise it will be lost!
+ item->key = key;
+ item->key_length = key_length;
+ item->value = value;
+ CLEAR_FLAGS(&item->timestamp);
+ item->timestamp = generation;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Swaps two hash table items.
+ */
+static inline void ck_swap_items(ck_hash_table_item_t **item1,
+ ck_hash_table_item_t **item2)
+{
+ ck_hash_table_item_t *tmp = *item1;
+ *item1 = *item2;
+ *item2 = tmp;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the \a item pointer to the \a to pointer.
+ */
+static inline void ck_put_item(ck_hash_table_item_t **to,
+ ck_hash_table_item_t *item)
+{
+ *to = item;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the hash was already used twice.
+ *
+ * If yes, it means we entered a loop in the hashing process, so we must stop.
+ * Otherwise it remembers that we used the hash.
+ *
+ * \note According to Kirsch, et al. a check that at most one hash was used
+ * twice should be sufficient. We will retain our version for now.
+ *
+ * \param used Array of used table indices (hashes).
+ * \param hash Hash to check.
+ *
+ * \retval -1 if the hash was already used twice.
+ * \retval -2 if an error occured.
+ * \retval 0 if the hash was not used twice yet.
+ */
+static uint ck_check_used_twice(da_array_t *used, uint32_t hash)
+{
+ uint i = 0, found = 0;
+ while (i <= da_get_count(used) && found < 2) {
+ ++i;
+ if (((uint *)(da_get_items(used)))[i] == hash) {
+ ++found;
+ }
+ }
+
+ if (i <= da_get_count(used) && found == 2) {
+ dbg_ck_hash("Hashing entered infinite loop.\n");
+ return -1;
+ } else {
+ if (da_reserve(used, 1) < 0) {
+ ERR_ALLOC_FAILED;
+ return -2;
+ }
+ ((uint *)da_get_items(used))[da_get_count(used)] = hash;
+ da_occupy(used, 1);
+ assert(da_get_count(used) < RELOCATIONS_MAX);
+ return 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the key of item with the given key.
+ *
+ * \param item Item to compare with.
+ * \param key Key to compare.
+ * \param length Size of the key in bytes.
+ *
+ * \return <> 0 if the keys match.
+ * \return 0 if they don't.
+ */
+static inline uint ck_items_match(const ck_hash_table_item_t *item,
+ const char *key, size_t length)
+{
+ return (length == item->key_length
+ && (strncmp(item->key, key, length) == 0));
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Switches the given table number to a randomly chosen other table
+ * number.
+ */
+static inline void ck_next_table(uint *table, uint table_count)
+{
+ uint next;
+ while ((*table) == (next = knot_quick_rand() % table_count)) {}
+ *table = next;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find the given key in the hash table's stash.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ *
+ * \return Hash table item matching the key or NULL if not found in the stash.
+ */
+static ck_hash_table_item_t **ck_find_in_stash(const ck_hash_table_t *table,
+ const char *key, uint length)
+{
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ dbg_ck("Comparing item in stash (key: %.*s (size %zu))"
+ "with searched item (key %.*s (size %u)).\n",
+ (int)item->item->key_length, item->item->key,
+ item->item->key_length, (int)length, key, length);
+ if (ck_items_match(item->item, key, length)) {
+ return &item->item;
+ }
+ item = item->next;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find item with given key using hash functions from the given
+ * generation.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ * \param generation Generation of items (table) to use. Items having other
+ * generation are ignored.
+ */
+static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table,
+ const char *key,
+ size_t length, uint8_t generation)
+{
+ uint32_t hash;
+ dbg_ck("Finding item in generation: %u\n", generation);
+
+ // check hash tables
+ for (uint t = 0; t < table->table_count; ++t) {
+ hash = HASH(&table->hash_system, key, length,
+ table->table_size_exp, generation, t);
+
+ dbg_ck("Hash: %u, key: %.*s\n", hash, (int)length, key);
+ dbg_ck("Table %d, hash: %u, item: %p\n", t + 1, hash,
+ table->tables[t][hash]);
+ if (table->tables[t][hash] != NULL) {
+ dbg_ck("Table %u, key: %.*s, value: %p, key "
+ "length: %zu\n",
+ t + 1, (int)table->tables[t][hash]->key_length,
+ table->tables[t][hash]->key,
+ table->tables[t][hash]->value,
+ table->tables[t][hash]->key_length);
+ }
+
+ if (table->tables[t][hash] &&
+ ck_items_match(table->tables[t][hash], key, length)) {
+ // found
+ return &table->tables[t][hash];
+ }
+ }
+
+ // try to find in stash
+ dbg_ck("Searching in stash...\n");
+
+ ck_hash_table_item_t **found =
+ ck_find_in_stash(table, key, length);
+
+ dbg_ck("Found pointer: %p\n", found);
+ if (found != NULL) {
+ dbg_ck("Stash, key: %.*s, value: %p, key length: %zu\n",
+ (int)(*found)->key_length, (*found)->key,
+ (*found)->value, (*found)->key_length);
+ }
+
+ // ck_find_in_buffer returns NULL if not found, otherwise pointer to
+ // item
+ return found;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds item with given key and returns non-constant pointer to pointer
+ * to the appropriate hash table item.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ */
+static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table,
+ const char *key, size_t length)
+{
+ // get the generation of the table so that we use the same value
+ uint8_t generation = table->generation;
+
+ // find item using the table generation's hash functions
+ ck_hash_table_item_t **found = ck_find_gen(table, key, length,
+ GET_GENERATION(generation));
+ // if rehashing is in progress, try the next generation's functions
+ if (!found && IS_REHASHING(generation)) {
+ found = ck_find_gen(table, key, length,
+ NEXT_GENERATION(generation));
+ }
+
+ return found;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Hashes the given item using the given generation.
+ *
+ * \param table Hash table where to put the item.
+ * \param to_hash In: Item to hash. Out: NULL if successful, item that failed
+ * to hash if not.
+ * \param free Free place where to put the last moved item when the hasing
+ * is unsuccessful.
+ * \param generation Generation of items (table) to be used for hashing.
+ *
+ * \retval 0 if successful and no loop occured.
+ * \retval 1 if a loop occured and the item was inserted to the \a free place.
+ */
+static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
+ ck_hash_table_item_t **free, uint8_t generation)
+{
+ da_array_t used[table->table_count];
+ for (uint i = 0; i < table->table_count; ++i) {
+ da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint));
+ }
+
+ // hash until empty cell is encountered or until loop appears
+
+ dbg_ck_hash("Hashing key: %.*s of size %zu.\n",
+ (int)(*to_hash)->key_length, (*to_hash)->key,
+ (*to_hash)->key_length);
+
+ uint next_table = 0;
+
+ uint32_t hash = HASH(&table->hash_system, (*to_hash)->key,
+ (*to_hash)->key_length, table->table_size_exp,
+ generation, next_table);
+
+ dbg_ck_hash("New hash: %u.\n", hash);
+ assert(hash < hashsize(table->table_size_exp));
+
+ ((uint *)da_get_items(&used[next_table]))
+ [da_get_count(&used[next_table])] = hash;
+ ck_hash_table_item_t **next = &table->tables[next_table][hash];
+ dbg_ck_hash("Item to be moved: %p, place in table: %p\n",
+ *next, next);
+ ck_hash_table_item_t **moving = to_hash;
+
+ int loop = 0;
+
+ while (*next != NULL) {
+ dbg_ck_hash("Swapping items to hash: %p and Moving: %p\n",
+ to_hash, moving);
+ ck_swap_items(to_hash, moving); // first time it's unnecessary
+
+ // set the generation of the inserted item to the next
+ SET_GENERATION(&(*moving)->timestamp, generation);
+
+ moving = next;
+
+ dbg_ck_hash("Moving item from table %u, key: %.*s, hash %u ",
+ next_table + 1, (int)(*moving)->key_length,
+ (*moving)->key, hash);
+
+ // if rehashing and the 'next' item is from the old generation,
+ // start from table 1
+ if (generation != table->generation &&
+ EQUAL_GENERATIONS((*next)->timestamp, table->generation)) {
+ next_table = 0;
+ } else {
+ ck_next_table(&next_table, table->table_count);
+ }
+
+ hash = HASH(&table->hash_system, (*next)->key,
+ (*next)->key_length, table->table_size_exp,
+ generation, next_table);
+
+ next = &table->tables[next_table][hash];
+
+ dbg_ck_hash("to table %u, hash %u, item: %p, place: %p\n",
+ next_table + 1, hash, *next, next);
+
+ if ((*next) != NULL) {
+ dbg_ck_hash("Table %u, hash: %u, key: %.*s\n",
+ next_table + 1, hash,
+ (int)(*next)->key_length, (*next)->key);
+ }
+
+ // check if this cell wasn't already used in this item's hashing
+ if (ck_check_used_twice(&used[next_table], hash) != 0) {
+ next = free;
+ loop = -1;
+ break;
+ }
+ }
+
+ dbg_ck_hash("Putting pointer %p (*moving) to item %p (next).\n",
+ *moving, next);
+
+ ck_put_item(next, *moving);
+ // set the new generation for the inserted item
+ SET_GENERATION(&(*next)->timestamp, generation);
+ dbg_ck_hash("Putting pointer %p (*old) to item %p (moving).\n",
+ *to_hash, moving);
+
+ ck_put_item(moving, *to_hash);
+
+ // set the new generation for the inserted item
+ SET_GENERATION(&(*moving)->timestamp, generation);
+ *to_hash = NULL;
+
+ for (uint i = 0; i < table->table_count; ++i) {
+ da_destroy(&used[i]);
+ }
+
+ return loop;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void ck_rollback_rehash(ck_hash_table_t *table)
+{
+ // set old generation in tables
+ for (int i = 0; i < hashsize(table->table_size_exp); ++i) {
+ // no need for locking - timestamp is not used in lookup
+ // and two paralel insertions (and thus rehashings) are
+ // impossible
+ for (uint t = 0; t < table->table_count; ++t) {
+ if (table->tables[t][i] != NULL) {
+ SET_GENERATION(&table->tables[t][i]->timestamp,
+ table->generation);
+ }
+ }
+ }
+
+ // set old generation in stash
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ assert(item->item != NULL);
+ SET_GENERATION(&item->item->timestamp, table->generation);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds the given item to the hash table's stash.
+ *
+ * \param table Hash table to add the item to.
+ * \param item Item to add.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if an error occured.
+ */
+int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item)
+{
+ ck_stash_item_t *new_item
+ = (ck_stash_item_t *)malloc(sizeof(ck_stash_item_t));
+ if (new_item == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ new_item->item = item;
+ new_item->next = table->stash;
+ table->stash = new_item;
+
+ dbg_ck_hash("First item in stash (now inserted): key: %.*s (size %zu)"
+ ", value: %p\n", (int)table->stash->item->key_length,
+ table->stash->item->key, table->stash->item->key_length,
+ table->stash->item->value);
+
+ // increase count of items in stash
+ ++table->items_in_stash;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_new_table(ck_hash_table_item_t ***table, int exp)
+{
+ *table = (ck_hash_table_item_t **)
+ malloc(hashsize(exp) * sizeof(ck_hash_table_item_t *));
+ if (*table == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ // set to 0
+ memset(*table, 0, hashsize(exp) * sizeof(ck_hash_table_item_t *));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_t *ck_create_table(uint items)
+{
+ ck_hash_table_t *table =
+ (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t));
+
+ if (table == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ memset(table, 0, sizeof(ck_hash_table_t));
+
+ // determine ideal size of one table in powers of 2 and save the
+ // exponent
+ table->table_size_exp = get_table_exp_and_count(items,
+ &table->table_count);
+ assert(table->table_size_exp <= 32);
+
+ if (table->table_size_exp == 0) {
+ dbg_ck("Failed to count exponent of the hash table.\n");
+ return NULL;
+ }
+
+ dbg_ck("Creating hash table for %u items.\n", items);
+ dbg_ck("Exponent: %u, number of tables: %u\n ",
+ table->table_size_exp, table->table_count);
+ dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n",
+ hashsize(table->table_size_exp),
+ sizeof(ck_hash_table_item_t *),
+ hashsize(table->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+
+ // create tables
+ for (uint t = 0; t < table->table_count; ++t) {
+ dbg_ck("Creating table %u...\n", t);
+ if (ck_new_table(&table->tables[t], table->table_size_exp)
+ != 0) {
+ for (uint i = 0; i < t; ++i) {
+ free(table->tables[i]);
+ }
+ free(table);
+ return NULL;
+ }
+ }
+
+ assert(table->stash == NULL);
+ assert(table->hashed == NULL);
+ assert(table->items == 0);
+ assert(table->items_in_stash == 0);
+ assert(table->table_count == MAX_TABLES
+ || table->tables[table->table_count] == NULL);
+
+ // initialize rehash/insert mutex
+ pthread_mutex_init(&table->mtx_table, NULL);
+
+ // set the generation to 1 and initialize the universal system
+ CLEAR_FLAGS(&table->generation);
+ SET_GENERATION1(&table->generation);
+
+ us_initialize(&table->hash_system);
+
+ return table;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value),
+ int delete_key)
+{
+ assert(table);
+ assert(*table);
+ pthread_mutex_lock(&(*table)->mtx_table);
+
+ // destroy items in tables
+ for (uint i = 0; i < hashsize((*table)->table_size_exp); ++i) {
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ if ((*table)->tables[t][i] != NULL) {
+ if (dtor_value) {
+ dtor_value(
+ (*table)->tables[t][i]->value);
+ }
+ if (delete_key != 0) {
+ free(
+ (void *)(*table)->tables[t][i]->key);
+ }
+ free((void *)(*table)->tables[t][i]);
+ }
+ }
+ }
+
+ // destroy items in stash
+// ck_hash_table_item_t **stash =
+// ((ck_hash_table_item_t **)(da_get_items(&(*table)->stash)));
+// for (uint i = 0; i < da_get_count(&(*table)->stash); ++i) {
+// assert(stash[i] != NULL);
+// if (dtor_value) {
+// dtor_value(stash[i]->value);
+// }
+// if (delete_key != 0) {
+// free((void *)stash[i]->key);
+// }
+// free((void *)stash[i]);
+// }
+ ck_stash_item_t *item = (*table)->stash;
+ while (item != NULL) {
+ // disconnect the item
+ (*table)->stash = item->next;
+ /*! \todo Investigate this. */
+ assert(item->item != NULL);
+
+ if (dtor_value) {
+ dtor_value(item->item->value);
+ }
+ if (delete_key) {
+ free((void *)item->item->key);
+ }
+
+ free((void *)item->item);
+ free(item);
+ item = (*table)->stash;
+ }
+
+ // deallocate tables
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ free((*table)->tables[t]);
+ }
+ // destroy stash
+// da_destroy(&(*table)->stash);
+
+ pthread_mutex_unlock(&(*table)->mtx_table);
+ // destroy mutex, assuming that here noone will lock the mutex again
+ pthread_mutex_destroy(&(*table)->mtx_table);
+
+ free(*table);
+ (*table) = NULL;
+}
+
+void ck_table_free(ck_hash_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&(*table)->mtx_table);
+
+ ck_stash_item_t *item = (*table)->stash;
+ while (item != NULL) {
+ // disconnect the item
+ (*table)->stash = item->next;
+ free(item);
+ item = (*table)->stash;
+ }
+
+ // deallocate tables
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ free((*table)->tables[t]);
+ }
+
+ pthread_mutex_unlock(&(*table)->mtx_table);
+ pthread_mutex_destroy(&(*table)->mtx_table);
+
+ free(*table);
+ (*table) = NULL;
+}
+
+int ck_resize_table(ck_hash_table_t *table)
+{
+ dbg_ck("Resizing hash table.\n");
+
+ /*
+ * Easiest is just to increment the exponent, resulting in doubling
+ * the table sizes. This is not very memory-effective, but should do
+ * the job.
+ */
+
+ if (table->table_size_exp == 31) {
+ dbg_ck("Hash tables achieved max size (exponent 31).\n");
+ return -1;
+ }
+
+ ck_hash_table_item_t **tables_new[MAX_TABLES];
+ ck_hash_table_item_t **tables_old[MAX_TABLES];
+ int exp_new = table->table_size_exp + 1;
+
+ dbg_ck("New tables exponent: %d\n", exp_new);
+
+ for (int t = 0; t < table->table_count; ++t) {
+ if (ck_new_table(&tables_new[t], exp_new) != 0) {
+ dbg_ck("Failed to create new table.\n");
+ for (int i = 0; i < t; ++i) {
+ free(tables_new[i]);
+ }
+ return -1;
+ }
+ }
+
+ dbg_ck("Created new tables, copying data to them.\n");
+
+ for (int t = 0; t < table->table_count; ++t) {
+ size_t old_size = hashsize(table->table_size_exp)
+ * sizeof(ck_hash_table_item_t *);
+
+ // copy the old table items
+ dbg_ck("Copying to: %p, from %p, size: %zu\n",
+ tables_new[t], table->tables[t], old_size);
+ memcpy(tables_new[t], table->tables[t], old_size);
+ // set the rest to 0
+ dbg_ck("Setting to 0 from %p, size %zu\n",
+ tables_new[t] + hashsize(table->table_size_exp),
+ (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
+ - old_size);
+ memset(tables_new[t] + hashsize(table->table_size_exp), 0,
+ (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
+ - old_size);
+ }
+
+ dbg_ck("Done, switching the tables and running rehash.\n");
+
+
+ memcpy(tables_old, table->tables,
+ MAX_TABLES * sizeof(ck_hash_table_item_t **));
+ memcpy(table->tables, tables_new,
+ MAX_TABLES * sizeof(ck_hash_table_item_t **));
+
+ table->table_size_exp = exp_new;
+
+ // delete the old tables
+ for (int t = 0; t < table->table_count; ++t) {
+ free(tables_old[t]);
+ }
+
+ return ck_rehash(table);
+ //return 0;
+}
+
+int ck_insert_item(ck_hash_table_t *table, const char *key,
+ size_t length, void *value)
+{
+ // lock mutex to avoid write conflicts
+ pthread_mutex_lock(&table->mtx_table);
+
+ assert(value != NULL);
+
+ dbg_ck_hash("Inserting item with key: %.*s.\n", (int)length, key);
+ dbg_ck_hash_hex(key, length);
+ dbg_ck_hash("\n");
+
+ // create item structure and fill in the given data, key won't be copied
+ ck_hash_table_item_t *new_item =
+ (ck_hash_table_item_t *)malloc((sizeof(ck_hash_table_item_t)));
+ ck_fill_item(key, length, value, GET_GENERATION(table->generation),
+ new_item);
+
+ // check if the table is not full; if yes, resize and rehash!
+ if (ck_is_full(table)) {
+ dbg_ck("Table is full, resize needed.\n");
+ if (ck_resize_table(table) != 0) {
+ dbg_ck("Failed to resize hash table!\n");
+ return -1;
+ }
+ }
+
+ // there should be at least 2 free places
+ //assert(da_try_reserve(&table->stash, 2) == 0);
+ //da_reserve(&table->stash, 1);
+ ck_hash_table_item_t *free_place = NULL;
+ if (ck_hash_item(table, &new_item, &free_place,
+ table->generation) != 0) {
+
+ dbg_ck("Adding item with key %.*s to stash.\n",
+ (int)free_place->key_length, free_place->key);
+
+ // maybe some limit on the stash and rehash if full
+ if (ck_add_to_stash(table, free_place) != 0) {
+ dbg_ck_hash("Could not add item to stash!!\n");
+ assert(0);
+ }
+
+ if (ck_stash_is_full(table)) {
+ dbg_ck("Stash is full, resize needed.\n");
+ if (ck_resize_table(table) != 0) {
+ dbg_ck("Failed to resize hash table!\n");
+ return -1;
+ }
+ }
+ }
+
+ ++table->items;
+ pthread_mutex_unlock(&table->mtx_table);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const ck_hash_table_item_t *ck_find_item(const ck_hash_table_t *table,
+ const char *key, size_t length)
+{
+ dbg_ck("ck_find_item(), key: %.*s, size: %zu\n",
+ (int)length, key, length);
+
+ ck_hash_table_item_t **found = ck_find_item_nc(table, key, length);
+
+ return (found == NULL) ? NULL : rcu_dereference(*found);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_update_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void *new_value, void (*dtor_value)(void *value))
+{
+ rcu_read_lock(); // is needed?
+
+ assert(new_value != NULL);
+
+ ck_hash_table_item_t **item = ck_find_item_nc(table, key, length);
+
+ if (item == NULL || (*item) == NULL) {
+ return -1;
+ }
+
+ void *old = rcu_xchg_pointer(&(*item)->value, new_value);
+ rcu_read_unlock();
+
+ synchronize_rcu();
+ if (dtor_value) {
+ dtor_value(old);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_delete_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void (*dtor_value)(void *value), int delete_key)
+{
+ rcu_read_lock(); // is needed?
+ ck_hash_table_item_t **place = ck_find_item_nc(table, key, length);
+
+ if (place == NULL) {
+ return -1;
+ }
+
+ ck_hash_table_item_t *item = *place;
+
+ assert(item != NULL);
+
+ ck_put_item(place, NULL);
+ rcu_read_unlock();
+
+ synchronize_rcu();
+ if (dtor_value) {
+ dtor_value(item->value);
+ }
+ item->value = NULL;
+ if (delete_key != 0) {
+ free((void *)item->key);
+ }
+ free(item);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key,
+ size_t length)
+{
+ ck_hash_table_item_t **place = ck_find_item_nc(table, key, length);
+ if (place == NULL) {
+ return NULL;
+ }
+
+ ck_hash_table_item_t *item = *place;
+ *place = NULL;
+ return item;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return -1;
+ }
+
+ *to = (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t));
+
+ if (*to == NULL) {
+ ERR_ALLOC_FAILED;
+ return -2;
+ }
+ memset(*to, 0, sizeof(ck_hash_table_t));
+
+ // copy table count and table size exponent
+ (*to)->table_size_exp = from->table_size_exp;
+ (*to)->table_count = from->table_count;
+ assert((*to)->table_size_exp <= 32);
+
+ dbg_ck("Creating hash table for %u items.\n", from->table_count);
+ dbg_ck("Exponent: %u, number of tables: %u\n ",
+ (*to)->table_size_exp, (*to)->table_count);
+ dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n",
+ hashsize((*to)->table_size_exp),
+ sizeof(ck_hash_table_item_t *),
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+
+ // create tables
+ for (uint t = 0; t < (*to)->table_count; ++t) {
+ dbg_ck("Creating table %u...\n", t);
+ (*to)->tables[t] = (ck_hash_table_item_t **)malloc(
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+ if ((*to)->tables[t] == NULL) {
+ ERR_ALLOC_FAILED;
+ for (uint i = 0; i < t; ++i) {
+ free((*to)->tables[i]);
+ }
+ free(*to);
+ return -2;
+ }
+
+ // copy the table
+ memcpy((*to)->tables[t], from->tables[t],
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+ }
+
+ // copy the stash - we must explicitly copy each stash item, but do not
+ // copy the ck_hash_table_item_t within them.
+ ck_stash_item_t *si = from->stash;
+ ck_stash_item_t **pos = &(*to)->stash;
+ dbg_ck_verb(stderr, "Copying hash table stash.\n");
+ while (si != NULL) {
+ ck_stash_item_t *si_new = (ck_stash_item_t *)
+ malloc(sizeof(ck_stash_item_t));
+ if (si_new == NULL) {
+ ERR_ALLOC_FAILED;
+ // delete tables
+ for (uint i = 0; i < (*to)->table_count; ++i) {
+ free((*to)->tables[i]);
+ }
+ // delete created stash items
+ si_new = (*to)->stash;
+ while (si_new != NULL) {
+ ck_stash_item_t *prev = si_new;
+ si_new = si_new->next;
+ free(prev);
+ }
+ free(*to);
+ return -2;
+ }
+
+ dbg_ck("Copying stash item: %p with item %p, ", si, si->item);
+ dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+
+ si_new->item = si->item;
+ *pos = si_new;
+ pos = &si_new->next;
+ si = si->next;
+
+
+ dbg_ck("Old stash item: %p with item %p, ", si,
+ ((si == NULL) ? NULL : si->item));
+ if (si != NULL) {
+ dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+ } else {
+ dbg_ck("\n");
+ }
+ dbg_ck("New stash item: %p with item %p, ", si_new,
+ si_new->item);
+ dbg_ck("key: %.*s\n", (int)si_new->item->key_length,
+ si_new->item->key);
+ }
+
+ *pos = NULL;
+
+ // there should be no item being hashed right now
+ /*! \todo This operation should not be done while inserting / rehashing.
+ */
+ assert(from->hashed == NULL);
+ (*to)->hashed = NULL;
+
+ // initialize rehash/insert mutex
+ pthread_mutex_init(&(*to)->mtx_table, NULL);
+
+ // copy the generation
+ (*to)->generation = from->generation;
+
+ // copy the hash functions
+ memcpy(&(*to)->hash_system, &from->hash_system, sizeof(us_system_t));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_apply(ck_hash_table_t *table,
+ void (*function)(ck_hash_table_item_t *item, void *data),
+ void *data)
+{
+ if (table == NULL || function == NULL) {
+ return -1;
+ }
+
+ /*! \todo Ensure that no insertion nor rehash is made during applying.*/
+
+ // apply the function to all items in all tables
+ for (int t = 0; t < table->table_count; ++t) {
+ for (int i = 0; i < hashsize(table->table_size_exp); ++i) {
+ function(table->tables[t][i], data);
+ }
+ }
+
+ // apply the function to the stash items
+ ck_stash_item_t *si = table->stash;
+ while (si != NULL) {
+ function(si->item, data);
+ si = si->next;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_rehash(ck_hash_table_t *table)
+{
+ dbg_ck_hash("Rehashing items in table.\n");
+ SET_REHASHING_ON(&table->generation);
+
+ ck_stash_item_t *free_stash_items = NULL;
+
+ do {
+ // 1) Rehash items from stash
+ dbg_ck_rehash("Rehashing items from stash.\n");
+ ck_stash_item_t *item = table->stash;
+ ck_stash_item_t **item_place = &table->stash;
+ // terminate when at the end; this way the newly added items
+ // (added to the beginning) will be properly ignored
+ while (item != NULL) {
+ dbg_ck_rehash("Rehashing item with "
+ "key (length %zu): %.*s, generation: %hu, "
+ "table generation: %hu.\n", item->item->key_length,
+ (int)item->item->key_length, item->item->key,
+ GET_GENERATION(
+ item->item->timestamp),
+ GET_GENERATION(table->generation));
+
+ // put the hashed item to the prepared space
+ table->hashed = item->item;
+ item->item = NULL;
+ // we may use the place in the stash item as the free
+ // place for rehashing
+ if (ck_hash_item(table, &table->hashed, &item->item,
+ NEXT_GENERATION(table->generation)) != 0) {
+ // the free place was used
+ assert(item->item != NULL);
+ // we may leave the item there (in the stash)
+ assert(EQUAL_GENERATIONS(item->item->timestamp,
+ NEXT_GENERATION(table->generation)));
+ //assert(item->item == table->hashed);
+
+ item_place = &item->next;
+ item = item->next;
+ } else {
+ // the free place should be free
+ assert(item->item == NULL);
+ // and the item should be hashed too
+// assert(table->hashed == NULL);
+
+ // fix the pointer from the previous hash item
+ *item_place = item->next;
+ // and do not change the item place pointer
+
+ // put the stash item into list of free stash
+ // items
+ item->next = free_stash_items;
+ free_stash_items = item;
+
+ item = *item_place;
+ }
+ }
+
+ // 2) Rehash items from tables
+
+ // in case of failure, save the item in a temp variable
+ // which will be put to the stash
+ ck_hash_table_item_t *free = NULL;
+ assert(table->hashed == NULL);
+// ck_hash_table_item_t *old = table->hashed;
+
+ for (uint t = 0; t < table->table_count; ++t) {
+ uint rehashed = 0;
+
+ dbg_ck_rehash("Rehashing table %d.\n", t);
+
+ while (rehashed < hashsize(table->table_size_exp)) {
+
+ // if item's generation is the new generation,
+ // skip
+ if (table->tables[t][rehashed] == NULL
+ || !(EQUAL_GENERATIONS(
+ table->tables[t][rehashed]->timestamp,
+ table->generation))) {
+ dbg_ck_rehash("Skipping item.\n");
+ ++rehashed;
+ continue;
+ }
+
+ dbg_ck_rehash("Rehashing item with hash %u, "
+ "key (length %zu): %.*s, generation: %hu, "
+ "table generation: %hu.\n", rehashed,
+ table->tables[t][rehashed]->key_length,
+ (int)(table->tables[t][rehashed]->key_length),
+ table->tables[t][rehashed]->key,
+ GET_GENERATION(
+ table->tables[t][rehashed]->timestamp),
+ GET_GENERATION(table->generation));
+
+ // otherwise copy the item for rehashing
+ ck_put_item(&table->hashed, table->tables[t][rehashed]);
+ // clear the place so that this item will not
+ // get rehashed again
+ ck_clear_item(&table->tables[t][rehashed]);
+
+ dbg_ck_rehash("Table generation: %hu, next "
+ "generation: %hu.\n",
+ GET_GENERATION(table->generation),
+ NEXT_GENERATION(table->generation));
+
+ if (ck_hash_item(table, &table->hashed, &free,
+ NEXT_GENERATION(table->generation)) != 0) {
+ // loop occured
+ dbg_ck_hash("Hashing entered a loop."
+ "\n");
+ dbg_ck_rehash("Item with key %.*s "
+ "inserted into the free slot.\n",
+ free->key_length, free->key);
+
+ //assert(old == free);
+
+ // put the item into the stash, but
+ // try the free stash items first
+ if (free_stash_items != NULL) {
+ // take first
+ ck_stash_item_t *item =
+ free_stash_items;
+ free_stash_items = item->next;
+
+ item->item = free;
+ item->next = table->stash;
+ table->stash = item;
+ } else {
+ if (ck_add_to_stash(table, free)
+ != 0) {
+ ck_rollback_rehash(
+ table);
+ }
+ }
+
+ free = NULL;
+ table->hashed = NULL;
+ }
+ ++rehashed;
+ }
+ }
+
+ dbg_ck_rehash("Old table generation: %u\n",
+ GET_GENERATION(table->generation));
+ // rehashing completed, switch generation of the table
+ SET_NEXT_GENERATION(&table->generation);
+ dbg_ck_rehash("New table generation: %u\n",
+ GET_GENERATION(table->generation));
+ // generate new hash functions for the old generation
+ dbg_ck_rehash("Generating coeficients for generation: %u\n",
+ NEXT_GENERATION(table->generation));
+ us_next(&table->hash_system,
+ NEXT_GENERATION(table->generation));
+
+ } while (false /*! \todo Add proper condition!! */);
+
+ SET_REHASHING_OFF(&table->generation);
+
+ assert(table->hashed == NULL);
+
+
+ while (free_stash_items != NULL) {
+ ck_stash_item_t *item = free_stash_items;
+ free_stash_items = item->next;
+ assert(item->item == NULL);
+ free(item);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Rehashes the whole table.
+ *
+ * \param table Hash table to be rehashed.
+ *
+ * \note While rehashing no item should be inserted as it will result in a
+ * deadlock.
+ *
+ * \retval 0 No error.
+ * \retval -1 Rehashing failed. Some items may have been already moved and the
+ * rehashing flag remains set.
+ *
+ * \todo What if the stash is reallocated during ck_hash_item()? We'd be using
+ * the old stash for saving items! The old stash would not get deallocated
+ * (due to RCU - maybe put some rcu_read_lock() here), but the item
+ * would not be saved into the new stash!
+ * Maybe add a function for getting a pointer to particular item from
+ * the dynamic array and protect it using rcu_read_lock().
+ * Other option: Do not use pointer to an item in stash in the call to
+ * ck_hash_item(). Use some new place & put the item to the stash
+ * afterwards, protecting it using rcu_read_lock() and rcu_assign_pointer.
+ */
+//int ck_rehash(ck_hash_table_t *table)
+//{
+// dbg_ck_rehash("Rehashing items in table.\n");
+// SET_REHASHING_ON(&table->generation);
+
+// // we already have functions for the next generation, begin rehashing
+// // we wil use the last item in the buffer as free cell for hashing
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// ck_hash_table_item_t *old = (ck_hash_table_item_t *)
+// (malloc(sizeof(ck_hash_table_item_t)));
+
+// do {
+// dbg_ck_hash("Rehash!\n");
+
+// if (da_get_count(&table->stash) > STASH_SIZE) {
+// dbg_ck_hash("STASH RESIZED!!! (new stash size: %d)\n",
+// da_get_count(&table->stash));
+// }
+
+// // rehash items from stash, starting from the last old item
+// int stash_i = da_get_count(&table->stash) - 1;
+// while (stash_i >= 0) {
+// // if item's generation is the new generation, skip
+// if (STASH_ITEMS(&table->stash)[stash_i] == NULL
+// || !(EQUAL_GENERATIONS(STASH_ITEMS(&table->stash)
+// [stash_i]->timestamp,
+// table->generation))) {
+// dbg_ck_rehash("Skipping item.\n");
+// --stash_i;
+// continue;
+// }
+
+// dbg_ck_rehash("Rehashing item from buffer position %u"
+// ", key (length %u): %.*s, generation: "
+// "%hu, table generation: %hu.\n",
+// stash_i,
+// STASH_ITEMS(&table->stash)[stash_i]->key_length,
+// (int)STASH_ITEMS(&table->stash)[stash_i]->key_length,
+// STASH_ITEMS(&table->stash)[stash_i]->key,
+// GET_GENERATION(
+// STASH_ITEMS(&table->stash)[stash_i]->timestamp),
+// GET_GENERATION(table->generation));
+
+// // otherwise copy the item for rehashing
+// ck_put_item(&old, STASH_ITEMS(&table->stash)[stash_i]);
+// // clear the place so that this item will not get
+// // rehashed again
+// ck_clear_item(&STASH_ITEMS(&table->stash)[stash_i]);
+// da_release(&table->stash, 1);
+
+// // there should be at least one place in the stash
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// da_reserve(&table->stash, 1);
+
+// assert(STASH_ITEMS(&table->stash)[stash_i] == NULL);
+
+// // and start rehashing
+// if (ck_hash_item(table, &old,
+// &STASH_ITEMS(&table->stash)[stash_i],
+// NEXT_GENERATION(table->generation)) != 0) {
+// // loop occured
+// dbg_ck_hash("Hashing entered a loop.\n");
+
+// dbg_ck_rehash("Item with key %.*s inserted "
+// "into the stash on position %d.\n",
+// STASH_ITEMS(&table->stash)
+// [stash_i]->key_length,
+// STASH_ITEMS(&table->stash)
+// [stash_i]->key,
+// da_get_count(&table->stash));
+
+// // hashing unsuccessful, the item was inserted
+// // into the stash
+// da_occupy(&table->stash, 1);
+// assert(STASH_ITEMS(&table->stash)[stash_i]
+// != NULL);
+
+// // if only one place left, resize the stash
+// // TODO: Why???
+// if (da_reserve(&table->stash, 2) < 0) {
+// // stash could not be resized => !!!
+// dbg_ck_hash("Failed to rehash items "
+// "from "
+// "table, no other rehash possible!\n");
+// // so rollback
+// ck_rollback_rehash(table);
+// // clear the 'old' item
+// ck_clear_item(&old);
+// return -1;
+// }
+// }
+
+// // clear the 'old' item
+// ck_clear_item(&old);
+// // decrement the index
+// --stash_i;
+// }
+
+// uint i = 0;
+// while (i < da_get_count(&table->stash)) {
+// assert(STASH_ITEMS(&table->stash)[i] != NULL);
+// ++i;
+// }
+// dbg_ck_hash("OK\n");
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// assert(STASH_ITEMS(&table->stash)[da_get_count(&table->stash)]
+// == NULL);
+
+// // rehash items from hash tables
+// for (uint t = TABLE_FIRST;
+// t <= TABLE_LAST(table->table_count); ++t) {
+// dbg_ck_rehash("Rehashing items from table %d.\n",
+// t + 1);
+// uint rehashed = 0;
+
+// while (rehashed < hashsize(table->table_size_exp)) {
+
+// // if item's generation is the new generation,
+// // skip
+// if (table->tables[t][rehashed] == NULL
+// || !(EQUAL_GENERATIONS(
+// table->tables[t][rehashed]->timestamp,
+// table->generation))) {
+// dbg_ck_rehash("Skipping item.\n");
+// ++rehashed;
+// continue;
+// }
+
+// dbg_ck_rehash("Rehashing item with hash %u, "
+// "key (length %u): %.*s, generation: %hu, "
+// "table generation: %hu.\n", rehashed,
+// table->tables[t][rehashed]->key_length,
+// (int)(table->tables[t][rehashed]->key_length),
+// table->tables[t][rehashed]->key,
+// GET_GENERATION(
+// table->tables[t][rehashed]->timestamp),
+// GET_GENERATION(table->generation));
+
+// // otherwise copy the item for rehashing
+// ck_put_item(&old, table->tables[t][rehashed]);
+// // clear the place so that this item will not
+// // get rehashed again
+// ck_clear_item(&table->tables[t][rehashed]);
+
+// dbg_ck_rehash("Table generation: %hu, next "
+// "generation: %hu.\n",
+// GET_GENERATION(table->generation),
+// NEXT_GENERATION(table->generation));
+
+// // and start rehashing
+// assert(&old != &STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]);
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// da_reserve(&table->stash, 1);
+
+// if (ck_hash_item(table, &old,
+// &STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)],
+// NEXT_GENERATION(table->generation)) != 0) {
+// // loop occured
+// dbg_ck_hash("Hashing entered a loop."
+// "\n");
+// dbg_ck_rehash("Item with key %.*s "
+// "inserted into the stash on position "
+// "%d.\n", STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]
+// ->key_length,
+// STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]->key,
+// da_get_count(&table->stash));
+
+// assert(STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)] != NULL);
+// // loop occured, the item is already at
+// // its new place in the buffer, so just
+// // increment the index
+// da_occupy(&table->stash, 1);
+
+// // if only one place left, resize the
+// // stash TODO: Why?
+// if (da_reserve(&table->stash, 2) < 0) {
+// // stash could not be resized
+// dbg_ck_hash("Failed to rehash"
+// " items from table, no other "
+// "rehash possible!\n");
+// // so rollback
+// ck_rollback_rehash(table);
+// // clear the 'old' item
+// ck_clear_item(&old);
+// return -1;
+// }
+// }
+// ++rehashed;
+// }
+// }
+
+// dbg_ck_rehash("Old table generation: %u\n",
+// GET_GENERATION(table->generation));
+// // rehashing completed, switch generation of the table
+// SET_NEXT_GENERATION(&table->generation);
+// dbg_ck_rehash("New table generation: %u\n",
+// GET_GENERATION(table->generation));
+// // generate new hash functions for the old generation
+// dbg_ck_rehash("Generating coeficients for generation: %u\n",
+// NEXT_GENERATION(table->generation));
+// us_next(NEXT_GENERATION(table->generation));
+
+// // repeat rehashing while there are more items in the stash than
+// // its initial size
+// if (da_get_count(&table->stash) > STASH_SIZE) {
+// dbg_ck_rehash("Rehashing again!\n");
+// }
+// } while (da_get_count(&table->stash) > STASH_SIZE);
+
+// SET_REHASHING_OFF(&table->generation);
+
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+void ck_dump_table(const ck_hash_table_t *table)
+{
+#ifdef CUCKOO_DEBUG
+ uint i = 0;
+ dbg_ck("----------------------------------------------\n");
+ dbg_ck("Hash table dump:\n\n");
+ dbg_ck("Size of each table: %u\n\n", hashsize(table->table_size_exp));
+
+ for (uint t = 0; t < table->table_count; ++t) {
+ dbg_ck("Table %d:\n", t + 1);
+
+ for (i = 0; i < hashsize(table->table_size_exp); i++) {
+ dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i,
+ (int)(table->tables[t])[i]->key_length,
+ (table->tables[t])[i]->key,
+ (table->tables[t])[i]->value);
+ }
+ }
+
+ dbg_ck("Stash:\n");
+// for (i = 0; i < da_get_count(&table->stash); ++i) {
+// dbg_ck("Index: %u, Key: %.*s Value: %p.\n", i,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->key_length,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->key,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->value);
+// }
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i,
+ (int)item->item->key_length, item->item->key,
+ item->item->value);
+ item = item->next;
+ }
+
+ dbg_ck("\n");
+#endif
+}
diff --git a/src/libknot/hash/cuckoo-hash-table.h b/src/libknot/hash/cuckoo-hash-table.h
new file mode 100644
index 0000000..dd78294
--- /dev/null
+++ b/src/libknot/hash/cuckoo-hash-table.h
@@ -0,0 +1,333 @@
+/*!
+ * \file cuckoo-hash-table.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Implementation of Cuckoo hashing scheme.
+ *
+ * Uses d-ary Cuckoo hashing with stash.
+ *
+ * \todo Maybe provide some way to resize the whole table if the number of items
+ * grows too much.
+ * \todo Check size of integers, the table size may be larger than unsigned int.
+ * \todo Maybe do not return ck_hash_table_item from ck_find_item(), but only
+ * its value.
+ * \todo When hashing an item, only the first table is tried for this item.
+ * We may try all tables. (But it is not neccessary.)
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_CUCKOO_HASH_TABLE_H_
+#define _KNOT_CUCKOO_HASH_TABLE_H_
+
+#include <stdint.h> /* uint32_t */
+#include <stdlib.h> /* size_t */
+#include <pthread.h>
+
+#include "hash/universal-system.h"
+#include "common/dynamic-array.h"
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Macro for getting one hash table size. */
+#define hashsize(n) ((uint32_t)1 << (n))
+
+/*!
+ * \brief Max number of hash tables - must be the same as number of the hash
+ * functions in each generation of the universal system.
+ */
+#define MAX_TABLES US_FNC_COUNT
+
+/*! \brief Default stash size. */
+static const uint STASH_SIZE = 10;
+
+/*! \brief Maximum stash size. When achieved, rehashing is needed. */
+static const uint STASH_SIZE_MAX = 30;
+
+/*----------------------------------------------------------------------------*/
+/* Public structures */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for storing the hashed data.
+ */
+struct ck_hash_table_item {
+ const char *key; /*!< Key of the item, used for hashing. */
+
+ size_t key_length; /*!< Length of the key in octets. */
+
+ void *value; /*!< The actual item stored in the table. */
+
+ /*!
+ * \brief Flags. Currently used for keeping the generation of the item,
+ * i.e. the generation of the functions used for hashing this
+ * item.
+ *
+ * Form: 000000xy;
+ * xy - generation; may be 01 (1) or 10 (2).
+ */
+ uint8_t timestamp;
+};
+
+typedef struct ck_hash_table_item ck_hash_table_item_t;
+
+struct ck_stash_item {
+ ck_hash_table_item_t *item;
+ struct ck_stash_item *next;
+};
+
+typedef struct ck_stash_item ck_stash_item_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Hash table structure which uses cuckoo hashing.
+ *
+ * Keys are expected to be strings of characters (char *), not necesarily
+ * null-terminated. It uses the Fowler/Noll/Vo (FNV) hash function to
+ * obtain a 32bit unsigned integer from the character data and a function
+ * randomly chosen from an universal system (see universal-system.h) to obtain
+ * the final hash. The FNV hash was taken from
+ * http://home.comcast.net/~bretm/hash/6.html and the universal system is
+ * constructed according to Katajainen J., Lykke M., Experiments with universal
+ * hashing (obtained from
+ * http://www.diku.dk/OLD/publikationer/tekniske.rapporter/rapporter/96-08.pdf).
+ *
+ * The table uses either 3-ary or 4-ary cuckoo hashing (and thus 3 or 4 tables)
+ * with stash, according to the number of items provided to ck_create_table()
+ * function. The number of table pointers is however set to be the larger value
+ * (4) always, so the \a tables array may be statically allocated. Size of one
+ * table is always a power of 2 (due to the character of the hash function).
+ * The stash has a default size STASH_SIZE, but can be resized if needed.
+ * However, the resizing is only done in rehashing process, if the items do not
+ * fit into the table and the original stash.
+ *
+ * Rehashing is done when the stash gets full (actually, last item is always
+ * free and is used in the rehashing process as a temporary variable).
+ */
+struct ck_hash_table {
+ uint table_count; /*!< Actual number of hash tables used. */
+
+ /*!
+ * \brief Exponent of one table's size (2^table_size_exp is table size).
+ */
+ int table_size_exp;
+
+ ck_hash_table_item_t **tables[MAX_TABLES]; /*!< Array of hash tables. */
+
+ //da_array_t stash; /*!< Stash implemented as a dynamic array. */
+ ck_stash_item_t *stash;
+
+ /*! \brief Temporary storage for item being hashed. */
+ ck_hash_table_item_t *hashed;
+
+ /*! \brief Mutex for avoiding multiple insertions / rehashes at once. */
+ pthread_mutex_t mtx_table;
+
+ /*!
+ * \brief Flags used for determining which hash functions are currently
+ * used
+ *
+ * Form: 00000xyz.
+ * x - rehash flag (1 if rehashing is in progress)
+ * yz - generation (may be 10 = 2, or 01 = 1)
+ *
+ * There are always two sets of hash functions available via the
+ * us_hash() function (see universal-hashing.h). Normally all items in
+ * the table are hashed using one set of functions. However, during
+ * rehash, the other set is used for rehashing. In this case the rehash
+ * flag (x) is set, so the lookup function (ck_find_item()) tries to use
+ * both sets of functions when searching for item.
+ */
+ uint8_t generation;
+
+ us_system_t hash_system; /*!< Universal system of hash functions. */
+
+ size_t items;
+ size_t items_in_stash;
+};
+
+typedef struct ck_hash_table ck_hash_table_t;
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates and initializes the hash table structure.
+ *
+ * All hash tables are allocated and their items initialized to 0 (NULL).
+ * A stash of default size is also created. The \a generation flags are set to
+ * 0.
+ *
+ * \param items Number of items to be hashed to the table. This number
+ * determines the size of the hash table that will be created.
+ *
+ *
+ * \return Pointer to the initialized hash table.
+ */
+ck_hash_table_t *ck_create_table(uint items);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys the whole hash table together with the saved values.
+ *
+ * \param table Pointer to pointer to the hash table.
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ * \param delete_key Set to 0 if you do not want the function to delete the
+ * key of the item (e.g. when used elsewhere). Set to any
+ * other value otherwise.
+ *
+ * \note Make sure the table and its items are not used anymore when calling
+ * this function.
+ */
+void ck_destroy_table(ck_hash_table_t **table,
+ void (*dtor_value)(void *value), int delete_key);
+
+/*!
+ * \brief Destroys the table structures, but does not remove the individual
+ * hash table items.
+ */
+void ck_table_free(ck_hash_table_t **table);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Inserts item into the hash table.
+ *
+ * Insertion starts always by trying to hash the item into the first table. The
+ * possible displaced item is then hashed into randomly chosen other table,
+ * etc., until a free place is found or a loop occured. A loop occurs when one
+ * position in one table is tried more than twice.
+ *
+ * \param table Hash table the item should be inserted into.
+ * \param key Item's key. It can be any string of octets. The key is not copied
+ * by the function.
+ * \param length Length of the key in bytes (octets).
+ * \param value Pointer to the actual item to be inserted into the hash table.
+ *
+ * \note This function does not copy the key.
+ * \note This function may trigger rehash of the whole table in case the stash
+ * gets full.
+ *
+ * \retval 0 No error.
+ * \retval -1 Insertion failed. This may occur only when the rehashing fails.
+ * In this case it is necessary to somehow manually force another
+ * rehash as no other rehash would be possible.
+ */
+int ck_insert_item(ck_hash_table_t *table, const char *key, size_t length,
+ void *value);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds item in table.
+ *
+ * \param table Hash table to search in.
+ * \param key Key of the item. It can be an arbitrary string of octets.
+ * \param length Length of the key in bytes (octets).
+ *
+ * \return Pointer to the item if found. NULL otherwise.
+ */
+const ck_hash_table_item_t *ck_find_item(const ck_hash_table_t *table,
+ const char *key, size_t length);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Updates item with the given key by replacing its value.
+ *
+ * The update process is synchronized using RCU mechanism, so the old item's
+ * value will not be deleted while some thread is using it.
+ *
+ * \param table Hash table where to search for the item.
+ * \param key Key of the item to be updated. It can be an arbitrary string of
+ * octets.
+ * \param length Length of the key in bytes (octets).
+ * \param new_value New value for the item with key \a key.
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If the item was not found in the table. No changes are made.
+ */
+int ck_update_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void *new_value, void (*dtor_value)(void *value));
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Removes item with the given key from table.
+ *
+ * The deletion process is synchronized using RCU mechanism, so the old item
+ * will not be deleted while some thread is using it.
+ *
+ * \param table Hash table where to search for the item.
+ * \param key Key of the item to be removed. It can be an arbitrary string of
+ * octets.
+ * \param length Length of the key in bytes (octets).
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ * \param delete_key Set to 0 if you do not want the function to delete the
+ * key of the item (e.g. when used elsewhere). Set to any
+ * other value otherwise.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If the item was not found in the table.
+ */
+int ck_delete_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void (*dtor_value)(void *value), int delete_key);
+
+ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key,
+ size_t length);
+
+/*!
+ * \brief Creates a shallow copy of the cuckoo hash table.
+ *
+ * This function creates just the ck_hash_table_t structure and its tables and
+ * stash. It does not copy individual ck_hash_table_item_t structures.
+ *
+ * \param from Table to copy.
+ * \param to The new copy will be stored here.
+ *
+ * \retval 0 if successful.
+ * \retval
+ */
+int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to);
+
+int ck_apply(ck_hash_table_t *table,
+ void (*function)(ck_hash_table_item_t *item, void *data),
+ void *data);
+
+/*----------------------------------------------------------------------------*/
+
+int ck_rehash(ck_hash_table_t *table);
+
+// for testing purposes only
+int ck_resize_table(ck_hash_table_t *table);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Dumps the whole hash table to the standard output.
+ */
+void ck_dump_table(const ck_hash_table_t *table);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_CUCKOO_HASH_TABLE_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/hash-functions.c b/src/libknot/hash/hash-functions.c
new file mode 100644
index 0000000..a33dd6b
--- /dev/null
+++ b/src/libknot/hash/hash-functions.c
@@ -0,0 +1,241 @@
+/* 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 "hash-functions.h"
+
+/*--------------------------------- FNV HASH ---------------------------------*/
+
+unsigned long int fnv_hash(const char *data, int size, int bits)
+{
+ int shift, i;
+ unsigned long int mask;
+ unsigned long int hash = 2166136261;
+
+ if (bits == -1) {
+ shift = 0;
+ mask = 0xFFFFFFFF;
+ } else {
+ shift = 32 - bits;
+ mask = (1U << shift) - 1U;
+ }
+
+ for (i = 0; i < size; i++) {
+ hash = (hash * 16777619) ^ data[i];
+ }
+
+ if (shift == 0) {
+ return hash;
+ }
+
+ return (hash ^(hash >> shift)) & mask;
+}
+
+/*------------------------------- JENKINS HASH -------------------------------*/
+
+/* The mixing step */
+/*
+#define mix(a,b,c) \
+ { \
+ a=a-b; a=a-c; a=a^(c>>13); \
+ b=b-c; b=b-a; b=b^(a<<8); \
+ c=c-a; c=c-b; c=c^(b>>13); \
+ a=a-b; a=a-c; a=a^(c>>12); \
+ b=b-c; b=b-a; b=b^(a<<16); \
+ c=c-a; c=c-b; c=c^(b>>5); \
+ a=a-b; a=a-c; a=a^(c>>3); \
+ b=b-c; b=b-a; b=b^(a<<10); \
+ c=c-a; c=c-b; c=c^(b>>15); \
+ }
+*/
+
+///* The whole new hash function */
+//u4 jhash(register u1 *k, u4 length, u4 initval)
+//{
+// register u4 a, b, c; /* the internal state */
+// u4 len; /* how many key bytes still need mixing */
+
+// /* Set up the internal state */
+// len = length;
+// a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+// c = initval; /* variable initialization of internal state */
+
+// /*---------------------------------------- handle most of the key */
+// while (len >= 12) {
+// a = a + (k[0] + ((u4)k[1] << 8)
+// + ((u4)k[2] << 16) + ((u4)k[3] << 24));
+// b = b + (k[4] + ((u4)k[5] << 8)
+// + ((u4)k[6] << 16) + ((u4)k[7] << 24));
+// c = c + (k[8] + ((u4)k[9] << 8)
+// + ((u4)k[10] << 16) + ((u4)k[11] << 24));
+// mix(a, b, c);
+// k = k + 12;
+// len = len - 12;
+// }
+
+// /*------------------------------------- handle the last 11 bytes */
+// c = c + length;
+// switch (len) { /* all the case statements fall through */
+// case 11:
+// c = c + ((u4)k[10] << 24);
+// case 10:
+// c = c + ((u4)k[9] << 16);
+// case 9 :
+// c = c + ((u4)k[8] << 8);
+// /* the first byte of c is reserved for the length */
+// case 8 :
+// b = b + ((u4)k[7] << 24);
+// case 7 :
+// b = b + ((u4)k[6] << 16);
+// case 6 :
+// b = b + ((u4)k[5] << 8);
+// case 5 :
+// b = b + k[4];
+// case 4 :
+// a = a + ((u4)k[3] << 24);
+// case 3 :
+// a = a + ((u4)k[2] << 16);
+// case 2 :
+// a = a + ((u4)k[1] << 8);
+// case 1 :
+// a = a + k[0];
+// /* case 0: nothing left to add */
+// }
+// mix(a, b, c);
+// /*-------------------------------------------- report the result */
+// return c;
+//}
+
+
+
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bits set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 6*len+35 instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+ub4 jhash(k, length, initval)
+register ub1 *k; /* the key */
+register ub4 length; /* the length of the key */
+register ub4 initval; /* the previous hash, or an arbitrary value */
+{
+ register ub4 a,b,c,len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((ub4)k[10]<<24);
+ case 10: c+=((ub4)k[9]<<16);
+ case 9 : c+=((ub4)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((ub4)k[7]<<24);
+ case 7 : b+=((ub4)k[6]<<16);
+ case 6 : b+=((ub4)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((ub4)k[3]<<24);
+ case 3 : a+=((ub4)k[2]<<16);
+ case 2 : a+=((ub4)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+ /*-------------------------------------------- report the result */
+ return c;
+}
+
+#undef hashsize
+#undef hashmask
+
diff --git a/src/libknot/hash/hash-functions.h b/src/libknot/hash/hash-functions.h
new file mode 100644
index 0000000..f23730b
--- /dev/null
+++ b/src/libknot/hash/hash-functions.h
@@ -0,0 +1,85 @@
+/*!
+ * \file hash-functions.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Various hash functions.
+ *
+ * All of the hash functions are downloaded from various sources.
+ *
+ * \todo Add references to sources.
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_HASH_FUNCTIONS_H_
+#define _KNOT_HASH_FUNCTIONS_H_
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Fowler / Noll / Vo Hash (FNV Hash)
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * This is an implementation of the algorithms posted above.
+ * This file is placed in the public domain by Peter Wemm.
+ *
+ * $FreeBSD: src/sys/sys/fnv_hash.h,v 1.2.2.1 2001/03/21 10:50:59 peter Exp $
+ */
+
+typedef uint32_t Fnv32_t;
+
+#define FNV1_32_INIT ((Fnv32_t) 33554467UL)
+
+#define FNV_32_PRIME ((Fnv32_t) 0x01000193UL)
+
+static __inline Fnv32_t
+fnv_32_buf(const void *buf, size_t len, Fnv32_t hval)
+{
+ const uint8_t *s = (const uint8_t *)buf;
+
+ while (len-- != 0) {
+ hval *= FNV_32_PRIME;
+ hval ^= *s++;
+ }
+ return hval;
+}
+
+/*!
+ * \brief Jenkins hash function.
+ *
+ * Downloaded from http://burtleburtle.net/bob/hash/evahash.html
+ *
+ * \param k Data to hash
+ * \param length Size of the data in bytes.
+ * \param initval The previous hash or an arbitrary value.
+ *
+ * \return Hash of the data.
+ *
+ * \todo Add source.
+ */
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+typedef unsigned char ub1; /* unsigned 1-byte quantities */
+
+ub4 jhash(register ub1 *k, register ub4 length, register ub4 initval);
+
+#endif /* _KNOT_HASH_FUNCTIONS_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/universal-system.c b/src/libknot/hash/universal-system.c
new file mode 100644
index 0000000..096974c
--- /dev/null
+++ b/src/libknot/hash/universal-system.c
@@ -0,0 +1,116 @@
+/* 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 <limits.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "universal-system.h"
+#include "common.h"
+#include "util/utils.h"
+
+/*----------------------------------------------------------------------------*/
+
+const uint MAX_UINT_EXP = 32;
+const unsigned long MAX_UINT_MY = UINT32_MAX; /* 4294967295 */
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Generates new set of coeficients.
+ *
+ * \param system Universal system to generate the coeficients for.
+ * \param from First coeficient to be replaced.
+ * \param to Up to this the coeficients will be replaced.
+ */
+static void us_generate_coefs(us_system_t *system, uint from, uint to)
+{
+ assert(system != NULL);
+
+ for (uint i = from; i < to; ++i) {
+ int used = 0;
+
+ do {
+ // generate random odd number
+ system->coefs[i] = knot_quick_rand() % MAX_UINT_MY;
+ if (system->coefs[i] % 2 == 0) {
+ system->coefs[i] = (system->coefs[i] == 0)
+ ? 1
+ : system->coefs[i] - 1;
+ }
+ // check if this coeficient is already used
+ uint j = from;
+ while (used == 0 && j < i) {
+ if (system->coefs[j++] == system->coefs[i]) {
+ used = 1;
+ }
+ }
+ // if already used, generate again
+ } while (used != 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+void us_initialize(us_system_t *system)
+{
+ assert(system != NULL);
+ assert(UINT_MAX == MAX_UINT_MY);
+
+ // Initialize both generations of functions by generating random odd
+ // numbers
+ us_generate_coefs(system, 0, US_FNC_COUNT * GEN_COUNT);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \note \a generation starts from 1
+ */
+int us_next(us_system_t *system, uint generation)
+{
+ assert(system != NULL);
+ // generate new coeficients for the new generation
+ us_generate_coefs(system, (generation - 1) * US_FNC_COUNT,
+ generation * US_FNC_COUNT);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint32_t us_hash(const us_system_t *system, uint32_t value, uint table_exp,
+ uint fnc, uint generation)
+{
+ /*
+ * multiplication should overflow if larger than MAX_UINT
+ * this is the same as (coef * value) mod MAX_UINT
+ *
+ * TODO: maybe we should not rely on this
+ */
+ assert(system != NULL);
+ assert(table_exp <= 32);
+ assert(fnc < US_FNC_COUNT);
+ assert(generation <= GEN_COUNT);
+
+ return ((system->coefs[((generation - 1) * US_FNC_COUNT) + fnc] * value)
+ >> (MAX_UINT_EXP - table_exp));
+}
diff --git a/src/libknot/hash/universal-system.h b/src/libknot/hash/universal-system.h
new file mode 100644
index 0000000..25330de
--- /dev/null
+++ b/src/libknot/hash/universal-system.h
@@ -0,0 +1,109 @@
+/*!
+ * \file universal-system.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * This file provides interface to a 2-universal system of hash functions that
+ * hash from 32-bit unsigned integer to a 32-bit unsigned integer within a given
+ * range. The range is always a power of two and is given by the exponent (see
+ * function us_hash().
+ *
+ * Before using the system, it must be initialized by calling us_initialize().
+ * The system stores 2 sets (generations), each of US_FNC_COUNT functions.
+ * For generating a new set of coeficients (i.e. hash functions) use the
+ * us_next() function.
+ *
+ * For hashing use the us_hash() function.
+ *
+ * \todo What if all numbers are tried and still need rehash?
+ * (that means 2mld rehashes - we can probably live with that ;)
+ * \todo Consider counting generations from 0, will be easier!
+ * \todo Check out some better random number generator.
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_UNIVERSAL_SYSTEM_H_
+#define _KNOT_UNIVERSAL_SYSTEM_H_
+
+#include <stdint.h>
+#include "common.h"
+
+
+enum { US_FNC_COUNT = 4 /*!< Number of functions for one generation. */ };
+
+enum { GEN_COUNT = 2 /*!< Number of generations. */ };
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Analytically defined universal system of hashing functions. */
+struct us_system {
+ /*! \brief Coeficients for the functions */
+ uint coefs[US_FNC_COUNT * GEN_COUNT];
+};
+
+typedef struct us_system us_system_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes the universal system by generating coeficients for all
+ * hash functions and all generations.
+ *
+ * \param system Universal system to be used.
+ */
+void us_initialize(us_system_t *system);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Generates new hash functions' coeficients for the given \a generation.
+ *
+ * \param system Universal system to be used.
+ * \param generation Generation for which to generate the new coeficients.
+ *
+ * \return 0
+ */
+int us_next(us_system_t *system, uint generation);
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Hashes the \a value using the given \a exponent and function.
+ *
+ * The actual formula of the hash is:
+ * h = ((coef * value) mod 2^32) / 2^(32 - table_exp)
+ * where \a coef is the proper coeficient.
+ *
+ * \param system Universal system to be used.
+ * \param value Value to be hashed.
+ * \param table_exp Determines the upper bound for the result - the hash will
+ * be between 0 and 2^(32 - table_exp).
+ * \param fnc Which function from the set should be used.
+ * \param generation Which set (generation) of functions should be used.
+ *
+ * \todo Make inline?
+ *
+ * \return Hash value (32bit unsigned).
+ */
+uint32_t us_hash(const us_system_t *system, uint32_t value, uint table_exp,
+ uint fnc, uint generation);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_UNIVERSAL_SYSTEM_H_ */
+
+/*! @} */
diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h
new file mode 100644
index 0000000..a401be7
--- /dev/null
+++ b/src/libknot/libknot.h
@@ -0,0 +1,48 @@
+/*!
+ * \file libknot.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Convenience header for including whole library.
+ *
+ * \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_LIBKNOT_H_
+#define _KNOT_LIBKNOT_H_
+
+#include "consts.h"
+#include "util/descriptor.h"
+#include "dname.h"
+#include "edns.h"
+#include "zone/node.h"
+#include "nsec3.h"
+#include "util/wire.h"
+#include "rdata.h"
+#include "packet/response.h"
+#include "rrset.h"
+#include "util/tolower.h"
+#include "util/utils.h"
+#include "zone/zone.h"
+#include "zone/zonedb.h"
+#include "util/error.h"
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
new file mode 100644
index 0000000..f88f802
--- /dev/null
+++ b/src/libknot/nameserver/name-server.c
@@ -0,0 +1,3663 @@
+/* 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 <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <urcu.h>
+
+#include "nameserver/name-server.h"
+#include "updates/xfr-in.h"
+
+#include "util/error.h"
+#include "libknot.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+#include "packet/response.h"
+#include "packet/query.h"
+#include "consts.h"
+#include "updates/changesets.h"
+#include "updates/ddns.h"
+#include "tsig-op.h"
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Maximum UDP payload with EDNS enabled. */
+static const uint16_t MAX_UDP_PAYLOAD_EDNS = 4096;
+/*! \brief Maximum UDP payload with EDNS disabled. */
+static const uint16_t MAX_UDP_PAYLOAD = 504; // 512 - 8B header
+/*! \brief Maximum size of one AXFR response packet. */
+static const uint16_t MAX_AXFR_PAYLOAD = 65535;
+/*! \brief Supported EDNS version. */
+static const uint8_t EDNS_VERSION = 0;
+/*! \brief Determines whether EDNS is enabled. */
+static const int EDNS_ENABLED = 1;
+
+/*! \brief TTL of a CNAME synthetized from a DNAME. */
+static const uint32_t SYNTH_CNAME_TTL = 0;
+
+/*! \brief Determines whether DNSSEC is enabled. */
+static const int DNSSEC_ENABLED = 1;
+
+/*! \brief Determines whether NSID is enabled. */
+static const int NSID_ENABLED = 1;
+
+/*! \brief Length of NSID option data. */
+static const uint16_t NSID_LENGTH = 6;
+/*! \brief NSID option data. */
+static const uint8_t NSID_DATA[6] = {0x46, 0x6f, 0x6f, 0x42, 0x61, 0x72};
+
+/*! \brief Internal error code to propagate need for SERVFAIL response. */
+static const int NS_ERR_SERVFAIL = -999;
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds zone where to search for the QNAME.
+ *
+ * \note As QTYPE DS requires special handling, this function finds a zone for
+ * a direct predecessor of QNAME in such case.
+ *
+ * \param zdb Zone database where to search for the proper zone.
+ * \param qname QNAME.
+ * \param qtype QTYPE.
+ *
+ * \return Zone to which QNAME belongs (according to QTYPE), or NULL if no such
+ * zone was found.
+ */
+static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb,
+ const knot_dname_t *qname,
+ uint16_t qtype)
+{
+ const knot_zone_t *zone;
+ /*
+ * Find a zone in which to search.
+ *
+ * In case of DS query, we strip the leftmost label when searching for
+ * the zone (but use whole qname in search for the record), as the DS
+ * 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);
+ } else {
+ zone = knot_zonedb_find_zone_for_name(zdb, qname);
+ }
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Synthetizes RRSet from a wildcard RRSet using the given QNAME.
+ *
+ * The synthetized RRSet is identical to the wildcard RRSets, except that the
+ * owner name is replaced by \a qname.
+ *
+ * \param wildcard_rrset Wildcard RRSet to synthetize from.
+ * \param qname Domain name to be used as the owner of the synthetized RRset.
+ *
+ * \return The synthetized RRSet (this is a newly created RRSet, remember to
+ * free it).
+ */
+static knot_rrset_t *ns_synth_from_wildcard(
+ const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname)
+{
+ dbg_ns("Synthetizing RRSet from wildcard...\n");
+
+ knot_dname_t *owner = knot_dname_deep_copy(qname);
+// printf("Copied owner ptr: %p\n", owner);
+
+ knot_rrset_t *synth_rrset = knot_rrset_new(
+ owner, knot_rrset_type(wildcard_rrset),
+ knot_rrset_class(wildcard_rrset),
+ knot_rrset_ttl(wildcard_rrset));
+
+ /* Release owner, as it's retained in rrset. */
+ knot_dname_release(owner);
+
+ if (synth_rrset == NULL) {
+ return NULL;
+ }
+
+ dbg_ns("Created RRSet header:\n");
+ knot_rrset_dump(synth_rrset, 1);
+
+ // copy all RDATA
+ const knot_rdata_t *rdata = knot_rrset_rdata(wildcard_rrset);
+ while (rdata != NULL) {
+ // we could use the RDATA from the wildcard rrset
+ // but there is no way to distinguish it when deleting
+ // temporary RRSets
+ knot_rdata_t *rdata_copy = knot_rdata_deep_copy(rdata,
+ knot_rrset_type(synth_rrset));
+ if (rdata_copy == NULL) {
+ knot_rrset_deep_free(&synth_rrset, 1, 1, 0);
+ return NULL;
+ }
+
+ dbg_ns("Copied RDATA:\n");
+ knot_rdata_dump(rdata_copy,
+ knot_rrset_type(synth_rrset), 1);
+
+ knot_rrset_add_rdata(synth_rrset, rdata_copy);
+ rdata = knot_rrset_rdata_next(wildcard_rrset, rdata);
+ }
+
+// printf("Synthetized RRSet pointer: %p\n", synth_rrset);
+ return synth_rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the given RRSet is a wildcard RRSet and replaces it with
+ * a synthetized RRSet if required.
+ *
+ * \param name Domain name to be used as the owner of the possibly synthetized
+ * RRSet
+ * \param resp Response to which the synthetized RRSet should be stored (as a
+ * temporary RRSet).
+ * \param rrset RRSet to check (and possibly replace).
+ */
+static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
+ const knot_rrset_t **rrset)
+{
+ assert(name != NULL);
+ assert(resp != NULL);
+ assert(rrset != NULL);
+ assert(*rrset != NULL);
+
+ if (knot_dname_is_wildcard((*rrset)->owner)) {
+ knot_rrset_t *synth_rrset =
+ ns_synth_from_wildcard(*rrset, name);
+ dbg_ns("Synthetized RRSet:\n");
+ knot_rrset_dump(synth_rrset, 1);
+ knot_packet_add_tmp_rrset(resp, synth_rrset);
+ *rrset = synth_rrset;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds signatures (RRSIGs) for the given RRSet to the response.
+ *
+ * This function first checks if DNSSEC is enabled and if it was requested in
+ * the response (DO bit set). If not, it does nothing and returns 0. If yes,
+ * it retrieves RRSIGs stored in the RRSet, deals with possible wildcard owner
+ * and adds the RRSIGs to response using the given function (that determines
+ * to which section of the response they will be added).
+ *
+ * \param rrset RRSet to get the RRSIGs from.
+ * \param resp Response where to add the RRSIGs.
+ * \param name Actual name to be used as owner in case of wildcard RRSet.
+ * \param add_rrset_to_resp Function for adding the RRSIG RRset to the response.
+ * \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the
+ * TC bit in the response.
+ *
+ * \return KNOT_EOK
+ * \return KNOT_ENOMEM
+ * \return KNOT_ESPACE
+ */
+static int ns_add_rrsigs(const knot_rrset_t *rrset, knot_packet_t *resp,
+ const knot_dname_t *name,
+ int (*add_rrset_to_resp)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int),
+ int tc)
+{
+ const knot_rrset_t *rrsigs;
+
+ dbg_ns("Adding RRSIGs for RRSet, type: %s.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ assert(resp != NULL);
+ assert(add_rrset_to_resp != NULL);
+
+ dbg_ns("DNSSEC requested: %d\n",
+ knot_query_dnssec_requested(knot_packet_query(resp)));
+ dbg_ns("RRSIGS: %p\n", knot_rrset_rrsigs(rrset));
+
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && (rrsigs = knot_rrset_rrsigs(rrset)) != NULL) {
+ if (name != NULL) {
+ ns_check_wildcard(name, resp, &rrsigs);
+ }
+ return add_rrset_to_resp(resp, rrsigs, tc, 0, 0);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Resolves CNAME chain starting in \a node, stores all the CNAMEs in the
+ * response and updates \a node and \a qname to the last node in the
+ * chain.
+ *
+ * \param node Node (possibly) containing a CNAME RR.
+ * \param qname Searched name. Will be updated to the canonical name.
+ * \param resp Response where to add the CNAME RRs.
+ * \param add_rrset_to_resp Function for adding the CNAME RRs to the response.
+ * \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the
+ * TC bit in the response.
+ */
+static void ns_follow_cname(const knot_node_t **node,
+ const knot_dname_t **qname,
+ knot_packet_t *resp,
+ int (*add_rrset_to_resp)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int),
+ int tc)
+{
+ dbg_ns("Resolving CNAME chain...\n");
+ const knot_rrset_t *cname_rrset;
+
+ while (*node != NULL
+ && (cname_rrset = knot_node_rrset(*node, KNOT_RRTYPE_CNAME))
+ != NULL) {
+ /* put the CNAME record to answer, but replace the possible
+ wildcard name with qname */
+
+ assert(cname_rrset != NULL);
+
+ dbg_ns("CNAME RRSet: %p, owner: %p\n", cname_rrset,
+ cname_rrset->owner);
+
+ const knot_rrset_t *rrset = cname_rrset;
+
+ // ignoring other than the first record
+ if (knot_dname_is_wildcard(knot_node_owner(*node))) {
+ /* if wildcard node, we must copy the RRSet and
+ replace its owner */
+ rrset = ns_synth_from_wildcard(cname_rrset, *qname);
+ knot_packet_add_tmp_rrset(resp, (knot_rrset_t *)rrset);
+ add_rrset_to_resp(resp, rrset, tc, 0, 0);
+ ns_add_rrsigs(cname_rrset, resp, *qname,
+ add_rrset_to_resp, tc);
+ } else {
+ add_rrset_to_resp(resp, rrset, tc, 0, 0);
+ ns_add_rrsigs(rrset, resp, *qname, add_rrset_to_resp,
+ tc);
+ }
+
+ dbg_ns("Using RRSet: %p, owner: %p\n", rrset, rrset->owner);
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(rrset));
+ dbg_ns("CNAME record for owner %s put to response.\n", name);
+ free(name);
+);
+
+ // get the name from the CNAME RDATA
+ const knot_dname_t *cname = knot_rdata_cname_name(
+ knot_rrset_rdata(cname_rrset));
+ dbg_ns("CNAME name from RDATA: %p\n", cname);
+ // change the node to the node of that name
+ *node = knot_dname_node(cname, 1);
+ dbg_ns("This name's node: %p\n", *node);
+// // it is not an old node and if yes, skip it
+// if (knot_node_is_old(*node)) {
+// *node = knot_node_new_node(*node);
+// }
+
+ // save the new name which should be used for replacing wildcard
+ *qname = cname;
+ };
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves RRSet(s) of given type from the given node and adds them to
+ * the response's Answer section.
+ *
+ * \param node Node where to take the RRSet from.
+ * \param name Actual searched name (used in case of wildcard RRSet(s)).
+ * \param type Type of the RRSet(s). If set to KNOT_RRTYPE_ANY, all RRSets
+ * from the node will be added to the answer.
+ * \param resp Response where to add the RRSets.
+ *
+ * \return Number of RRSets added.
+ */
+static int ns_put_answer(const knot_node_t *node, const knot_dname_t *name,
+ uint16_t type, knot_packet_t *resp)
+{
+ int added = 0;
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(node->owner);
+ dbg_ns("Putting answers from node %s.\n", name_str);
+ free(name_str);
+);
+
+ switch (type) {
+ case KNOT_RRTYPE_ANY: {
+ dbg_ns("Returning all RRTYPES.\n");
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ break;
+ }
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = rrsets[i];
+
+ dbg_ns(" Type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ ns_check_wildcard(name, resp, &rrset);
+ ret = knot_response_add_rrset_answer(resp, rrset, 1,
+ 0, 0);
+ if (ret >= 0 && (added += 1)
+ && (ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1))
+ >=0 ) {
+ added += 1;
+ } else {
+ free(rrsets);
+ rrsets = NULL;
+ break;
+ }
+
+ ++i;
+ }
+ if (rrsets != NULL) {
+ free(rrsets);
+ }
+ break;
+ }
+ case KNOT_RRTYPE_RRSIG: {
+ dbg_ns("Returning all RRSIGs.\n");
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ break;
+ }
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = knot_rrset_rrsigs(rrsets[i]);
+
+ if (rrset == NULL) {
+ ++i;
+ continue;
+ }
+
+ ns_check_wildcard(name, resp, &rrset);
+ ret = knot_response_add_rrset_answer(resp, rrset, 1,
+ 0, 0);
+
+ if (ret < 0) {
+ break;
+ }
+
+ added += 1;
+ ++i;
+ }
+ free(rrsets);
+ break;
+ }
+ default: {
+ int ret = 0;
+ const knot_rrset_t *rrset = knot_node_rrset(node, type);
+ const knot_rrset_t *rrset2 = rrset;
+ if (rrset != NULL) {
+ dbg_ns("Found RRSet of type %s\n",
+ knot_rrtype_to_string(type));
+ ns_check_wildcard(name, resp, &rrset2);
+ ret = knot_response_add_rrset_answer(resp, rrset2, 1,
+ 0, 0);
+ if (ret >= 0 && (added += 1)
+ && (ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1)) > 0) {
+ added += 1;
+ }
+ }
+ }
+ }
+
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ return added;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds RRSets to Additional section of the response.
+ *
+ * This function uses knot_rdata_get_name() to get the domain name from the
+ * RDATA of the RRSet according to its type. It also does not search for the
+ * retrieved domain name, but just uses its node field. Thus to work correctly,
+ * the zone where the RRSet is from should be adjusted using
+ * knot_zone_adjust_dnames().
+ *
+ * A and AAAA RRSets (and possible CNAMEs) for the found domain names are added.
+ *
+ * \warning Use this function only with types containing some domain name,
+ * otherwise it will crash (or behave strangely).
+ *
+ * \param resp Response where to add the Additional data.
+ * \param rrset RRSet to get the Additional data for.
+ */
+static void ns_put_additional_for_rrset(knot_packet_t *resp,
+ const knot_rrset_t *rrset)
+{
+ const knot_node_t *node = NULL;
+ const knot_rdata_t *rdata = NULL;
+ const knot_dname_t *dname = NULL;
+
+ // for all RRs in the RRset
+ rdata = knot_rrset_rdata(rrset);
+ while (rdata != NULL) {
+ dbg_ns("Getting name from RDATA, type %s..\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+ dname = knot_rdata_get_name(rdata,
+ knot_rrset_type(rrset));
+ assert(dname != NULL);
+ node = knot_dname_node(dname, 1);
+// // check if the node is not old and if yes, take the new one
+// if (knot_node_is_old(node)) {
+// node = knot_node_new_node(node);
+// }
+
+ dbg_ns_detail("Node saved in RDATA dname: %p\n", node);
+
+ if (node != NULL && node->owner != dname) {
+ // the stored node should be the closest encloser
+ assert(knot_dname_is_subdomain(dname, node->owner));
+ // try the wildcard child, if any
+ node = knot_node_wildcard_child(node, 1);
+// // this should not be old node!!
+// assert(!knot_node_is_old(node));
+ }
+
+ const knot_rrset_t *rrset_add;
+
+ if (node != NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_ns("Putting additional from node %s\n", name);
+ free(name);
+);
+ dbg_ns("Checking CNAMEs...\n");
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)
+ != NULL) {
+ dbg_ns("Found CNAME in node, following...\n");
+ const knot_dname_t *dname
+ = knot_node_owner(node);
+ ns_follow_cname(&node, &dname, resp,
+ knot_response_add_rrset_additional, 0);
+ }
+
+ // A RRSet
+ dbg_ns("A RRSets...\n");
+ rrset_add = knot_node_rrset(node, KNOT_RRTYPE_A);
+ if (rrset_add != NULL) {
+ dbg_ns("Found A RRsets.\n");
+ const knot_rrset_t *rrset_add2 = rrset_add;
+ ns_check_wildcard(dname, resp, &rrset_add2);
+ knot_response_add_rrset_additional(
+ resp, rrset_add2, 0, 1, 0);
+ ns_add_rrsigs(rrset_add, resp, dname,
+ knot_response_add_rrset_additional, 0);
+ }
+
+ // AAAA RRSet
+ dbg_ns("AAAA RRSets...\n");
+ rrset_add = knot_node_rrset(node, KNOT_RRTYPE_AAAA);
+ if (rrset_add != NULL) {
+ dbg_ns("Found AAAA RRsets.\n");
+ const knot_rrset_t *rrset_add2 = rrset_add;
+ ns_check_wildcard(dname, resp, &rrset_add2);
+ knot_response_add_rrset_additional(
+ resp, rrset_add2, 0, 1, 0);
+ ns_add_rrsigs(rrset_add, resp, dname,
+ knot_response_add_rrset_additional, 0);
+ }
+ }
+
+ assert(rrset != NULL);
+ assert(rdata != NULL);
+ rdata = knot_rrset_rdata_next(rrset, rdata);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks whether the given type requires additional processing.
+ *
+ * Only MX, NS and SRV types require additional processing.
+ *
+ * \param qtype Type to check.
+ *
+ * \retval <> 0 if additional processing is needed for \a qtype.
+ * \retval 0 otherwise.
+ */
+static int ns_additional_needed(uint16_t qtype)
+{
+ return (qtype == KNOT_RRTYPE_MX ||
+ qtype == KNOT_RRTYPE_NS ||
+ qtype == KNOT_RRTYPE_SRV);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds whatever Additional RRSets are required for the response.
+ *
+ * For each RRSet in Answer and Authority sections this function checks if
+ * additional processing is needed and if yes, it puts any Additional RRSets
+ * available to the Additional section of the response.
+ *
+ * \param resp Response to process.
+ */
+static void ns_put_additional(knot_packet_t *resp)
+{
+ dbg_ns("ADDITIONAL SECTION PROCESSING\n");
+
+ const knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < knot_packet_answer_rrset_count(resp); ++i) {
+ rrset = knot_packet_answer_rrset(resp, i);
+ assert(rrset != NULL);
+ if (ns_additional_needed(knot_rrset_type(rrset))) {
+ ns_put_additional_for_rrset(resp, rrset);
+ }
+ }
+
+ for (int i = 0; i < knot_packet_authority_rrset_count(resp); ++i) {
+ rrset = knot_packet_authority_rrset(resp, i);
+ if (ns_additional_needed(knot_rrset_type(rrset))) {
+ ns_put_additional_for_rrset(resp, rrset);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts authority NS RRSet to the Auhority section of the response.
+ *
+ * \param zone Zone to take the authority NS RRSet from.
+ * \param resp Response where to add the RRSet.
+ */
+static void ns_put_authority_ns(const knot_zone_contents_t *zone,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *ns_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_NS);
+
+ if (ns_rrset != NULL) {
+ knot_response_add_rrset_authority(resp, ns_rrset, 0, 1, 0);
+ ns_add_rrsigs(ns_rrset, resp, knot_node_owner(
+ knot_zone_contents_apex(zone)),
+ knot_response_add_rrset_authority, 1);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts SOA RRSet to the Auhority section of the response.
+ *
+ * \param zone Zone to take the SOA RRSet from.
+ * \param resp Response where to add the RRSet.
+ */
+static void ns_put_authority_soa(const knot_zone_contents_t *zone,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *soa_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
+ assert(soa_rrset != NULL);
+
+ knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0);
+ ns_add_rrsigs(soa_rrset, resp,
+ knot_node_owner(knot_zone_contents_apex(zone)),
+ knot_response_add_rrset_authority, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a 'next closer name' to the given domain name.
+ *
+ * For definition of 'next closer name', see RFC5155, Page 6.
+ *
+ * \param closest_encloser Closest encloser of \a name.
+ * \param name Domain name to create the 'next closer' name to.
+ *
+ * \return 'Next closer name' to the given domain name or NULL if an error
+ * occured.
+ */
+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);
+
+ assert(ce_labels < qname_labels);
+
+ // the common labels should match
+ assert(knot_dname_matched_labels(closest_encloser, name)
+ == 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);
+ }
+
+ return next_closer;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds NSEC3 RRSet (together with corresponding RRSIGs) from the given
+ * node into the response.
+ *
+ * \param node Node to get the NSEC3 RRSet from.
+ * \param resp Response where to add the RRSets.
+ */
+static void ns_put_nsec3_from_node(const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ const knot_rrset_t *rrset = knot_node_rrset(node,
+ KNOT_RRTYPE_NSEC3);
+ assert(rrset != NULL);
+
+ int res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0);
+ // add RRSIG for the RRSet
+ if (res == 0 && (rrset = knot_rrset_rrsigs(rrset)) != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds and adds NSEC3 covering the given domain name (and their
+ * associated RRSIGs) to the response.
+ *
+ * \param zone Zone used for answering.
+ * \param name Domain name to cover.
+ * \param resp Response where to add the RRSets.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL if a runtime collision occured. The server should
+ * respond with SERVFAIL in such case.
+ */
+static int ns_put_covering_nsec3(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ knot_packet_t *resp)
+{
+ const knot_node_t *prev, *node;
+ /*! \todo Check version. */
+ int match = knot_zone_contents_find_nsec3_for_name(zone, name,
+ &node, &prev, 1);
+ assert(match >= 0);
+ node = knot_node_current(node);
+ prev = knot_node_current(prev);
+
+ if (match == KNOT_ZONE_NAME_FOUND){
+ // run-time collision => SERVFAIL
+ return KNOT_EOK;
+ }
+
+// // check if the prev node is not old and if yes, take the new one
+// if (knot_node_is_old(prev)) {
+// prev = knot_node_new_node(prev);
+// assert(prev != NULL);
+// }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(prev->owner);
+ dbg_ns("Covering NSEC3 node: %s\n", name);
+ free(name);
+);
+
+ ns_put_nsec3_from_node(prev, resp);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds NSEC3s comprising the 'closest encloser proof' for the given
+ * (non-existent) domain name (and their associated RRSIGs) to the
+ * response.
+ *
+ * For definition of 'closest encloser proof', see RFC5155, section 7.2.1,
+ * Page 18.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param qname Searched (non-existent) name.
+ * \param resp Response where to add the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_closest_encloser_proof(
+ const knot_zone_contents_t *zone,
+ const knot_node_t **closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ assert(zone != NULL);
+ assert(closest_encloser != NULL);
+ assert(*closest_encloser != NULL);
+ assert(qname != NULL);
+ assert(resp != NULL);
+
+ if (knot_zone_contents_nsec3params(zone) == NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_ns("No NSEC3PARAM found in zone %s.\n", name);
+ free(name);
+);
+ return KNOT_EOK;
+ }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(*closest_encloser));
+ dbg_ns("Closest encloser: %s\n", name);
+ free(name);
+);
+
+ /*
+ * 1) NSEC3 that matches closest provable encloser.
+ */
+ const knot_node_t *nsec3_node = NULL;
+ const knot_dname_t *next_closer = NULL;
+ while ((nsec3_node = knot_node_nsec3_node((*closest_encloser), 1))
+ == NULL) {
+ next_closer = knot_node_owner((*closest_encloser));
+ *closest_encloser = knot_node_parent(*closest_encloser, 1);
+ if (*closest_encloser == NULL) {
+ // there are no NSEC3s to add
+ return KNOT_EOK;
+ }
+ }
+
+ assert(nsec3_node != NULL);
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(nsec3_node->owner);
+ dbg_ns("NSEC3 node: %s\n", name);
+ free(name);
+ name = knot_dname_to_str((*closest_encloser)->owner);
+ dbg_ns("Closest provable encloser: %s\n", name);
+ free(name);
+ if (next_closer != NULL) {
+ name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+ } else {
+ dbg_ns("Next closer name: none\n");
+ }
+);
+
+ ns_put_nsec3_from_node(nsec3_node, resp);
+
+ /*
+ * 2) NSEC3 that covers the "next closer" name.
+ */
+ int ret = 0;
+ if (next_closer == NULL) {
+ // create the "next closer" name by appending from qname
+ next_closer = ns_next_closer(
+ knot_node_owner(*closest_encloser), qname);
+
+ if (next_closer == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+dbg_ns_exec(
+ char *name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+);
+ ret = ns_put_covering_nsec3(zone, next_closer, resp);
+
+ // the cast is ugly, but no better way around it
+ knot_dname_release((knot_dname_t *)next_closer);
+ } else {
+ ret = ns_put_covering_nsec3(zone, next_closer, resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a name of a wildcard child of \a name.
+ *
+ * \param name Domain name to get the wildcard child name of.
+ *
+ * \return Wildcard child name or NULL if an error occured.
+ */
+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);
+ if (wildcard == NULL) {
+ return NULL;
+ }
+
+ if (knot_dname_cat(wildcard, name) == NULL) {
+ /* Directly discard dname. */
+ knot_dname_free(&wildcard);
+ return NULL;
+ }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(wildcard);
+ dbg_ns("Wildcard: %s\n", name);
+ free(name);
+);
+ return wildcard;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s covering the non-existent wildcard child of a node
+ * (and their associated RRSIGs) into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param node Node whose non-existent wildcard child should be covered.
+ * \param resp Response where to add the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_no_wildcard_child(const knot_zone_contents_t *zone,
+ const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ assert(node != NULL);
+ assert(resp != NULL);
+ assert(node->owner != NULL);
+
+ int ret = 0;
+ knot_dname_t *wildcard = ns_wildcard_child_name(node->owner);
+ if (wildcard == NULL) {
+ ret = NS_ERR_SERVFAIL;
+ } else {
+ ret = ns_put_covering_nsec3(zone, wildcard, resp);
+
+ /* Directly discard wildcard. */
+ knot_dname_free(&wildcard);
+ }
+
+ return ret;
+}
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for NODATA error (and their associated RRSIGs)
+ * to the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ * \note Note that for each zone there are either NSEC or NSEC3 records used.
+ *
+ * \param node Node which generated the NODATA response (i.e. not containing
+ * RRSets of the requested type).
+ * \param resp Response where to add the NSECs or NSEC3s.
+ */
+static void ns_put_nsec_nsec3_nodata(const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ if (!DNSSEC_ENABLED ||
+ !knot_query_dnssec_requested(knot_packet_query(resp))) {
+ return;
+ }
+
+ const knot_node_t *nsec3_node = knot_node_nsec3_node(node, 1);
+ const knot_rrset_t *rrset = NULL;
+ if ((rrset = knot_node_rrset(node, KNOT_RRTYPE_NSEC)) != NULL
+ || (nsec3_node != NULL && (rrset =
+ knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3)) != NULL)) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ // add RRSIG for the RRSet
+ if ((rrset = knot_rrset_rrsigs(rrset)) != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs for NXDOMAIN error to the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param qname QNAME which generated the NXDOMAIN error (i.e. not found in the
+ * zone).
+ * \param zone Zone used for answering.
+ * \param previous Previous node to \a qname in the zone. May also be NULL. In
+ * such case the function finds the previous node in the zone.
+ * \param closest_encloser Closest encloser of \a qname. Must not be NULL.
+ * \param resp Response where to put the NSECs.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
+ const knot_zone_contents_t *zone,
+ const knot_node_t *previous,
+ const knot_node_t *closest_encloser,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *rrset = NULL;
+
+ // check if we have previous; if not, find one using the tree
+ if (previous == NULL) {
+ /*! \todo Check version. */
+ previous = knot_zone_contents_find_previous(zone, qname);
+
+ while (!knot_node_is_auth(previous)) {
+ previous = knot_node_previous(previous, 1);
+ }
+
+ previous = knot_node_current(previous);
+ assert(previous != NULL);
+ }
+
+ char *name = knot_dname_to_str(previous->owner);
+ dbg_ns("Previous node: %s\n", name);
+ free(name);
+
+ // 1) NSEC proving that there is no node with the searched name
+ rrset = knot_node_rrset(previous, KNOT_RRTYPE_NSEC);
+ if (rrset == NULL) {
+ // no NSEC records
+ //return NS_ERR_SERVFAIL;
+ return KNOT_EOK;
+
+ }
+
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+
+ // 2) NSEC proving that there is no wildcard covering the name
+ // this is only different from 1) if the wildcard would be
+ // before 'previous' in canonical order, i.e. we can
+ // search for previous until we find name lesser than wildcard
+ assert(closest_encloser != NULL);
+
+ knot_dname_t *wildcard =
+ ns_wildcard_child_name(closest_encloser->owner);
+ if (wildcard == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+
+ const knot_node_t *prev_new = previous;
+
+ while (knot_dname_compare(knot_node_owner(prev_new),
+ wildcard) > 0) {
+ dbg_ns("Previous node: %s\n",
+ knot_dname_to_str(knot_node_owner(prev_new)));
+ assert(prev_new != knot_zone_contents_apex(zone));
+ prev_new = knot_node_previous(prev_new, 1);
+ }
+ assert(knot_dname_compare(knot_node_owner(prev_new),
+ wildcard) < 0);
+
+ dbg_ns("Previous node: %s\n",
+ knot_dname_to_str(knot_node_owner(prev_new)));
+
+ /* Directly discard dname. */
+ knot_dname_free(&wildcard);
+
+ if (prev_new != previous) {
+ rrset = knot_node_rrset(prev_new, KNOT_RRTYPE_NSEC);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s for NXDOMAIN error to the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname.
+ * \param qname Domain name which generated the NXDOMAIN error (i.e. not found
+ * in the zone.
+ * \param resp Response where to put the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_nxdomain(const knot_zone_contents_t *zone,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ // 1) Closest encloser proof
+ dbg_ns("Putting closest encloser proof.\n");
+ int ret = ns_put_nsec3_closest_encloser_proof(zone, &closest_encloser,
+ qname, resp);
+ // 2) NSEC3 covering non-existent wildcard
+ if (ret == KNOT_EOK && closest_encloser != NULL) {
+ dbg_ns("Putting NSEC3 for no wildcard child of closest "
+ "encloser.\n");
+ ret = ns_put_nsec3_no_wildcard_child(zone, closest_encloser,
+ resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for the NXDOMAIN error to the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ * \note Note that for each zone there are either NSEC or NSEC3 records used.
+ *
+ * \param zone Zone used for answering.
+ * \param previous Previous node to \a qname in the zone. May also be NULL. In
+ * such case the function finds the previous node in the zone.
+ * \param closest_encloser Closest encloser of \a qname. Must not be NULL.
+ * \param qname QNAME which generated the NXDOMAIN error (i.e. not found in the
+ * zone).
+ * \param resp Response where to put the NSECs.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_nxdomain(const knot_zone_contents_t *zone,
+ const knot_node_t *previous,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int ret = 0;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ ret = ns_put_nsec3_nxdomain(zone, closest_encloser,
+ qname, resp);
+ } else {
+ ret = ns_put_nsec_nxdomain(qname, zone, previous,
+ closest_encloser, resp);
+ }
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s for wildcard answer into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone. In this
+ * case it is the parent of the source of synthesis.
+ * \param qname Domain name covered by the wildcard used for answering the
+ * query.
+ * \param resp Response to put the NSEC3s into.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_wildcard(const knot_zone_contents_t *zone,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ assert(closest_encloser != NULL);
+ assert(qname != NULL);
+ assert(resp != NULL);
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ if (!knot_zone_contents_nsec3_enabled(zone)) {
+ return KNOT_EOK;
+ }
+
+ /*
+ * NSEC3 that covers the "next closer" name.
+ */
+ // create the "next closer" name by appending from qname
+ dbg_ns("Finding next closer name for wildcard NSEC3.\n");
+ knot_dname_t *next_closer =
+ ns_next_closer(closest_encloser->owner, qname);
+
+ if (next_closer == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+dbg_ns_exec(
+ char *name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+);
+ int ret = ns_put_covering_nsec3(zone, next_closer, resp);
+
+
+ /* Duplicate from ns_next_close(), safe to discard. */
+ knot_dname_release(next_closer);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs for wildcard answer into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param qname Domain name covered by the wildcard used for answering the
+ * query.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param resp Response to put the NSEC3s into.
+ */
+static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ const knot_node_t *previous,
+ knot_packet_t *resp)
+{
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ // check if we have previous; if not, find one using the tree
+ if (previous == NULL) {
+ previous = knot_zone_contents_find_previous(zone, qname);
+
+ while (!knot_node_is_auth(previous)) {
+ previous = knot_node_previous(previous, 1);
+ }
+
+ previous = knot_node_current(previous);
+ assert(previous != NULL);
+ }
+
+ const knot_rrset_t *rrset =
+ knot_node_rrset(previous, KNOT_RRTYPE_NSEC);
+ if (rrset != NULL) {
+ // NSEC proving that there is no node with the searched name
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for wildcard NODATA answer into the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ *
+ * \param node Node used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Actual searched domain name.
+ * \param resp Response where to put the NSECs and NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_wildcard_nodata(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int ret = KNOT_EOK;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ ret = ns_put_nsec3_closest_encloser_proof(zone,
+ &closest_encloser,
+ qname, resp);
+
+ const knot_node_t *nsec3_node;
+ if (ret == KNOT_EOK
+ && (nsec3_node = knot_node_nsec3_node(node, 1))
+ != NULL) {
+ ns_put_nsec3_from_node(nsec3_node, resp);
+ }
+ } else {
+ ns_put_nsec_wildcard(zone, qname, previous, resp);
+ }
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for wildcard answer into the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query and if the node's owner is a wildcard.
+ *
+ * \param node Node used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Actual searched domain name.
+ * \param resp Response where to put the NSECs and NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_wildcard_answer(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int r = KNOT_EOK;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && knot_dname_is_wildcard(knot_node_owner(node))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ r = ns_put_nsec3_wildcard(zone, closest_encloser, qname,
+ resp);
+ } else {
+ ns_put_nsec_wildcard(zone, qname, previous, resp);
+ }
+ }
+ return r;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a referral response.
+ *
+ * This function puts the delegation NS RRSet to the Authority section of the
+ * response, possibly adds DS and their associated RRSIGs (if DNSSEC is enabled
+ * and requested by the query) and adds any available additional data (A and
+ * AAAA RRSets for the names in the NS RRs) with their associated RRSIGs
+ * to the Additional section.
+ *
+ * \param node Delegation point node.
+ * \param zone Parent zone (the one from which the response is generated).
+ * \param qname Searched name (which caused the referral).
+ * \param resp Response.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static inline int ns_referral(const knot_node_t *node,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ dbg_ns("Referral response.\n");
+
+ while (!knot_node_is_deleg_point(node)) {
+ assert(knot_node_parent(node, 1) != NULL);
+ node = knot_node_parent(node, 1);
+ }
+
+ const knot_rrset_t *rrset = knot_node_rrset(node, KNOT_RRTYPE_NS);
+ assert(rrset != NULL);
+
+ // TODO: wildcards??
+ //ns_check_wildcard(name, resp, &rrset);
+
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+
+ int ret = KNOT_EOK;
+ // add DS records
+ dbg_ns("DNSSEC requested: %d\n",
+ knot_query_dnssec_requested(knot_packet_query(resp)));
+ dbg_ns("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS));
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ rrset = knot_node_rrset(node, KNOT_RRTYPE_DS);
+ if (rrset != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0,
+ 0);
+ ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+ } else {
+ // no DS, add NSEC3 or NSEC
+ // if NSEC3 enabled, search for NSEC3
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ const knot_node_t *nsec3_node =
+ knot_node_nsec3_node(node, 1);
+ dbg_ns("There is no DS, putting NSEC3s...\n");
+ if (nsec3_node != NULL) {
+ dbg_ns("Putting NSEC3s from the node.\n");
+ ns_put_nsec3_from_node(nsec3_node, resp);
+ } else {
+ dbg_ns("Putting Opt-Out NSEC3s.\n");
+ // no NSEC3 (probably Opt-Out)
+ // TODO: check if the zone is Opt-Out
+ ret = ns_put_nsec3_closest_encloser_proof(zone,
+ &node, qname, resp);
+ }
+ } else {
+ const knot_rrset_t *nsec = knot_node_rrset(
+ node, KNOT_RRTYPE_NSEC);
+ if (nsec) {
+ /*! \todo Check return value? */
+ knot_response_add_rrset_authority(
+ resp, nsec, 1, 1, 0);
+ if ((nsec = knot_rrset_rrsigs(nsec)) != NULL) {
+ knot_response_add_rrset_authority(resp, nsec, 1,
+ 1, 0);
+ }
+ }
+ }
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ ns_put_additional(resp);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Tries to answer the query from the given node.
+ *
+ * Tries to put RRSets of requested type (\a qtype) to the Answer section of the
+ * response. If successful, it also adds authority NS RRSet to the Authority
+ * section and it may add NSEC or NSEC3s in case of a wildcard answer (\a node
+ * is a wildcard node). If not successful (there are no such RRSets), it adds
+ * the SOA record to the Authority section and may add NSEC or NSEC3s according
+ * to the type of the response (NXDOMAIN if \a node is an empty non-terminal,
+ * NODATA if it is a regular node). It also adds any additional data that may
+ * be required.
+ *
+ * \param node Node to answer from.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous domain name of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Searched domain name.
+ * \param qtype Searched RR type.
+ * \param resp Response.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_answer_from_node(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname, uint16_t qtype,
+ knot_packet_t *resp)
+{
+ dbg_ns("Putting answers from found node to the response...\n");
+ int answers = ns_put_answer(node, qname, qtype, resp);
+
+ int ret = KNOT_EOK;
+ if (answers == 0) { // if NODATA response, put SOA
+ if (knot_node_rrset_count(node) == 0
+ && !knot_zone_contents_nsec3_enabled(zone)) {
+ // node is an empty non-terminal => NSEC for NXDOMAIN
+ //assert(knot_node_rrset_count(closest_encloser) > 0);
+ dbg_ns("Adding NSEC/NSEC3 for NXDOMAIN.\n");
+ ret = ns_put_nsec_nsec3_nxdomain(zone,
+ knot_node_previous(node, 1), closest_encloser,
+ qname, resp);
+ } else {
+ dbg_ns("Adding NSEC/NSEC3 for NODATA.\n");
+ ns_put_nsec_nsec3_nodata(node, resp);
+ if (knot_dname_is_wildcard(node->owner)) {
+ dbg_ns("Putting NSEC/NSEC3 for wildcard"
+ " NODATA\n");
+ ret = ns_put_nsec_nsec3_wildcard_nodata(node,
+ closest_encloser, previous, zone, qname,
+ resp);
+ }
+ }
+ ns_put_authority_soa(zone, resp);
+ } else { // else put authority NS
+ // if wildcard answer, add NSEC / NSEC3
+ dbg_ns("Adding NSEC/NSEC3 for wildcard answer.\n");
+ ret = ns_put_nsec_nsec3_wildcard_answer(node, closest_encloser,
+ previous, zone, qname, resp);
+ ns_put_authority_ns(zone, resp);
+ }
+
+ if (ret == KNOT_EOK) {
+ ns_put_additional(resp);
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Synthetizes a CNAME RR from a DNAME.
+ *
+ * \param dname_rrset DNAME RRSet to synthetize from (only the first RR is
+ * used).
+ * \param qname Name to be used as the owner name of the synthetized CNAME.
+ *
+ * \return Synthetized CNAME RRset (this is a newly created RRSet, remember to
+ * free it).
+ */
+static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
+ const knot_dname_t *qname)
+{
+ dbg_ns("Synthetizing CNAME from DNAME...\n");
+
+ // create new CNAME RRSet
+
+ knot_dname_t *owner = knot_dname_deep_copy(qname);
+ if (owner == NULL) {
+ return NULL;
+ }
+
+ knot_rrset_t *cname_rrset = knot_rrset_new(
+ owner, KNOT_RRTYPE_CNAME, KNOT_CLASS_IN, SYNTH_CNAME_TTL);
+
+ /* Release owner, as it's retained in rrset. */
+ knot_dname_release(owner);
+
+ if (cname_rrset == NULL) {
+ return NULL;
+ }
+
+ // 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_rdata_get_item(knot_rrset_rdata(dname_rrset), 0)->dname);
+dbg_ns_exec(
+ char *name = knot_dname_to_str(cname);
+ dbg_ns("CNAME canonical name: %s.\n", name);
+ free(name);
+);
+ knot_rdata_t *cname_rdata = knot_rdata_new();
+ knot_rdata_item_t cname_rdata_item;
+ cname_rdata_item.dname = cname;
+ knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1);
+
+ knot_rrset_add_rdata(cname_rrset, cname_rdata);
+
+ return cname_rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the name created by replacing the owner of \a dname_rrset
+ * in the \a qname by the DNAME's target would be longer than allowed.
+ *
+ * \param dname_rrset DNAME RRSet to be used for the check.
+ * \param qname Name whose part is to be replaced.
+ *
+ * \retval <>0 if the created domain name would be too long.
+ * \retval 0 otherwise.
+ */
+static int ns_dname_is_too_long(const knot_rrset_t *dname_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(dname_rrset))
+ + knot_dname_label_count(knot_rdata_get_item(
+ knot_rrset_rdata(dname_rrset), 0)->dname)
+ > KNOT_MAX_DNAME_LENGTH) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief DNAME processing.
+ *
+ * This function adds the DNAME RRSet (and possibly its associated RRSIGs to the
+ * Answer section of the response, synthetizes CNAME record from the DNAME and
+ * adds it there too. It also stores the synthetized CNAME in the temporary
+ * RRSets of the response.
+ *
+ * \param dname_rrset DNAME RRSet to use.
+ * \param qname Searched name.
+ * \param resp Response.
+ */
+static void ns_process_dname(const knot_rrset_t *dname_rrset,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(dname_rrset));
+ dbg_ns("Processing DNAME for owner %s...\n", name);
+ free(name);
+);
+ // TODO: check the number of RRs in the RRSet??
+
+ // put the DNAME RRSet into the answer
+ knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0);
+ ns_add_rrsigs(dname_rrset, resp, qname,
+ knot_response_add_rrset_answer, 1);
+
+ if (ns_dname_is_too_long(dname_rrset, qname)) {
+ knot_response_set_rcode(resp, KNOT_RCODE_YXDOMAIN);
+ return;
+ }
+
+ // synthetize CNAME (no way to tell that client supports DNAME)
+ knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, qname);
+ // add the synthetized RRSet to the Answer
+ knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0);
+
+ // no RRSIGs for this RRSet
+
+ // add the synthetized RRSet into list of temporary RRSets of response
+ knot_packet_add_tmp_rrset(resp, synth_cname);
+
+ // do not search for the name in new zone (out-of-bailiwick)
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds DNSKEY RRSet from the apex of a zone to the response.
+ *
+ * \param apex Zone apex node.
+ * \param resp Response.
+ */
+static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
+{
+ const knot_rrset_t *rrset =
+ knot_node_rrset(apex, KNOT_RRTYPE_DNSKEY);
+ if (rrset != NULL) {
+ knot_response_add_rrset_additional(resp, rrset, 0, 0, 0);
+ ns_add_rrsigs(rrset, resp, apex->owner,
+ knot_response_add_rrset_additional, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Answers the query from the given zone.
+ *
+ * This function performs the actual answering logic.
+ *
+ * \param zone Zone to use for answering.
+ * \param qname QNAME from the query.
+ * \param qtype QTYPE from the query.
+ * \param resp Response to fill in.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ *
+ * \todo Describe the answering logic in detail.
+ */
+static int ns_answer_from_zone(const knot_zone_contents_t *zone,
+ const knot_dname_t *qname, uint16_t qtype,
+ knot_packet_t *resp)
+{
+ const knot_node_t *node = NULL, *closest_encloser = NULL,
+ *previous = NULL;
+ int cname = 0, auth_soa = 0, ret = 0, find_ret = 0;
+
+search:
+#ifdef USE_HASH_TABLE
+ /*! \todo Check version. */
+ find_ret = knot_zone_contents_find_dname_hash(zone, qname, &node,
+ &closest_encloser);
+// node = knot_node_current(node);
+// closest_encloser = knot_node_current(closest_encloser);
+#else
+ /*! \todo Check version. */
+ find_ret = knot_zone_contents_find_dname(zone, qname, &node,
+ &closest_encloser, &previous);
+ node = knot_node_current(node);
+ closest_encloser = knot_node_current(closest_encloser);
+ previous = knot_node_current(previous);
+#endif
+ if (find_ret == KNOT_EBADARG) {
+ return NS_ERR_SERVFAIL;
+ }
+
+dbg_ns_exec(
+ char *name;
+ if (node) {
+ name = knot_dname_to_str(node->owner);
+ dbg_ns("zone_find_dname() returned node %s ", name);
+ free(name);
+ } else {
+ dbg_ns("zone_find_dname() returned no node,");
+ }
+
+ if (closest_encloser != NULL) {
+ name = knot_dname_to_str(closest_encloser->owner);
+ dbg_ns(" closest encloser %s.\n", name);
+ free(name);
+ } else {
+ dbg_ns(" closest encloser (nil).\n");
+ }
+ if (previous != NULL) {
+ name = knot_dname_to_str(previous->owner);
+ dbg_ns(" and previous node: %s.\n", name);
+ free(name);
+ } else {
+ dbg_ns(" and previous node: (nil).\n");
+ }
+);
+ if (find_ret == KNOT_EBADZONE) {
+ // possible only if we followed cname
+ assert(cname != 0);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ auth_soa = 1;
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+
+have_node:
+ dbg_ns("Closest encloser is deleg. point? %s\n",
+ (knot_node_is_deleg_point(closest_encloser)) ? "yes" : "no");
+
+ dbg_ns("Closest encloser is non authoritative? %s\n",
+ (knot_node_is_non_auth(closest_encloser)) ? "yes" : "no");
+
+ if (knot_node_is_deleg_point(closest_encloser)
+ || knot_node_is_non_auth(closest_encloser)) {
+ ret = ns_referral(closest_encloser, zone, qname, resp);
+ goto finalize;
+ }
+
+ if (find_ret == KNOT_ZONE_NAME_NOT_FOUND) {
+ // DNAME?
+ const knot_rrset_t *dname_rrset = knot_node_rrset(
+ closest_encloser, KNOT_RRTYPE_DNAME);
+ if (dname_rrset != NULL) {
+ ns_process_dname(dname_rrset, qname, resp);
+ auth_soa = 1;
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+ // else check for a wildcard child
+ const knot_node_t *wildcard_node =
+ knot_node_wildcard_child(closest_encloser, 1);
+
+ if (wildcard_node == NULL) {
+ dbg_ns("No wildcard node. (cname: %d)\n",
+ cname);
+ auth_soa = 1;
+ if (cname == 0) {
+ dbg_ns("Setting NXDOMAIN RCODE.\n");
+ // return NXDOMAIN
+ knot_response_set_rcode(resp,
+ KNOT_RCODE_NXDOMAIN);
+ if (ns_put_nsec_nsec3_nxdomain(zone, previous,
+ closest_encloser, qname, resp) != 0) {
+ return NS_ERR_SERVFAIL;
+ }
+ } else {
+ knot_response_set_rcode(resp,
+ KNOT_RCODE_NOERROR);
+ }
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+ // else set the node from which to take the answers to wild.node
+ node = wildcard_node;
+ }
+
+ // now we have the node for answering
+ if (knot_node_is_deleg_point(node) || knot_node_is_non_auth(node)) {
+ ret = ns_referral(node, zone, qname, resp);
+ goto finalize;
+ }
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_ns("Node %s has CNAME record, resolving...\n",
+ name);
+ free(name);
+);
+ const knot_dname_t *act_name = qname;
+ ns_follow_cname(&node, &act_name, resp,
+ knot_response_add_rrset_answer, 1);
+dbg_ns_exec(
+ char *name = (node != NULL) ? knot_dname_to_str(node->owner)
+ : "(nil)";
+ char *name2 = knot_dname_to_str(act_name);
+ dbg_ns("Canonical name: %s (%p), node found: %p\n",
+ name2, act_name, node);
+ dbg_ns("The node's owner: %s (%p)\n", name, (node != NULL)
+ ? node->owner : NULL);
+ if (node != NULL) {
+ free(name);
+ }
+ free(name2);
+);
+ qname = act_name;
+ cname = 1;
+
+ // otherwise search for the new name
+ if (node == NULL) {
+ goto search;
+ } else if (node->owner != act_name) {
+ // the stored node is closest encloser
+ find_ret = KNOT_ZONE_NAME_NOT_FOUND;
+ closest_encloser = node;
+ node = NULL;
+ goto have_node;
+ } // else do nothing, just continue
+ }
+
+ ret = ns_answer_from_node(node, closest_encloser, previous, zone, qname,
+ qtype, resp);
+ if (ret == NS_ERR_SERVFAIL) {
+ // in this case we should drop the response and send an error
+ // for now, just send the error code with a non-complete answer
+// knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
+// goto finalize;
+ return ret;
+ } else if (ret != KNOT_EOK) {
+ /*! \todo Handle RCODE return values!!! */
+ goto finalize;
+ }
+ knot_response_set_aa(resp);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+
+ // this is the only case when the servers answers from
+ // particular node, i.e. the only case when it may return SOA
+ // or NS records in Answer section
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && node == knot_zone_contents_apex(zone)
+ && (qtype == KNOT_RRTYPE_SOA || qtype == KNOT_RRTYPE_NS)) {
+ ns_add_dnskey(node, resp);
+ }
+
+finalize:
+ if (ret == KNOT_EOK && auth_soa) {
+ ns_put_authority_soa(zone, resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Answers the query from the given zone database.
+ *
+ * First it searches for a zone to answer from. If there is none, it sets
+ * RCODE REFUSED to the response and ends. Otherwise it tries to answer the
+ * query using the found zone (see ns_answer_from_zone()).
+ *
+ * \param db Zone database to use for answering.
+ * \param resp Response that holds the parsed query.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_answer(knot_zonedb_t *db, knot_packet_t *resp)
+{
+ const knot_dname_t *qname = knot_packet_qname(resp);
+ assert(qname != NULL);
+
+ uint16_t qtype = knot_packet_qtype(resp);
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns("Trying to find zone for QNAME %s\n", name_str);
+ free(name_str);
+);
+ // find zone in which to search for the name
+ const knot_zone_t *zone =
+ ns_get_zone_for_qname(db, qname, qtype);
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+
+ // if no zone found, return REFUSED
+ if (zone == NULL) {
+ dbg_ns("No zone found.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_REFUSED);
+ //knot_dname_free(&qname);
+ return KNOT_EOK;
+ } else if (contents == NULL) {
+ dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
+ return KNOT_EOK;
+ }
+
+dbg_ns_exec(
+ char *name_str2 = knot_dname_to_str(zone->contents->apex->owner);
+ dbg_ns("Found zone for QNAME %s\n", name_str2);
+ free(name_str2);
+);
+
+ // take the zone contents and use only them for answering
+
+ return ns_answer_from_zone(contents, qname, qtype, resp);
+
+ //knot_dname_free(&qname);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Converts the response to wire format.
+ *
+ * \param resp Response to convert.
+ * \param wire Place for the wire format of the response.
+ * \param wire_size In: space available for the wire format in bytes.
+ * Out: actual size of the wire format in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size)
+{
+ uint8_t *rwire = NULL;
+ size_t rsize = 0;
+ int ret = 0;
+
+ if ((ret = knot_packet_to_wire(resp, &rwire, &rsize))
+ != KNOT_EOK) {
+ dbg_ns("Error converting response packet "
+ "to wire format (error %d).\n", ret);
+ return NS_ERR_SERVFAIL;
+ }
+
+ if (rsize > *wire_size) {
+ dbg_ns("Reponse size (%zu) larger than allowed wire size "
+ "(%zu).\n", rsize, *wire_size);
+ return NS_ERR_SERVFAIL;
+ }
+
+ if (rwire != wire) {
+ dbg_ns("Wire format reallocated, copying to place for "
+ "wire.\n");
+ memcpy(wire, rwire, rsize);
+ } else {
+ dbg_ns("Using the same space or wire format.\n");
+ }
+
+ *wire_size = rsize;
+ //free(rwire);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a wire format of an error response from partially created
+ * response.
+ *
+ * \param resp Response to use.
+ * \param wire Place for the wire format of the response.
+ * \param wire_size In: space available for the wire format in bytes.
+ * Out: actual size of the wire format in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_error_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size)
+{
+ /* Do not call the packet conversion function
+ * wire format is assembled, but COUNTs in header are not set.
+ * This is ideal, we just truncate the packet after the question.
+ */
+ dbg_ns("Creating error response.\n");
+
+ size_t rsize = knot_packet_question_size(knot_packet_query(resp));
+ dbg_ns("Error response (~ query) size: %zu\n", rsize);
+
+ // take 'qsize' from the current wireformat of the response
+ // it is already assembled - Header and Question section are copied
+ const uint8_t *rwire = knot_packet_wireformat(resp);
+ if (rsize > *wire_size) {
+ dbg_ns("Reponse size (%zu) larger than allowed wire size"
+ " (%zu).\n", rsize, *wire_size);
+ return NS_ERR_SERVFAIL;
+ }
+
+ assert(rwire != wire);
+
+ /*! \todo Why is this copied?? Why we cannot use resp->wireformat?? */
+ memcpy(wire, rwire, rsize);
+
+ *wire_size = rsize;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct ns_axfr_params {
+ knot_ns_xfr_t *xfr;
+ int ret;
+} ns_axfr_params_t;
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_tsig_required(int packet_nr)
+{
+ dbg_ns_detail("ns_tsig_required(%d): %d\n", packet_nr,
+ (packet_nr % KNOT_NS_TSIG_FREQ == 0));
+ return (packet_nr % KNOT_NS_TSIG_FREQ == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(xfr->wire != NULL);
+ assert(xfr->send != NULL);
+
+ // Transform the packet into wire format
+ dbg_ns("Converting response to wire format..\n");
+ size_t real_size = xfr->wire_size;
+ if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) {
+ return NS_ERR_SERVFAIL;
+// // send back SERVFAIL (as this is our problem)
+// ns_error_response(nameserver,
+// knot_wire_get_id(query_wire),
+// KNOT_RCODE_SERVFAIL, response_wire,
+// rsize);
+ }
+
+ int res = 0;
+
+ size_t digest_real_size = xfr->digest_max_size;
+
+ dbg_ns_detail("xfr->tsig_key=%p\n", xfr->tsig_key);
+ /*! \note [TSIG] Generate TSIG if required (during XFR/IN). */
+ if (xfr->tsig_key && add_tsig) {
+ if (xfr->packet_nr == 0) {
+ /* Add key, digest and digest length. */
+ dbg_ns_detail("Calling tsig_sign(): %p, %zu, %zu, "
+ "%p, %zu, %p, %zu, %p\n",
+ xfr->wire, real_size, xfr->wire_size,
+ xfr->digest, xfr->digest_size, xfr->digest,
+ digest_real_size, xfr->tsig_key);
+ res = knot_tsig_sign(xfr->wire, &real_size,
+ xfr->wire_size, xfr->digest,
+ xfr->digest_size, xfr->digest,
+ &digest_real_size,
+ xfr->tsig_key);
+ } else {
+ /* Add key, digest and digest length. */
+ dbg_ns_detail("Calling tsig_sign_next()\n");
+ res = knot_tsig_sign_next(xfr->wire, &real_size,
+ xfr->wire_size,
+ xfr->digest,
+ xfr->digest_size,
+ xfr->digest,
+ &digest_real_size,
+ xfr->tsig_key);
+ }
+
+ dbg_ns_detail("Sign function returned: %s\n",
+ knot_strerror(res));
+ dbg_ns_detail("Real size of digest: %zu\n", digest_real_size);
+
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ assert(digest_real_size > 0);
+ // save the new previous digest size
+ xfr->digest_size = digest_real_size;
+ }
+
+ // Send the response
+ dbg_ns("Sending response (size %zu)..\n", real_size);
+ //dbg_ns_hex((const char *)xfr->wire, real_size);
+ res = xfr->send(xfr->session, &xfr->addr, xfr->wire, real_size);
+ if (res < 0) {
+ dbg_ns("Send returned %d\n", res);
+ return res;
+ } else if (res != real_size) {
+ dbg_ns("AXFR did not send right amount of bytes."
+ " Transfer size: %zu, sent: %d\n",
+ real_size, res);
+ }
+
+ // Clean the response structure
+ dbg_ns("Clearing response structure..\n");
+ knot_response_clear(xfr->response, 0);
+
+ // increment the packet number
+ ++xfr->packet_nr;
+ if (xfr->tsig_key && add_tsig) {
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ } else {
+ knot_packet_set_tsig_size(xfr->response, 0);
+ }
+
+ dbg_ns("Response structure after clearing:\n");
+ knot_packet_dump(xfr->response);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void ns_axfr_from_node(knot_node_t *node, void *data)
+{
+ assert(node != NULL);
+ assert(data != NULL);
+
+ ns_axfr_params_t *params = (ns_axfr_params_t *)data;
+
+ if (params->ret != KNOT_EOK) {
+ // just skip (will be called on next node with the same params
+ dbg_ns("Params contain error: %s, skipping node...\n",
+ knot_strerror(params->ret));
+ return;
+ }
+
+ dbg_ns("Params OK, answering AXFR from node %p.\n", node);
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(node));
+ dbg_ns("Node ownerr: %s\n", name);
+ free(name);
+);
+
+ if (knot_node_rrset_count(node) == 0) {
+ return;
+ }
+
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ params->ret = KNOT_ENOMEM;
+ return;
+ }
+
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset = NULL;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = rrsets[i];
+rrset:
+ dbg_ns(" Type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ // do not add SOA
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
+ ++i;
+ continue;
+ }
+
+ ret = knot_response_add_rrset_answer(params->xfr->response,
+ rrset, 0, 0, 1);
+
+ if (ret == KNOT_ESPACE) {
+ // TODO: send the packet and clean the structure
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(params->xfr,
+ knot_ns_tsig_required(params->xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+ // otherwise try once more with the same RRSet
+ goto rrset;
+ } else if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+
+ // we can send the RRSets in any order, so add the RRSIGs now
+ rrset = knot_rrset_rrsigs(rrset);
+rrsigs:
+ if (rrset == NULL) {
+ ++i;
+ continue;
+ }
+
+ ret = knot_response_add_rrset_answer(params->xfr->response,
+ rrset, 0, 0, 1);
+
+ if (ret == KNOT_ESPACE) {
+ // TODO: send the packet and clean the structure
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(params->xfr,
+ knot_ns_tsig_required(params->xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+ // otherwise try once more with the same RRSet
+ goto rrsigs;
+ } else if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+
+ // this way only whole RRSets are always sent
+ // we guess it will not create too much overhead
+
+ ++i;
+ }
+ if (rrsets != NULL) {
+ free(rrsets);
+ }
+
+ /*! \todo maybe distinguish some error codes. */
+ //params->ret = (ret == 0) ? KNOT_EOK : KNOT_ERROR;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_axfr_from_zone(knot_zone_contents_t *zone, knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(xfr->wire != NULL);
+ assert(xfr->send != NULL);
+
+ ns_axfr_params_t params;
+ memset(&params, 0, sizeof(ns_axfr_params_t));
+ params.xfr = xfr;
+ params.ret = KNOT_EOK;
+
+ xfr->packet_nr = 0;
+
+ /*
+ * First SOA
+ */
+
+ // retrieve SOA - must be send as first and last RR
+ const knot_rrset_t *soa_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
+ if (soa_rrset == NULL) {
+ // some really serious error
+ return KNOT_ERROR;
+ }
+
+ int ret;
+
+ // add SOA RR to the response
+ ret = knot_response_add_rrset_answer(xfr->response, soa_rrset, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ // something is really wrong
+ return KNOT_ERROR;
+ }
+
+ // add the SOA's RRSIG
+ const knot_rrset_t *rrset = knot_rrset_rrsigs(soa_rrset);
+ if (rrset != NULL
+ && (ret = knot_response_add_rrset_answer(xfr->response, rrset,
+ 0, 0, 1)) != KNOT_EOK) {
+ // something is really wrong, these should definitely fit in
+ return KNOT_ERROR;
+ }
+
+ knot_zone_contents_tree_apply_inorder(zone, ns_axfr_from_node,
+ &params);
+
+ if (params.ret != KNOT_EOK) {
+ return KNOT_ERROR; // maybe do something with the code
+ }
+
+ knot_zone_contents_nsec3_apply_inorder(zone, ns_axfr_from_node,
+ &params);
+
+ if (params.ret != KNOT_EOK) {
+ return KNOT_ERROR; // maybe do something with the code
+ }
+
+ /*
+ * Last SOA
+ */
+
+ // try to add the SOA to the response again (last RR)
+ ret = knot_response_add_rrset_answer(xfr->response, soa_rrset, 0, 0, 1);
+ if (ret == KNOT_ESPACE) {
+
+ // if there is not enough space, send the response and
+ // add the SOA record to a new packet
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_response_add_rrset_answer(xfr->response,
+ soa_rrset, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ } else if (ret != KNOT_EOK) {
+ // something is really wrong
+ return KNOT_ERROR;
+ }
+
+ dbg_ns("Sending packet...\n");
+ return ns_xfr_send_and_clear(xfr, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, const knot_rrset_t *rrset)
+{
+ int res = knot_response_add_rrset_answer(xfr->response, rrset,
+ 0, 0, 0);
+ if (res == KNOT_ESPACE) {
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_NOERROR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, knot_ns_tsig_required(xfr->packet_nr));
+
+ res = knot_response_add_rrset_answer(xfr->response,
+ rrset, 0, 0, 0);
+ }
+
+ if (res != KNOT_EOK) {
+ dbg_ns("Error putting origin SOA to IXFR reply: %s\n",
+ knot_strerror(res));
+ /*! \todo Probably send back AXFR instead. */
+ knot_response_set_rcode(xfr->response,
+ KNOT_RCODE_SERVFAIL);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ // 2) put remove RRSets
+ for (int i = 0; i < chgset->remove_count; ++i) {
+ res = ns_ixfr_put_rrset(xfr, chgset->remove[i]);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+ }
+
+ // 1) 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]);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->zone != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(knot_packet_authority_rrset_count(xfr->query) > 0);
+ assert(xfr->data != NULL);
+
+ /*! \todo REMOVE start */
+// const knot_rrset_t *zone_soa =
+// knot_node_rrset(knot_zone_contents_apex(
+// knot_zone_contents(xfr->zone)),
+// KNOT_RRTYPE_SOA);
+// // retrieve origin (xfr) serial and target (zone) serial
+// uint32_t zone_serial = knot_rdata_soa_serial(
+// knot_rrset_rdata(zone_soa));
+// uint32_t xfr_serial = knot_rdata_soa_serial(knot_rrset_rdata(
+// knot_packet_authority_rrset(xfr->query, 0)));
+
+// // 3) load changesets from journal
+// knot_changesets_t *chgsets = (knot_changesets_t *)
+// calloc(1, sizeof(knot_changesets_t));
+// int res = xfr_load_changesets(xfr->zone, chgsets, xfr_serial,
+// zone_serial);
+// if (res != KNOT_EOK) {
+// dbg_ns("IXFR query cannot be answered: %s.\n",
+// knot_strerror(res));
+// /*! \todo Probably send back AXFR instead. */
+// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
+// /*! \todo Probably rename the function. */
+// ns_axfr_send_and_clear(xfr);
+// //socket_close(xfr->session); /*! \todo Remove for UDP. */
+// return 1;
+// }
+
+ /*! \todo REMOVE end */
+
+ knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
+ knot_zone_contents_t* contents = knot_zone_get_contents(xfr->zone);
+ assert(contents);
+ const knot_rrset_t *zone_soa =
+ knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+
+ // 4) put the zone SOA as the first Answer RR
+ int res = knot_response_add_rrset_answer(xfr->response, zone_soa, 0,
+ 0, 0);
+ if (res != KNOT_EOK) {
+ dbg_ns("IXFR query cannot be answered: %s.\n",
+ knot_strerror(res));
+ knot_response_set_rcode(xfr->response,
+ KNOT_RCODE_SERVFAIL);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+// socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return 1;
+ }
+
+ // 5) put the changesets into the response while they fit in
+ for (int i = 0; i < chgsets->count; ++i) {
+ res = ns_ixfr_put_changeset(xfr, &chgsets->sets[i]);
+ if (res != KNOT_EOK) {
+ // answer is sent, socket is closed
+ return KNOT_EOK;
+ }
+ }
+
+ if (chgsets->count > 0) {
+ res = ns_ixfr_put_rrset(xfr, zone_soa);
+ }
+
+ if (res == KNOT_EOK) {
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return 1;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr(knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR);
+
+ // check if there is the required authority record
+ if ((knot_packet_authority_rrset_count(xfr->query) <= 0)) {
+ // malformed packet
+ dbg_ns("IXFR query does not contain authority record.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session);
+ return 1;
+ }
+
+ const knot_rrset_t *soa = knot_packet_authority_rrset(xfr->query, 0);
+ const knot_dname_t *qname = knot_packet_qname(xfr->response);
+
+ // 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) {
+ // malformed packet
+ dbg_ns("IXFR query is malformed.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP. */
+ return 1;
+ }
+
+ return ns_ixfr_from_zone(xfr);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ns_prepare_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ size_t max_size)
+{
+ assert(max_size >= 500);
+
+ // initialize response packet structure
+ *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ if (*resp == NULL) {
+ dbg_ns("Failed to create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_set_max_size(*resp, max_size);
+ //(*resp)->wireformat = response_wire;;
+ //(*resp)->max_size = max_size;
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ knot_packet_free(resp);
+ return ret;
+ }
+
+ ret = knot_response_init_from_query(*resp, query);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ knot_packet_free(resp);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int32_t ns_serial_difference(uint32_t s1, uint32_t s2)
+{
+ return (((int64_t)s1 - s2) % ((int64_t)1 << 32));
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+knot_nameserver_t *knot_ns_create()
+{
+ knot_nameserver_t *ns = malloc(sizeof(knot_nameserver_t));
+ if (ns == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ ns->data = 0;
+
+ // Create zone database structure
+ dbg_ns("Creating Zone Database structure...\n");
+ ns->zone_db = knot_zonedb_new();
+ if (ns->zone_db == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ns);
+ return NULL;
+ }
+
+ // prepare empty response with SERVFAIL error
+ knot_packet_t *err = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (err == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ns);
+ return NULL;
+ }
+
+ dbg_ns("Created default empty response...\n");
+
+ int rc = knot_packet_set_max_size(err, KNOT_WIRE_HEADER_SIZE);
+ if (rc != KNOT_EOK) {
+ dbg_ns("Error creating default error response: %s.\n",
+ knot_strerror(rc));
+ free(ns);
+ knot_packet_free(&err);
+ return NULL;
+ }
+
+ rc = knot_response_init(err);
+ if (rc != KNOT_EOK) {
+ dbg_ns("Error initializing default error response:"
+ " %s.\n", knot_strerror(rc));
+ free(ns);
+ knot_packet_free(&err);
+ return NULL;
+ }
+
+ knot_response_set_rcode(err, KNOT_RCODE_SERVFAIL);
+ ns->err_resp_size = 0;
+
+ dbg_ns("Converting default empty response to wire format...\n");
+
+ uint8_t *error_wire = NULL;
+
+ if (knot_packet_to_wire(err, &error_wire, &ns->err_resp_size) != 0) {
+ dbg_ns("Error while converting "
+ "default error response to "
+ "wire format \n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+
+ ns->err_response = (uint8_t *)malloc(ns->err_resp_size);
+ if (ns->err_response == NULL) {
+ dbg_ns("Error while converting default "
+ "error response to wire format \n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+
+ memcpy(ns->err_response, error_wire, ns->err_resp_size);
+
+ dbg_ns("Done..\n");
+
+ knot_packet_free(&err);
+
+ if (EDNS_ENABLED) {
+ ns->opt_rr = knot_edns_new();
+ if (ns->opt_rr == NULL) {
+ dbg_ns("Error while preparing OPT RR of the"
+ " server.\n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+ knot_edns_set_version(ns->opt_rr, EDNS_VERSION);
+ knot_edns_set_payload(ns->opt_rr, MAX_UDP_PAYLOAD_EDNS);
+ } else {
+ ns->opt_rr = NULL;
+ }
+
+ knot_packet_free(&err);
+
+ return ns;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
+ knot_packet_t *packet, knot_packet_type_t *type)
+{
+ if (packet == NULL || query_wire == NULL || type == NULL) {
+ dbg_ns("Missing parameter to query parsing.\n");
+ return KNOT_EBADARG;
+ }
+
+ dbg_ns("ns_parse_packet() called with query size %zu.\n", qsize);
+ //dbg_ns_hex((char *)query_wire, qsize);
+
+ if (qsize < 2) {
+ return KNOT_EMALF;
+ }
+
+ // 1) create empty response
+ dbg_ns("Parsing packet...\n");
+ //parsed = knot_response_new_empty(NULL);
+
+ int ret = 0;
+
+ if ((ret = knot_packet_parse_from_wire(packet, query_wire,
+ qsize, 1)) != 0) {
+ dbg_ns("Error while parsing packet, "
+ "libknot error '%s'.\n", knot_strerror(ret));
+// knot_response_free(&parsed);
+ return KNOT_RCODE_FORMERR;
+ }
+
+ dbg_ns("Parsed packet header and Question:\n");
+ knot_packet_dump(packet);
+
+ // 3) determine the query type
+ switch (knot_packet_opcode(packet)) {
+ case KNOT_OPCODE_QUERY:
+ switch (knot_packet_qtype(packet)) {
+ case KNOT_RRTYPE_AXFR:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_AXFR : KNOT_RESPONSE_AXFR;
+ break;
+ case KNOT_RRTYPE_IXFR:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_IXFR : KNOT_RESPONSE_IXFR;
+ break;
+ default:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_NORMAL : KNOT_RESPONSE_NORMAL;
+ }
+
+ break;
+ case KNOT_OPCODE_NOTIFY:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_NOTIFY : KNOT_RESPONSE_NOTIFY;
+ break;
+ case KNOT_OPCODE_UPDATE:
+ if(knot_packet_is_query(packet)) {
+ *type = KNOT_QUERY_UPDATE;
+ } else {
+ return KNOT_RCODE_FORMERR;
+ }
+ break;
+ default:
+ return KNOT_RCODE_NOTIMPL;
+ }
+
+// knot_packet_free(&packet);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id,
+ uint8_t rcode, uint8_t *response_wire, size_t *rsize)
+{
+ //dbg_ns("Error response: \n");
+ //dbg_ns_hex((const char *)nameserver->err_response,
+ // nameserver->err_resp_size);
+
+ memcpy(response_wire, nameserver->err_response,
+ nameserver->err_resp_size);
+ // copy ID of the query
+ knot_wire_set_id(response_wire, query_id);
+ // set the RCODE
+ knot_wire_set_rcode(response_wire, rcode);
+ *rsize = nameserver->err_resp_size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_error_response_full(knot_nameserver_t *nameserver,
+ knot_packet_t *response, uint8_t rcode,
+ uint8_t *response_wire, size_t *rsize)
+{
+ knot_response_set_rcode(response, rcode);
+
+ if (ns_error_response_to_wire(response, response_wire, rsize) != 0) {
+ knot_ns_error_response(nameserver, knot_packet_id(
+ knot_packet_query(response)),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize)
+{
+ dbg_ns("ns_answer_normal()\n");
+
+ // first, parse the rest of the packet
+ assert(knot_packet_is_query(query));
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
+ int ret;
+
+ ret = knot_packet_parse_rest(query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ (ret == KNOT_EMALF)
+ ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ /*
+ * Semantic checks - if ANCOUNT > 0 or NSCOUNT > 0, return FORMERR.
+ *
+ * If any xxCOUNT is less or more than actual RR count
+ * the previously called knot_packet_parse_rest() will recognize this.
+ *
+ * Check the QDCOUNT and in case of anything but 1 send back
+ * FORMERR
+ */
+ if (knot_packet_ancount(query) > 0
+ || knot_packet_nscount(query) > 0
+ || knot_packet_qdcount(query) != 1) {
+ dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n");
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_FORMERR, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ size_t resp_max_size = 0;
+
+ assert(*rsize >= MAX_UDP_PAYLOAD);
+
+ knot_packet_dump(query);
+
+ if (knot_query_edns_supported(query)) {
+ if (knot_edns_get_payload(&query->opt_rr) <
+ knot_edns_get_payload(nameserver->opt_rr)) {
+ resp_max_size = knot_edns_get_payload(&query->opt_rr);
+ } else {
+ resp_max_size = knot_edns_get_payload(
+ nameserver->opt_rr);
+ }
+ }
+
+ if (resp_max_size < MAX_UDP_PAYLOAD) {
+ resp_max_size = MAX_UDP_PAYLOAD;
+ }
+
+ knot_packet_t *response;
+ ret = knot_ns_prepare_response(nameserver, query, &response,
+ resp_max_size);
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+ dbg_ns("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
+
+ // get the answer for the query
+ rcu_read_lock();
+ knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+
+ dbg_ns("EDNS supported in query: %d\n",
+ knot_query_edns_supported(query));
+
+ // set the OPT RR to the response
+ if (knot_query_edns_supported(query)) {
+ /*! \todo API. */
+// if (knot_edns_get_payload(&query->opt_rr) > MAX_UDP_PAYLOAD) {
+// ret = knot_packet_set_max_size(response,
+// knot_edns_get_payload(&query->opt_rr));
+// } else {
+// ret = knot_packet_set_max_size(response,
+// MAX_UDP_PAYLOAD);
+// }
+
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to set max size.\n");
+// knot_ns_error_response_full(nameserver, response,
+// KNOT_RCODE_SERVFAIL,
+// response_wire, rsize);
+// return KNOT_EOK;
+// }
+
+ ret = knot_response_add_opt(response, nameserver->opt_rr, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to set OPT RR to the response"
+ ": %s\n",knot_strerror(ret));
+ } else {
+ // copy the DO bit from the query
+ if (knot_query_dnssec_requested(query)) {
+ /*! \todo API for this. */
+ knot_edns_set_do(&response->opt_rr);
+ }
+ }
+ }/* else {
+ dbg_ns("Setting max size to %u.\n", MAX_UDP_PAYLOAD);
+ ret = knot_packet_set_max_size(response, MAX_UDP_PAYLOAD);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to set max size to %u\n",
+ MAX_UDP_PAYLOAD);
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ return KNOT_EOK;
+ }
+ }*/
+
+ dbg_ns("Response max size: %zu\n", response->max_size);
+
+ ret = ns_answer(zonedb, response);
+ if (ret != 0) {
+ // now only one type of error (SERVFAIL), later maybe more
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ } else {
+ dbg_ns("Created response packet.\n");
+ //knot_response_dump(resp);
+ knot_packet_dump(response);
+
+ // 4) Transform the packet into wire format
+ if (ns_response_to_wire(response, response_wire, rsize) != 0) {
+ // send back SERVFAIL (as this is our problem)
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ }
+ }
+
+ rcu_read_unlock();
+ knot_packet_free(&response);
+
+ dbg_ns("Returning response with wire size %zu\n", *rsize);
+ //dbg_ns_hex((char *)response_wire, *rsize);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ dbg_ns("knot_ns_init_xfr()\n");
+
+ if (nameserver == NULL || xfr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // no need to parse rest of the packet
+ /*! \todo Parse rest of packet because of EDNS. */
+ int ret = knot_packet_parse_rest(xfr->query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: %s\n",
+ knot_strerror(ret));
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL,
+ xfr->wire, &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ return ret;
+ }
+
+ dbg_packet("Parsed XFR query:\n");
+ knot_packet_dump(xfr->query);
+
+ // initialize response packet structure
+ knot_packet_t *response = knot_packet_new(
+ KNOT_PACKET_PREALLOC_RESPONSE);
+ if (response == NULL) {
+ dbg_ns("Failed to create packet structure.\n");
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ knot_packet_free(&response);
+ return ret;
+ }
+
+ //int ret = knot_packet_set_max_size(response, xfr->wire_size);
+ response->wireformat = xfr->wire;
+ response->max_size = xfr->wire_size;
+
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to init response structure.\n");
+// /*! \todo xfr->wire is not NULL, will fail on assert! */
+// knot_ns_error_response(nameserver, xfr->query->header.id,
+// KNOT_RCODE_SERVFAIL, xfr->wire,
+// &xfr->wire_size);
+// int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+// xfr->wire_size);
+// knot_packet_free(&response);
+// return res;
+// }
+
+ ret = knot_response_init_from_query(response, xfr->query);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ knot_packet_free(&response);
+ return res;
+ }
+
+ xfr->response = response;
+
+ knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+
+ const knot_dname_t *qname = knot_packet_qname(xfr->response);
+
+ assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_AXFR ||
+ knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR);
+
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns("Trying to find zone with name %s\n", name_str);
+ free(name_str);
+);
+ // find zone in which to search for the name
+ knot_zone_t *zone = knot_zonedb_find_zone(zonedb, qname);
+
+ // if no zone found, return NotAuth
+ if (zone == NULL) {
+ dbg_ns("No zone found.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_NOTAUTH);
+ ns_xfr_send_and_clear(xfr, 1);
+ return KNOT_ENOZONE;
+ }
+
+dbg_ns_exec(
+ char *name2_str = knot_dname_to_str(qname);
+ dbg_ns("Found zone for name %s\n", name2_str);
+ free(name2_str);
+);
+ xfr->zone = zone;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ns_serial_compare(uint32_t s1, uint32_t s2)
+{
+ int32_t diff = ns_serial_difference(s1, s2);
+ return (s1 == s2) /* s1 equal to s2 */
+ ? 0
+ :((diff >= 1 && diff < ((uint32_t)1 << 31))
+ ? 1 /* s1 larger than s2 */
+ : -1); /* s1 less than s2 */
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
+ uint32_t *serial_to)
+{
+ if (xfr == NULL || xfr->zone == NULL || serial_from == NULL
+ || serial_to == NULL) {
+ dbg_ns_detail("Wrong parameters: xfr=%p,"
+ " xfr->zone = %p\n", xfr, xfr->zone);
+ return KNOT_EBADARG;
+ }
+
+ const knot_zone_t *zone = xfr->zone;
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+ if (!contents) {
+ dbg_ns_detail("Missing contents\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_zone_contents_apex(contents) == NULL) {
+ dbg_ns_detail("No apex.\n");
+ return KNOT_EBADARG;
+ }
+
+ const knot_rrset_t *zone_soa =
+ knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ if (zone_soa == NULL) {
+ dbg_ns_verb("No SOA.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_packet_nscount(xfr->query) < 1) {
+ dbg_ns_verb("No Authority record.\n");
+ return KNOT_EMALF;
+ }
+
+ if (knot_packet_authority_rrset(xfr->query, 0) == NULL) {
+ dbg_ns_verb("Authority record missing.\n");
+ return KNOT_ERROR;
+ }
+
+ // retrieve origin (xfr) serial and target (zone) serial
+ *serial_to = knot_rdata_soa_serial(knot_rrset_rdata(zone_soa));
+ *serial_from = knot_rdata_soa_serial(knot_rrset_rdata(
+ knot_packet_authority_rrset(xfr->query, 0)));
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr, knot_rcode_t rcode)
+{
+ /*! \todo Handle TSIG errors differently. */
+ knot_response_set_rcode(xfr->response, rcode);
+
+ /*! \todo Probably rename the function. */
+ int ret = 0;
+ if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK) {
+ size_t size = 0;
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire, &size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || nameserver == NULL || xfr->zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ rcu_read_lock();
+
+ // take the contents and answer from them
+ int ret = 0;
+ knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone);
+ if (!contents) {
+ dbg_ns("AXFR failed on stub zone\n");
+ /*! \todo replace with knot_ns_xfr_send_error() */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ rcu_read_unlock();
+ knot_packet_free(&xfr->response);
+ return KNOT_EOK;
+ }
+
+ /*!
+ * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure.
+ */
+
+ /*! \todo [TSIG] Get the TSIG size from some API function. */
+ if (xfr->tsig_size > 0) {
+ dbg_ns_detail("Setting TSIG size in packet: %zu\n",
+ xfr->tsig_size);
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ }
+
+ ret = ns_axfr_from_zone(contents, xfr);
+
+ /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
+ * and when it does not. E.g. if there was problem in sending
+ * packet, it will probably fail when sending the SERVFAIL also.
+ */
+ if (ret < 0) {
+ dbg_ns("AXFR failed, sending SERVFAIL.\n");
+ // now only one type of error (SERVFAIL), later maybe more
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ /*! \todo replace with knot_ns_xfr_send_error() */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ } else if (ret > 0) {
+ ret = KNOT_ERROR;
+ }
+
+ rcu_read_unlock();
+
+ knot_packet_free(&xfr->response);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ if (nameserver == NULL || xfr == NULL || xfr->zone == NULL
+ || xfr->response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ //uint8_t *wire = NULL;
+ //size_t size = xfr->wire_size;
+
+ // parse rest of the packet (we need the Authority record)
+ int ret = knot_packet_parse_rest(xfr->query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n");
+// knot_ns_error_response_full(nameserver, xfr->response,
+// KNOT_RCODE_FORMERR, xfr->wire,
+// &size);
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR);
+
+ //ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ knot_packet_free(&xfr->response);
+ return ret;
+ }
+
+ // check if the zone has contents
+ if (knot_zone_contents(xfr->zone) == NULL) {
+ dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
+ ret = knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+// knot_ns_error_response_full(nameserver, xfr->response,
+// KNOT_RCODE_SERVFAIL, xfr->wire,
+// &size);
+
+// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ knot_packet_free(&xfr->response);
+ return ret;
+ }
+
+ /*!
+ * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure. This should be optional, only if
+ * the request contained TSIG, i.e. if there is the data in 'xfr'.
+ */
+
+ /*! \todo [TSIG] Get the TSIG size from some API function. */
+ if (xfr->tsig_size > 0) {
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ }
+
+ ret = ns_ixfr(xfr);
+
+ /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
+ * and when it does not. E.g. if there was problem in sending
+ * packet, it will probably fail when sending the SERVFAIL also.
+ */
+ if (ret < 0) {
+ dbg_ns("IXFR failed, sending SERVFAIL.\n");
+ // now only one type of error (SERVFAIL), later maybe more
+
+ /*! \todo Extract this to some function. */
+// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
+// uint8_t *wire = NULL;
+// ret = knot_packet_to_wire(xfr->response, &wire, &size);
+// if (ret != KNOT_EOK) {
+//// knot_ns_error_response(nameserver,
+//// xfr->query->header.id,
+//// KNOT_RCODE_SERVFAIL, xfr->wire,
+//// &size);
+//// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+//// size);
+// knot_ns_xfr_send_error(xfr, KNOT_RCODE_SERVFAIL);
+// knot_packet_free(&xfr->response);
+// return ret;
+// } else {
+// ret = xfr->send(xfr->session, &xfr->addr, wire, size);
+// }
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+ } /*else if (ret > 0) {
+ ret = KNOT_ERROR;
+ }*/
+
+ knot_packet_free(&xfr->response);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ /*!
+ * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
+ */
+
+ dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n",
+ xfr->wire_size);
+
+ int ret = xfrin_process_axfr_packet(/*xfr->wire, xfr->wire_size,*/
+ /*(xfrin_constructed_zone_t **)(&xfr->data)*/
+ xfr);
+
+ if (ret > 0) { // transfer finished
+ dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n");
+ /*
+ * Adjust zone so that node count is set properly and nodes are
+ * marked authoritative / delegation point.
+ */
+ xfrin_constructed_zone_t *constr_zone =
+ (xfrin_constructed_zone_t *)xfr->data;
+ knot_zone_contents_t *zone = constr_zone->contents;
+ assert(zone != NULL);
+
+ dbg_ns("ns_process_axfrin: adjusting zone.\n");
+ knot_zone_contents_adjust(zone, 0);
+
+ /* Create and fill hash table */
+ dbg_ns("ns_process_axfrin: filling hash table.\n");
+ int rc = knot_zone_contents_create_and_fill_hash_table(zone);
+ if (rc != KNOT_EOK) {
+ return KNOT_ERROR; // TODO: change error code
+ }
+
+ // save the zone contents to the xfr->data
+ xfr->data = zone;
+
+ // free the structure used for processing XFR
+ assert(constr_zone->rrsigs == NULL);
+ free(constr_zone);
+
+ //knot_zone_contents_dump(zone, 0);
+ }
+
+ /*!
+ * \todo In case of error, shouldn't the zone be destroyed here?
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_switch_zone(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || nameserver == NULL || xfr->data == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)xfr->data;
+
+ dbg_ns("Replacing zone by new one: %p\n", zone);
+
+ // find the zone in the zone db
+ knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db,
+ knot_node_owner(knot_zone_contents_apex(zone)));
+ if (z == NULL) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_ns("Failed to replace zone %s, old zone "
+ "not found\n", name);
+ free(name);
+ } else {
+ zone->zone = z;
+ }
+
+ knot_zone_contents_t *old = rcu_xchg_pointer(&z->contents, zone);
+
+// knot_zone_t *old = knot_zonedb_replace_zone(nameserver->zone_db,
+// zone);
+ dbg_ns("Old zone: %p\n", old);
+// if (old == NULL) {
+// char *name = knot_dname_to_str(
+// knot_node_owner(knot_zone_apex(zone)));
+// dbg_ns("Failed to replace zone %s\n", name);
+// free(name);
+// }
+
+ // wait for readers to finish
+ dbg_ns("Waiting for readers to finish...\n");
+ synchronize_rcu();
+ // destroy the old zone
+ dbg_ns("Freeing old zone: %p\n", old);
+ knot_zone_contents_deep_free(&old, 0);
+
+dbg_ns_exec(
+ dbg_ns("Zone db contents: (zone count: %zu)\n",
+ nameserver->zone_db->zone_count);
+
+ const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db);
+ for (int i = 0; i < knot_zonedb_zone_count
+ (nameserver->zone_db); i++) {
+ dbg_ns("%d. zone: %p", i, zones[i]);
+ char *name = knot_dname_to_str(zones[i]->name);
+ dbg_ns(" zone name: %s\n", name);
+ free(name);
+ }
+ free(zones);
+);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*! \todo In this function, xfr->zone is properly set. If this is so, we do not
+ * have to search for the zone after the transfer has finished.
+ */
+int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr)
+{
+ dbg_ns("ns_process_ixfrin: incoming packet\n");
+
+ /*!
+ * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
+ */
+
+ int ret = xfrin_process_ixfr_packet(xfr/*xfr->wire, xfr->wire_size,
+ (knot_changesets_t **)(&xfr->data)*/);
+
+ if (ret == XFRIN_RES_FALLBACK) {
+ dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n");
+ assert(xfr->data == NULL);
+// dbg_ns("xfr->zone = %p\n", xfr->zone);
+// dbg_ns("Zone name: %.*s\n",
+// xfr->zone->name->size, xfr->zone->name->name);
+// assert(xfr->zone == NULL);
+ knot_packet_free(&xfr->query);
+ return KNOT_ENOIXFR;
+ }
+
+ if (ret > 0) {
+ dbg_ns("ns_process_ixfrin: IXFR finished\n");
+
+ knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
+ if (chgsets == NULL || chgsets->first_soa == NULL) {
+ // nothing to be done??
+ dbg_ns("No changesets created for incoming IXFR!\n");
+ return ret;
+ }
+
+ // find zone associated with the changesets
+ knot_zone_t *zone = knot_zonedb_find_zone(
+ nameserver->zone_db,
+ knot_rrset_owner(chgsets->first_soa));
+ if (zone == NULL) {
+ dbg_ns("No zone found for incoming IXFR!\n");
+ knot_free_changesets(
+ (knot_changesets_t **)(&xfr->data));
+ return KNOT_ENOZONE; /*! \todo Other error code? */
+ }
+
+ switch (ret) {
+ case XFRIN_RES_COMPLETE:
+ xfr->zone = zone;
+ break;
+ case XFRIN_RES_SOA_ONLY: {
+ // compare the SERIAL from the changeset with the zone's
+ // serial
+ const knot_node_t *apex = knot_zone_contents_apex(
+ knot_zone_contents(zone));
+ if (apex == NULL) {
+ return KNOT_ERROR;
+ }
+
+ const knot_rrset_t *zone_soa = knot_node_rrset(
+ apex, KNOT_RRTYPE_SOA);
+ if (zone_soa == NULL) {
+ return KNOT_ERROR;
+ }
+
+ if (knot_rdata_soa_serial(knot_rrset_rdata(
+ chgsets->first_soa))
+ != knot_rdata_soa_serial(knot_rrset_rdata(
+ zone_soa))) {
+ dbg_ns("Update did not fit.\n");
+ return KNOT_EAGAIN;
+ } else {
+ // free changesets
+ dbg_ns("No update needed.\n");
+ knot_free_changesets(
+ (knot_changesets_t **)(&xfr->data));
+ return KNOT_ENOXFR;
+ }
+ } break;
+ }
+ }
+
+ /*!
+ * \todo In case of error, shouldn't the zone be destroyed here?
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize,
+ knot_zone_t **zone, knot_changeset_t **changeset)
+{
+ // 1) Parse the rest of the packet
+ assert(knot_packet_is_query(query));
+
+ knot_packet_t *response;
+ assert(*rsize >= MAX_UDP_PAYLOAD);
+ int ret = knot_ns_prepare_response(nameserver, query, &response,
+ MAX_UDP_PAYLOAD);
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ assert(response != NULL);
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+
+ if (knot_packet_parsed(query) < knot_packet_size(query)) {
+ ret = knot_packet_parse_rest(query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response,
+ (ret == KNOT_EMALF)
+ ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+ }
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
+
+ /*! \todo API for EDNS values. */
+ dbg_ns("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
+
+ // 2) Find zone for the query
+ // we do not check if there is only one entry in the Question section
+ // because the packet structure does not allow it
+ /*! \todo Check number of Question entries while parsing. */
+ if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
+ dbg_ns("Question is not of type SOA.\n");
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_FORMERR,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ *zone = knot_zonedb_find_zone(nameserver->zone_db,
+ knot_packet_qname(query));
+ if (*zone == NULL) {
+ dbg_ns("Zone not found for the update.\n");
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_NOTAUTH,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ uint8_t rcode = 0;
+ // 3) Check zone
+ ret = knot_ddns_check_zone(*zone, query, &rcode);
+ if (ret == KNOT_EBADZONE) {
+ // zone is slave, forward the request
+ /*! \todo Implement forwarding. */
+ return KNOT_EBADZONE;
+ } else if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ // 4) Convert prerequisities
+ knot_ddns_prereq_t *prereqs = NULL;
+ 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));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ assert(prereqs != NULL);
+
+ // 5) Check prerequisities
+ /*! \todo Somehow ensure the zone will not be changed until the update
+ * is finished.
+ */
+ ret = knot_ddns_check_prereqs(knot_zone_contents(*zone), &prereqs,
+ &rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_ddns_prereqs_free(&prereqs);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ // 6) Convert update to changeset
+ ret = knot_ddns_process_update(query, changeset, &rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_ddns_prereqs_free(&prereqs);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ assert(changeset != NULL);
+
+ // 7) Create response
+ dbg_ns("Update converted successfuly.\n");
+
+ /*! \todo No response yet. Distinguish somehow in the caller.
+ * Maybe only this case will be EOK, other cases some error.
+ */
+
+ knot_packet_free(&response);
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_create_forward_query(const knot_packet_t *query,
+ uint8_t *query_wire, size_t *size)
+{
+ // just copy the wireformat of the query and set a new random ID to it
+ if (knot_packet_size(query) > *size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(query_wire, knot_packet_wireformat(query),
+ knot_packet_size(query));
+ *size = knot_packet_size(query);
+
+ knot_wire_set_id(query_wire, knot_random_id());
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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
+
+ if (knot_packet_size(response) > *size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(response_wire, knot_packet_wireformat(response),
+ knot_packet_size(response));
+ *size = knot_packet_size(response);
+
+ knot_wire_set_id(response_wire, original_id);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *knot_ns_data(knot_nameserver_t *nameserver)
+{
+ return nameserver->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *knot_ns_get_data(knot_nameserver_t *nameserver)
+{
+ return nameserver->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_set_data(knot_nameserver_t *nameserver, void *data)
+{
+ nameserver->data = data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_destroy(knot_nameserver_t **nameserver)
+{
+ synchronize_rcu();
+
+ free((*nameserver)->err_response);
+ if ((*nameserver)->opt_rr != NULL) {
+ knot_edns_free(&(*nameserver)->opt_rr);
+ }
+
+ // destroy the zone db
+ knot_zonedb_deep_free(&(*nameserver)->zone_db);
+
+ free(*nameserver);
+ *nameserver = NULL;
+}
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
new file mode 100644
index 0000000..0d93df6
--- /dev/null
+++ b/src/libknot/nameserver/name-server.h
@@ -0,0 +1,358 @@
+/*!
+ * \file name-server.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * Contains the "name server" structure and interface for the main DNS
+ * functions. Currently only supports answering simple queries, without any
+ * extensions.
+ *
+ * \todo Consider saving pointer to the zdb_find_name() function in the
+ * nameserver structure. Probably not needed, these modules can be
+ * inter-connected.
+ * \todo Provide interface for other DNS functions - zone transfers, dynamic
+ * updates, etc.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_NAME_SERVER_H_
+#define _KNOT_NAME_SERVER_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "zone/zonedb.h"
+#include "edns.h"
+#include "consts.h"
+#include "tsig.h"
+#include "packet/packet.h"
+#include "common/sockaddr.h"
+#include "updates/changesets.h"
+
+struct conf_t;
+struct server_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Name server structure. Holds all important data needed for the
+ * supported DNS functions.
+ *
+ * Currently only holds pointer to the zone database for answering queries.
+ */
+typedef struct knot_nameserver {
+ /*!
+ * \brief Pointer to the zone database structure used for answering
+ * queries.
+ */
+ knot_zonedb_t *zone_db;
+ uint8_t *err_response; /*!< Prepared generic error response. */
+ size_t err_resp_size; /*!< Size of the prepared error response. */
+ knot_opt_rr_t *opt_rr; /*!< OPT RR with the server's EDNS0 info. */
+
+ void *data;
+} knot_nameserver_t;
+
+/*! \brief Callback for sending one packet back through a TCP connection. */
+typedef int (*xfr_callback_t)(int session, sockaddr_t *addr,
+ uint8_t *packet, size_t size);
+
+/*!
+ * \brief Single XFR operation structure.
+ *
+ * Used for communication with XFR handler.
+ */
+typedef struct knot_ns_xfr {
+ int type;
+ int flags;
+ sockaddr_t addr;
+ knot_packet_t *query;
+ knot_packet_t *response;
+ xfr_callback_t send;
+ int session;
+
+ /*!
+ * XFR-out: Output buffer.
+ * XFR-in: Buffer for query or incoming packet.
+ */
+ uint8_t *wire;
+
+ /*!
+ * XFR-out: Size of the output buffer.
+ * XFR-in: Size of the current packet.
+ */
+ size_t wire_size;
+ void *data;
+ knot_zone_t *zone;
+ void *owner;
+
+ /*! \note [TSIG] TSIG fields */
+ /*! \brief Message(s) to sign in wireformat.
+ *
+ * This field should be allocated at the start of transfer and
+ * freed at the end. During the transfer it is only rewritten.
+ */
+ uint8_t *tsig_data;
+ size_t tsig_data_size; /*!< Size of the message(s) in bytes */
+// const knot_rrset_t *tsig; /*!< Response TSIG.
+// \todo [TSIG] Replace with separate data. */
+ size_t tsig_size; /*!< Size of the TSIG RR wireformat in bytes.*/
+ knot_key_t *tsig_key; /*!< Associated TSIG key for signing. */
+
+ uint8_t *digest; /*!< Buffer for counting digest. */
+ size_t digest_size; /*!< Size of the digest. */
+ size_t digest_max_size; /*!< Size of the buffer. */
+
+ /*! \brief Previous digest or request digest.
+ *
+ * Should be allocated before the transfer (known size).
+ */
+// uint8_t *prev_digest;
+// size_t prev_digest_size; /*!< Size of previous digest in bytes. */
+
+ /*!
+ * \brief Number of the packet currently assembled.
+ *
+ * In case of XFR-in, this is not the overall number of packet, just
+ * number counted from last TSIG check.
+ */
+ int packet_nr;
+} knot_ns_xfr_t;
+
+
+static const int KNOT_NS_TSIG_FREQ = 100;
+
+static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;
+
+/*!
+ * \brief XFR request flags.
+ */
+enum knot_ns_xfr_flag_t {
+ XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */
+ XFR_FLAG_UDP = 1 << 1 /*!< XFR request is on UDP. */
+};
+
+/*!
+ * \brief XFR request types.
+ */
+typedef enum knot_ns_xfr_type_t {
+ /* Special events. */
+ XFR_TYPE_CLOSE = -1, /*!< Close connection event. */
+
+ /* DNS events. */
+ XFR_TYPE_AIN = 0, /*!< AXFR-IN request (start transfer). */
+ XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */
+ XFR_TYPE_IIN, /*!< IXFR-IN request (start transfer). */
+ XFR_TYPE_IOUT, /*!< IXFR-OUT request (incoming transfer). */
+ XFR_TYPE_SOA, /*!< Pending SOA request. */
+ XFR_TYPE_NOTIFY /*!< Pending NOTIFY query. */
+} knot_ns_xfr_type_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Allocates and initializes the name server structure.
+ *
+ * \return Pointer to the name server structure.
+ */
+knot_nameserver_t *knot_ns_create();
+
+/*!
+ * \brief Parses the given query into the response structure and recognizes
+ * type of the query.
+ *
+ * Some query types are distinguished by OPCODE (NOTIFY, UPDATE, etc.), some
+ * by QTYPE (AXFR, IXFR). As these information are needed on the same layer
+ * to decide what to do with the query, the knot_query_t type is used for this
+ * purpose.
+ *
+ * \param query_wire Wire format of the query.
+ * \param qsize Size of the query in octets.
+ * \param packet Packet structure to be filled with the parsed query.
+ * \param type Type of the query.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EMALF if the query is totally unusable. Such query must be
+ * ignored.
+ * \retval KNOT_RCODE_SERVFAIL if there was some internal error. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ * \retval KNOT_RCODE_FORMERR if the query was malformed, but can be used to
+ * construct an error response. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ * \retval KNOT_RCODE_NOTIMPL if the query has an unsupported type. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ */
+int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
+ knot_packet_t *packet, knot_packet_type_t *type);
+
+/*!
+ * \brief Prepares wire format of an error response using generic error template
+ * stored in the nameserver structure.
+ *
+ * The error response will not contain the Question section from the query, just
+ * a header with ID copied from the query and the given RCODE.
+ *
+ * \param nameserver Nameserver structure containing the error template.
+ * \param query_id ID of the query.
+ * \param rcode RCODE to set in the response.
+ * \param response_wire Place for wire format of the response.
+ * \param rsize Size of the error response will be stored here.
+ */
+void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id,
+ uint8_t rcode, uint8_t *response_wire, size_t *rsize);
+
+/*!
+ * \brief Creates a response for the given normal query using the data of the
+ * nameserver.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param resp Response structure with parsed query.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ * size of the response.
+ *
+ * \retval KNOT_EOK if a valid response was created.
+ * \retval KNOT_EMALF if an error occured and the response is not valid.
+ */
+int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize);
+
+int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Compares two zone serials.
+ *
+ * \retval < 0 if s1 is less than s2.
+ * \retval > 0 if s1 is larger than s2.
+ * \retval == 0 if s1 is equal to s2.
+ */
+int ns_serial_compare(uint32_t s1, uint32_t s2);
+
+int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
+ uint32_t *serial_to);
+
+int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr, knot_rcode_t rcode);
+
+/*!
+ * \brief Processes an AXFR query.
+ *
+ * This function sequentially creates DNS packets to be sent as a response
+ * to the AXFR query and sends each packet using the given callback (\a
+ * send_packet).
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \note Currently only a stub which sends one error response using the given
+ * callback.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ERROR
+ *
+ * \todo Maybe the place for the wire format should be passed in as in
+ * the ns_answer_request() function...?
+ */
+int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an IXFR query.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \todo Document properly.
+ */
+int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an AXFR-IN packet.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \todo Document me.
+ */
+int knot_ns_process_axfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+int knot_ns_switch_zone(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an IXFR-IN packet.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \retval KNOT_EOK If this packet was processed successfuly and another packet
+ * is expected. (RFC1995bis, case c)
+ * \retval KNOT_ENOXFR If the transfer is not taking place because server's
+ * SERIAL is the same as this client's SERIAL. The client
+ * should close the connection and do no further processing.
+ * (RFC1995bis case a).
+ * \retval KNOT_EAGAIN If the server could not fit the transfer into the packet.
+ * This should happen only if UDP was used. In this case
+ * the client should retry the request via TCP. If UDP was
+ * not used, it should be considered that the transfer was
+ * malformed and the connection should be closed.
+ * (RFC1995bis case b).
+ * \retval >0 Transfer successully finished. Changesets are created and furter
+ * processing is needed.
+ * \retval Other If any other error occured. The connection should be closed.
+ *
+ * \todo Document me.
+ */
+int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize,
+ knot_zone_t **zone, knot_changeset_t **changeset);
+
+int knot_ns_create_forward_query(const knot_packet_t *query,
+ uint8_t *query_wire, size_t *size);
+
+int knot_ns_process_forward_response(const knot_packet_t *response,
+ uint16_t original_id,
+ uint8_t *response_wire, size_t *size);
+
+void *knot_ns_data(knot_nameserver_t *nameserver);
+
+void *knot_ns_get_data(knot_nameserver_t *nameserver);
+
+void knot_ns_set_data(knot_nameserver_t *nameserver, void *data);
+
+int knot_ns_tsig_required(int packet_nr);
+
+/*!
+ * \brief Properly destroys the name server structure.
+ *
+ * \param nameserver Nameserver to destroy.
+ */
+void knot_ns_destroy(knot_nameserver_t **nameserver);
+
+
+#endif /* _KNOTNAME_SERVER_H_ */
+
+/*! @} */
diff --git a/src/libknot/nsec3.c b/src/libknot/nsec3.c
new file mode 100644
index 0000000..303d2e6
--- /dev/null
+++ b/src/libknot/nsec3.c
@@ -0,0 +1,265 @@
+/* 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 <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 "util/descriptor.h"
+#include "util/utils.h"
+#include "util/tolower.h"
+#include "util/error.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) {
+ return KNOT_EBADARG;
+ }
+
+ assert(knot_rrset_type(nsec3param) == KNOT_RRTYPE_NSEC3PARAM);
+ const knot_rdata_t *rdata = knot_rrset_rdata(nsec3param);
+
+ assert(rdata->count == 4);
+
+ params->algorithm = *(uint8_t *)
+ (&knot_rdata_item(rdata, 0)->raw_data[1]);
+ params->flags = *(uint8_t *)
+ (&knot_rdata_item(rdata, 1)->raw_data[1]);
+ params->iterations = knot_wire_read_u16(
+ (uint8_t *)(knot_rdata_item(rdata, 2)->raw_data + 1));
+
+ params->salt_length =
+ ((uint8_t *)knot_rdata_item(rdata, 3)->raw_data)[2];
+
+ if (params->salt_length > 0) {
+ params->salt = (uint8_t *)malloc(params->salt_length);
+ CHECK_ALLOC_LOG(params->salt, -1);
+ memcpy(params->salt,
+ (uint8_t *)knot_rdata_item(rdata, 3)->raw_data + 3,
+ params->salt_length);
+ } else {
+ params->salt = NULL;
+ }
+
+ dbg_nsec3("Parsed NSEC3PARAM:\n");
+ dbg_nsec3("Algorithm: %hu\n", params->algorithm);
+ dbg_nsec3("Flags: %hu\n", params->flags);
+ dbg_nsec3("Iterations: %hu\n", params->iterations);
+ dbg_nsec3("Salt length: %hu\n", params->salt_length);
+ dbg_nsec3("Salt: ");
+ 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 (int 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_EBADARG;
+ }
+
+ 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("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_EBADARG;
+ }
+
+ uint8_t *salt = params->salt;
+ uint8_t salt_length = params->salt_length;
+ uint16_t iterations = params->iterations;
+
+ dbg_nsec3("Hashing: \n");
+ dbg_nsec3(" Data: %.*s \n", size, data);
+ dbg_nsec3_hex((const char *)data, size);
+ dbg_nsec3(" (size %d)\n Iterations: %u\n", (int)size, iterations);
+ dbg_nsec3(" Salt length: %u\n", salt_length);
+ dbg_nsec3(" Salt: ");
+ if (salt_length > 0) {
+ dbg_nsec3_hex((char *)salt, salt_length);
+ dbg_nsec3("\n");
+ } else {
+ dbg_nsec3("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("Hash: %.*s\n", *digest_size, *digest);
+ dbg_nsec3_hex((const char *)*digest, *digest_size);
+ dbg_nsec3("\n");
+
+ free(data_low);
+ return KNOT_EOK;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+void knot_nsec3_params_free(knot_nsec3_params_t *params)
+{
+ if (params->salt != NULL) {
+ free(params->salt);
+ }
+}
diff --git a/src/libknot/nsec3.h b/src/libknot/nsec3.h
new file mode 100644
index 0000000..0ce6899
--- /dev/null
+++ b/src/libknot/nsec3.h
@@ -0,0 +1,92 @@
+/*!
+ * \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_EBADARG
+ * \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
new file mode 100644
index 0000000..82e818f
--- /dev/null
+++ b/src/libknot/packet/packet.c
@@ -0,0 +1,1532 @@
+/* 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 "packet/packet.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/wire.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)
+
+
+
+typedef enum {
+ KNOT_PACKET_DUPL_IGNORE,
+ KNOT_PACKET_DUPL_SKIP,
+ KNOT_PACKET_DUPL_MERGE
+} knot_packet_duplicate_handling_t;
+/*----------------------------------------------------------------------------*/
+/* 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("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("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;
+
+ pkt->owner_tmp = (uint8_t *)pos;
+ dbg_packet("Tmp owner: %p\n", pkt->owner_tmp);
+ pos += PREALLOC_RR_OWNER;
+
+ // 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("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("Additional section: %p\n", pkt->additional);
+
+ pkt->max_an_rrsets = DEFAULT_ANCOUNT;
+ pkt->max_ns_rrsets = DEFAULT_NSCOUNT;
+ pkt->max_ar_rrsets = DEFAULT_ARCOUNT;
+
+ // then domain names for compression and offsets
+ pkt->compression.dnames = (const knot_dname_t **)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(const knot_dname_t *);
+ pkt->compression.offsets = (size_t *)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t);
+
+ dbg_packet("Compression dnames: %p\n", pkt->compression.dnames);
+ dbg_packet("Compression offsets: %p\n", pkt->compression.offsets);
+
+ pkt->compression.max = DEFAULT_DOMAINS_IN_RESPONSE;
+
+ pkt->tmp_rrsets = (const knot_rrset_t **)pos;
+ pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *);
+
+ dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ 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("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("QNAME: %p (%zu after start of packet)\n",
+ pkt->question.qname,
+ (void *)pkt->question.qname - (void *)pkt);
+
+ pkt->question.qname->name = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_NAME;
+ pkt->question.qname->labels = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_LABELS;
+
+// pkt->owner_tmp = (uint8_t *)((char *)pkt->question.qname->labels
+// + PREALLOC_QNAME_LABELS);
+
+ // then answer, authority and additional sections
+ if (DEFAULT_ANCOUNT_QUERY == 0) {
+ 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("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("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("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY;
+
+// dbg_packet("End of data: %p (%zu after start of packet)\n",
+// pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY,
+// (void *)(pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY)
+// - (void *)pkt);
+ dbg_packet("Allocated total: %u\n", PREALLOC_QUERY);
+
+ 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_response("Not enough data to parse header.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ header->id = knot_wire_get_id(wire);
+ // copy some of the flags: OPCODE and RD
+ // do this by copying flags1 and setting QR to 1, AA to 0 and TC to 0
+ header->flags1 = knot_wire_get_flags1(wire);
+// knot_wire_flags_set_qr(&header->flags1);
+// knot_wire_flags_clear_aa(&header->flags1);
+// knot_wire_flags_clear_tc(&header->flags1);
+ // do not copy flags2 (all set by server)
+ header->qdcount = knot_wire_get_qdcount(wire);
+ header->ancount = knot_wire_get_ancount(wire);
+ header->nscount = knot_wire_get_nscount(wire);
+ 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
+ */
+static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
+ size_t size,
+ knot_question_t *question, int alloc)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(question != NULL);
+
+ if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // malformed
+ }
+
+ dbg_response("Parsing Question starting on position %zu.\n",
+ *pos);
+
+ // domain name must end with 0, so just search for 0
+ int i = *pos;
+ while (i < size && wire[i] != 0) {
+ ++i;
+ }
+
+ if (size - i - 1 < 4) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // no 0 found or not enough data left
+ }
+
+ dbg_response("Parsing dname starting on position %zu and "
+ "%zu bytes long.\n", *pos, i - *pos + 1);
+ dbg_response("Alloc: %d\n", alloc);
+ if (alloc) {
+ question->qname = knot_dname_new_from_wire(
+ wire + *pos, i - *pos + 1, NULL);
+ if (question->qname == NULL) {
+ return KNOT_ENOMEM;
+ }
+ } else {
+ int res = knot_dname_from_wire(wire + *pos, i - *pos + 1,
+ NULL, question->qname);
+ if (res != KNOT_EOK) {
+ assert(res != KNOT_EBADARG);
+ return res;
+ }
+ }
+
+ *pos = i + 1;
+ question->qtype = knot_wire_read_u16(wire + i + 1);
+ //*pos += 2;
+ question->qclass = knot_wire_read_u16(wire + i + 3);
+ //*pos += 2;
+
+ *pos += 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
+ short *max_count,
+ short default_max_count, short step)
+{
+ dbg_packet("Max count: %d, default max count: %d\n",
+ *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(
+ 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;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
+{
+// if (desc->type == 0) {
+// dbg_packet("Unknown RR type.\n");
+// return NULL;
+// }
+
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (rdata == NULL) {
+ return NULL;
+ }
+
+ int rc = knot_rdata_from_wire(rdata, wire, pos, total_size, rdlength,
+ desc);
+
+ if (rc != KNOT_EOK) {
+ dbg_packet("rdata_from_wire() returned: %s\n",
+ knot_strerror(rc));
+ knot_rdata_free(&rdata);
+ return NULL;
+ }
+
+ return rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
+ size_t size)
+{
+// knot_rrset_t *rrset =
+// (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+// CHECK_ALLOC_LOG(rrset, NULL);
+
+ dbg_packet("Parsing RR from position: %zu, total size: %zu\n",
+ *pos, size);
+
+ knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
+ NULL);
+ dbg_packet("Created owner: %p, actual position: %zu\n", owner,
+ *pos);
+ if (owner == NULL) {
+ return NULL;
+ }
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(owner);
+ dbg_packet("Parsed name: %s\n", name);
+ free(name);
+);
+
+ //*remaining -= knot_dname_size(rrset->owner);
+
+ /*! @todo Get rid of the numerical constant. */
+ if (size - *pos < 10) {
+ dbg_packet("Malformed RR: Not enough data to parse RR"
+ " header.\n");
+ knot_dname_release(owner);
+ return NULL;
+ }
+
+ dbg_packet("Reading type from position %zu\n", *pos);
+
+ uint16_t type = knot_wire_read_u16(wire + *pos);
+ uint16_t rclass = knot_wire_read_u16(wire + *pos + 2);
+ 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) {
+ return NULL;
+ }
+
+ uint16_t rdlength = knot_wire_read_u16(wire + *pos + 8);
+
+ dbg_packet("Read RR header: type %u, class %u, ttl %u, "
+ "rdlength %u\n", rrset->type, rrset->rclass,
+ rrset->ttl, rdlength);
+
+ *pos += 10;
+
+ 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, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ rrset->rrsigs = NULL;
+
+ if (rdlength == 0) {
+ return rrset;
+ }
+
+ // parse RDATA
+ knot_rdata_t *rdata = knot_packet_parse_rdata(wire, pos, size,
+ rdlength,
+ knot_rrtype_descriptor_by_type(rrset->type));
+ if (rdata == NULL) {
+ dbg_packet("Malformed RR: Could not parse RDATA.\n");
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ if (knot_rrset_add_rdata(rrset, rdata) != KNOT_EOK) {
+ dbg_packet("Malformed RR: Could not add RDATA to RRSet"
+ ".\n");
+ knot_rdata_free(&rdata);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ return rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_add_rrset(knot_rrset_t *rrset,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count,
+ short *max_rrsets,
+ short default_rrsets,
+ const knot_packet_t *packet,
+ knot_packet_duplicate_handling_t dupl)
+{
+
+ assert(rrset != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_packet("packet_add_rrset(), owner: %s, type: %s\n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+);
+
+ if (*rrset_count == *max_rrsets
+ && knot_packet_realloc_rrsets(rrsets, max_rrsets, default_rrsets,
+ STEP_ANCOUNT) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_SKIP &&
+ knot_packet_contains(packet, rrset, KNOT_RRSET_COMPARE_PTR)) {
+ /*! \todo This should also return > 0, as it means that the
+ RRSet was not used actually. */
+ return KNOT_EOK;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_MERGE) {
+ // try to find the RRSet in this array of RRSets
+ for (int i = 0; i < *rrset_count; ++i) {
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str((*rrsets)[i]->owner);
+ dbg_packet("Comparing to RRSet: owner: %s, "
+ "type: %s\n", name,
+ knot_rrtype_to_string(
+ (*rrsets)[i]->type));
+ free(name);
+);
+
+ if (knot_rrset_compare((*rrsets)[i], rrset,
+ KNOT_RRSET_COMPARE_HEADER)) {
+ //const knot_rrset_t *r = (*rrsets)
+ /*! \todo Test this!!! */
+ int rc = knot_rrset_merge(
+ (void **)((*rrsets) + i), (void **)&rrset);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ return 1;
+ }
+ }
+ }
+
+ (*rrsets)[*rrset_count] = rrset;
+ ++(*rrset_count);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
+ size_t size, uint16_t rr_count,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count, short *max_rrsets,
+ short default_rrsets,
+ knot_packet_t *packet)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+ assert(packet != NULL);
+
+ dbg_packet("Parsing RRSets starting on position: %zu\n",
+ *pos);
+
+// if (*rrsets == NULL) {
+// knot_packet_realloc_rrsets(rrsets, max_rrsets, 0, 1);
+// }
+
+ /*
+ * The RRs from one RRSet may be scattered in the current section.
+ * We must parse all RRs separately and try to add them to already
+ * parsed RRSets.
+ */
+ int err = KNOT_EOK;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < rr_count; ++i) {
+ rrset = knot_packet_parse_rr(wire, pos, size);
+ if (rrset == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ err = KNOT_EMALF;
+ break;
+ }
+
+ err = knot_packet_add_rrset(rrset, rrsets, rrset_count,
+ max_rrsets, default_rrsets, packet,
+ KNOT_PACKET_DUPL_MERGE);
+ if (err < 0) {
+ break;
+ } else if (err > 0) { // merged
+ dbg_packet("RRSet merged, freeing.\n");
+ knot_rrset_deep_free(&rrset, 1, 0, 0); // TODO: ok??
+ continue;
+ }
+
+ err = knot_packet_add_tmp_rrset(packet, rrset);
+ if (err != KNOT_EOK) {
+ // remove the last RRSet from the list of RRSets
+ // - just decrement the count
+ --(*rrset_count);
+ knot_rrset_deep_free(&rrset, 1, 1, 1);
+ break;
+ }
+ }
+
+ return (err < 0) ? err : KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Deallocates all space which was allocated additionally to the
+ * pre-allocated space of the response structure.
+ *
+ * \param resp Response structure that holds pointers to the allocated space.
+ */
+static void knot_packet_free_allocated_space(knot_packet_t *pkt)
+{
+ dbg_packet("Freeing additional space in packet.\n");
+ if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) {
+ dbg_packet("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->compression.max > DEFAULT_DOMAINS_IN_RESPONSE) {
+ free(pkt->compression.dnames);
+ free(pkt->compression.offsets);
+ }
+
+ if (pkt->tmp_rrsets_max > DEFAULT_RRSET_COUNT(TMP_RRSETS, pkt)) {
+ free(pkt->tmp_rrsets);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rr_sections(knot_packet_t *packet,
+ size_t *pos)
+{
+ assert(packet != NULL);
+ assert(packet->wireformat != NULL);
+ assert(packet->size > 0);
+ assert(pos != NULL);
+ assert(*pos > 0);
+
+ int err;
+
+ dbg_packet("Parsing Answer RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.ancount, &packet->answer,
+ &packet->an_rrsets, &packet->max_an_rrsets,
+ DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Authority RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.nscount, &packet->authority,
+ &packet->ns_rrsets, &packet->max_ns_rrsets,
+ DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Additional RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.arcount, &packet->additional,
+ &packet->ar_rrsets, &packet->max_ar_rrsets,
+ DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Trying to find OPT RR in the packet.\n");
+
+ for (int i = 0; i < packet->ar_rrsets; ++i) {
+ assert(packet->additional[i] != NULL);
+ if (knot_rrset_type(packet->additional[i])
+ == KNOT_RRTYPE_OPT) {
+ dbg_packet("Found OPT RR, filling.\n");
+ err = knot_edns_new_from_rr(&packet->opt_rr,
+ packet->additional[i]);
+ if (err != KNOT_EOK) {
+ return err;
+ }
+ break;
+ }
+ }
+
+ packet->parsed = *pos;
+
+ if (*pos < packet->size) {
+ // some trailing garbage; ignore, but log
+ dbg_response("Packet: %zu bytes of trailing garbage "
+ "in packet.\n", packet->size - (*pos));
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc)
+{
+ knot_packet_t *pkt;
+ 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 *)malloc(size);
+ CHECK_ALLOC_LOG(pkt, NULL);
+ memset(pkt, 0, size);
+ if (init_pointers != NULL) {
+ init_pointers(pkt);
+ }
+
+ pkt->prealloc_type = prealloc;
+
+ // set EDNS version to not supported
+ pkt->opt_rr.version = EDNS_NOT_SUPPORTED;
+
+ return pkt;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_from_wire(knot_packet_t *packet,
+ const uint8_t *wireformat, size_t size,
+ int question_only)
+{
+ if (packet == NULL || wireformat == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int err;
+
+ // save the wireformat in the packet
+ // TODO: can we just save the pointer, or we have to copy the data??
+ assert(packet->wireformat == NULL);
+ packet->wireformat = (uint8_t*)wireformat;
+ packet->size = size;
+ packet->free_wireformat = 0;
+
+ //uint8_t *pos = wireformat;
+ size_t pos = 0;
+ //size_t remaining = size;
+
+ dbg_packet("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;
+
+ dbg_packet("Question (prealloc type: %d)...\n", packet->prealloc_type);
+
+ if (packet->header.qdcount > 1) {
+ dbg_packet("QDCOUNT larger than 1, FORMERR.\n");
+ return KNOT_EMALF;
+ }
+
+ knot_packet_dump(packet);
+
+ if (packet->header.qdcount == 1) {
+ if ((err = knot_packet_parse_question(wireformat, &pos, size,
+ &packet->question, packet->prealloc_type
+ == KNOT_PACKET_PREALLOC_NONE)
+ ) != KNOT_EOK) {
+ return err;
+ }
+ packet->parsed = pos;
+ }
+
+ knot_packet_dump(packet);
+
+ if (question_only) {
+ return KNOT_EOK;
+ }
+
+ /*! \todo Replace by call to parse_rest()? */
+ err = knot_packet_parse_rr_sections(packet, &pos);
+
+#ifdef KNOT_PACKET_DEBUG
+ knot_packet_dump(packet);
+#endif /* KNOT_RESPONSE_DEBUG */
+
+ return err;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_rest(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+// if (packet->parsed >= packet->size) {
+// return KNOT_EOK;
+// }
+
+ if (packet->parsed == packet->size) {
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ return knot_packet_parse_rr_sections(packet, &pos);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->an_rrsets <= packet->header.ancount);
+ if (packet->an_rrsets != packet->header.ancount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->an_rrsets == packet->header.ancount) {
+ assert(packet->parsed < packet->size);
+ //dbg_packet("Trailing garbage, ignoring...\n");
+ // there may be other data in the packet
+ // (authority or additional).
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Answer RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->an_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ /*! \todo Implement. */
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->ar_rrsets <= packet->header.arcount);
+ if (packet->ar_rrsets != packet->header.arcount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->ar_rrsets == packet->header.arcount) {
+ assert(packet->parsed < packet->size);
+ dbg_packet("Trailing garbage, ignoring...\n");
+ /*! \todo Do not ignore. */
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Additional RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->ar_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_size(const knot_packet_t *packet)
+{
+ return packet->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_question_size(const knot_packet_t *packet)
+{
+ return (KNOT_WIRE_HEADER_SIZE + 4
+ + knot_dname_size(packet->question.qname));
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_parsed(const knot_packet_t *packet)
+{
+ return packet->parsed;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
+{
+ if (packet == NULL || max_size <= 0) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->max_size < max_size) {
+ // reallocate space for the wire format (and copy anything
+ // that might have been there before
+ uint8_t *wire_new = (uint8_t *)malloc(max_size);
+ if (wire_new == NULL) {
+ 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) {
+ free(wire_old);
+ }
+
+ packet->free_wireformat = 1;
+ }
+
+ // set max size
+ packet->max_size = max_size;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_random_id(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ packet->header.id = knot_random_id();
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_packet_opcode(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return knot_wire_flags_get_opcode(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->question.qname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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, knot_rr_type_t qtype)
+{
+ assert(packet != NULL);
+ packet->question.qtype = qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_qclass(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->question.qclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_is_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_wire_flags_get_qr(packet->header.flags1) == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_packet_t *knot_packet_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->query;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_rcode(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_rcode(packet->header.flags2);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_tc(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_tc(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_qdcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.qdcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_ancount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.ancount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_nscount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.nscount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_arcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.arcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size)
+{
+ packet->tsig_size = tsig_size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_answer_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->an_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_authority_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ns_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_additional_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ar_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_answer_rrset(
+ const knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->an_rrsets) {
+ return NULL;
+ }
+
+ return packet->answer[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_authority_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ns_rrsets) {
+ return NULL;
+ }
+
+ return packet->authority[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_additional_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ar_rrsets) {
+ return NULL;
+ }
+
+ return packet->additional[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_contains(const knot_packet_t *packet,
+ const knot_rrset_t *rrset,
+ knot_rrset_compare_type_t cmp)
+{
+ if (packet == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < packet->header.ancount; ++i) {
+ if (knot_rrset_compare(packet->answer[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.nscount; ++i) {
+ if (knot_rrset_compare(packet->authority[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.arcount; ++i) {
+ if (knot_rrset_compare(packet->additional[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_add_tmp_rrset(knot_packet_t *packet,
+ knot_rrset_t *tmp_rrset)
+{
+ if (packet == NULL || tmp_rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ 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;
+ }
+
+ packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset;
+ dbg_packet("Current tmp RRSets count: %d, max count: %d\n",
+ packet->tmp_rrsets_count, packet->tmp_rrsets_max);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Frees all temporary RRSets stored in the response structure.
+ *
+ * \param resp Response structure to free the temporary RRSets from.
+ */
+void knot_packet_free_tmp_rrsets(knot_packet_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < pkt->tmp_rrsets_count; ++i) {
+dbg_packet_exec(
+ char *name = knot_dname_to_str(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->owner);
+ dbg_packet("Freeing tmp RRSet on ptr: %p (ptr to ptr:"
+ " %p, type: %s, owner: %s)\n",
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ knot_rrtype_to_string(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->type),
+ name);
+ free(name);
+);
+ // TODO: this is quite ugly, but better than copying whole
+ // function (for reallocating rrset array)
+ knot_rrset_deep_free(
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), 1, 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_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) {
+ return KNOT_EBADARG;
+ }
+
+ 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);
+
+// int err = 0;
+ // TODO: put the qname into the compression table
+// // TODO: get rid of the numeric constants
+// if ((err = knot_response_store_dname_pos(&packet->compression,
+// packet->question.qname,0, 12, 12)) != KNOT_EOK) {
+// return err;
+// }
+
+ packet->size += knot_dname_size(packet->question.qname) + 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_edns_to_wire(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ packet->size += knot_edns_to_wire(&packet->opt_rr,
+ packet->wireformat + packet->size,
+ packet->max_size - packet->size);
+
+ packet->header.arcount += 1;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_to_wire(knot_packet_t *packet,
+ uint8_t **wire, size_t *wire_size)
+{
+ if (packet == NULL || wire == NULL || wire_size == NULL
+ || *wire != NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(packet->size <= packet->max_size);
+
+ // if there are no additional RRSets, add EDNS OPT RR
+ if (packet->header.arcount == 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;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const uint8_t *knot_packet_wireformat(const knot_packet_t *packet)
+{
+ return packet->wireformat;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_free(knot_packet_t **packet)
+{
+ if (packet == NULL || *packet == NULL) {
+ return;
+ }
+
+ // free temporary domain names
+ dbg_packet("Freeing tmp RRSets...\n");
+ knot_packet_free_tmp_rrsets(*packet);
+
+ // check if some additional space was allocated for the packet
+ dbg_packet("Freeing additional allocated space...\n");
+ knot_packet_free_allocated_space(*packet);
+
+ // free the space for wireformat
+// assert((*packet)->wireformat != NULL);
+// free((*packet)->wireformat);
+ if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) {
+ free((*packet)->wireformat);
+ }
+
+ dbg_packet("Freeing packet structure\n");
+ free(*packet);
+ *packet = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef KNOT_PACKET_DEBUG
+static void knot_packet_dump_rrsets(const knot_rrset_t **rrsets,
+ int count)
+{
+ assert((rrsets != NULL && *rrsets != NULL) || count < 1);
+
+ for (int i = 0; i < count; ++i) {
+ knot_rrset_dump(rrsets[i], 0);
+ }
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_dump(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+#ifdef KNOT_PACKET_DEBUG
+ dbg_packet("DNS packet:\n-----------------------------\n");
+
+ dbg_packet("\nHeader:\n");
+ dbg_packet(" ID: %u", packet->header.id);
+ 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(" 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);
+
+ if (knot_packet_qdcount(packet) > 0) {
+ dbg_packet("\nQuestion:\n");
+ char *qname = knot_dname_to_str(packet->question.qname);
+ dbg_packet(" QNAME: %s\n", qname);
+ free(qname);
+ dbg_packet(" QTYPE: %u (%s)\n", packet->question.qtype,
+ knot_rrtype_to_string(packet->question.qtype));
+ dbg_packet(" QCLASS: %u (%s)\n", packet->question.qclass,
+ knot_rrclass_to_string(packet->question.qclass));
+ }
+
+ dbg_packet("\nAnswer RRSets:\n");
+ knot_packet_dump_rrsets(packet->answer, packet->an_rrsets);
+ dbg_packet("\nAuthority RRSets:\n");
+ knot_packet_dump_rrsets(packet->authority, packet->ns_rrsets);
+ dbg_packet("\nAdditional RRSets:\n");
+ knot_packet_dump_rrsets(packet->additional, packet->ar_rrsets);
+
+ /*! \todo Dumping of Answer, Authority and Additional sections. */
+
+ dbg_packet("\nEDNS:\n");
+ dbg_packet(" Version: %u\n", packet->opt_rr.version);
+ dbg_packet(" Payload: %u\n", packet->opt_rr.payload);
+ dbg_packet(" Extended RCODE: %u\n",
+ packet->opt_rr.ext_rcode);
+
+ dbg_packet("\nPacket size: %zu\n", packet->size);
+ dbg_packet("\n-----------------------------\n");
+#endif
+}
+
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
new file mode 100644
index 0000000..1bf74a9
--- /dev/null
+++ b/src/libknot/packet/packet.h
@@ -0,0 +1,538 @@
+/*!
+ * \file packet.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure for holding DNS packet data and metadata.
+ *
+ * \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_PACKET_H_
+#define _KNOT_PACKET_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for holding information needed for compressing domain names.
+ *
+ * It's a simple table of domain names and their offsets in wire format of the
+ * packet.
+ *
+ * \todo Consider using some better lookup structure, such as skip-list.
+ */
+struct knot_compressed_dnames {
+ const knot_dname_t **dnames; /*!< Domain names present in packet. */
+ size_t *offsets; /*!< Offsets of domain names in the packet. */
+ short count; /*!< Count of items in the previous arrays. */
+ short max; /*!< Capacity of the structure (allocated). */
+};
+
+typedef struct knot_compressed_dnames knot_compressed_dnames_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;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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;
+
+ uint8_t *owner_tmp; /*!< Allocated space for RRSet owner wire format.*/
+
+ const knot_rrset_t **answer; /*!< Answer RRSets. */
+ const knot_rrset_t **authority; /*!< Authority RRSets. */
+ const knot_rrset_t **additional; /*!< Additional RRSets. */
+
+ short an_rrsets; /*!< Count of Answer RRSets in the response. */
+ short ns_rrsets; /*!< Count of Authority RRSets in the response. */
+ short ar_rrsets; /*!< Count of Additional RRSets in the response. */
+
+ short max_an_rrsets; /*!< Allocated space for Answer RRsets. */
+ short max_ns_rrsets; /*!< Allocated space for Authority RRsets. */
+ short max_ar_rrsets; /*!< Allocated space for Additional RRsets. */
+
+ knot_opt_rr_t opt_rr; /*!< OPT RR included in the packet. */
+
+ uint8_t *wireformat; /*!< Wire format of the packet. */
+
+ short free_wireformat;
+ size_t parsed;
+
+ size_t size; /*!< Current wire size of the packet. */
+ size_t max_size; /*!< Maximum allowed size of the packet. */
+
+ /*! \brief Information needed for compressing domain names in packet. */
+ knot_compressed_dnames_t compression;
+
+ /*! \brief RRSets to be destroyed with the packet structure. */
+ const knot_rrset_t **tmp_rrsets;
+ short tmp_rrsets_count; /*!< Count of temporary RRSets. */
+ short tmp_rrsets_max; /*!< Allocated space for temporary RRSets. */
+
+ struct knot_packet *query; /*!< Associated query. */
+
+ knot_packet_prealloc_type_t prealloc_type;
+
+ size_t tsig_size; /*!< Space to reserve for the TSIG RR. */
+};
+
+typedef struct knot_packet knot_packet_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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 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. */
+};
+
+/*----------------------------------------------------------------------------*/
+#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,
+ /*!
+ * \brief Space for RR owner wire format.
+ *
+ * Temporary buffer, used when putting RRSets to the response.
+ */
+ PREALLOC_RR_OWNER = 256,
+
+// /*! \brief Space for Answer RRSets. */
+// PREALLOC_ANSWER = DEFAULT_ANCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Space for Authority RRSets. */
+// PREALLOC_AUTHORITY = DEFAULT_NSCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Space for Additional RRSets. */
+// PREALLOC_ADDITIONAL = DEFAULT_ARCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Total size for Answer, Authority and Additional RRSets. */
+// PREALLOC_RRSETS = PREALLOC_ANSWER
+// + PREALLOC_AUTHORITY
+// + PREALLOC_ADDITIONAL,
+ /*! \brief Space for one part of the compression table (domain names).*/
+ PREALLOC_DOMAINS =
+ DEFAULT_DOMAINS_IN_RESPONSE * sizeof(knot_dname_t *),
+ /*! \brief Space for other part of the compression table (offsets). */
+ PREALLOC_OFFSETS =
+ DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t),
+ PREALLOC_COMPRESSION = PREALLOC_DOMAINS + PREALLOC_OFFSETS,
+
+// /*! \brief Space for temporary RRSets. */
+// PREALLOC_TMP_RRSETS =
+// DEFAULT_TMP_RRSETS * sizeof(knot_rrset_t *),
+
+ 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_RR_OWNER
+ + PREALLOC_RRSETS(DEFAULT_ANCOUNT)
+ + PREALLOC_RRSETS(DEFAULT_NSCOUNT)
+ + PREALLOC_RRSETS(DEFAULT_ARCOUNT)
+ + PREALLOC_COMPRESSION
+ + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS)
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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);
+
+/*!
+ * \brief Parses the DNS packet from wire format.
+ *
+ * \param packet Packet structure to parse into.
+ * \param wireformat Wire format of the DNS packet.
+ * \param size Size of the wire format in bytes.
+ * \param question_only Set to <> 0 if you do not want to parse the whole
+ * packet. In such case the parsing will end after the
+ * Question section. Set to 0 to parse the whole packet.
+ *
+ * \retval KNOT_EOK
+ */
+int knot_packet_parse_from_wire(knot_packet_t *packet,
+ const uint8_t *wireformat, size_t size,
+ int question_only);
+
+int knot_packet_parse_rest(knot_packet_t *packet);
+
+int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
+ knot_rrset_t **rr);
+
+int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
+ knot_rrset_t **rr);
+
+size_t knot_packet_size(const knot_packet_t *packet);
+
+/*! \brief Returns size of the wireformat of Header and Question sections. */
+size_t knot_packet_question_size(const knot_packet_t *packet);
+
+size_t knot_packet_parsed(const knot_packet_t *packet);
+
+/*!
+ * \brief Sets the maximum size of the packet and allocates space for wire
+ * format (if needed).
+ *
+ * This function also allocates space for the wireformat of the packet, if
+ * the given max size is larger than the current maximum size of the packet
+ * and copies the current wireformat over to the new space.
+ *
+ * \warning Do not call this function if you are not completely sure that the
+ * current wire format of the packet fits into the new space.
+ * It does not update the current size of the wire format, so the
+ * produced packet may be larger than the given max size.
+ *
+ * \param packet Packet to set the maximum size of.
+ * \param max_size Maximum size of the packet in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int knot_packet_set_max_size(knot_packet_t *packet, int max_size);
+
+uint16_t knot_packet_id(const knot_packet_t *packet);
+
+void knot_packet_set_id(knot_packet_t *packet, uint16_t id);
+
+void knot_packet_set_random_id(knot_packet_t *packet);
+
+/*!
+ * \brief Returns the OPCODE of the packet.
+ *
+ * \param packet Packet (with parsed query) to get the OPCODE from.
+ *
+ * \return OPCODE stored in the packet.
+ */
+uint8_t knot_packet_opcode(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the QNAME from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QNAME from.
+ *
+ * \return QNAME stored in the packet.
+ */
+const knot_dname_t *knot_packet_qname(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the QTYPE from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QTYPE from.
+ *
+ * \return QTYPE stored in the packet.
+ */
+uint16_t knot_packet_qtype(const knot_packet_t *packet);
+
+/*!
+ * \brief Set the QTYPE of the packet.
+ *
+ * \param packet Packet containing question.
+ * \param qtype New QTYPE for question.
+ */
+void knot_packet_set_qtype(knot_packet_t *packet, knot_rr_type_t qtype);
+
+
+/*!
+ * \brief Returns the QCLASS from the packet.
+ *
+ * \param response Packet (with parsed query) to get the QCLASS from.
+ *
+ * \return QCLASS stored in the packet.
+ */
+uint16_t knot_packet_qclass(const knot_packet_t *packet);
+
+int knot_packet_is_query(const knot_packet_t *packet);
+
+const knot_packet_t *knot_packet_query(const knot_packet_t *packet);
+
+int knot_packet_rcode(const knot_packet_t *packet);
+
+int knot_packet_tc(const knot_packet_t *packet);
+
+int knot_packet_qdcount(const knot_packet_t *packet);
+
+int knot_packet_ancount(const knot_packet_t *packet);
+
+int knot_packet_nscount(const knot_packet_t *packet);
+
+int knot_packet_arcount(const knot_packet_t *packet);
+
+void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size);
+
+/*!
+ * \brief Returns number of RRSets in Answer section of the packet.
+ *
+ * \param response Packet to get the Answer RRSet count from.
+ */
+short knot_packet_answer_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Authority section of the packet.
+ *
+ * \param response Packet to get the Authority RRSet count from.
+ */
+short knot_packet_authority_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Additional section of the packet.
+ *
+ * \param response Packet to get the Additional RRSet count from.
+ */
+short knot_packet_additional_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the requested Answer RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Answer section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Answer section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_answer_rrset(
+ const knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Authority RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Authority section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Authority section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_authority_rrset(
+ knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Additional RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Additional section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Additional section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_additional_rrset(
+ knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Checks if the packet already contains the given RRSet.
+ *
+ * It searches for the RRSet in the three lists of RRSets corresponding to
+ * Answer, Authority and Additional sections of the packet.
+ *
+ * \note Only pointers are compared, i.e. two instances of knot_rrset_t with
+ * the same data will be considered different.
+ *
+ * \param packet Packet to look for the RRSet in.
+ * \param rrset RRSet to look for.
+ *
+ * \retval 0 if \a resp does not contain \a rrset.
+ * \retval <> 0 if \a resp does contain \a rrset.
+ */
+int knot_packet_contains(const knot_packet_t *packet,
+ const knot_rrset_t *rrset,
+ knot_rrset_compare_type_t cmp);
+
+/*!
+ * \brief Adds RRSet to the list of temporary RRSets.
+ *
+ * Temporary RRSets are fully freed when the response structure is destroyed.
+ *
+ * \param response Response to which the temporary RRSet should be added.
+ * \param tmp_rrset Temporary RRSet to be stored in the response.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_packet_add_tmp_rrset(knot_packet_t *response,
+ knot_rrset_t *tmp_rrset);
+
+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);
+
+/*!
+ * \brief Converts the stored response OPT RR to wire format and adds it to
+ * the response wire format.
+ *
+ * \param resp Response structure.
+ */
+int knot_packet_edns_to_wire(knot_packet_t *packet);
+
+/*!
+ * \brief Converts the packet to wire format.
+ *
+ * \param packet Packet to be converted to wire format.
+ * \param wire Here the wire format of the packet will be stored.
+ * Space for the packet will be allocated. *resp_wire must
+ * be set to NULL (to avoid leaks).
+ * \param wire_size The size of the packet in wire format will be stored here.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_packet_to_wire(knot_packet_t *packet, uint8_t **wire,
+ size_t *wire_size);
+
+const uint8_t *knot_packet_wireformat(const knot_packet_t *packet);
+
+/*!
+ * \brief Properly destroys the packet structure.
+ *
+ * \param response Packet to be destroyed.
+ */
+void knot_packet_free(knot_packet_t **packet);
+
+/*!
+ * \brief Dumps the whole packet in human-readable form.
+ *
+ * \note This function is empty unless KNOT_PACKET_DEBUG is defined.
+ *
+ * \param resp Packet to dump.
+ */
+void knot_packet_dump(const knot_packet_t *packet);
+
+#endif /* _KNOT_PACKET_H_ */
+
+/*! @} */
diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c
new file mode 100644
index 0000000..63e902a
--- /dev/null
+++ b/src/libknot/packet/query.c
@@ -0,0 +1,228 @@
+/* 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 <stdlib.h>
+#include "packet/query.h"
+
+#include "util/error.h"
+#include "util/wire.h"
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_rr_to_wire(const knot_rrset_t *rrset, const knot_rdata_t *rdata,
+ uint8_t **wire, uint8_t *endp)
+{
+ /* Store owner. */
+ knot_dname_t *owner = rrset->owner;
+ if (*wire + owner->size > endp) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(*wire, owner->name, owner->size);
+ *wire += owner->size;
+
+ if (*wire + 10 > endp) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Write RR header. */
+ knot_wire_write_u16(*wire, rrset->type); *wire += 2;
+ knot_wire_write_u16(*wire, rrset->rclass); *wire += 2;
+ knot_wire_write_u32(*wire, rrset->ttl); *wire += 4;
+ knot_wire_write_u16(*wire, 0); *wire += 2; /* RDLENGTH reserve. */
+ uint8_t *rdlength_p = *wire - 2;
+ uint16_t rdlength = 0;
+
+ /* Write data. */
+ knot_dname_t *dname = 0;
+ uint16_t *raw_data = 0;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ for (int i = 0; i < rdata->count; ++i) {
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+
+ /* Check space for dname. */
+ dname = knot_rdata_item(rdata, i)->dname;
+ if (*wire + dname->size > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Save domain name. */
+ memcpy(*wire, dname->name, dname->size);
+ *wire += dname->size;
+ rdlength += dname->size;
+ break;
+ default:
+ raw_data = knot_rdata_item(rdata, i)->raw_data;
+ if (*wire + raw_data[0] > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Copy data. */
+ memcpy(*wire, raw_data + 1, raw_data[0]);
+ *wire += raw_data[0];
+ rdlength += raw_data[0];
+ break;
+
+ }
+ }
+
+ /* Store rdlength. */
+ knot_wire_write_u16(rdlength_p, rdlength);
+
+ return KNOT_EOK;
+}
+/*----------------------------------------------------------------------------*/
+
+int knot_query_dnssec_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_do(&query->opt_rr));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_nsid_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_has_option(&query->opt_rr, EDNS_OPTION_NSID));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_edns_supported(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_init(knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // 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);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_question(knot_packet_t *query,
+ const knot_question_t *question)
+{
+ if (query == NULL || question == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ query->question.qname = question->qname;
+ query->question.qclass = question->qclass;
+ query->question.qtype = question->qtype;
+ query->header.qdcount = 1;
+
+ // convert the Question to wire format right away
+ knot_packet_question_to_wire(query);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // 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);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_add_rrset_authority(knot_packet_t *query,
+ const knot_rrset_t *rrset)
+{
+ if (query == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (query->ns_rrsets == query->max_ns_rrsets) {
+ size_t oldsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ ++query->max_ns_rrsets;
+ size_t newsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ const knot_rrset_t ** na = malloc(newsize);
+ if (na == 0) {
+ query->max_ns_rrsets = 0;
+ return KNOT_ENOMEM;
+ } else {
+ memcpy(na, query->authority, oldsize);
+ free(query->authority);
+ query->authority = na;
+ }
+ }
+
+ /* Append to packet. */
+ query->authority[query->ns_rrsets] = rrset;
+
+ /* Write to wire. */
+ uint8_t *startp = query->wireformat + query->size;
+ uint8_t *endp = query->wireformat + query->max_size;
+
+ assert(endp - startp > query->opt_rr.size + query->tsig_size);
+ // reserve space for OPT RR
+ endp -= query->opt_rr.size;
+ /*! \note [TSIG] reserve space for TSIG RR */
+ endp -= query->tsig_size;
+
+ uint8_t *pos = startp;
+
+ const knot_rdata_t *rdata = 0;
+ while ((rdata = knot_rrset_rdata_next(rrset, rdata))) {
+ knot_query_rr_to_wire(rrset, rdata, &pos, endp);
+ }
+
+ size_t written = (pos - startp);
+ query->size += written;
+ ++query->ns_rrsets;
+ ++query->header.nscount;
+
+ return KNOT_EOK;
+}
+
diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h
new file mode 100644
index 0000000..a979641
--- /dev/null
+++ b/src/libknot/packet/query.h
@@ -0,0 +1,93 @@
+/*!
+ * \file query.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for manipulating queries.
+ *
+ * \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_QUERY_H_
+#define _KNOT_QUERY_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "packet/packet.h"
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if DNSSEC was requested in the query (i.e. the DO bit was set).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the DO bit was not set in the query, or the query is not yet
+ * parsed.
+ * \retval > 0 if DO bit was set in the query.
+ */
+int knot_query_dnssec_requested(const knot_packet_t *query);
+
+/*!
+ * \brief Checks if NSID was requested in the query (i.e. the NSID option was
+ * present in the query OPT RR).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the NSID option was not present in the query, or the query is
+ * not yet parsed.
+ * \retval > 0 if the NSID option was present in the query.
+ */
+int knot_query_nsid_requested(const knot_packet_t *query);
+
+int knot_query_edns_supported(const knot_packet_t *query);
+
+//int knot_query_set_qname(knot_packet_t *query, const knot_dname_t *qname);
+
+//int knot_query_set_qtype(knot_packet_t *query, uint16_t qtype);
+
+//int knot_query_set_qclass(knot_packet_t *query, uint16_t qclass);
+
+int knot_query_init(knot_packet_t *query);
+
+int knot_query_set_question(knot_packet_t *query,
+ const knot_question_t *question);
+
+int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode);
+
+/*!
+ * \brief Adds a RRSet to the Authority section of the query.
+ *
+ * \param query Query to add the RRSet into.
+ * \param rrset RRSet to be added.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the query.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_query_add_rrset_authority(knot_packet_t *query,
+ const knot_rrset_t *rrset);
+
+
+#endif /* _KNOT_QUERY_H_ */
+
+/*! @} */
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
new file mode 100644
index 0000000..e6f89d0
--- /dev/null
+++ b/src/libknot/packet/response.c
@@ -0,0 +1,1170 @@
+/* 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 <stdlib.h>
+
+#include "packet/response.h"
+#include "util/wire.h"
+#include "util/descriptor.h"
+#include "common.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Holds information about compressed domain name.
+ *
+ * Used only to pass information between functions.
+ *
+ * \todo This description should be revised and clarified.
+ */
+struct knot_compr_owner {
+ /*!
+ * \brief Place where the name is stored in the wire format of the
+ * packet.
+ */
+ uint8_t *wire;
+ short size; /*!< Size of the domain name in bytes. */
+ /*! \brief Position of the name relative to the start of the packet. */
+ size_t pos;
+};
+
+typedef struct knot_compr_owner knot_compr_owner_t;
+
+/*!
+ * \brief Holds information about compressed domain names in packet.
+ *
+ * Used only to pass information between functions.
+ *
+ * \todo This description should be revised and clarified.
+ */
+struct knot_compr {
+ knot_compressed_dnames_t *table; /*!< Compression table. */
+ size_t wire_pos; /*!< Current position in the wire format. */
+ knot_compr_owner_t owner; /*!< Information about the current name. */
+};
+
+typedef struct knot_compr knot_compr_t;
+
+static const size_t KNOT_RESPONSE_MAX_PTR = 16383;
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Reallocates space for compression table.
+ *
+ * \param table Compression table to reallocate space for.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_response_realloc_compr(knot_compressed_dnames_t *table)
+{
+ int free_old = table->max != DEFAULT_DOMAINS_IN_RESPONSE;
+ size_t *old_offsets = table->offsets;
+ const knot_dname_t **old_dnames = table->dnames;
+
+ short new_max_count = table->max + STEP_DOMAINS;
+
+ size_t *new_offsets = (size_t *)malloc(new_max_count * sizeof(size_t));
+ CHECK_ALLOC_LOG(new_offsets, -1);
+
+ const knot_dname_t **new_dnames = (const knot_dname_t **)malloc(
+ new_max_count * sizeof(knot_dname_t *));
+ if (new_dnames == NULL) {
+ ERR_ALLOC_FAILED;
+ free(new_offsets);
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(new_offsets, table->offsets, table->max * sizeof(size_t));
+ memcpy(new_dnames, table->dnames,
+ table->max * sizeof(knot_dname_t *));
+
+ table->offsets = new_offsets;
+ table->dnames = new_dnames;
+ table->max = new_max_count;
+
+ if (free_old) {
+ free(old_offsets);
+ free(old_dnames);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Stores new mapping between domain name and offset in the compression
+ * table.
+ *
+ * If the domain name is already present in the table, it is not inserted again.
+ *
+ * \param table Compression table to save the mapping into.
+ * \param dname Domain name to insert.
+ * \param pos Position of the domain name in the packet's wire format.
+ */
+static void knot_response_compr_save(knot_compressed_dnames_t *table,
+ const knot_dname_t *dname, size_t pos)
+{
+ assert(table->count < table->max);
+
+ for (int i = 0; i < table->count; ++i) {
+ if (table->dnames[i] == dname) {
+ dbg_response("Already present, skipping..\n");
+ return;
+ }
+ }
+
+ table->dnames[table->count] = dname;
+ table->offsets[table->count] = pos;
+ ++table->count;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Stores domain name position and positions of its parent domain names
+ * to the compression table.
+ *
+ * If part of the domain name (\a dname) was not found previously in the
+ * compression table, this part and all its parent domains is stored also, to
+ * maximize compression potential.
+ *
+ * \param table Compression table to save the information into.
+ * \param dname Domain name to save.
+ * \param not_matched Count of labels not matched when previously searching in
+ * the compression table for \a dname.
+ * \param pos Position of the domain name in the wire format of the packet.
+ * \param unmatched_offset Position of the unmatched parent domain of \a dname.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_response_store_dname_pos(knot_compressed_dnames_t *table,
+ const knot_dname_t *dname,
+ int not_matched, size_t pos,
+ size_t unmatched_offset,
+ int compr_cs)
+{
+dbg_response_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_response("Putting dname %s into compression table."
+ " Labels not matched: %d, position: %zu,"
+ ", pointer: %p, unmatched off: %zu\n", name,
+ not_matched, pos, dname, unmatched_offset);
+ free(name);
+);
+ if (pos > KNOT_RESPONSE_MAX_PTR) {
+ dbg_response("Pointer larger than it can be, not"
+ " saving\n");
+ return KNOT_EDNAMEPTR;
+ }
+
+ if (table->count == table->max &&
+ knot_response_realloc_compr(table) != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ // store the position of the name
+// table->dnames[table->count] = dname;
+// table->offsets[table->count] = pos;
+// ++table->count;
+
+ /*
+ * Store positions of ancestors if more than 1 label was not matched.
+ *
+ * In case the name is not in the zone, the counting to not_matched
+ * may be limiting, because the search stopped before after the first
+ * label (i.e. not_matched == 1). So we do not store the parents in
+ * this case. However, storing them will require creating those domain
+ * names, as they do not exist.
+ *
+ * The same problem is with domain names synthetized from wildcards.
+ * These also do not have any node to follow.
+ *
+ * We accept this as performance has higher
+ * priority than the best possible compression.
+ */
+ const knot_dname_t *to_save = dname;
+ size_t parent_pos = pos;
+ int i = 0;
+
+ while (to_save != NULL && i < knot_dname_label_count(dname)) {
+ if (i == not_matched) {
+ parent_pos = unmatched_offset;
+ }
+
+dbg_response_exec(
+ char *name = knot_dname_to_str(to_save);
+ dbg_response("Putting dname %s into compression table."
+ " Position: %zu, pointer: %p\n",
+ name, parent_pos, to_save);
+ free(name);
+);
+
+ if (table->count == table->max &&
+ knot_response_realloc_compr(table) != 0) {
+ dbg_response("Unable to realloc.\n");
+ return KNOT_ENOMEM;
+ }
+
+// dbg_response("Saving..\n");
+ knot_response_compr_save(table, to_save, parent_pos);
+
+ /*! \todo Remove '!compr_cs'. */
+ // This is a temporary hack to avoid the wrong behaviour
+ // when the wrong not_matched count is used to compare with i
+ // and resulting in using the 0 offset.
+ // If case-sensitive search is in place, we should not save the
+ // node's parent's positions.
+
+ to_save = !compr_cs && (knot_dname_node(to_save, 1) != NULL
+ && knot_node_parent(knot_dname_node(to_save, 1), 1)
+ != NULL) ? knot_node_owner(knot_node_parent(
+ knot_dname_node(to_save, 1), 1))
+ : NULL;
+
+ dbg_response("i: %d\n", i);
+ parent_pos += knot_dname_label_size(dname, i) + 1;
+// parent_pos += (i > 0)
+// ? knot_dname_label_size(dname, i - 1) + 1 : 0;
+ ++i;
+ }
+
+ return KNOT_EOK;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find offset of domain name in the compression table.
+ *
+ * \param table Compression table to search in.
+ * \param dname Domain name to search for.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Offset of \a dname stored in the compression table or -1 if the name
+ * was not found in the table.
+ */
+static size_t knot_response_find_dname_pos(
+ const knot_compressed_dnames_t *table,
+ const knot_dname_t *dname, int compr_cs)
+{
+ for (int i = 0; i < table->count; ++i) {
+// dbg_response("Comparing dnames %p and %p\n",
+// dname, table->dnames[i]);
+//dbg_response_exec(
+// char *name = knot_dname_to_str(dname);
+// dbg_response("(%s and ", name);
+// name = knot_dname_to_str(table->dnames[i]);
+// dbg_response("%s)\n", name);
+// free(name);
+//);
+ //if (table->dnames[i] == dname) {
+ int ret = (compr_cs)
+ ? knot_dname_compare_cs(table->dnames[i], dname)
+ : knot_dname_compare(table->dnames[i], dname);
+ if (ret == 0) {
+ dbg_response("Found offset: %zu\n",
+ table->offsets[i]);
+ return table->offsets[i];
+ }
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Put a compressed domain name to the wire format of the packet.
+ *
+ * Puts the not matched part of the domain name to the wire format and puts
+ * a pointer to the rest of the name after that.
+ *
+ * \param dname Domain name to put to the wire format.
+ * \param not_matched Size of the part of domain name that cannot be compressed.
+ * \param offset Position of the rest of the domain name in the packet's wire
+ * format.
+ * \param wire Place where to put the wire format of the name.
+ * \param max Maximum available size of the place for the wire format.
+ *
+ * \return Size of the compressed domain name put into the wire format or
+ * KNOT_ESPACE if it did not fit.
+ */
+static int knot_response_put_dname_ptr(const knot_dname_t *dname,
+ int not_matched, size_t offset,
+ uint8_t *wire, size_t max)
+{
+ // put the not matched labels
+ short size = knot_dname_size_part(dname, not_matched);
+ if (size + 2 > max) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(wire, knot_dname_name(dname), size);
+ knot_wire_put_pointer(wire + size, offset);
+
+ dbg_response("Size of the dname with ptr: %d\n", size + 2);
+
+ return size + 2;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to compress domain name and creates its wire format.
+ *
+ * \param dname Domain name to convert and compress.
+ * \param compr Compression table holding information about offsets of domain
+ * names in the packet.
+ * \param dname_wire Place where to put the wire format of the name.
+ * \param max Maximum available size of the place for the wire format.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Size of the domain name's wire format or KNOT_ESPACE if it did not
+ * fit into the provided space.
+ */
+static int knot_response_compress_dname(const knot_dname_t *dname,
+ knot_compr_t *compr, uint8_t *dname_wire, size_t max, int compr_cs)
+{
+ int size = 0;
+ /*!
+ * \todo Compress!!
+ *
+ * if pos == 0, do not store the position!
+ */
+
+ // try to find the name or one of its ancestors in the compr. table
+#ifdef COMPRESSION_PEDANTIC
+ //knot_dname_t *to_find = knot_dname_copy(dname);
+ knot_dname_t *to_find = (knot_dname_t *)dname;
+ int copied = 0;
+#else
+ const knot_dname_t *to_find = dname;
+#endif
+ size_t offset = 0;
+ int not_matched = 0;
+
+ while (to_find != NULL && knot_dname_label_count(to_find) != 0) {
+dbg_response_exec(
+ char *name = knot_dname_to_str(to_find);
+ dbg_response("Searching for name %s in the compression"
+ " table, not matched labels: %d\n", name,
+ not_matched);
+ free(name);
+);
+ offset = knot_response_find_dname_pos(compr->table, to_find,
+ compr_cs);
+ if (offset == 0) {
+ ++not_matched;
+ } else {
+ break;
+ }
+#ifdef COMPRESSION_PEDANTIC
+ if (compr_cs || to_find->node == NULL
+ || to_find->node->owner != to_find
+ || to_find->node->parent == NULL) {
+ if (!copied) {
+ to_find = knot_dname_left_chop(to_find);
+ copied = 1;
+ } else {
+ knot_dname_left_chop_no_copy(to_find);
+ }
+ } else {
+ assert(to_find->node != to_find->node->parent);
+ assert(to_find != to_find->node->parent->owner);
+ to_find = to_find->node->parent->owner;
+ }
+#else
+ // if case-sensitive comparation, we cannot just take the parent
+ if (compr_cs || knot_dname_node(to_find, 1) == NULL
+ || knot_node_owner(knot_dname_node(to_find, 1)) != to_find
+ || knot_node_parent(knot_dname_node(to_find, 1), 1)
+ == NULL) {
+ dbg_response("compr_cs: %d\n", compr_cs);
+ dbg_response("knot_dname_node(to_find, 1) == %p"
+ "\n", knot_dname_node(to_find, 1));
+
+ if (knot_dname_node(to_find, 1) != NULL) {
+ dbg_response("knot_node_owner(knot_dname_node("
+ "to_find, 1)) = %p, to_find = %p\n",
+ knot_node_owner(knot_dname_node(to_find, 1)),
+ to_find);
+ dbg_response("knot_node_parent(knot_dname_node("
+ "to_find, 1), 1) = %p\n",
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ }
+ break;
+ } else {
+ assert(knot_dname_node(to_find, 1) !=
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ assert(to_find != knot_node_owner(
+ knot_node_parent(knot_dname_node(to_find, 1), 1)));
+ to_find = knot_node_owner(
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ dbg_response("New to_find: %p\n", to_find);
+ }
+#endif
+ }
+
+#ifdef COMPRESSION_PEDANTIC
+ if (copied) {
+ knot_dname_free(&to_find);
+ }
+#endif
+
+ dbg_response("Max size available for domain name: %zu\n", max);
+
+ if (offset > 0) { // found such dname somewhere in the packet
+ dbg_response("Found name in the compression table.\n");
+ assert(offset >= KNOT_WIRE_HEADER_SIZE);
+ size = knot_response_put_dname_ptr(dname, not_matched, offset,
+ dname_wire, max);
+ if (size <= 0) {
+ return KNOT_ESPACE;
+ }
+ } else {
+ dbg_response("Not found, putting whole name.\n");
+ // now just copy the dname without compressing
+ if (dname->size > max) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(dname_wire, dname->name, dname->size);
+ size = dname->size;
+ }
+
+ // in either way, put info into the compression table
+ /*! \todo This is useless if the name was already in the table.
+ * It is meaningful only if the found name is the one from QNAME
+ * and thus its parents are not stored yet.
+ */
+ assert(compr->wire_pos >= 0);
+
+ if (knot_response_store_dname_pos(compr->table, dname, not_matched,
+ compr->wire_pos, offset, compr_cs)
+ != 0) {
+ dbg_response("Compression info could not be stored."
+ "\n");
+ }
+
+ return size;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Convert one RR into wire format.
+ *
+ * \param[in] rrset RRSet to which the RR belongs.
+ * \param[in] rdata The actual RDATA of this RR.
+ * \param[in] compr Information about compressed domain names in the packet.
+ * \param[out] rrset_wire Place to put the wire format of the RR into.
+ * \param[in] max_size Size of space available for the wire format.
+ * \param[in] compr_cs Set to <> 0 if dname compression should use case
+ * sensitive comparation. Set to 0 otherwise.
+ *
+ * \return Size of the RR's wire format or KNOT_ESPACE if it did not fit into
+ * the provided space.
+ */
+static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata,
+ knot_compr_t *compr,
+ uint8_t **rrset_wire, size_t max_size,
+ int compr_cs)
+{
+ int size = 0;
+
+ dbg_response("Max size: %zu, owner pos: %zu, owner size: %d\n",
+ max_size, compr->owner.pos, compr->owner.size);
+
+ if (size + ((compr->owner.pos == 0
+ || compr->owner.pos > KNOT_RESPONSE_MAX_PTR)
+ ? compr->owner.size : 2) + 10
+ > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("Owner position: %zu\n", compr->owner.pos);
+
+ // put owner if needed (already compressed)
+ if (compr->owner.pos == 0 || compr->owner.pos > KNOT_RESPONSE_MAX_PTR) {
+ memcpy(*rrset_wire, compr->owner.wire, compr->owner.size);
+ compr->owner.pos = compr->wire_pos;
+ *rrset_wire += compr->owner.size;
+ size += compr->owner.size;
+ } else {
+ dbg_response("Putting pointer: %zu\n",
+ compr->owner.pos);
+ knot_wire_put_pointer(*rrset_wire, compr->owner.pos);
+ *rrset_wire += 2;
+ size += 2;
+ }
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ dbg_response("Wire format:\n");
+
+ // put rest of RR 'header'
+ knot_wire_write_u16(*rrset_wire, rrset->type);
+ dbg_response(" Type: %u\n", rrset->type);
+ dbg_response(" Type in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 2);
+ *rrset_wire += 2;
+
+ knot_wire_write_u16(*rrset_wire, rrset->rclass);
+ dbg_response(" Class: %u\n", rrset->rclass);
+ dbg_response(" Class in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 2);
+ *rrset_wire += 2;
+
+ knot_wire_write_u32(*rrset_wire, rrset->ttl);
+ dbg_response(" TTL: %u\n", rrset->ttl);
+ dbg_response(" TTL in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 4);
+ *rrset_wire += 4;
+
+ // save space for RDLENGTH
+ uint8_t *rdlength_pos = *rrset_wire;
+ *rrset_wire += 2;
+
+ size += 10;
+ compr->wire_pos += size;
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ uint16_t rdlength = 0;
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (max_size < size + rdlength) {
+ return KNOT_ESPACE;
+ }
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME: {
+ int ret = knot_response_compress_dname(
+ knot_rdata_item(rdata, i)->dname,
+ compr, *rrset_wire, max_size - size - rdlength,
+ compr_cs);
+
+ if (ret < 0) {
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("Compressed dname size: %d\n",
+ ret);
+ *rrset_wire += ret;
+ rdlength += ret;
+ compr->wire_pos += ret;
+ // TODO: compress domain name
+ break;
+ }
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME: {
+ knot_dname_t *dname =
+ knot_rdata_item(rdata, i)->dname;
+ if (size + rdlength + dname->size > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // save whole domain name
+ memcpy(*rrset_wire, dname->name, dname->size);
+ dbg_response("Uncompressed dname size: %d\n",
+ dname->size);
+ *rrset_wire += dname->size;
+ rdlength += dname->size;
+ compr->wire_pos += dname->size;
+ break;
+ }
+// case KNOT_RDATA_WF_BINARYWITHLENGTH: {
+// uint16_t *raw_data =
+// knot_rdata_item(rdata, i)->raw_data;
+
+// if (size + raw_data[0] + 1 > max_size) {
+// return KNOT_ESPACE;
+// }
+
+// // copy also the rdata item size
+// assert(raw_data[0] < 256);
+// **rrset_wire = raw_data[0];
+// *rrset_wire += 1;
+// memcpy(*rrset_wire, raw_data + 1, raw_data[0]);
+// dbg_response("Raw data size: %d\n",
+// raw_data[0] + 1);
+// *rrset_wire += raw_data[0];
+// rdlength += raw_data[0] + 1;
+// compr->wire_pos += raw_data[0] + 1;
+// break;
+// }
+ default: {
+ uint16_t *raw_data =
+ knot_rdata_item(rdata, i)->raw_data;
+
+ if (size + rdlength + raw_data[0] > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // copy just the rdata item data (without size)
+ memcpy(*rrset_wire, raw_data + 1, raw_data[0]);
+ dbg_response("Raw data size: %d\n",
+ raw_data[0]);
+ *rrset_wire += raw_data[0];
+ rdlength += raw_data[0];
+ compr->wire_pos += raw_data[0];
+ break;
+ }
+ }
+ }
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ assert(size + rdlength <= max_size);
+ size += rdlength;
+ knot_wire_write_u16(rdlength_pos, rdlength);
+
+ return size;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Convert whole RRSet into wire format.
+ *
+ * \param[in] rrset RRSet to convert
+ * \param[out] pos Place where to put the wire format.
+ * \param[out] size Size of the converted wire format.
+ * \param[in] max_size Maximum available space for the wire format.
+ * \param wire_pos Current position in the wire format of the whole packet.
+ * \param owner_tmp Wire format of the RRSet's owner, possibly compressed.
+ * \param compr Information about compressed domain names in the packet.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Size of the RRSet's wire format or KNOT_ESPACE if it did not fit
+ * into the provided space.
+ */
+static int knot_response_rrset_to_wire(const knot_rrset_t *rrset,
+ uint8_t **pos, size_t *size,
+ size_t max_size, size_t wire_pos,
+ uint8_t *owner_tmp,
+ knot_compressed_dnames_t *compr,
+ int compr_cs)
+{
+dbg_response_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_response("Converting RRSet with owner %s, type %s\n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+ dbg_response(" Size before: %zu\n", *size);
+);
+
+ // if no RDATA in RRSet, return
+ if (rrset->rdata == NULL) {
+ return KNOT_EOK;
+ }
+
+ //uint8_t *rrset_wire = (uint8_t *)malloc(PREALLOC_RRSET_WIRE);
+ //short rrset_size = 0;
+
+ //uint8_t *owner_wire = (uint8_t *)malloc(rrset->owner->size);
+ /*
+ * We may pass the current position to the compression function
+ * because if the owner will be put somewhere, it will be on the
+ * current position (first item of a RR). If it will not be put into
+ * the wireformat, we may remove the dname (and possibly its parents)
+ * from the compression table.
+ */
+
+ knot_compr_t compr_info;
+ //compr_info.new_entries = 0;
+ compr_info.table = compr;
+ compr_info.wire_pos = wire_pos;
+ compr_info.owner.pos = 0;
+ compr_info.owner.wire = owner_tmp;
+ compr_info.owner.size =
+ knot_response_compress_dname(rrset->owner, &compr_info,
+ owner_tmp, max_size, compr_cs);
+
+ dbg_response(" Owner size: %d, position: %zu\n",
+ compr_info.owner.size, compr_info.owner.pos);
+ if (compr_info.owner.size < 0) {
+ return KNOT_ESPACE;
+ }
+
+ int rrs = 0;
+ short rrset_size = 0;
+
+ const knot_rdata_t *rdata = rrset->rdata;
+ do {
+ int ret = knot_response_rr_to_wire(rrset, rdata, &compr_info,
+ pos, max_size - rrset_size,
+ compr_cs);
+
+ assert(ret != 0);
+
+ if (ret < 0) {
+ // some RR didn't fit in, so no RRs should be used
+ // TODO: remove last entries from compression table
+ dbg_response("Some RR didn't fit in.\n");
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("RR of size %d added.\n", ret);
+ rrset_size += ret;
+ ++rrs;
+ } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
+
+ //memcpy(*pos, rrset_wire, rrset_size);
+ //*size += rrset_size;
+ //*pos += rrset_size;
+
+ // the whole RRSet did fit in
+ assert (rrset_size <= max_size);
+ *size += rrset_size;
+
+ dbg_response(" Size after: %zu\n", *size);
+
+ return rrs;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to add RRSet to the response.
+ *
+ * This function tries to convert the RRSet to wire format and add it to the
+ * wire format of the response and if successful, adds the RRSet to the given
+ * list (and updates its size). If the RRSet did not fit into the available
+ * space (\a max_size), it is omitted as a whole and the TC bit may be set
+ * (according to \a tc).
+ *
+ * \param rrsets Lists of RRSets to which this RRSet should be added.
+ * \param rrset_count Number of RRSets in the list.
+ * \param resp Response structure where the RRSet should be added.
+ * \param max_size Maximum available space in wire format of the response.
+ * \param rrset RRSet to add.
+ * \param tc Set to <> 0 if omitting the RRSet should cause the TC bit to be
+ * set in the response.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Count of RRs added to the response or KNOT_ESPACE if the RRSet did
+ * not fit in the available space.
+ */
+static int knot_response_try_add_rrset(const knot_rrset_t **rrsets,
+ short *rrset_count,
+ knot_packet_t *resp,
+ size_t max_size,
+ const knot_rrset_t *rrset, int tc,
+ int compr_cs)
+{
+ //short size = knot_response_rrset_size(rrset, &resp->compression);
+
+dbg_response_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_response("\nAdding RRSet with owner %s and type %s: \n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+);
+
+ uint8_t *pos = resp->wireformat + resp->size;
+ size_t size = 0;
+ int rrs = knot_response_rrset_to_wire(rrset, &pos, &size, max_size,
+ resp->size, resp->owner_tmp,
+ &resp->compression, compr_cs);
+
+ if (rrs >= 0) {
+ rrsets[(*rrset_count)++] = rrset;
+ resp->size += size;
+ dbg_response("RRset added, size: %zu, RRs: %d, total "
+ "size of response: %zu\n\n", size, rrs,
+ resp->size);
+ } else if (tc) {
+ dbg_response("Setting TC bit.\n");
+ knot_wire_flags_set_tc(&resp->header.flags1);
+ knot_wire_set_tc(resp->wireformat);
+ }
+
+ return rrs;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_response_init(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (response->max_size < KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ESPACE;
+ }
+
+ // set the qr bit to 1
+ knot_wire_flags_set_qr(&response->header.flags1);
+
+ uint8_t *pos = response->wireformat;
+ knot_packet_header_to_wire(&response->header, &pos,
+ &response->size);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_init_from_query(knot_packet_t *response,
+ knot_packet_t *query)
+{
+
+ if (response == NULL || query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // copy the header from the query
+ memcpy(&response->header, &query->header, sizeof(knot_header_t));
+// memmove(&response->header, &query->header, sizeof(knot_header_t));
+
+ // copy the Question section (but do not copy the QNAME)
+ memcpy(&response->question, &query->question,
+ sizeof(knot_question_t));
+// memmove(&response->question, &query->question,
+// sizeof(knot_question_t));
+
+ int err = 0;
+ // put the qname into the compression table
+ // TODO: get rid of the numeric constants
+ if ((err = knot_response_store_dname_pos(&response->compression,
+ response->question.qname, 0, 12, 12, 0)) != KNOT_EOK) {
+ return err;
+ }
+
+ // copy the wireformat of Header and Question from the query
+ // TODO: get rid of the numeric constants
+ size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname);
+
+ assert(response->max_size >= to_copy);
+// printf("Resp init from query: Copying from: %p to: %p size: %d\n",
+// response->wireformat, query->wireformat,
+// to_copy);
+// printf("Resp init from query: Question name size: %d Query name size: %d\n",
+// knot_dname_size(response->question.qname),
+// knot_dname_size(query->question.qname));
+ memcpy(response->wireformat, query->wireformat, to_copy);
+ response->size = to_copy;
+
+ // set the qr bit to 1
+ knot_wire_flags_set_qr(&response->header.flags1);
+ knot_wire_set_qr(response->wireformat);
+
+ // clear 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);
+
+ // set counts to 0
+ response->header.ancount = 0;
+ response->header.nscount = 0;
+ response->header.arcount = 0;
+
+ response->query = query;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_clear(knot_packet_t *resp, int clear_question)
+{
+ if (resp == NULL) {
+ return;
+ }
+
+ resp->size = (clear_question) ? KNOT_WIRE_HEADER_SIZE
+ : KNOT_WIRE_HEADER_SIZE + 4
+ + knot_dname_size(resp->question.qname);
+ resp->an_rrsets = 0;
+ resp->ns_rrsets = 0;
+ resp->ar_rrsets = 0;
+ resp->compression.count = 0;
+ knot_packet_free_tmp_rrsets(resp);
+ resp->tmp_rrsets_count = 0;
+ resp->header.ancount = 0;
+ resp->header.nscount = 0;
+ resp->header.arcount = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_opt(knot_packet_t *resp,
+ const knot_opt_rr_t *opt_rr,
+ int override_max_size)
+{
+ if (resp == NULL || opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // copy the OPT RR
+ resp->opt_rr.version = opt_rr->version;
+ resp->opt_rr.ext_rcode = opt_rr->ext_rcode;
+ resp->opt_rr.payload = opt_rr->payload;
+ resp->opt_rr.size = opt_rr->size;
+
+ // if max size is set, it means there is some reason to be that way,
+ // so we can't just set it to higher value
+
+ if (override_max_size && resp->max_size > 0
+ && resp->max_size < opt_rr->payload) {
+ return KNOT_EPAYLOAD;
+ }
+
+ // set max size (less is OK)
+ if (override_max_size) {
+ dbg_response("Overriding max size to: %u\n",
+ resp->opt_rr.payload);
+ return knot_packet_set_max_size(resp, resp->opt_rr.payload);
+ //resp->max_size = resp->opt_rr.payload;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_answer(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_response("add_rrset_answer()\n");
+ assert(response->header.arcount == 0);
+ assert(response->header.nscount == 0);
+
+ if (response->an_rrsets == response->max_an_rrsets
+ && knot_response_realloc_rrsets(&response->answer,
+ &response->max_an_rrsets, DEFAULT_ANCOUNT, STEP_ANCOUNT)
+ != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Answer section.\n");
+ dbg_response("RRset: %p\n", rrset);
+ dbg_response("Owner: %p\n", rrset->owner);
+
+ int rrs = knot_response_try_add_rrset(response->answer,
+ &response->an_rrsets, response,
+ response->max_size
+ - response->size
+ - response->opt_rr.size
+ - response->tsig_size,
+ rrset, tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.ancount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_authority(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(response->header.arcount == 0);
+
+ if (response->ns_rrsets == response->max_ns_rrsets
+ && knot_response_realloc_rrsets(&response->authority,
+ &response->max_ns_rrsets, DEFAULT_NSCOUNT, STEP_NSCOUNT)
+ != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Authority section.\n");
+
+ int rrs = knot_response_try_add_rrset(response->authority,
+ &response->ns_rrsets, response,
+ response->max_size
+ - response->size
+ - response->opt_rr.size
+ - response->tsig_size,
+ rrset, tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.nscount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_additional(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret;
+
+ // if this is the first additional RRSet, add EDNS OPT RR first
+ if (response->header.arcount == 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)
+ != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Additional section.\n");
+
+ int rrs = knot_response_try_add_rrset(response->additional,
+ &response->ar_rrsets, response,
+ response->max_size
+ - response->size
+ - response->tsig_size, rrset,
+ tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.arcount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_rcode(knot_packet_t *response, short rcode)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_rcode(&response->header.flags2, rcode);
+ knot_wire_set_rcode(response->wireformat, rcode);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_aa(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_aa(&response->header.flags1);
+ knot_wire_set_aa(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_tc(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_tc(&response->header.flags1);
+ knot_wire_set_tc(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_nsid(knot_packet_t *response, const uint8_t *data,
+ uint16_t length)
+{
+ if (response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_edns_add_option(&response->opt_rr,
+ EDNS_OPTION_NSID, length, data);
+}
diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h
new file mode 100644
index 0000000..38bd9a8
--- /dev/null
+++ b/src/libknot/packet/response.h
@@ -0,0 +1,198 @@
+/*!
+ * \file response.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for response manipulation.
+ *
+ * \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_response_H_
+#define _KNOT_response_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "packet/packet.h"
+
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*!
+ * \brief Default maximum DNS response size
+ *
+ * This size must be supported by all servers and clients.
+ */
+static const short KNOT_MAX_RESPONSE_SIZE = 512;
+
+/*----------------------------------------------------------------------------*/
+int knot_response_init(knot_packet_t *response);
+
+/*!
+ * \brief Initializes response from the given query.
+ *
+ * Copies the header, Changes QR bit to 1, copies the Question section and
+ * stores pointer to the query packet structure in the response packet
+ * structure.
+ *
+ * \warning Never free the query packet structure after calling this function,
+ * it will be freed when the response structure is freed.
+ *
+ * \param response Packet structure representing the response.
+ * \param query Packet structure representing the query.
+ *
+ * \retval KNOT_EOK
+ */
+int knot_response_init_from_query(knot_packet_t *response,
+ knot_packet_t *query);
+
+/*!
+ * \brief Clears the response structure for reuse.
+ *
+ * After call to this function, the response will be in the same state as if
+ * knot_response_new() was called. The maximum wire size is retained.
+ *
+ * \param response Response structure to clear.
+ *
+ * \todo Replace the use of this function with something else maybe?
+ */
+void knot_response_clear(knot_packet_t *resp, int clear_question);
+
+/*!
+ * \brief Sets the OPT RR of the response.
+ *
+ * This function also allocates space for the wireformat of the response, if
+ * the payload in the OPT RR is larger than the current maximum size of the
+ * response and copies the current wireformat over to the new space.
+ *
+ * \note The contents of the OPT RR are copied.
+ *
+ * \param resp Response to set the OPT RR to.
+ * \param opt_rr OPT RR to set.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int knot_response_add_opt(knot_packet_t *resp,
+ const knot_opt_rr_t *opt_rr,
+ int override_max_size);
+
+/*!
+ * \brief Adds a RRSet to the Answer section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_answer(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Authority section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_authority(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Additional section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_additional(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Sets the RCODE of the response.
+ *
+ * \param response Response to set the RCODE in.
+ * \param rcode RCODE to set.
+ */
+void knot_response_set_rcode(knot_packet_t *response, short rcode);
+
+/*!
+ * \brief Sets the AA bit of the response to 1.
+ *
+ * \param response Response in which the AA bit should be set.
+ */
+void knot_response_set_aa(knot_packet_t *response);
+
+/*!
+ * \brief Sets the TC bit of the response to 1.
+ *
+ * \param response Response in which the TC bit should be set.
+ */
+void knot_response_set_tc(knot_packet_t *response);
+
+/*!
+ * \brief Adds NSID option to the response.
+ *
+ * \param response Response to add the NSID option into.
+ * \param data NSID data.
+ * \param length Size of NSID data in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_response_add_nsid(knot_packet_t *response, const uint8_t *data,
+ uint16_t length);
+
+#endif /* _KNOT_response_H_ */
+
+/*! @} */
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
new file mode 100644
index 0000000..0c51f5b
--- /dev/null
+++ b/src/libknot/rdata.c
@@ -0,0 +1,838 @@
+/* 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "rdata.h"
+#include "util/descriptor.h"
+#include "dname.h"
+#include "util/error.h"
+#include "zone/node.h"
+#include "util/utils.h"
+#include "util/debug.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares two RDATA items as binary data.
+ *
+ * \param d1 First item.
+ * \param d2 Second item.
+ * \param count1 Size of the first item in bytes. If set to < 0, the size will
+ * be taken from the first two bytes of \a d1.
+ * \param count2 Size of the second item in bytes. If set to < 0, the size will
+ * be taken from the first two bytes of \a d2.
+ *
+ * \retval 0 if the items are identical.
+ * \retval < 0 if \a d1 goes before \a d2 in canonical order.
+ * \retval > 0 if \a d1 goes after \a d2 in canonical order.
+ */
+static int knot_rdata_compare_binary(const uint8_t *d1, const uint8_t *d2,
+ int count1, int count2)
+{
+ int i1 = 0, i2 = 0;
+
+ // length stored in the first octet
+ if (count1 < 0) {
+ // take count from the first two bytes
+ count1 = (int)(*(uint16_t *)d1);
+ // and start from the third byte
+ i1 = 2;
+ }
+ if (count2 < 0) { // dtto
+ count2 = (int)(*(uint16_t *)d2);
+ i2 = 2;
+ }
+
+
+ while (i1 < count1 && i2 < count2 && d1[i1] == d2[i2]) {
+ ++i1;
+ ++i2;
+ }
+
+ if (i1 == count1 && i2 == count2) {
+ return 0;
+ }
+
+ if (i1 == count1 && i2 < count2) {
+ return -1;
+ } else if (i2 == count2 && i1 < count1) {
+ return 1;
+ } else {
+ assert(i1 < count1 && i2 < count2);
+ return (d1[i1] < d2[i2]) ? -1 : 1;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from MX RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the second
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the MX domain name from.
+ *
+ * \return MX domain name stored in \a rdata or NULL if \a rdata has less than 2
+ * items.
+ */
+static const knot_dname_t *knot_rdata_mx_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 2) {
+ return NULL;
+ }
+ return rdata->items[1].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from NS RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the NS domain name from.
+ *
+ * \return NS domain name stored in \a rdata or NULL if \a rdata has no items.
+ */
+static const knot_dname_t *knot_rdata_ns_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from SRV RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the fourth
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the SRV domain name from.
+ *
+ * \return SRV domain name stored in \a rdata or NULL if \a rdata has less than
+ * 4 items.
+ */
+static const knot_dname_t *knot_rdata_srv_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 4) {
+ return NULL;
+ }
+ return rdata->items[3].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rdata_new()
+{
+ knot_rdata_t *rdata =
+ (knot_rdata_t *)malloc(sizeof(knot_rdata_t));
+ if (rdata == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ rdata->items = NULL;
+ rdata->count = 0;
+ rdata->next = NULL;
+
+ return rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
+{
+ int i = 0;
+ uint8_t item_type;
+ size_t parsed = 0;
+
+ if (rdlength == 0) {
+ rdata->items = NULL;
+ return KNOT_EOK;
+ }
+
+ knot_rdata_item_t *items = (knot_rdata_item_t *)malloc(
+ desc->length * sizeof(knot_rdata_item_t));
+ CHECK_ALLOC_LOG(items, KNOT_ENOMEM);
+
+ size_t item_size = 0;
+ uint8_t gateway_type = 0; // only to handle IPSECKEY record
+ knot_dname_t *dname = NULL;
+
+ while (i < desc->length && (desc->fixed_items || parsed < rdlength)) {
+
+ item_type = desc->wireformat[i];
+ item_size = 0;
+
+ size_t pos2;
+
+ switch (item_type) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ pos2 = *pos;
+ dname = knot_dname_parse_from_wire(
+ wire, &pos2, total_size, NULL);
+ if (dname == NULL) {
+ free(items);
+ return KNOT_ERROR;
+ }
+ items[i].dname = dname;
+ //*pos += dname->size;
+ parsed += pos2 - *pos;
+ *pos = pos2;
+ dname = 0;
+ break;
+ case KNOT_RDATA_WF_BYTE:
+ if (desc->type == KNOT_RRTYPE_IPSECKEY && i == 1) {
+ gateway_type = *(wire + *pos);
+ }
+ item_size = 1;
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ item_size = 2;
+ break;
+ case KNOT_RDATA_WF_LONG:
+ item_size = 4;
+ break;
+ case KNOT_RDATA_WF_UINT48:
+ item_size = 6;
+ break;
+ case KNOT_RDATA_WF_TEXT:
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_TEXT_SINGLE:
+ item_size = *(wire + *pos) + 1;
+ break;
+ case KNOT_RDATA_WF_A:
+ item_size = 4;
+ break;
+ case KNOT_RDATA_WF_AAAA:
+ item_size = 16;
+ break;
+ case KNOT_RDATA_WF_BINARY:
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_BINARYWITHLENGTH:
+ item_size = *(wire + *pos) + 1;
+ break;
+ case KNOT_RDATA_WF_BINARYWITHSHORT:
+ item_size = knot_wire_read_u16(wire + *pos) + 2;
+ break;
+ case KNOT_RDATA_WF_APL:
+ // WTF? what to do with this??
+ // Same as TXT, I guess.
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_IPSECGATEWAY:
+ // determine size based on the 'gateway type' field
+ switch (gateway_type) {
+ case 0:
+ item_size = 0;
+ break;
+ case 1:
+ item_size = 4;
+ break;
+ case 2:
+ item_size = 16;
+ break;
+ case 3:
+ pos2 = *pos;
+ fprintf(stderr, "reading dname from pos: %zu\n", pos2);
+ dname =
+ knot_dname_parse_from_wire(
+ wire, &pos2, total_size, NULL);
+ if (dname == NULL) {
+ return KNOT_ERROR;
+ }
+ items[i].dname = dname;
+ //*pos += dname->size;
+ parsed += pos2 - *pos;
+
+ fprintf(stderr, "read %zu bytes.\n", parsed);
+ *pos = pos2;
+ dname = 0;
+ break;
+ default:
+ assert(0);
+ }
+
+ break;
+ default:
+ return KNOT_EMALF;
+
+ }
+
+ if (item_size != 0) {
+ if (parsed + item_size > rdlength) {
+ free(items);
+ return KNOT_EFEWDATA;
+ }
+
+ items[i].raw_data = (uint16_t *)malloc(item_size + 2);
+ if (items[i].raw_data == NULL) {
+ free(items);
+ return KNOT_ENOMEM;
+ }
+ memcpy(items[i].raw_data, &item_size, 2);
+ memcpy(items[i].raw_data + 1, wire + *pos, item_size);
+ *pos += item_size;
+ parsed += item_size;
+ } else if (item_type == KNOT_RDATA_WF_BINARY
+ || item_type == KNOT_RDATA_WF_IPSECGATEWAY) {
+ fprintf(stderr, "item_size was 0, creating empty rdata item.\n");
+ // in this case we are at the end of the RDATA
+ // and should create an empty RDATA item
+ items[i].raw_data = (uint16_t *)malloc(2);
+ if (items[i].raw_data == NULL) {
+ free(items);
+ return KNOT_ENOMEM;
+ }
+ memcpy(items[i].raw_data, &item_size, 2);
+ } else if (item_type != KNOT_RDATA_WF_COMPRESSED_DNAME
+ && item_type != KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ && item_type != KNOT_RDATA_WF_LITERAL_DNAME) {
+ fprintf(stderr, "RDATA item not set (i: %d), type: %u"
+ " RDATA item type: %d\n", i, desc->type ,item_type);
+ assert(0);
+ }
+
+ ++i;
+ }
+
+ assert(!desc->fixed_items || i == desc->length);
+
+ // all items are parsed, insert into the RDATA
+ int rc;
+ rc = knot_rdata_set_items(rdata, items, i);
+
+ for (int j = 0; j < i; ++j) {
+ assert(rdata->items[j].raw_data != NULL);
+ }
+
+ free(items);
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_set_item(knot_rdata_t *rdata, uint pos,
+ knot_rdata_item_t item)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ /*! \todo As in set_items() we should increment refcounter for dnames,
+ * but we don't know the item type.
+ */
+
+ rdata->items[pos] = item; // this should copy the union; or use memcpy?
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_rdata_item_count(const knot_rdata_t *rdata)
+{
+ return rdata->count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_set_items(knot_rdata_t *rdata,
+ const knot_rdata_item_t *items, uint count)
+{
+ if (rdata == NULL || items == NULL || count == 0 ||
+ rdata->items != NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(rdata->count == 0);
+ if ((rdata->items = (knot_rdata_item_t *)malloc(
+ count * sizeof(knot_rdata_item_t))) == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(rdata->items, items, count * sizeof(knot_rdata_item_t));
+ rdata->count = count;
+
+ /*! \todo Cannot determine items type, so the dname
+ * refcounters should be increased in caller.
+ */
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_item_t *knot_rdata_item(const knot_rdata_t *rdata,
+ uint pos)
+{
+ if (pos >= rdata->count) {
+ return NULL;
+ } else {
+ return &rdata->items[pos];
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_item_t *knot_rdata_get_item(const knot_rdata_t *rdata,
+ uint pos)
+{
+ if (pos >= rdata->count) {
+ return NULL;
+ } else {
+ return &rdata->items[pos];
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_item_set_dname(knot_rdata_t *rdata, uint pos,
+ knot_dname_t *dname)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ /* Retain dname. */
+ knot_dname_retain(dname);
+
+ rdata->items[pos].dname = dname;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, uint pos,
+ uint16_t *raw_data)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ rdata->items[pos].raw_data = raw_data;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rdata_free(knot_rdata_t **rdata)
+{
+ if (rdata == NULL || *rdata == NULL) {
+ return;
+ }
+
+ if ((*rdata)->items) {
+ free((*rdata)->items);
+ }
+ free(*rdata);
+ *rdata = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rdata_deep_free(knot_rdata_t **rdata, uint type,
+ int free_all_dnames)
+{
+ if (rdata == NULL || *rdata == NULL) {
+ return;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+
+ assert((*rdata)->count <= desc->length);
+
+ for (int i = 0; i < (*rdata)->count; i++) {
+ if (&((*rdata)->items[i]) == NULL) {
+ continue;
+ }
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ) {
+ if (((*rdata)->items[i].dname != NULL)) {
+ /*! \todo This is hack to prevent memory errors,
+ * as the rdata_set_items() cannot determine
+ * items type and so cannot increment
+ * reference count in case of dname type.
+ * Free would then release dnames that
+ * aren't referenced by the rdata.
+ */
+ if (free_all_dnames) {
+ knot_dname_release((*rdata)->items[i].dname);
+ }
+ }
+ } else {
+ free((*rdata)->items[i].raw_data);
+ }
+ }
+
+ if ((*rdata)->items) {
+ free((*rdata)->items);
+ }
+ free(*rdata);
+ *rdata = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/* CLEANUP */
+//uint knot_rdata_wire_size(const knot_rdata_t *rdata,
+// const uint8_t *format)
+//{
+// uint size = 0;
+
+// for (int i = 0; i < rdata->count; ++i) {
+// switch (format[i]) {
+// case KNOT_RDATA_WF_COMPRESSED_DNAME:
+// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+// case KNOT_RDATA_WF_LITERAL_DNAME:
+// size += knot_dname_size(rdata->items[i].dname);
+// break;
+// case KNOT_RDATA_WF_BYTE:
+// size += 1;
+// break;
+// case KNOT_RDATA_WF_SHORT:
+// size += 2;
+// break;
+// case KNOT_RDATA_WF_LONG:
+// size += 4;
+// break;
+// case KNOT_RDATA_WF_A:
+// size += 4;
+// break;
+// case KNOT_RDATA_WF_AAAA:
+// size += 16;
+// break;
+// case KNOT_RDATA_WF_BINARY:
+// case KNOT_RDATA_WF_APL: // saved as binary
+// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
+// size += rdata->items[i].raw_data[0];
+// break;
+// case KNOT_RDATA_WF_TEXT:
+// case KNOT_RDATA_WF_BINARYWITHLENGTH:
+// size += rdata->items[i].raw_data[0] + 1;
+// break;
+// default:
+// assert(0);
+// }
+// }
+// return size;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+//int knot_rdata_to_wire(const knot_rdata_t *rdata, const uint8_t *format,
+// uint8_t *buffer, uint buf_size)
+//{
+// uint copied = 0;
+// uint8_t tmp[KNOT_MAX_RDATA_WIRE_SIZE];
+// uint8_t *to = tmp;
+
+// for (int i = 0; i < rdata->count; ++i) {
+// assert(copied < KNOT_MAX_RDATA_WIRE_SIZE);
+
+// const uint8_t *from = (uint8_t *)rdata->items[i].raw_data;
+// uint size = 0;
+
+// switch (format[i]) {
+// case KNOT_RDATA_WF_COMPRESSED_DNAME:
+// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+// case KNOT_RDATA_WF_LITERAL_DNAME:
+// size = knot_dname_size(rdata->items[i].dname);
+// from = knot_dname_name(rdata->items[i].dname);
+
+// break;
+// case KNOT_RDATA_WF_BYTE:
+// size = 1;
+// break;
+// case KNOT_RDATA_WF_SHORT:
+// size = 2;
+// break;
+// case KNOT_RDATA_WF_LONG:
+// size = 4;
+// break;
+// case KNOT_RDATA_WF_A:
+// size = 4;
+// break;
+// case KNOT_RDATA_WF_AAAA:
+// size = 16;
+// break;
+// case KNOT_RDATA_WF_TEXT:
+// case KNOT_RDATA_WF_BINARYWITHLENGTH:
+// // size stored in the first two bytes, but in little
+// // endian and we need only the lower byte from it
+// *to = *from; // lower byte is the first in little endian
+// to += 1;
+// case KNOT_RDATA_WF_BINARY:
+// case KNOT_RDATA_WF_APL: // saved as binary
+// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
+// // size stored in the first two bytes, those bytes
+// // must not be copied
+// size = rdata->items[i].raw_data[0];
+// from += 2; // skip the first two bytes
+// break;
+// default:
+// assert(0);
+// }
+
+// assert(size != 0);
+// assert(copied + size < KNOT_MAX_RDATA_WIRE_SIZE);
+
+// memcpy(to, from, size);
+// to += size;
+// copied += size;
+// }
+
+// if (copied > buf_size) {
+// dbg_rdata("Not enough place allocated for function "
+// "knot_rdata_to_wire(). Allocated %u, need %u\n",
+// buf_size, copied);
+// return -1;
+// }
+
+// memcpy(buffer, tmp, copied);
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata,
+ uint16_t type)
+{
+ knot_rdata_t *copy = knot_rdata_new();
+ CHECK_ALLOC_LOG(copy, NULL);
+
+
+ if ((copy->items = (knot_rdata_item_t *)malloc(
+ rdata->count * sizeof(knot_rdata_item_t))) == NULL) {
+ knot_rdata_free(&copy);
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ copy->count = rdata->count;
+
+ knot_rrtype_descriptor_t *d = knot_rrtype_descriptor_by_type(type);
+
+ assert(copy->count <= d->length);
+
+ // copy all items one by one
+ for (int i = 0; i < copy->count; ++i) {
+ if (d->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || d->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || d->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ copy->items[i].dname =
+ knot_dname_deep_copy(rdata->items[i].dname);
+ } else {
+ copy->items[i].raw_data = (uint16_t *)malloc(
+ rdata->items[i].raw_data[0] + 2);
+ if (copy->items[i].raw_data == NULL) {
+ knot_rdata_deep_free(&copy, type, 1);
+ return NULL;
+ }
+ memcpy(copy->items[i].raw_data,
+ rdata->items[i].raw_data,
+ rdata->items[i].raw_data[0] + 2);
+ }
+ }
+
+ return copy;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2,
+ const uint8_t *format)
+{
+ uint count = (r1->count < r2->count) ? r1->count : r2->count;
+
+ int cmp = 0;
+
+ for (int i = 0; i < count; ++i) {
+ /* CLEANUP */
+// const uint8_t *data1, *data2;
+// int size1, size2;
+
+ if (format[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ format[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ format[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ cmp = knot_dname_compare(r1->items[i].dname,
+ r2->items[i].dname);
+// data1 = knot_dname_name(r1->items[i].dname);
+// data2 = knot_dname_name(r2->items[i].dname);
+// size1 = knot_dname_size(r2->items[i].dname);
+// size2 = knot_dname_size(r2->items[i].dname);
+ } else {
+ cmp = knot_rdata_compare_binary(
+ (uint8_t *)(r1->items[i].raw_data + 1),
+ (uint8_t *)(r2->items[i].raw_data + 1),
+ r1->items[i].raw_data[0],
+ r1->items[i].raw_data[0]);
+// data1 = (uint8_t *)(r1->items[i].raw_data + 1);
+// data2 = (uint8_t *)(r2->items[i].raw_data + 1);
+// size1 = r1->items[i].raw_data[0];
+// size2 = r1->items[i].raw_data[0];
+ }
+
+// cmp =
+
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+
+ assert(cmp == 0);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_cname_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_dname_target(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*---------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata,
+ uint16_t type)
+{
+ // iterate over the rdata items or act as if we knew where the name is?
+
+ switch (type) {
+ case KNOT_RRTYPE_NS:
+ return knot_rdata_ns_name(rdata);
+ case KNOT_RRTYPE_MX:
+ return knot_rdata_mx_name(rdata);
+ case KNOT_RRTYPE_SRV:
+ return knot_rdata_srv_name(rdata);
+ case KNOT_RRTYPE_CNAME:
+ return knot_rdata_cname_name(rdata);
+ }
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*/
+int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return -1;
+ }
+
+ if (rdata->count < 3) {
+ return -1;
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[2].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return 0;
+ }
+
+ if (rdata->count < 4) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[3].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return 0;
+ }
+
+ if (rdata->count < 5) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[4].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return -1;
+ }
+
+ if (rdata->count < 6) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[5].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return 0;
+ }
+
+ return knot_wire_read_u16((uint8_t *)(rdata->items[0].raw_data + 1));
+}
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
new file mode 100644
index 0000000..5d328c9
--- /dev/null
+++ b/src/libknot/rdata.h
@@ -0,0 +1,339 @@
+/*!
+ * \file rdata.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structures representing RDATA and its items and API for manipulating
+ * both.
+ *
+ * \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_RDATA_H_
+#define _KNOT_RDATA_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "util/descriptor.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief RDATA item structure.
+ *
+ * Each RDATA may be logically divided into items, each of possible different
+ * type. This structure distinguishes between general data (\a raw_data)
+ * represented as an array of octets, and domain name (\a dname) as domain names
+ * require special treatment within some RDATA (e.g. compressing in packets).
+ */
+union knot_rdata_item {
+ knot_dname_t *dname; /*!< RDATA item represented as a domain name. */
+
+ /*!
+ * \brief RDATA item represented as raw array of octets.
+ *
+ * The first two bytes hold the length of the item in bytes. The length
+ * is stored in little endian.
+ *
+ * In some cases the stored length is also used in the wire format of
+ * RDATA (e.g. character-data as defined in RFC1035). In such case,
+ * the length should be less than 256, so that it fits into one byte
+ * in the wireformat.
+ *
+ * \todo Store the length in system byte order.
+ */
+ uint16_t *raw_data;
+};
+
+typedef union knot_rdata_item knot_rdata_item_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief RDATA structure.
+ *
+ * Each RDATA may be logically divided into items, each of possible different
+ * type (see knot_rdata_item). This structure stores an array of such items.
+ * It is not dynamic, so any RDATA structure may hold either 0 or one specified
+ * number of items which cannot be changed later. However, the number of items
+ * may be different for each RDATA structure. The number of items should be
+ * given by descriptors for each RR type. Some types may have variable number
+ * of items. In such cases, the last item in the array will be set tu NULL
+ * to distinguish the actual count of items.
+ *
+ * This structure does not hold information about the RDATA items, such as
+ * what type is which item or how long are they. This information should be
+ * stored elsewhere (in descriptors) as it is RR-specific and given for each
+ * RR type.
+ *
+ * \todo Find out whether NULL is appropriate value. If it is a possible
+ * value for some of the items, we must find some other way to deal with
+ * this.
+ * \todo Add some function for freeing particular item? Or a non-const getter?
+ */
+struct knot_rdata {
+ knot_rdata_item_t *items; /*!< RDATA items comprising this RDATA. */
+ unsigned int count; /*! < Count of RDATA items in this RDATA. */
+ struct knot_rdata *next; /*!< Next RDATA item in a linked list. */
+};
+
+typedef struct knot_rdata knot_rdata_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates an empty RDATA structure.
+ *
+ * \return Pointer to the new RDATA structure or NULL if an error occured.
+ */
+knot_rdata_t *knot_rdata_new();
+
+/*!
+ * \brief Parses RDATA from the given data in wire format.
+ *
+ * \param rdata RDATA to fill.
+ * \param wire Wire format of the whole data in which the RDATA are present.
+ * \param pos Position in \a wire where to start parsing.
+ * \param total_size Size of the whole data.
+ * \param rdlength Size of the RDATA to parse in bytes.
+ * \param desc RR type descriptor for the RDATA type.
+ *
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_EMALF
+ * \retval KNOT_ERROR
+ * \retval KNOT_EOK
+ */
+int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc);
+
+/*!
+ * \brief Sets the RDATA item on position \a pos.
+ *
+ * \param rdata RDATA structure in which the item should be set.
+ * \param pos Position of the RDATA item to be set.
+ * \param item RDATA item value to be set.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG if \a pos is not a valid position.
+ *
+ * \todo Use the union or a pointer to it as parameter? IMHO there is always
+ * only one pointer that is copied, so it doesn't matter.
+ */
+int knot_rdata_set_item(knot_rdata_t *rdata, unsigned int pos,
+ knot_rdata_item_t item);
+
+/*!
+ * \brief Sets all RDATA items within the given RDATA structure.
+ *
+ * \a rdata must be empty so far (\a rdata->count == 0). The necessary space
+ * is allocated.
+ *
+ * This function copies the array of RDATA items from \a items to \a rdata.
+ *
+ * \param rdata RDATA structure to store the items in.
+ * \param items An array of RDATA items to be stored in this RDATA structure.
+ * \param count Count of RDATA items to be stored.
+ *
+ * \retval 0 if successful.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_rdata_set_items(knot_rdata_t *rdata,
+ const knot_rdata_item_t *items,
+ unsigned int count);
+
+unsigned int knot_rdata_item_count(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Returns the RDATA item on position \a pos.
+ *
+ * \note Although returning union would be OK (no overhead), we need to be able
+ * to distinguish errors (in this case by returning NULL).
+ *
+ * \param rdata RDATA structure to get the item from.
+ * \param pos Position of the item to retrieve.
+ *
+ * \return The RDATA item on position \a pos, or NULL if such position does not
+ * exist within the given RDATA structure.
+ */
+knot_rdata_item_t *knot_rdata_get_item(const knot_rdata_t *rdata,
+ unsigned int pos);
+
+/*!
+ * \brief Returns the RDATA item on position \a pos.
+ *
+ * \note Although returning union would be OK (no overhead), we need to be able
+ * to distinguish errors (in this case by returning NULL).
+ * \note This function is identical to knot_rdata_get_item(), only it returns
+ * constant data.
+ *
+ * \param rdata RDATA structure to get the item from.
+ * \param pos Position of the item to retrieve.
+ *
+ * \return The RDATA item on position \a pos, or NULL if such position does not
+ * exist within the given RDATA structure.
+ */
+const knot_rdata_item_t *knot_rdata_item(const knot_rdata_t *rdata,
+ unsigned int pos);
+
+/*!
+ * \brief Sets the given domain name as a value of RDATA item on position
+ * \a pos.
+ *
+ * \param rdata RDATA structure to set the item in.
+ * \param pos Position of the RDATA item to set.
+ * \param dname Domain name to set to the item.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG
+ */
+int knot_rdata_item_set_dname(knot_rdata_t *rdata, unsigned int pos,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Sets the given raw data as a value of RDATA item on position \a pos.
+ *
+ * \param rdata RDATA structure to set the item in.
+ * \param pos Position of the RDATA item to set.
+ * \param raw_data Raw data to set to the item.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG
+ */
+int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, unsigned int pos,
+ uint16_t *raw_data);
+
+/*!
+ * \brief Copies the given RDATA.
+ *
+ * \param rdata RDATA to copy.
+ * \param type RR type of the RDATA.
+ *
+ * \return Copy of \a rdata.
+ */
+knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata,
+ uint16_t type);
+
+/*!
+ * \brief Destroys the RDATA structure without deleting RDATA items.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rdata RDATA structure to be destroyed.
+ */
+void knot_rdata_free(knot_rdata_t **rdata);
+
+/*!
+ * \brief Destroys the RDATA structure and all its RDATA items.
+ *
+ * RDATA items are deleted according to the given RR Type. In case of domain
+ * name, it is deallocated only if either the free_all_dnames parameter is set
+ * to <> 0 or the name does not contain reference to a node (i.e. it is not an
+ * owner of some node) or if it does contain a reference to a node, but is
+ * not equal to its owner. (If free_all_dnames is set to <> 0, no other
+ * condition is evaluated.)
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rdata RDATA structure to be destroyed.
+ * \param type RR Type of the RDATA.
+ * \param free_all_dnames Set to <> 0 if you want to delete ALL domain names
+ * from the RDATA. Set to 0 otherwise.
+ */
+void knot_rdata_deep_free(knot_rdata_t **rdata, unsigned int type,
+ int free_all_dnames);
+
+/*!
+ * \brief Compares two RDATAs of the same type.
+ *
+ * \note Compares domain names normally (dname_compare()), i.e.
+ * case-insensitive.
+ *
+ * \param r1 First RDATA.
+ * \param r2 Second RDATA.
+ * \param format Descriptor of the RDATA format.
+ *
+ * \retval 0 if RDATAs are equal.
+ * \retval < 0 if \a r1 goes before \a r2 in canonical order.
+ * \retval > 0 if \a r1 goes after \a r2 in canonical order.
+ */
+int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2,
+ const uint8_t *format);
+
+/*!
+ * \brief Retrieves the domain name from CNAME RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the CNAME domain name from.
+ *
+ * \return Canonical name stored in \a rdata or NULL if \a rdata has no items.
+ */
+const knot_dname_t *knot_rdata_cname_name(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Retrieves the domain name from DNAME RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the DNAME domain name from.
+ *
+ * \return Target domain name stored in \a rdata or NULL if \a rdata has no
+ * items.
+ */
+const knot_dname_t *knot_rdata_dname_target(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Retrieves the domain name from RDATA of given type.
+ *
+ * Supported types:
+ * - KNOT_RRTYPE_NS
+ * - KNOT_RRTYPE_MX
+ * - KNOT_RRTYPE_SRV
+ * - KNOT_RRTYPE_CNAME
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the RDATA
+ * item according to the given type, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the domain name from.
+ * \param type RR type of the RDATA.
+ *
+ * \return Domain name stored in \a rdata or NULL if \a rdata has not enough
+ * items.
+ */
+const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata,
+ uint16_t type);
+
+int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata);
+
+uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata);
+uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata);
+uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata);
+
+uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata);
+
+#endif /* _KNOT_RDATA_H */
+
+/*! @} */
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
new file mode 100644
index 0000000..6083f77
--- /dev/null
+++ b/src/libknot/rrset.c
@@ -0,0 +1,719 @@
+/* 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 <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "rrset.h"
+#include "util/descriptor.h"
+#include "util/error.h"
+#include "util/utils.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+static void knot_rrset_disconnect_rdata(knot_rrset_t *rrset,
+ knot_rdata_t *prev, knot_rdata_t *rdata)
+{
+ if (prev == NULL) {
+ // find the previous RDATA in the series, as its pointer must
+ // be changed
+ prev = rdata->next;
+ while (prev->next != rdata) {
+ prev = prev->next;
+ }
+ }
+
+ assert(prev);
+ assert(prev->next == rdata);
+
+ prev->next = rdata->next;
+
+ if (rrset->rdata == rdata) {
+ if (rdata->next == rdata) {
+ rrset->rdata = NULL;
+ } else {
+ rrset->rdata = rdata->next;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl)
+{
+ knot_rrset_t *ret = malloc(sizeof(knot_rrset_t));
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ ret->rdata = NULL;
+
+ /* Retain reference to owner. */
+ knot_dname_retain(owner);
+
+ ret->owner = owner;
+ ret->type = type;
+ ret->rclass = rclass;
+ ret->ttl = ttl;
+ ret->rrsigs = NULL;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (rrset->rdata == NULL) {
+ rrset->rdata = rdata;
+ rrset->rdata->next = rrset->rdata;
+ } else {
+ knot_rdata_t *tmp;
+
+ tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata) {
+ tmp = tmp->next;
+ }
+ rdata->next = tmp->next;
+ tmp->next = rdata;
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_remove_rdata(knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return NULL;
+ }
+
+ knot_rdata_t *prev = NULL;
+ knot_rdata_t *rr = rrset->rdata;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ if (desc == NULL) {
+ return NULL;
+ }
+
+ while (rr != NULL) {
+ /*! \todo maybe the dnames should be compared case-insensitive*/
+ if (knot_rdata_compare(rr, rdata, desc->wireformat) == 0) {
+ knot_rrset_disconnect_rdata(rrset, prev, rr);
+ return rr;
+ }
+ prev = rr;
+ rr = knot_rrset_rdata_get_next(rrset, rr);
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
+{
+ if (rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ rrset->rrsigs = 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(rrset->owner, rrsigs->owner) != 0) {
+ return KNOT_EBADARG;
+ }
+
+ int rc;
+ if (rrset->rrsigs != NULL) {
+ if (dupl == KNOT_RRSET_DUPL_MERGE) {
+ rc = knot_rrset_merge((void **)&rrset->rrsigs,
+ (void **)&rrsigs);
+ if (rc != KNOT_EOK) {
+ return rc;
+ } else {
+ return 1;
+ }
+ } else if (dupl == KNOT_RRSET_DUPL_SKIP) {
+ return 2;
+ } else if (dupl == KNOT_RRSET_DUPL_REPLACE) {
+ rrset->rrsigs = rrsigs;
+ }
+ } else {
+ rrset->rrsigs = 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)
+{
+ if (rrset) {
+ /* Retain new owner and release old owner. */
+ knot_dname_retain(owner);
+ knot_dname_release(rrset->owner);
+ rrset->owner = owner;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+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;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata(const knot_rrset_t *rrset)
+{
+ return rrset->rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata_next(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rdata == NULL) {
+ return rrset->rdata;
+ }
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return NULL;
+ } else {
+ return rrset->rdata;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
+ knot_rdata_t *rdata)
+{
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
+{
+ int count = 0;
+ const knot_rdata_t *rdata = rrset->rdata;
+
+ while (rdata != NULL) {
+ ++count;
+ rdata = knot_rrset_rdata_next(rrset, rdata);
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2)
+{
+ if (r1 == NULL || r2 == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(r1->type);
+ if (desc == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // compare RDATA sets (order is not significant)
+ const knot_rdata_t *rdata1= knot_rrset_rdata(r1);
+ const knot_rdata_t *rdata2;
+
+ // find all RDATA from r1 in r2
+ while (rdata1 != NULL) {
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ if (rdata2 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ // find all RDATA from r2 in r1 (this can be done in a better way)
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL) {
+ rdata1 = knot_rrset_rdata(r1);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ if (rdata1 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ // all RDATA found
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata, uint8_t **pos,
+ size_t max_size)
+{
+ int size = 0;
+
+ assert(rrset != NULL);
+ assert(rrset->owner != NULL);
+ assert(rdata != NULL);
+ assert(pos != NULL);
+ assert(*pos != NULL);
+
+ fprintf(stderr, "Max size: %zu, owner: %p, owner size: %d\n",
+ max_size, rrset->owner, rrset->owner->size);
+
+ // check if owner fits
+ if (size + knot_dname_size(rrset->owner) + 10 > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(*pos, knot_dname_name(rrset->owner),
+ knot_dname_size(rrset->owner));
+ *pos += knot_dname_size(rrset->owner);
+ size += knot_dname_size(rrset->owner);
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ fprintf(stderr, "Wire format:\n");
+
+ // put rest of RR 'header'
+ knot_wire_write_u16(*pos, rrset->type);
+ fprintf(stderr, " Type: %u\n", rrset->type);
+ *pos += 2;
+
+ knot_wire_write_u16(*pos, rrset->rclass);
+ fprintf(stderr, " Class: %u\n", rrset->rclass);
+ *pos += 2;
+
+ knot_wire_write_u32(*pos, rrset->ttl);
+ fprintf(stderr, " TTL: %u\n", rrset->ttl);
+ *pos += 4;
+
+ // save space for RDLENGTH
+ uint8_t *rdlength_pos = *pos;
+ *pos += 2;
+
+ size += 10;
+// compr->wire_pos += size;
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ uint16_t rdlength = 0;
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (max_size < size + rdlength) {
+ return KNOT_ESPACE;
+ }
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME: {
+ knot_dname_t *dname =
+ knot_rdata_item(rdata, i)->dname;
+ if (size + rdlength + dname->size > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // save whole domain name
+ memcpy(*pos, knot_dname_name(dname),
+ knot_dname_size(dname));
+ fprintf(stderr, "Uncompressed dname size: %d\n",
+ knot_dname_size(dname));
+ *pos += knot_dname_size(dname);
+ rdlength += knot_dname_size(dname);
+// compr->wire_pos += dname->size;
+ break;
+ }
+ default: {
+ uint16_t *raw_data =
+ knot_rdata_item(rdata, i)->raw_data;
+
+ if (size + rdlength + raw_data[0] > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // copy just the rdata item data (without size)
+ memcpy(*pos, raw_data + 1, raw_data[0]);
+ fprintf(stderr, "Raw data size: %d\n", raw_data[0]);
+ *pos += raw_data[0];
+ rdlength += raw_data[0];
+// compr->wire_pos += raw_data[0];
+ break;
+ }
+ }
+ }
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ assert(size + rdlength <= max_size);
+ size += rdlength;
+ knot_wire_write_u16(rdlength_pos, rdlength);
+
+ return size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
+ int *rr_count)
+{
+ // if no RDATA in RRSet, return
+ if (rrset->rdata == NULL) {
+ *size = 0;
+ *rr_count = 0;
+ return KNOT_EOK;
+ }
+
+
+ uint8_t *pos = wire;
+ int rrs = 0;
+ short rrset_size = 0;
+
+ const knot_rdata_t *rdata = rrset->rdata;
+ do {
+ int ret = knot_rrset_rr_to_wire(rrset, rdata, &pos,
+ *size - rrset_size);
+
+ assert(ret != 0);
+
+ if (ret < 0) {
+ // some RR didn't fit in, so no RRs should be used
+ // TODO: remove last entries from compression table
+ fprintf(stderr, "Some RR didn't fit in.\n");
+ return KNOT_ESPACE;
+ }
+
+ fprintf(stderr, "RR of size %d added.\n", ret);
+ rrset_size += ret;
+ ++rrs;
+ } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
+
+ // the whole RRSet did fit in
+ assert(rrset_size <= *size);
+ assert(pos - wire == rrset_size);
+ *size = rrset_size;
+
+ fprintf(stderr, " Size after: %zu\n", *size);
+
+ *rr_count = rrs;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ knot_rrset_compare_type_t cmp)
+{
+ if (cmp == KNOT_RRSET_COMPARE_PTR) {
+ return (r1 == r2);
+ }
+
+ int res = ((r1->rclass == r2->rclass)
+ && (r1->type == r2->type)
+ && (r1->ttl == r2->ttl)
+ && knot_dname_compare(r1->owner, r2->owner) == 0);
+
+ if (cmp == KNOT_RRSET_COMPARE_WHOLE && res) {
+ res = knot_rrset_compare_rdata(r1, r2);
+ if (res < 0) {
+ return 0;
+ }
+ }
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret;
+
+ *to = (knot_rrset_t *)calloc(1, sizeof(knot_rrset_t));
+ CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+
+ (*to)->owner = knot_dname_deep_copy(from->owner);
+ (*to)->rclass = from->rclass;
+ (*to)->ttl = from->ttl;
+ (*to)->type = from->type;
+ if (from->rrsigs != NULL) {
+ ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 0, 0);
+ return ret;
+ }
+ }
+ assert((*to)->rrsigs == NULL || from->rrsigs != NULL);
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(from);
+
+ /*! \note Order of RDATA will be reversed. */
+ while (rdata != NULL) {
+ ret = knot_rrset_add_rdata(*to, knot_rdata_deep_copy(rdata,
+ knot_rrset_type(from)));
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 1, 1);
+ return ret;
+ }
+ rdata = knot_rrset_rdata_next(from, rdata);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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);
+
+ memcpy(*to, from, sizeof(knot_rrset_t));
+
+ /* Retain owner. */
+ knot_dname_retain((*to)->owner);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_free(knot_rrset_t **rrset)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ /*! \todo Shouldn't we always release owner reference? */
+ knot_dname_release((*rrset)->owner);
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
+ int free_rdata, int free_rdata_dnames)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ if (free_rdata) {
+ knot_rdata_t *tmp_rdata;
+ knot_rdata_t *next_rdata;
+ tmp_rdata = (*rrset)->rdata;
+
+ while ((tmp_rdata != NULL)
+ && (tmp_rdata->next != (*rrset)->rdata)
+ && (tmp_rdata->next != NULL)) {
+ next_rdata = tmp_rdata->next;
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ tmp_rdata = next_rdata;
+ }
+
+ assert(tmp_rdata == NULL
+ || tmp_rdata->next == (*rrset)->rdata);
+
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ }
+
+ // RRSIGs should have the same owner as this RRSet, so do not delete it
+ if ((*rrset)->rrsigs != NULL) {
+ knot_rrset_deep_free(&(*rrset)->rrsigs, 0, 1,
+ free_rdata_dnames);
+ }
+
+ /*! \todo Release owner every time? */
+ //if (free_owner) {
+ knot_dname_release((*rrset)->owner);
+ //}
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_merge(void **r1, void **r2)
+{
+ knot_rrset_t *rrset1 = (knot_rrset_t *)(*r1);
+ knot_rrset_t *rrset2 = (knot_rrset_t *)(*r2);
+
+ if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0)
+ || rrset1->rclass != rrset2->rclass
+ || rrset1->type != rrset2->type
+ || rrset1->ttl != rrset2->ttl) {
+ return KNOT_EBADARG;
+ }
+
+ // add all RDATAs from rrset2 to rrset1 (i.e. concatenate linked lists)
+
+ // no RDATA in RRSet 1
+ assert(rrset1 && rrset2);
+ if (rrset1->rdata == NULL) {
+ rrset1->rdata = rrset2->rdata;
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *tmp_rdata = rrset1->rdata;
+
+ if (!tmp_rdata) {
+ return KNOT_EOK;
+ }
+
+ while (tmp_rdata->next != rrset1->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset2->rdata;
+
+ tmp_rdata = rrset2->rdata; //maybe unnecessary, but is clearer
+
+ while (tmp_rdata->next != rrset2->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset1->rdata;
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
new file mode 100644
index 0000000..7754c7f
--- /dev/null
+++ b/src/libknot/rrset.h
@@ -0,0 +1,306 @@
+/*!
+ * \file rrset.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief RRSet structure and API for manipulating it.
+ *
+ * \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_RRSET_H_
+#define _KNOT_RRSET_H_
+
+#include <stdint.h>
+
+#include "dname.h"
+#include "rdata.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for representing an RRSet.
+ *
+ * For definition of a RRSet see RFC2181, Section 5.
+ *
+ * As all RRs within a RRSet share the same OWNER, TYPE, CLASS and TTL (see
+ * Section 5.2 of RFC2181), there is no need to duplicate these data in the
+ * program. Distinct Resource Records are thus represented only as distinct
+ * RDATA sections of corresponding RRs.
+ */
+struct knot_rrset {
+ /*! \brief Domain name being the owner of the RRSet. */
+ knot_dname_t *owner;
+ uint16_t type; /*!< TYPE of the RRset. */
+ uint16_t rclass; /*!< CLASS of the RRSet. */
+ uint32_t ttl; /*!< TTL of the RRSet. */
+ /*!
+ * \brief First item in an ordered cyclic list of RDATA items.
+ *
+ * \note The fact that the list is cyclic will easily allow for
+ * possible round-robin rotation of RRSets.
+ */
+ knot_rdata_t *rdata;
+ struct knot_rrset *rrsigs; /*!< Set of RRSIGs covering this RRSet. */
+};
+
+typedef struct knot_rrset knot_rrset_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef enum {
+ KNOT_RRSET_COMPARE_PTR,
+ KNOT_RRSET_COMPARE_HEADER,
+ KNOT_RRSET_COMPARE_WHOLE
+} knot_rrset_compare_type_t;
+
+typedef enum {
+ KNOT_RRSET_DUPL_MERGE,
+ KNOT_RRSET_DUPL_REPLACE,
+ KNOT_RRSET_DUPL_SKIP
+} knot_rrset_dupl_handling_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a new RRSet with the given properties.
+ *
+ * The created RRSet contains no RDATAs (i.e. is actually empty).
+ *
+ * \param owner OWNER of the RRSet.
+ * \param type TYPE of the RRSet.
+ * \param rclass CLASS of the RRSet.
+ * \param ttl TTL of the RRset.
+ *
+ * \return New RRSet structure with the given OWNER, TYPE, CLASS and TTL or NULL
+ * if an error occured.
+ */
+knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl);
+
+/*!
+ * \brief Adds the given RDATA to the RRSet.
+ *
+ * \param rrset RRSet to add the RDATA to.
+ * \param rdata RDATA to add to the RRSet.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ *
+ * \todo Provide some function for comparing RDATAs.
+ */
+int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata);
+
+knot_rdata_t * knot_rrset_remove_rdata(knot_rrset_t *rrset,
+ const knot_rdata_t *rdata);
+
+/*!
+ * \brief Adds RRSIG signatures to this RRSet.
+ *
+ * \param rrset RRSet to add the signatures into.
+ * \param rrsigs Set of RRSIGs covering this RRSet.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs);
+
+int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
+ knot_rrset_dupl_handling_t dupl);
+
+/*!
+ * \brief Returns the Owner of the RRSet.
+ *
+ * \param rrset RRSet to get the Owner of.
+ *
+ * \return Owner of the given RRSet.
+ */
+const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset);
+
+/*!
+ * \todo Document me.
+ */
+knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Set rrset owner to specified dname.
+ *
+ * Previous owner will be replaced if exist.
+ *
+ * \param rrset Specified RRSet.
+ * \param owner New owner dname.
+ */
+void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner);
+
+/*!
+ * \brief Returns the TYPE of the RRSet.
+ *
+ * \param rrset RRSet to get the TYPE of.
+ *
+ * \return TYPE of the given RRSet.
+ */
+uint16_t knot_rrset_type(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the CLASS of the RRSet.
+ *
+ * \param rrset RRSet to get the CLASS of.
+ *
+ * \return CLASS of the given RRSet.
+ */
+uint16_t knot_rrset_class(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the TTL of the RRSet.
+ *
+ * \param rrset RRSet to get the TTL of.
+ *
+ * \return TTL of the given RRSet.
+ */
+uint32_t knot_rrset_ttl(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the first RDATA in the RRSet.
+ *
+ * RDATAs in a RRSet are stored in a ordered cyclic list.
+ *
+ * \note If later a round-robin rotation of RRSets is employed, the RDATA
+ * returned by this function may not be the first RDATA in canonical
+ * order.
+ *
+ * \param rrset RRSet to get the RDATA from.
+ *
+ * \return First RDATA in the given RRSet.
+ */
+const knot_rdata_t *knot_rrset_rdata(const knot_rrset_t *rrset);
+
+const knot_rdata_t *knot_rrset_rdata_next(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata);
+
+/*!
+ * \brief Returns the first RDATA in the RRSet (non-const version).
+ *
+ * RDATAs in a RRSet are stored in a ordered cyclic list.
+ *
+ * \note If later a round-robin rotation of RRSets is employed, the RDATA
+ * returned by this function may not be the first RDATA in canonical
+ * order.
+ *
+ * \param rrset RRSet to get the RDATA from.
+ *
+ * \return First RDATA in the given RRSet or NULL if there is none or if no
+ * rrset was provided (\a rrset is NULL).
+ */
+knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset);
+
+knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
+ knot_rdata_t *rdata);
+
+int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the set of RRSIGs covering the given RRSet.
+ *
+ * \param rrset RRSet to get the signatures for.
+ *
+ * \return Set of RRSIGs which cover the given RRSet or NULL if there is none or
+ * if no rrset was provided (\a rrset is NULL).
+ */
+const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset);
+
+knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset);
+
+int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2);
+
+int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
+ int *rr_count);
+
+/*!
+ * \brief Compares two RRSets.
+ *
+ * \note This function does not return 'standard' compare return values, because
+ * there is no way to define which RRSet is 'larger'.
+ *
+ * \param r1 First RRSet.
+ * \param r2 Second RRSet.
+ * \param cmp Type of comparison to perform.
+ *
+ * \retval <> 0 If RRSets are equal.
+ * \retval 0 if RRSets are not equal.
+ */
+int knot_rrset_compare(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ knot_rrset_compare_type_t cmp);
+
+/*! \todo Add unit test. */
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to);
+
+/*! \todo Add unit test. */
+int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to);
+
+/*!
+ * \brief Destroys the RRSet structure.
+ *
+ * Does not destroy the OWNER domain name structure, nor the signatures, as
+ * these may be used elsewhere.
+ *
+ * Does not destroy RDATA structures neither, as they need special processing.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rrset RRset to be destroyed.
+ */
+void knot_rrset_free(knot_rrset_t **rrset);
+
+/*!
+ * \brief Destroys the RRSet structure and all its substructures.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rrset RRset to be destroyed.
+ * \param free_owner Set to 0 if you do not want the owner domain name to be
+ * destroyed also. Set to <> 0 otherwise.
+ * \param free_rdata ***\todo DOCUMENT ME***
+ * \param free_rdata_dnames Set to <> 0 if you want to delete ALL domain names
+ * 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, int free_rdata_dnames);
+
+/*!
+ * \brief Merges two RRSets.
+ *
+ * Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after
+ * the list of RDATAs in \a r1. \a r2 is unaffected by this, though you must not
+ * destroy the RDATAs in \a r2 as they are now also in \a r1. (You may use
+ * function knot_rrset_free() though, as it does not touch RDATAs).
+ *
+ * \note Member \a rrsigs is preserved from the first RRSet.
+ *
+ * \param r1 Pointer to RRSet to be merged into.
+ * \param r2 Poitner to RRSet to be merged.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG if the RRSets could not be merged, because their
+ * Owner, Type, Class or TTL does not match.
+ */
+int knot_rrset_merge(void **r1, void **r2);
+
+#endif /* _KNOT_RRSET_H_ */
+
+/*! @} */
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
new file mode 100644
index 0000000..3178a23
--- /dev/null
+++ b/src/libknot/tsig-op.c
@@ -0,0 +1,1089 @@
+/* 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 <stdint.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "tsig.h"
+#include "tsig-op.h"
+#include "util/wire.h"
+#include "util/error.h"
+#include "util/debug.h"
+
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+
+static int b64rmap_initialized = 0;
+static uint8_t b64rmap[256];
+
+static const uint8_t b64rmap_special = 0xf0;
+static const uint8_t b64rmap_end = 0xfd;
+static const uint8_t b64rmap_space = 0xfe;
+static const uint8_t b64rmap_invalid = 0xff;
+
+/**
+ * Initializing the reverse map is not thread safe.
+ * Which is fine for NSD. For now...
+ **/
+void b64_initialize_rmap()
+{
+ int i;
+ char ch;
+
+ /* Null: end of string, stop parsing */
+ b64rmap[0] = b64rmap_end;
+
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ /* Whitespaces */
+ if (isspace(ch)) {
+ b64rmap[i] = b64rmap_space;
+ }
+ /* Padding: stop parsing */
+ else if (ch == Pad64) {
+ b64rmap[i] = b64rmap_end;
+ }
+ /* Non-base64 char */
+ else {
+ b64rmap[i] = b64rmap_invalid;
+ }
+ }
+
+ /* Fill reverse mapping for base64 chars */
+ for (i = 0; Base64[i] != '\0'; ++i) {
+ b64rmap[(uint8_t)Base64[i]] = i;
+ }
+
+ b64rmap_initialized = 1;
+}
+
+int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f)
+ << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03)
+ << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target[tarindex] != 0) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+
+int b64_pton_len(char const *src)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ state = 1;
+ break;
+ case 1:
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ if (!b64rmap_initialized) {
+ b64_initialize_rmap();
+ }
+
+ if (target) {
+ return b64_pton_do(src, target, targsize);
+ } else {
+ return b64_pton_len(src);
+ }
+}
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+
+
+
+
+
+
+
+
+
+
+
+
+const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
+
+
+static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr)
+{
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+ if (alg == 0) {
+ /*!< \todo is this error OK? */
+ dbg_tsig("TSIG: unknown algorithm.\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
+ const knot_key_t *tsig_key)
+{
+ const knot_dname_t *tsig_name = knot_rrset_owner(tsig_rr);
+ if (!tsig_name) {
+ return KNOT_EMALF;
+ }
+
+ const char *name = knot_dname_to_str(tsig_name);
+ if (!name) {
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_compare(tsig_name, tsig_key->name) != 0) {
+ /*!< \todo which error. */
+ dbg_tsig("TSIG: unknown key: %s\n", name);
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!wire || !digest || !digest_len || !key) {
+ dbg_tsig("TSIG: digest: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (!key->name) {
+ dbg_tsig("TSIG: digest: no algorithm\n");
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t tsig_alg = key->algorithm;
+ if (tsig_alg == 0) {
+ dbg_tsig("TSIG: digest: unknown algorithm\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ /* Create digest, using length of the algorithm. */
+// *digest = malloc(sizeof(uint8_t) * tsig_alg_digest_length(tsig_alg));
+// if (!digest) {
+// ERR_ALLOC_FAILED;
+// return KNOT_ENOMEM;
+// }
+
+ /* Decode key from Base64. */
+ char decoded_key[B64BUFSIZE];
+
+ int decoded_key_size = b64_pton(key->secret, (uint8_t *)decoded_key,
+ B64BUFSIZE);
+ if (decoded_key_size < 0) {
+ dbg_tsig("TSIG: Could not decode Base64\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_tsig("TSIG: decoded key size: %d\n", decoded_key_size);
+ dbg_tsig("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
+
+ dbg_tsig("TSIG: using this wire for digest calculation\n");
+
+ //dbg_tsig_hex(wire, wire_len);
+
+ /* Compute digest. */
+ HMAC_CTX ctx;
+
+ switch (tsig_alg) {
+ case KNOT_TSIG_ALG_HMAC_MD5:
+ HMAC_Init(&ctx, decoded_key,
+ decoded_key_size, EVP_md5());
+ break;
+ default:
+ return KNOT_ENOTSUP;
+ } /* switch */
+
+ unsigned tmp_dig_len = *digest_len;
+ HMAC_Update(&ctx, (const unsigned char *)wire, wire_len);
+ HMAC_Final(&ctx, digest, &tmp_dig_len);
+ *digest_len = tmp_dig_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr)
+{
+ if (!tsig_rr) {
+ return KNOT_EBADARG;
+ }
+
+ /* Get the time signed and fudge values. */
+ uint64_t time_signed = tsig_rdata_time_signed(tsig_rr);
+ if (time_signed == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+ uint16_t fudge = tsig_rdata_fudge(tsig_rr);
+ if (fudge == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ /* Get the current time. */
+ time_t curr_time = time(NULL);
+
+ /*!< \todo bleeding eyes. */
+ if (difftime(curr_time, (time_t)time_signed) > fudge) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ // put time signed
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+
+ // put fudge
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_variables(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ /* Copy TSIG variables - starting with key name. */
+ const knot_dname_t *tsig_owner = knot_rrset_owner(tsig_rr);
+ if (!tsig_owner) {
+ dbg_tsig("TSIG: write variables: no owner.\n");
+ return KNOT_EBADARG;
+ }
+
+ int offset = 0;
+
+ memcpy(wire + offset, knot_dname_name(tsig_owner),
+ sizeof(uint8_t) * knot_dname_size(tsig_owner));
+ dbg_tsig("TSIG: write variables: written owner (tsig alg): \n");
+ /*knot_rrset_class(tsig_rr));*/
+ dbg_tsig_hex_detail(wire + offset, knot_dname_size(tsig_owner));
+ offset += knot_dname_size(tsig_owner);
+
+ /*!< \todo which order? */
+
+ /* Copy class. */
+ knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr));
+ dbg_tsig("TSIG: write variables: written CLASS: %u - ",
+ knot_rrset_class(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t));
+ offset += sizeof(uint16_t);
+
+ /* Copy TTL - always 0. */
+ knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr));
+ dbg_tsig("TSIG: write variables: written TTL: %u - ",
+ knot_rrset_ttl(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+
+ /* Copy alg name. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ dbg_tsig("TSIG: write variables: no algorithm name.\n");
+ return KNOT_EBADARG;
+ }
+// alg_name = knot_dname_new_from_str("HMAC-MD5.SIG-ALG.REG.INT.",
+ //strlen("HMAC-MD5.SIG-ALG.REG.INT."),
+ //NULL);
+
+ memcpy(wire + offset, knot_dname_name(alg_name),
+ sizeof(uint8_t) * knot_dname_size(alg_name));
+ offset += knot_dname_size(alg_name);
+ dbg_tsig_detail("TSIG: write variables: written alg name: %s\n",
+ knot_dname_to_str(alg_name));
+
+ /* Following data are written in network order. */
+ /* Time signed. */
+ knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr));
+ offset += 6;
+ dbg_tsig_detail("TSIG: write variables: time signed: %llu - ",
+ tsig_rdata_time_signed(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset - 6, 6);
+ /* Fudge. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_fudge(tsig_rr));
+ offset += sizeof(uint16_t);
+ dbg_tsig_detail("TSIG: write variables: fudge: %hu\n",
+ tsig_rdata_fudge(tsig_rr));
+ /* TSIG error. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_error(tsig_rr));
+ offset += sizeof(uint16_t);
+ /* Get other data length. */
+ uint16_t other_data_length = tsig_rdata_other_data_length(tsig_rr);
+ /* Get other data. */
+ const uint8_t *other_data = tsig_rdata_other_data(tsig_rr);
+ if (!other_data) {
+ dbg_tsig("TSIG: write variables: no other data.\n");
+ return KNOT_EBADARG;
+ }
+
+ /*
+ * We cannot write the whole other_data, as it contains its length in
+ * machine order.
+ */
+ knot_wire_write_u16(wire + offset, other_data_length);
+ offset += sizeof(uint16_t);
+
+ /* Skip the length. */
+ dbg_tsig_detail("Copying other data.\n");
+ memcpy(wire + offset, other_data, other_data_length);
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_wire_write_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
+ /*size_t msg_max_len, */const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+// knot_rrset_t *tmp_tsig =
+// knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+// if (!tmp_tsig) {
+// return KNOT_ENOMEM;
+// }
+
+// tsig_rdata_store_current_time(tmp_tsig);
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, request_mac_len,
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + request_mac_len + ((request_mac_len > 0)
+ ? 2 : 0) +
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ uint8_t *pos = wire;
+
+ /* Copy the request MAC - should work even if NULL. */
+ if (request_mac_len > 0) {
+ dbg_tsig_detail("Copying request MAC size\n");
+ knot_wire_write_u16(pos, request_mac_len);
+ pos += 2;
+ }
+ dbg_tsig("Copying request mac.\n");
+ memcpy(pos, request_mac, sizeof(uint8_t) * request_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(pos, request_mac_len);
+ pos += request_mac_len;
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(pos, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(pos, msg_len);
+ pos += msg_len;
+ /* Copy TSIG variables. */
+ dbg_tsig("Writing TSIG variables.\n");
+ ret = knot_tsig_write_tsig_variables(pos, tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "variables: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+// assert(digest_tmp_len > 0);
+ free(wire);
+
+// if (digest_tmp_len > *digest_len) {
+// *digest_len = 0;
+// return KNOT_ESPACE;
+// }
+
+// knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ // everything went ok, save the digest to the output parameter
+// memcpy(digest, digest_tmp, digest_tmp_len);
+// *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
+ const uint8_t *prev_mac,
+ size_t prev_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, prev_mac_len,
+ tsig_rdata_tsig_timers_length());
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + prev_mac_len +
+ tsig_rdata_tsig_timers_length());
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ /* Copy the request MAC - should work even if NULL. */
+ dbg_tsig("Copying request mac.\n");
+ memcpy(wire, prev_mac, sizeof(uint8_t) * prev_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(wire, prev_mac_len);
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(wire + prev_mac_len, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len);
+ /* Copy TSIG variables. */
+
+ dbg_tsig("Writing TSIG timers.\n");
+ ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len,
+ tmp_tsig);
+// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len,
+// tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "timers: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ free(wire);
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
+ size_t msg_max_len, const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name);
+ if (!key_name_copy) {
+ dbg_tsig_detail("TSIG: key_name_copy = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key_name_copy,
+ KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (!rdata) {
+ dbg_tsig_detail("TSIG: rdata = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_add_rdata(tmp_tsig, rdata);
+
+ /* Create items for TSIG RR. */
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG);
+ assert(desc);
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (!items) {
+ dbg_tsig_detail("TSIG: items = NULL\n");
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(items, 0, sizeof(knot_rdata_item_t) * desc->length);
+
+ int ret = knot_rdata_set_items(rdata, items, desc->length);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ return ret;
+ }
+ free(items);
+
+ tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+ tsig_rdata_store_current_time(tmp_tsig);
+ tsig_rdata_set_fudge(tmp_tsig, 300);
+
+ /* Set original ID */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set error */
+ /*! \todo [TSIG] Set error and other data if appropriate. */
+ tsig_rdata_set_tsig_error(tmp_tsig, 0);
+
+ /* Set other len. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ dbg_tsig_detail("tmp_tsig before sign_wire():\n");
+ knot_rrset_dump(tmp_tsig, 0);
+
+ ret = knot_tsig_create_sign_wire(msg, *msg_len, /*msg_max_len,*/
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tmp_tsig, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: could not create wire or sign wire: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Set the digest. */
+ size_t tsig_wire_len = msg_max_len - *msg_len;
+ int rr_count = 0;
+ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ //knot_rrset_dump(tmp_tsig, 1);
+
+ /* Write RRSet to wire */
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_len, &rr_count);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_len;
+
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ // everything went ok, save the digest to the output parameter
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || !key || !digest || !digest_len) {
+ return KNOT_EBADARG;
+ }
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ 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);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ tsig_rdata_store_current_time(tmp_tsig);
+
+ /* Create wire to be signed. */
+ size_t wire_len = prev_digest_len + *msg_len + KNOT_TSIG_TIMERS_LENGTH;
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+ memset(wire, 0, wire_len);
+
+ /* Write previous digest. */
+ memcpy(wire, prev_digest, sizeof(uint8_t) * prev_digest_len);
+ /* Write original message. */
+ memcpy(msg + prev_digest_len, msg, *msg_len);
+ /* Write timers. */
+ knot_tsig_wire_write_timers(msg + prev_digest_len + *msg_len, tmp_tsig);
+
+ int ret = 0;
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest_tmp, &digest_tmp_len, key);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ if (digest_tmp_len > *digest_len) {
+ *digest_len = 0;
+ return KNOT_ESPACE;
+ }
+
+ free(wire);
+
+ /* Set the MAC. */
+ tsig_rdata_set_mac(tmp_tsig, *digest_len, digest);
+
+ size_t tsig_wire_size = msg_max_len - *msg_len;
+ int rr_count = 0;
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_size, &rr_count);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_size;
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac,
+ size_t request_mac_len,
+ const knot_key_t *tsig_key,
+ int use_times)
+{
+ if (!tsig_rr || !wire || !tsig_key) {
+ return KNOT_EBADARG;
+ }
+
+ /* Check time signed. */
+ int ret = knot_tsig_check_time_signed(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: time checked.\n");
+
+ /* Check that libknot knows the algorithm. */
+ ret = knot_tsig_check_algorithm(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: algorithm checked.\n");
+
+ /* Check that key is valid, ie. the same as given in args. */
+ ret = knot_tsig_check_key(tsig_rr, tsig_key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: key validity checked.\n");
+
+ /* Time OK algorithm OK, key name OK - do digest. */
+ /* Calculate the size of TSIG RR. */
+ size_t tsig_len = tsig_wire_actsize(tsig_rr);
+
+ dbg_tsig_detail("TSIG: check digest: wire before strip: \n");
+ //dbg_tsig_hex_detail(wire, size);
+
+ /* Strip the TSIG. */
+ size -= tsig_len;
+
+ dbg_tsig_detail("TSIG: check digest: wire after strip (stripped %zu):\n",
+ tsig_len);
+ //dbg_tsig_hex_detail(wire, size);
+
+ uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size);
+ if (!wire_to_sign) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire_to_sign, 0, sizeof(uint8_t) * size);
+ memcpy(wire_to_sign, wire, size);
+
+ /* Decrease arcount. */
+ knot_wire_set_arcount(wire_to_sign,
+ knot_wire_get_arcount(wire_to_sign) - 1);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+ assert(tsig_rr->rdata);
+
+ if (use_times) {
+ ret = knot_tsig_create_sign_wire_next(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ } else {
+ ret = knot_tsig_create_sign_wire(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ }
+
+ assert(tsig_rr->rdata);
+ free(wire_to_sign);
+
+ if (ret != KNOT_EOK) {
+ dbg_tsig("Failed to create wire format for checking: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+// uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+// size_t digest_tmp_len = 0;
+// ret = knot_tsig_compute_digest(wire, size, digest_tmp,
+// &digest_tmp_len, tsig_key);
+// if (ret != KNOT_EOK) {
+// dbg_tsig("TSIG: digest could not be calculated\n");
+// return ret;
+// }
+
+ dbg_tsig("TSIG: digest calculated\n");
+
+ /* Compare MAC from TSIG RR RDATA with just computed digest. */
+
+ /*!< \todo move to function. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+
+ /*! \todo [TSIG] TRUNCATION */
+ uint16_t mac_length = tsig_rdata_mac_length(tsig_rr);
+ const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr);
+
+ if (mac_length != tsig_alg_digest_length(alg)) {
+ dbg_tsig("TSIG: calculated digest length and given length do not match!\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+// assert(tsig_alg_digest_length(alg) == mac_length);
+
+ dbg_tsig("TSIG: calc digest : ");
+ dbg_tsig_hex(digest_tmp, digest_tmp_len);
+
+ dbg_tsig("TSIG: given digest: ");
+ dbg_tsig_hex(tsig_mac, mac_length);
+
+ if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp,
+ mac_length) != 0) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_server_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0);
+}
+
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_client_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, request_mac,
+ request_mac_len, tsig_key, 0);
+}
+
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_key_t *tsig_key)
+{
+// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest,
+// prev_digest_len, tsig_key);
+ dbg_tsig_verb("tsig_client_check_next()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest,
+ prev_digest_len, tsig_key, 1);
+ return KNOT_ENOTSUP;
+}
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
new file mode 100644
index 0000000..b206dc7
--- /dev/null
+++ b/src/libknot/tsig-op.h
@@ -0,0 +1,161 @@
+/*!
+ * \file tsig-op.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief TSIG signing and validating.
+ *
+ * \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_TSIG_OP_H_
+#define _KNOT_TSIG_OP_H_
+
+#include <stdint.h>
+
+#include "tsig.h"
+#include "rrset.h"
+
+/*!
+ * \brief Generate TSIG signature of a message.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows to sent the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param tsig_rr RRSet containing the TSIG RR to be used. Data from the RR are
+ * appended to the signed message.
+ *
+ * \retval KNOT_EOK if everything went OK.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *request_mac, size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Generate TSIG signature of a 2nd or later message in a TCP session.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows to sent the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param tsig_rr RRSet containing the TSIG RR to be used. Data from the RR are
+ * appended to the signed message.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Checks incoming request.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_key_t *tsig_key);
+
+/*!
+ * \brief Checks incoming response.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Checks signature of 2nd or next packet in a TCP session.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_key_t *key);
+
+#endif /* _KNOT_TSIG_H_ */
+
+/*! @} */
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
new file mode 100644
index 0000000..432539f
--- /dev/null
+++ b/src/libknot/tsig.c
@@ -0,0 +1,618 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <time.h>
+
+#include "tsig.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common.h"
+#include "util/utils.h"
+#include "rrset.h"
+#include "rdata.h"
+#include "dname.h"
+
+/*! \brief TSIG algorithms table. */
+#define TSIG_ALG_TABLE_SIZE 8
+static knot_lookup_table_t tsig_alg_table[TSIG_ALG_TABLE_SIZE] = {
+ { 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." },
+ { KNOT_TSIG_ALG_HMAC_SHA224, "hmac-sha224." },
+ { KNOT_TSIG_ALG_HMAC_SHA256, "hmac-sha256." },
+ { KNOT_TSIG_ALG_HMAC_SHA384, "hmac-sha384." },
+ { KNOT_TSIG_ALG_HMAC_SHA512, "hmac-sha512." },
+ { KNOT_TSIG_ALG_NULL, NULL }
+};
+
+int tsig_rdata_init(knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ /* Initializes rdata. */
+ tsig->rdata = knot_rdata_new();
+ if (!tsig->rdata) {
+ return KNOT_ENOMEM;
+ }
+
+ tsig->rdata->items =
+ malloc(sizeof(knot_rdata_item_t) * KNOT_TSIG_ITEM_COUNT);
+ if (!tsig->rdata->items) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(tsig->rdata->items, 0,
+ sizeof(knot_rdata_item_t) * KNOT_TSIG_ITEM_COUNT);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 1);
+
+ knot_dname_t *alg_name_copy = knot_dname_deep_copy(alg_name);
+ if (!alg_name_copy) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 1);
+
+ const char *alg_str = tsig_alg_to_str(alg);
+ knot_dname_t *alg_name_copy = knot_dname_new_from_str(alg_str,
+ strlen(alg_str),
+ 0);
+ if (!alg_name_copy) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 2);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 6 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 6. */
+ wire[0] = 6;
+ knot_wire_write_u48((uint8_t *)(wire + 1), time);
+
+ knot_rdata_item_set_raw_data(rdata, 1, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 3);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), fudge);
+
+ knot_rdata_item_set_raw_data(rdata, 2, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 4);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * length + 2 * sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length. */
+ wire[0] = length + sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), length);
+ /* Copy the actual MAC. */
+ memcpy((uint8_t *)(wire + 2), mac, sizeof(uint8_t) * length);
+ knot_rdata_item_set_raw_data(rdata, 3, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 5);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), id);
+
+ knot_rdata_item_set_raw_data(rdata, 4, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 6);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), tsig_error);
+
+ knot_rdata_item_set_raw_data(rdata, 5, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 6);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * length + 2 * sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length. */
+ wire[0] = length + 2;
+ knot_wire_write_u16((uint8_t *)(wire + 1), length);
+ /* Copy the actual data. */
+ memcpy(wire + 2, other_data, sizeof(uint8_t) * length);
+ knot_rdata_item_set_raw_data(rdata, 6, wire);
+
+ return KNOT_EOK;
+}
+
+const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return NULL;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ dbg_tsig("TSIG: rdata: alg name: no rdata.\n");
+ return NULL;
+ }
+
+ if (knot_rdata_item_count(rdata) < 1) {
+ dbg_tsig("TSIG: rdata: alg name: not enough items.\n");
+ return NULL;
+ }
+
+ return knot_rdata_item(rdata, 0)->dname;
+}
+
+tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
+{
+ /*! \todo [TSIG] Implement me. */
+ return KNOT_TSIG_ALG_HMAC_MD5;
+}
+
+uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 2) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 1)->raw_data;
+ assert(wire[0] == 6);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u48((uint8_t *)wire);
+}
+
+uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 3) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 2)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 4) {
+ return 0;
+ }
+
+ return (uint8_t*)(knot_rdata_item(rdata, 3)->raw_data + 2);
+}
+
+size_t tsig_rdata_mac_length(const knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata || knot_rdata_item_count(rdata) < 4) {
+ return 0;
+ }
+
+ return knot_wire_read_u16(
+ (uint8_t *)(knot_rdata_item(rdata, 3)->raw_data + 1));
+}
+
+uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 5) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 4)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+uint16_t tsig_rdata_error(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 6) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 5)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 7) {
+ return 0;
+ }
+
+ return (uint8_t *)(knot_rdata_item(rdata, 6)->raw_data + 2);
+}
+
+uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 7) {
+ return 0;
+ }
+
+ return knot_wire_read_u16((uint8_t *)
+ (knot_rdata_item(rdata, 6)->raw_data + 1));
+}
+
+int tsig_alg_from_name(const knot_dname_t *alg_name)
+{
+ if (!alg_name) {
+ return 0;
+ }
+
+ char *name = knot_dname_to_str(alg_name);
+ if (!name) {
+ return 0;
+ }
+
+ knot_lookup_table_t *found =
+ knot_lookup_by_name(tsig_alg_table, name);
+
+ if (!found) {
+ dbg_tsig("Unknown algorithm: %s \n", name);
+ free(name);
+ return 0;
+ }
+
+ free(name);
+
+ return found->id;
+}
+
+uint16_t tsig_alg_digest_length(tsig_algorithm_t alg)
+{
+ switch (alg) {
+ case KNOT_TSIG_ALG_GSS_TSIG:
+ return KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG;
+ case KNOT_TSIG_ALG_HMAC_MD5:
+ return KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5;
+ case KNOT_TSIG_ALG_HMAC_SHA1:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA1;
+ case KNOT_TSIG_ALG_HMAC_SHA224:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA224;
+ case KNOT_TSIG_ALG_HMAC_SHA256:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA384;
+ case KNOT_TSIG_ALG_HMAC_SHA512:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA512;
+ default:
+ return 0;
+ } /* switch(alg) */
+}
+
+size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
+{
+ /* Key name, Algorithm name and Other data have variable lengths. */
+ const knot_dname_t *key_name = knot_rrset_owner(tsig);
+ if (!key_name) {
+ return 0;
+ }
+
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return 0;
+ }
+
+// dbg_tsig_detail("key_name: %.*s (size: %u) alg_name: %.*s (size: %u)\n", knot_dname_size(key_name),
+// key_name->name, alg_name->size, alg_name->name,
+// key_name->size, alg_name->size);
+
+// dbg_tsig_hex_detail(key_name->name, key_name->size);
+// dbg_tsig_hex_detail(alg_name->name, alg_name->size);
+
+ uint16_t other_data_length = tsig_rdata_other_data_length(tsig);
+
+ return knot_dname_size(key_name) + knot_dname_size(alg_name) +
+ other_data_length + KNOT_TSIG_VARIABLES_LENGTH;
+}
+
+size_t tsig_rdata_tsig_timers_length()
+{
+ return KNOT_TSIG_TIMERS_LENGTH;
+}
+
+
+int tsig_rdata_store_current_time(knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+ time_t curr_time = time(NULL);
+ /*!< \todo bleeding eyes. */
+ tsig_rdata_set_time_signed(tsig, (uint64_t)curr_time);
+ return KNOT_EOK;
+}
+
+const char* tsig_alg_to_str(tsig_algorithm_t alg)
+{
+ for (unsigned i = 0; i < TSIG_ALG_TABLE_SIZE; ++i) {
+ if (tsig_alg_table[i].id == alg) {
+ return tsig_alg_table[i].name;
+ }
+ }
+
+ return "";
+}
+
+size_t tsig_wire_maxsize(const knot_key_t* key)
+{
+ size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1;
+
+ return knot_dname_size(key->name) +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDLENGTH */
+ alg_name_size + /* Alg. name */
+ 6 * sizeof(uint8_t) + /* Time signed */
+ sizeof(uint16_t) + /* Fudge */
+ sizeof(uint16_t) + /* MAC size */
+ tsig_alg_digest_length(key->algorithm) + /* MAC */
+ sizeof(uint16_t) + /* Original ID */
+ sizeof(uint16_t) + /* Error */
+ sizeof(uint16_t) + /* Other len */
+ 6* sizeof(uint8_t); /* uint48_t in case of BADTIME RCODE */
+}
+
+size_t tsig_wire_actsize(const knot_rrset_t *tsig)
+{
+ return knot_dname_size(knot_rrset_owner(tsig)) +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDLENGTH */
+ knot_dname_size(tsig_rdata_alg_name(tsig)) +
+ 6 * sizeof(uint8_t) + /* Time signed */
+ sizeof(uint16_t) + /* Fudge */
+ sizeof(uint16_t) + /* MAC size */
+ tsig_rdata_mac_length(tsig) +
+ sizeof(uint16_t) + /* Original ID */
+ sizeof(uint16_t) + /* Error */
+ sizeof(uint16_t) + /* Other len */
+ tsig_rdata_other_data_length(tsig);
+}
+
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
new file mode 100644
index 0000000..eafcfab
--- /dev/null
+++ b/src/libknot/tsig.h
@@ -0,0 +1,145 @@
+/*!
+ * \file tsig.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief TSIG manipulation.
+ *
+ * \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_TSIG_H_
+#define _KNOT_TSIG_H_
+
+#include <stdint.h>
+
+#include "rrset.h"
+#include "util/utils.h"
+
+/* The assigned numbers should not begin with 0 - reserved for error. */
+enum tsig_algorithm {
+ KNOT_TSIG_ALG_NULL = 0,
+ KNOT_TSIG_ALG_GSS_TSIG = 128, /*!< \brief gss-tsig. */
+ KNOT_TSIG_ALG_HMAC_MD5, /*!< \brief HMAC-MD5.SIG-ALG.REG.INT. */
+ KNOT_TSIG_ALG_HMAC_SHA1, /*!< \brief hmac-sha1. */
+ KNOT_TSIG_ALG_HMAC_SHA224, /*!< \brief hmac-sha224. */
+ KNOT_TSIG_ALG_HMAC_SHA256, /*!< \brief hmac-sha256. */
+ KNOT_TSIG_ALG_HMAC_SHA384, /*!< \brief hmac-sha384. */
+ KNOT_TSIG_ALG_HMAC_SHA512 /*!< \brief hmac-sha512. */
+};
+
+typedef enum tsig_algorithm tsig_algorithm_t;
+
+struct knot_key {
+ knot_dname_t *name; /*!< Key name. */
+ tsig_algorithm_t algorithm; /*!< Key algorithm. */
+ char *secret; /*!< Key data. */
+ size_t secret_size; /*!< Key length. */
+};
+
+typedef struct knot_key knot_key_t;
+
+/*!< \todo FIND ALG LENGTHS */
+enum tsig_algorithm_digest_length {
+ KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5 = 16,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA224 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA384 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA512 = 0
+};
+
+enum tsig_consts {
+ KNOT_TSIG_ITEM_COUNT = 7,
+ KNOT_TSIG_VARIABLES_LENGTH = sizeof(uint16_t) // class
+ + sizeof(uint32_t) // ttl
+ + 6 // time signed
+ + sizeof(uint16_t) // fudge
+ + sizeof(uint16_t) // error
+ + sizeof(uint16_t),// other data length
+ KNOT_TSIG_TIMERS_LENGTH = sizeof(uint16_t) //fugde
+ + 6 // time signed
+};
+
+/*! TSIG errors are defined in util/error.h
+ * and present negative value of the TSIG error to
+ * comply with other parts of the library.
+ *
+ * KNOT_TSIG_EBADSIG = -16
+ * KNOT_TSIG_EBADKEY = -17
+ * KNOT_TSIG_EBADTIME = -18
+ */
+
+/*!
+ * \note Uses the given domain name, do not deallocate it!
+ */
+int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name);
+int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg);
+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);
+int tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *mac);
+int tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id);
+int tsig_rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error);
+int tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data);
+
+const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig);
+tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig);
+uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig);
+const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig);
+size_t tsig_rdata_mac_length(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_error(const knot_rrset_t *tsig);
+const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig);
+size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig);
+
+size_t tsig_rdata_tsig_timers_length();
+
+int tsig_alg_from_name(const knot_dname_t *name);
+
+/*!
+ * \brief Convert TSIG algorithm identifier to name.
+ *
+ * \param alg TSIG algorithm identifier.
+ *
+ * \retval TSIG algorithm string name.
+ * \retval Empty string if undefined.
+ */
+const char* tsig_alg_to_str(tsig_algorithm_t alg);
+
+uint16_t tsig_alg_digest_length(tsig_algorithm_t alg);
+
+/*!
+ * \brief Return TSIG RRSET maximum wire size for given algorithm.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t tsig_wire_maxsize(const knot_key_t *key);
+size_t tsig_wire_actsize(const knot_rrset_t *tsig);
+
+#endif /* _KNOT_TSIG_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
new file mode 100644
index 0000000..cf9e6a0
--- /dev/null
+++ b/src/libknot/updates/changesets.c
@@ -0,0 +1,296 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "updates/changesets.h"
+
+#include "rrset.h"
+#include "util/error.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_check_count(knot_rrset_t ***rrsets, size_t count,
+ size_t *allocated)
+{
+ /* Check if allocated is sufficient. */
+ if (count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* How many steps is needed to content count? */
+ size_t extra = (count - *allocated) % KNOT_CHANGESET_RRSET_STEP;
+ extra = (extra + 1) * KNOT_CHANGESET_RRSET_STEP;
+
+ /* Allocate new memory block. */
+ const size_t item_len = sizeof(knot_rrset_t *);
+ const size_t new_count = *allocated + extra;
+ knot_rrset_t **rrsets_new = malloc(new_count * item_len);
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Clear old memory block and copy old data. */
+ memset(rrsets_new, 0, new_count * item_len);
+ memcpy(rrsets_new, *rrsets, (*allocated) * item_len);
+
+ /* Replace old rrsets. */
+ free(*rrsets);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2)
+{
+ return knot_rrset_compare(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
+ && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG
+ || knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrset1))
+ == knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrset2)));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_allocate(knot_changesets_t **changesets)
+{
+ // create new changesets
+ *changesets = (knot_changesets_t *)(malloc(sizeof(knot_changesets_t)));
+ if (*changesets == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(*changesets, 0, sizeof(knot_changesets_t));
+
+ return knot_changesets_check_size(*changesets);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated,
+ knot_rrset_t *rrset)
+{
+ int ret = knot_changeset_check_count(rrsets, *count + 1, allocated);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ (*rrsets)[*count] = rrset;
+ *count = *count + 1;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
+ size_t *allocated, knot_rrset_t *rr)
+{
+ // try to find the RRSet in the list of RRSets, but search backwards
+ // as it is probable that the last RRSet is the one to which the RR
+ // belongs
+ int i = *count - 1;
+
+ while (i >= 0 && !knot_changeset_rrsets_match((*rrsets)[i], rr)) {
+ --i;
+ }
+
+ if (i >= 0) {
+ // found RRSet to merge the new one into
+ if (knot_rrset_merge((void **)&(*rrsets)[i],
+ (void **)&rr) != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ // remove the RR
+ /*! \todo does this make sense? */
+ knot_rrset_free(&rr); // used to be deep free with all 1's
+
+ return KNOT_EOK;
+ } else {
+ return knot_changeset_add_rrset(rrsets, count, allocated, rr);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_new_rr(knot_changeset_t *changeset,
+ knot_rrset_t *rrset,
+ xfrin_changeset_part_t part)
+{
+ knot_rrset_t ***rrsets = NULL;
+ size_t *count = NULL;
+ size_t *allocated = NULL;
+
+ switch (part) {
+ case XFRIN_CHANGESET_ADD:
+ rrsets = &changeset->add;
+ count = &changeset->add_count;
+ allocated = &changeset->add_allocated;
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ rrsets = &changeset->remove;
+ count = &changeset->remove_count;
+ allocated = &changeset->remove_allocated;
+ break;
+ default:
+ assert(0);
+ }
+
+ assert(rrsets != NULL);
+ assert(count != NULL);
+ assert(allocated != NULL);
+
+ int ret = knot_changeset_add_rr(rrsets, count, allocated, rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_changeset_store_soa(knot_rrset_t **chg_soa,
+ uint32_t *chg_serial, knot_rrset_t *soa)
+{
+ *chg_soa = soa;
+ *chg_serial = knot_rdata_soa_serial(knot_rrset_rdata(soa));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+ xfrin_changeset_part_t part)
+{
+ switch (part) {
+ case XFRIN_CHANGESET_ADD:
+ knot_changeset_store_soa(&changeset->soa_to,
+ &changeset->serial_to, soa);
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ knot_changeset_store_soa(&changeset->soa_from,
+ &changeset->serial_from, soa);
+ break;
+ 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 old memory block and copy old data. */
+ memset(sets, 0, new_count * item_len);
+ memcpy(sets, changesets->sets, changesets->allocated * item_len);
+
+ /* Replace old changesets. */
+ free(changesets->sets);
+ changesets->sets = sets;
+ changesets->allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_free_changeset(knot_changeset_t **changeset)
+{
+ /* XXX XXX investigate wrong frees. */
+ assert((*changeset)->add_allocated >= (*changeset)->add_count);
+ assert((*changeset)->remove_allocated >= (*changeset)->remove_count);
+ assert((*changeset)->allocated >= (*changeset)->size);
+
+ int j;
+ for (j = 0; j < (*changeset)->add_count; ++j) {
+ knot_rrset_deep_free(&(*changeset)->add[j], 1, 1, 1);
+ }
+ free((*changeset)->add);
+
+ for (j = 0; j < (*changeset)->remove_count; ++j) {
+ knot_rrset_deep_free(&(*changeset)->remove[j], 1, 1, 1);
+ }
+ free((*changeset)->remove);
+
+ knot_rrset_deep_free(&(*changeset)->soa_from, 1, 1, 1);
+ knot_rrset_deep_free(&(*changeset)->soa_to, 1, 1, 1);
+
+ free((*changeset)->data);
+
+
+ *changeset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_free_changesets(knot_changesets_t **changesets)
+{
+ 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);
+ }
+
+ free((*changesets)->sets);
+
+ knot_rrset_deep_free(&(*changesets)->first_soa, 1, 1, 1);
+
+ free(*changesets);
+ *changesets = NULL;
+}
+
+/*---------------------------------------------------------------------------*/
+
+
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
new file mode 100644
index 0000000..e8d5e39
--- /dev/null
+++ b/src/libknot/updates/changesets.h
@@ -0,0 +1,102 @@
+/*!
+ * \file changesets.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure for representing IXFR/DDNS changeset and its API.
+ *
+ * \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_CHANGESETS_H_
+#define _KNOT_CHANGESETS_H_
+
+#include "rrset.h"
+
+/*! \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;
+} knot_changeset_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ knot_changeset_t *sets;
+ size_t count;
+ size_t allocated;
+ knot_rrset_t *first_soa;
+} knot_changesets_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef enum {
+ XFRIN_CHANGESET_ADD,
+ XFRIN_CHANGESET_REMOVE
+} xfrin_changeset_part_t;
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_allocate(knot_changesets_t **changesets);
+
+int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated,
+ knot_rrset_t *rrset);
+
+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,
+ xfrin_changeset_part_t part);
+
+void knot_changeset_store_soa(knot_rrset_t **chg_soa,
+ uint32_t *chg_serial, knot_rrset_t *soa);
+
+int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+ xfrin_changeset_part_t part);
+
+int knot_changesets_check_size(knot_changesets_t *changesets);
+
+void knot_free_changeset(knot_changeset_t **changeset);
+
+void knot_free_changesets(knot_changesets_t **changesets);
+
+#endif /* _KNOT_CHANGESETS_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
new file mode 100644
index 0000000..4c6ab7b
--- /dev/null
+++ b/src/libknot/updates/ddns.c
@@ -0,0 +1,638 @@
+/* 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 "updates/ddns.h"
+#include "updates/changesets.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+#include "util/error.h"
+#include "consts.h"
+
+/*----------------------------------------------------------------------------*/
+// Copied from XFR - maybe extract somewhere else
+static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated)
+{
+ int new_count = 0;
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+
+ knot_rrset_t **rrsets_new =
+ (knot_rrset_t **)calloc(new_count, sizeof(knot_rrset_t *));
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(rrsets_new, *rrsets, *count);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_prereq_check_dnames(knot_dname_t ***dnames,
+ size_t *count, size_t *allocated)
+{
+ int new_count = 0;
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+
+ knot_dname_t **dnames_new =
+ (knot_dname_t **)calloc(new_count, sizeof(knot_dname_t *));
+ if (dnames_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(dnames_new, *dnames, *count);
+ *dnames = dnames_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset,
+ knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocd)
+{
+ // check if such RRSet is not already there and merge if needed
+ int ret;
+ for (int i = 0; i < *count; ++i) {
+ if (knot_rrset_compare(rrset, (*rrsets)[i],
+ KNOT_RRSET_COMPARE_HEADER) == 0) {
+ ret = knot_rrset_merge((void **)&((*rrsets)[i]),
+ (void **)&rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ return KNOT_EOK;
+ }
+ }
+ }
+
+ // if we are here, the RRSet was not found
+ ret = knot_ddns_prereq_check_rrsets(rrsets, count, allocd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_rrset_t *new_rrset = NULL;
+ ret = knot_rrset_deep_copy(rrset, &new_rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ (*rrsets)[(*count)++] = new_rrset;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq_dname(const knot_dname_t *dname,
+ knot_dname_t ***dnames,
+ size_t *count, size_t *allocd)
+{
+ // we do not have to check if the name is not already there
+ // if it is, we will just check it twice in the zone
+
+ int ret = knot_ddns_prereq_check_dnames(dnames, count, allocd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_dname_t *dname_new = knot_dname_deep_copy(dname);
+ if (dname_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ (*dnames)[(*count)++] = dname_new;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq(knot_ddns_prereq_t *prereqs,
+ const knot_rrset_t *rrset, uint16_t qclass)
+{
+ assert(prereqs != NULL);
+ assert(rrset != NULL);
+
+ if (knot_rrset_ttl(rrset) != 0) {
+ return KNOT_EMALF;
+ }
+
+ int ret;
+
+ if (knot_rrset_class(rrset) == KNOT_CLASS_ANY) {
+ if (knot_rrset_rdata(rrset) != NULL) {
+ return KNOT_EMALF;
+ }
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_ANY) {
+ ret = knot_ddns_add_prereq_dname(
+ knot_rrset_owner(rrset), &prereqs->in_use,
+ &prereqs->in_use_count,
+ &prereqs->in_use_allocd);
+ } else {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->exist,
+ &prereqs->exist_count,
+ &prereqs->exist_allocd);
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_NONE) {
+ if (knot_rrset_rdata(rrset) != NULL) {
+ return KNOT_EMALF;
+ }
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_ANY) {
+ ret = knot_ddns_add_prereq_dname(
+ knot_rrset_owner(rrset), &prereqs->not_in_use,
+ &prereqs->not_in_use_count,
+ &prereqs->not_in_use_allocd);
+ } else {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->not_exist,
+ &prereqs->not_exist_count,
+ &prereqs->not_exist_allocd);
+ }
+ } else if (knot_rrset_class(rrset) == qclass) {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->exist_full,
+ &prereqs->exist_full_count,
+ &prereqs->exist_full_allocd);
+ } else {
+ return KNOT_EMALF;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_update(knot_changeset_t *changeset,
+ const knot_rrset_t *rrset, uint16_t qclass)
+{
+ assert(changeset != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ // create a copy of the RRSet
+ /*! \todo If the packet was not parsed all at once, we could save this
+ * copy.
+ */
+ knot_rrset_t *rrset_copy;
+ ret = knot_rrset_deep_copy(rrset, &rrset_copy);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /*! \todo What about the SOAs? */
+
+ if (knot_rrset_class(rrset) == qclass) {
+ // this RRSet should be added to the zone
+ ret = knot_changeset_add_rr(&changeset->add,
+ &changeset->add_count,
+ &changeset->add_allocated,
+ rrset_copy);
+ } else {
+ // this RRSet marks removal of something from zone
+ // what should be removed is distinguished when applying
+ 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, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_ANY);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_ENONODE;
+ } else if (knot_node_rrset(node, knot_rrset_type(rrset)) == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_ENORRSET;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_ANY);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ const knot_rrset_t *found;
+
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset)))
+ == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ } 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),
+ knot_rrset_owner(rrset)) == 0);
+ if (knot_rrset_compare_rdata(found, rrset) <= 0) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_NONE);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ const knot_rrset_t *found;
+
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ return KNOT_EOK;
+ } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset)))
+ == NULL) {
+ return KNOT_EOK;
+ } 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),
+ knot_rrset_owner(rrset)) == 0);
+ if (knot_rrset_compare_rdata(found, rrset) <= 0) {
+ return KNOT_EOK;
+ }
+ }
+
+ *rcode = KNOT_RCODE_YXRRSET;
+ return KNOT_EPREREQ;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
+ const knot_dname_t *dname, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(dname != NULL);
+ assert(rcode != NULL);
+
+ if (!knot_dname_is_subdomain(dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+
+ node = knot_zone_contents_find_node(zone, dname);
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXDOMAIN;
+ return KNOT_EPREREQ;
+ } else if (knot_node_rrset_count(node) == 0) {
+ *rcode = KNOT_RCODE_NXDOMAIN;
+ return KNOT_EPREREQ;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
+ const knot_dname_t *dname, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(dname != NULL);
+ assert(rcode != NULL);
+
+ if (!knot_dname_is_subdomain(dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+
+ node = knot_zone_contents_find_node(zone, dname);
+ if (node == NULL) {
+ return KNOT_EOK;
+ } else if (knot_node_rrset_count(node) == 0) {
+ return KNOT_EOK;
+ }
+
+ *rcode = KNOT_RCODE_YXDOMAIN;
+ return KNOT_EPREREQ;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
+ uint8_t *rcode)
+{
+ if (zone == NULL || query == NULL || rcode == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ if(!knot_zone_contents(zone)) {
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOT_ENOZONE;
+ }
+
+ // 1) check if the zone is master or slave
+ if (!knot_zone_is_master(zone)) {
+ return KNOT_EBADZONE;
+ }
+
+ // 2) check zone CLASS
+ if (knot_zone_contents_class(knot_zone_contents(zone)) !=
+ knot_packet_qclass(query)) {
+ *rcode = KNOT_RCODE_NOTAUTH;
+ return KNOT_ENOZONE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_process_prereqs(knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+{
+ /*! \todo Consider not parsing the whole packet at once, but
+ * parsing one RR at a time - could save some memory and time.
+ */
+
+ if (query == NULL || prereqs == NULL || rcode == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // allocate space for the prerequisities
+ *prereqs = (knot_ddns_prereq_t *)calloc(1, sizeof(knot_ddns_prereq_t));
+ CHECK_ALLOC_LOG(*prereqs, KNOT_ENOMEM);
+
+ int ret;
+
+ for (int i = 0; i < knot_packet_answer_rrset_count(query); ++i) {
+ // we must copy the RRSets, because all those stored in the
+ // packet will be destroyed
+ ret = knot_ddns_add_prereq(*prereqs,
+ knot_packet_answer_rrset(query, i),
+ knot_packet_qclass(query));
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add prerequisity RRSet:%s\n",
+ knot_strerror(ret));
+ *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL;
+ knot_ddns_prereqs_free(prereqs);
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+{
+ int i, ret;
+
+ for (i = 0; i < (*prereqs)->exist_count; ++i) {
+ ret = knot_ddns_check_exist(zone, (*prereqs)->exist[i], rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->exist_full_count; ++i) {
+ ret = knot_ddns_check_exist_full(zone,
+ (*prereqs)->exist_full[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->not_exist_count; ++i) {
+ ret = knot_ddns_check_not_exist(zone, (*prereqs)->not_exist[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->in_use_count; ++i) {
+ ret = knot_ddns_check_in_use(zone, (*prereqs)->in_use[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->not_in_use_count; ++i) {
+ ret = knot_ddns_check_not_in_use(zone,
+ (*prereqs)->not_in_use[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_update(const knot_rrset_t *rrset,
+ const knot_packet_t *query, uint8_t *rcode)
+{
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_packet_qname(query))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ if (knot_rrset_class(rrset) == knot_packet_qclass(query)) {
+ if (knot_rrtype_is_metatype(knot_rrset_type(rrset))) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_ANY) {
+ if (knot_rrset_rdata(rrset) != NULL
+ || (knot_rrtype_is_metatype(knot_rrset_type(rrset))
+ && knot_rrset_type(rrset) != KNOT_RRTYPE_ANY)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_NONE) {
+ if (knot_rrset_ttl(rrset) != 0
+ || knot_rrtype_is_metatype(knot_rrset_type(rrset))) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_process_update(knot_packet_t *query,
+ knot_changeset_t **changeset, uint8_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_EBADARG;
+ }
+
+ *changeset = (knot_changeset_t *)calloc(1, sizeof(knot_changeset_t));
+ CHECK_ALLOC_LOG(*changeset, KNOT_ENOMEM);
+
+ int ret;
+
+ for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) {
+
+ const knot_rrset_t *rrset =
+ knot_packet_authority_rrset(query, i);
+
+ ret = knot_ddns_check_update(rrset, query, rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_ddns_add_update(*changeset, rrset,
+ knot_packet_qclass(query));
+
+ 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;
+ knot_free_changeset(changeset);
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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, 1);
+ }
+
+ for (i = 0; i < (*prereq)->exist_full_count; ++i) {
+ knot_rrset_deep_free(&(*prereq)->exist_full[i], 1, 1, 1);
+ }
+
+ for (i = 0; i < (*prereq)->not_exist_count; ++i) {
+ knot_rrset_deep_free(&(*prereq)->not_exist[i], 1, 1, 1);
+ }
+
+ for (i = 0; i < (*prereq)->in_use_count; ++i) {
+ knot_dname_free(&(*prereq)->in_use[i]);
+ }
+
+ for (i = 0; i < (*prereq)->not_in_use_count; ++i) {
+ knot_dname_free(&(*prereq)->not_in_use[i]);
+ }
+
+ free(*prereq);
+ *prereq = NULL;
+}
diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h
new file mode 100644
index 0000000..dceebed
--- /dev/null
+++ b/src/libknot/updates/ddns.h
@@ -0,0 +1,74 @@
+/*!
+ * \file ddns.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Dynamic updates processing.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_DDNS_H_
+#define _KNOT_DDNS_H_
+
+#include "updates/changesets.h"
+#include "zone/zone.h"
+#include "packet/packet.h"
+#include "rrset.h"
+#include "dname.h"
+
+typedef struct knot_ddns_prereq_t {
+ knot_rrset_t **exist;
+ size_t exist_count;
+ size_t exist_allocd;
+
+ knot_rrset_t **exist_full;
+ size_t exist_full_count;
+ size_t exist_full_allocd;
+
+ knot_rrset_t **not_exist;
+ size_t not_exist_count;
+ size_t not_exist_allocd;
+
+ knot_dname_t **in_use;
+ size_t in_use_count;
+ size_t in_use_allocd;
+
+ knot_dname_t **not_in_use;
+ size_t not_in_use_count;
+ size_t not_in_use_allocd;
+} knot_ddns_prereq_t;
+
+int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
+ uint8_t *rcode);
+
+int knot_ddns_process_prereqs(knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+
+int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+
+int knot_ddns_process_update(knot_packet_t *query,
+ knot_changeset_t **changeset, uint8_t *rcode);
+
+void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq);
+
+#endif /* _KNOT_DDNS_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
new file mode 100644
index 0000000..51be430
--- /dev/null
+++ b/src/libknot/updates/xfr-in.c
@@ -0,0 +1,3013 @@
+/* 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 <urcu.h>
+
+#include "updates/xfr-in.h"
+
+#include "nameserver/name-server.h"
+#include "util/wire.h"
+#include "util/debug.h"
+// #include "knot/zone/zone-dump.h"
+// #include "knot/zone/zone-load.h"
+#include "packet/packet.h"
+#include "dname.h"
+#include "zone/zone.h"
+#include "packet/query.h"
+#include "packet/response.h"
+#include "util/error.h"
+#include "updates/changesets.h"
+#include "tsig.h"
+#include "tsig-op.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+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);
+ CHECK_ALLOC_LOG(pkt, KNOT_ENOMEM);
+
+ /*! \todo Get rid of the numeric constant. */
+ int rc = knot_packet_set_max_size(pkt, 512);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ rc = knot_query_init(pkt);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ 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);
+ if (rc != KNOT_EOK) {
+ knot_dname_release(question.qname);
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ /* Reserve space for TSIG. */
+ if (use_tsig && xfr->tsig_key) {
+ dbg_xfrin_detail("xfrin: setting packet TSIG size to %zu\n",
+ xfr->tsig_size);
+ knot_packet_set_tsig_size(pkt, xfr->tsig_size);
+ }
+
+ /* Add SOA RR to authority section for IXFR. */
+ if (qtype == KNOT_RRTYPE_IXFR && soa) {
+ knot_query_add_rrset_authority(pkt, soa);
+ }
+
+ /*! \todo OPT RR ?? */
+
+ uint8_t *wire = NULL;
+ size_t wire_size = 0;
+ 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;
+ }
+
+ if (wire_size > *size) {
+ dbg_xfrin("Not enough space provided for the wire "
+ "format of the query.\n");
+ knot_packet_free(&pkt);
+ return KNOT_ESPACE;
+ }
+
+ // wire format created, sign it with TSIG if required
+ if (use_tsig && xfr->tsig_key) {
+ char *name = knot_dname_to_str(xfr->tsig_key->name);
+ dbg_xfrin_detail("Signing XFR query with key (name %s): \n",
+ name);
+ free(name);
+ dbg_xfrin_hex_detail(xfr->tsig_key->secret,
+ xfr->tsig_key->secret_size);
+
+ xfr->digest_size = xfr->digest_max_size;
+ rc = knot_tsig_sign(wire, &wire_size, *size, NULL, 0,
+ xfr->digest, &xfr->digest_size, xfr->tsig_key);
+ if (rc != KNOT_EOK) {
+ /*! \todo [TSIG] Handle TSIG errors. */
+ knot_packet_free(&pkt);
+ return rc;
+ }
+
+ dbg_xfrin_detail("Signed XFR query, new wire size: %zu, digest:"
+ "\n", wire_size);
+ dbg_xfrin_hex_detail((const char*)xfr->digest, xfr->digest_size);
+ }
+
+ memcpy(xfr->wire, wire, wire_size);
+ *size = wire_size;
+
+ dbg_xfrin("Created query of size %zu.\n", *size);
+ knot_packet_dump(pkt);
+
+ knot_packet_free(&pkt);
+
+ /* Release qname. */
+ knot_dname_release(question.qname);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size)
+{
+ /*! \todo [TSIG] Should TSIG apply for SOA query too? */
+ return xfrin_create_query(owner, KNOT_RRTYPE_SOA,
+ KNOT_CLASS_IN, xfr, size, 0, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_transfer_needed(const knot_zone_contents_t *zone,
+ knot_packet_t *soa_response)
+{
+ // first, parse the rest of the packet
+ assert(!knot_packet_is_query(soa_response));
+ dbg_xfrin("Response - parsed: %zu, total wire size: %zu\n",
+ soa_response->parsed, soa_response->size);
+ int ret;
+
+ if (soa_response->parsed < soa_response->size) {
+ ret = knot_packet_parse_rest(soa_response);
+ if (ret != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+ }
+
+ /*
+ * Retrieve the local Serial
+ */
+ const knot_rrset_t *soa_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ if (soa_rrset == NULL) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_xfrin("SOA RRSet missing in the zone %s!\n", name);
+ free(name);
+ return KNOT_ERROR;
+ }
+
+ int64_t local_serial = knot_rdata_soa_serial(
+ knot_rrset_rdata(soa_rrset));
+ if (local_serial < 0) {
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(soa_rrset));
+ dbg_xfrin("Malformed data in SOA of zone %s\n", name);
+ free(name);
+);
+ return KNOT_EMALF; // maybe some other error
+ }
+
+ /*
+ * Retrieve the remote Serial
+ */
+ // the SOA should be the first (and only) RRSet in the response
+ soa_rrset = knot_packet_answer_rrset(soa_response, 0);
+ if (soa_rrset == NULL
+ || knot_rrset_type(soa_rrset) != KNOT_RRTYPE_SOA) {
+ return KNOT_EMALF;
+ }
+
+ int64_t remote_serial = knot_rdata_soa_serial(
+ knot_rrset_rdata(soa_rrset));
+ if (remote_serial < 0) {
+ return KNOT_EMALF; // maybe some other error
+ }
+
+ return (ns_serial_compare(local_serial, remote_serial) < 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_axfr_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size, int use_tsig)
+{
+ return xfrin_create_query(owner, KNOT_RRTYPE_AXFR,
+ KNOT_CLASS_IN, xfr, size, 0, use_tsig);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_ixfr_query(const knot_zone_contents_t *zone,
+ knot_ns_xfr_t *xfr, size_t *size, int use_tsig)
+{
+ /*!
+ * \todo Implement properly.
+ */
+ knot_node_t *apex = knot_zone_contents_get_apex(zone);
+ const knot_rrset_t *soa = knot_node_rrset(apex, KNOT_RRTYPE_SOA);
+
+ return xfrin_create_query(knot_node_get_owner(apex), KNOT_RRTYPE_IXFR,
+ KNOT_CLASS_IN, xfr, size, soa, use_tsig);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t *rrsigs,
+ knot_rrset_t *rr)
+{
+ // try to find similar RRSIGs (check owner and type covered) in the list
+ assert(knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG);
+
+ int ret = 0;
+ xfrin_orphan_rrsig_t **last = &rrsigs;
+ while (*last != NULL) {
+ // check if the RRSIG is not similar to the one we want to add
+ assert((*last)->rrsig != NULL);
+ if (knot_rrset_compare((*last)->rrsig, rr,
+ KNOT_RRSET_COMPARE_HEADER) == 1
+ && knot_rdata_rrsig_type_covered(knot_rrset_rdata(
+ (*last)->rrsig))
+ == knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) {
+ ret = knot_rrset_merge((void **)&(*last)->rrsig,
+ (void **)&rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ return 1;
+ }
+ }
+ last = &((*last)->next);
+ }
+
+ assert(*last == NULL);
+ // we did not find the right RRSIGs, add to the end
+ *last = (xfrin_orphan_rrsig_t *)malloc(sizeof(xfrin_orphan_rrsig_t));
+ CHECK_ALLOC_LOG(*last, KNOT_ENOMEM);
+
+ (*last)->rrsig = rr;
+ (*last)->next = NULL;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
+ xfrin_orphan_rrsig_t *rrsigs)
+{
+ xfrin_orphan_rrsig_t **last = &rrsigs;
+ int ret = 0;
+ while (*last != NULL) {
+ knot_rrset_t *rrset = NULL;
+ knot_node_t *node = NULL;
+ ret = knot_zone_contents_add_rrsigs(zone, (*last)->rrsig,
+ &rrset, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret > 0) {
+ knot_rrset_free(&(*last)->rrsig);
+ } else if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add orphan RRSIG to zone.\n");
+ return ret;
+ } else {
+ (*last)->rrsig = NULL;
+ }
+
+ last = &((*last)->next);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
+{
+ xfrin_orphan_rrsig_t *r = *rrsigs;
+ while (r != NULL) {
+ xfrin_orphan_rrsig_t *prev = r;
+ r = r->next;
+ free(prev);
+ }
+
+ *rrsigs = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*! \note [TSIG] */
+static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
+ int tsig_req)
+{
+ assert(packet != NULL);
+ assert(xfr != NULL);
+
+ dbg_xfrin_verb("xfrin_check_tsig(): packet nr: %d, required: %d\n",
+ xfr->packet_nr, tsig_req);
+
+ /*
+ * If we are expecting it (i.e. xfr->prev_digest_size > 0)
+ * a) it should be there (first, last or each 100th packet) and it
+ * is not
+ * Then we should discard the changes and close the connection.
+ * b) it should be there and it is or it may not be there (other
+ * packets) and it is
+ * We validate the TSIG and reset packet number counting and
+ * data aggregation.
+ *
+ * If we are not expecting it (i.e. xfr->prev_digest_size <= 0) and
+ * it is there => it should probably be considered an error
+ */
+ knot_rrset_t *tsig = NULL;
+ int ret = knot_packet_parse_next_rr_additional(packet, &tsig);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (xfr->tsig_key) {
+ if (tsig_req && tsig == NULL) {
+ // TSIG missing!!
+ return KNOT_EMALF;
+ } else if (tsig != NULL) {
+ // TSIG there, either required or not, process
+ if (xfr->packet_nr == 0) {
+ ret = knot_tsig_client_check(tsig,
+ xfr->wire, xfr->wire_size,
+ xfr->digest, xfr->digest_size,
+ xfr->tsig_key);
+ } else {
+ ret = knot_tsig_client_check_next(tsig,
+ xfr->wire, xfr->wire_size,
+ xfr->digest, xfr->digest_size,
+ xfr->tsig_key);
+ }
+
+ if (ret != KNOT_EOK) {
+ /*! \note [TSIG] No need to check TSIG error
+ * here, propagate and check elsewhere.*/
+ return ret;
+ }
+
+ // and reset the data storage
+ //xfr->packet_nr = 1;
+ xfr->tsig_data_size = 0;
+
+ // Extract the digest from the TSIG RDATA and store it.
+ if (xfr->digest_max_size < tsig_rdata_mac_length(tsig)) {
+ return KNOT_ESPACE;
+ }
+ memcpy(xfr->digest, tsig_rdata_mac(tsig),
+ tsig_rdata_mac_length(tsig));
+ xfr->digest_size = tsig_rdata_mac_length(tsig);
+
+ } else { // TSIG not required and not there
+ // just append the wireformat to the TSIG data
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size,
+ xfr->wire, xfr->wire_size);
+ xfr->tsig_data_size += xfr->wire_size;
+ }
+ } else if (tsig != NULL) {
+ // TSIG where it should not be
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
+ xfrin_constructed_zone_t **constr*/
+ knot_ns_xfr_t *xfr)
+{
+ const uint8_t *pkt = xfr->wire;
+ size_t size = xfr->wire_size;
+ xfrin_constructed_zone_t **constr =
+ (xfrin_constructed_zone_t **)(&xfr->data);
+
+ if (pkt == NULL || constr == NULL) {
+ dbg_xfrin("Wrong parameters supported.\n");
+ return KNOT_EBADARG;
+ }
+
+ dbg_xfrin("Processing AXFR packet of size %zu.\n", size);
+
+ // check if the response is OK
+ if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ return KNOT_EXFRREFUSED;
+ }
+
+ /*! \todo Should TC bit be checked? */
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (packet == NULL) {
+ dbg_xfrin("Could not create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_parse_from_wire(packet, pkt, size, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse packet: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ /*! \todo [TSIG] If packet RCODE is NOTAUTH(9), process as TSIG error. */
+
+ knot_rrset_t *rr = NULL;
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse first Answer RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ if (rr == NULL) {
+ dbg_xfrin("No RRs in the packet.\n");
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ /*! \todo We should probably test whether the Question of the first
+ * message corresponds to the SOA RR.
+ */
+
+ knot_node_t *node = NULL;
+ int in_zone = 0;
+ knot_zone_contents_t *zone = NULL;
+
+ if (*constr == NULL) {
+ // this should be the first packet
+ /*! \note [TSIG] Packet number for checking TSIG validation. */
+ xfr->packet_nr = 0;
+ /*! \note [TSIG] Storing total size of data for TSIG digest. */
+ xfr->tsig_data_size = 0;
+
+ // create new zone
+ /*! \todo Ensure that the packet is the first one. */
+ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ dbg_xfrin("No zone created, but the first RR in "
+ "Answer is not a SOA RR.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_compare(knot_rrset_owner(rr),
+ knot_packet_qname(packet)) != 0) {
+dbg_xfrin_exec(
+ char *rr_owner =
+ knot_dname_to_str(knot_rrset_owner(rr));
+ char *qname = knot_dname_to_str(
+ knot_packet_qname(packet));
+
+ dbg_xfrin("Owner of the first SOA RR (%s) does not"
+ " match QNAME (%s).\n", rr_owner, qname);
+
+ free(rr_owner);
+ free(qname);
+);
+ /*! \todo Cleanup. */
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_EMALF;
+ }
+
+ node = knot_node_new(rr->owner, NULL, 0);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node.\n");
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ // the first RR is SOA and its owner and QNAME are the same
+ // create the zone
+
+ *constr = (xfrin_constructed_zone_t *)malloc(
+ sizeof(xfrin_constructed_zone_t));
+ if (*constr == NULL) {
+ dbg_xfrin("Failed to create new constr. zone.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ memset(*constr, 0, sizeof(xfrin_constructed_zone_t));
+
+ (*constr)->contents = knot_zone_contents_new(node, 0, 1, NULL);
+// assert(0);
+ if ((*constr)->contents== NULL) {
+ dbg_xfrin("Failed to create new zone.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 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_node_add_rrset(node, rr, 0);
+ ret = knot_zone_contents_add_rrset(zone, rr, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to zone node: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ dbg_xfrin("Merged SOA RRSet.\n");
+ // merged, free the RRSet
+ //knot_rrset_deep_free(&rr, 1, 0, 0);
+ knot_rrset_free(&rr);
+ }
+
+ // take next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ } else {
+ zone = (*constr)->contents;
+ ++xfr->packet_nr;
+ }
+
+ /*! \note [TSIG] add the packet wire size to the data to be verified by
+ * TSIG
+ */
+ if (xfr->tsig_key) {
+ dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu,"
+ " adding: %zu).\n", xfr->tsig_data_size,
+ xfr->wire_size);
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire,
+ xfr->wire_size);
+ xfr->tsig_data_size += xfr->wire_size;
+ }
+
+ assert(zone != NULL);
+
+ while (ret == KNOT_EOK && rr != NULL) {
+ // process the parsed RR
+
+ dbg_xfrin("\nNext RR:\n\n");
+ knot_rrset_dump(rr, 0);
+
+ if (node != NULL
+ && knot_dname_compare(rr->owner, node->owner) != 0) {
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Node owner: %s\n", name);
+ free(name);
+);
+ if (!in_zone) {
+ // this should not happen
+ assert(0);
+ // the node is not in the zone and the RR has
+ // other owner, so a new node must be created
+ // insert the old node to the zone
+ }
+
+ node = NULL;
+ }
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ // this must be the last SOA, do not do anything more
+ // discard the RR
+ assert(knot_zone_contents_apex((zone)) != NULL);
+ assert(knot_node_rrset(knot_zone_contents_apex((zone)),
+ KNOT_RRTYPE_SOA) != NULL);
+ dbg_xfrin("Found last SOA, transfer finished.\n");
+
+ dbg_xfrin("Verifying TSIG...\n");
+ /*! \note [TSIG] Now check if there is not a TSIG record
+ * at the end of the packet.
+ */
+ ret = xfrin_check_tsig(packet, xfr, 1);
+
+ dbg_xfrin_detail("xfrin_check_tsig() returned %d\n",
+ ret);
+
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+
+ if (ret != KNOT_EOK) {
+ /*! \todo [TSIG] Handle TSIG errors. */
+ return ret;
+ }
+
+ // we must now find place for all orphan RRSIGs
+ ret = xfrin_process_orphan_rrsigs(zone,
+ (*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;
+ }
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG) {
+ // RRSIGs require special handling, as there are no
+ // nodes for them
+ knot_rrset_t *tmp_rrset = NULL;
+ ret = knot_zone_contents_add_rrsigs(zone, rr,
+ &tmp_rrset, &node, KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret == KNOT_ENONODE || ret == KNOT_ENORRSET) {
+ dbg_xfrin("No node or RRSet for RRSIGs\n");
+ dbg_xfrin("Saving for later insertion.\n");
+ ret = xfrin_add_orphan_rrsig((*constr)->rrsigs,
+ rr);
+ if (ret > 0) {
+ dbg_xfrin("Merged RRSIGs.\n");
+ knot_rrset_free(&rr);
+ } else if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to save orphan"
+ " RRSIGs.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return ret;
+ }
+ } else if (ret < 0) {
+ dbg_xfrin("Failed to add RRSIGs (%s).\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR; /*! \todo Other error code. */
+ } else if (ret == 1) {
+ assert(node != NULL);
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Found node for the record in "
+ "zone: %s.\n", name);
+ free(name);
+);
+ in_zone = 1;
+ knot_rrset_deep_free(&rr, 1, 0, 0);
+ } else if (ret == 2) {
+ // should not happen
+ assert(0);
+// knot_rrset_deep_free(&rr, 1, 1, 1);
+ } else {
+ assert(node != NULL);
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Found node for the record in "
+ "zone: %s.\n", name);
+ free(name);
+);
+ in_zone = 1;
+ assert(tmp_rrset->rrsigs == rr);
+ }
+
+ // parse next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+
+ continue;
+ }
+
+ /*! \note [TSIG] TSIG where it should not be - in Answer section.*/
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_TSIG) {
+ // not allowed here
+ dbg_xfrin(" in Answer section.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_EMALF;
+ }
+
+ knot_node_t *(*get_node)(const knot_zone_contents_t *,
+ const knot_dname_t *) = NULL;
+ int (*add_node)(knot_zone_contents_t *, knot_node_t *, int,
+ uint8_t, int) = NULL;
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3) {
+ get_node = knot_zone_contents_get_nsec3_node;
+ add_node = knot_zone_contents_add_nsec3_node;
+ } else {
+ get_node = knot_zone_contents_get_node;
+ add_node = knot_zone_contents_add_node;
+ }
+
+ if (node == NULL && (node = get_node(zone,
+ knot_rrset_owner(rr))) != NULL) {
+ // the node for this RR was found in the zone
+ dbg_xfrin("Found node for the record in zone.\n");
+ in_zone = 1;
+ }
+
+ if (node == NULL) {
+ // a new node for the RR is required but it is not
+ // in the zone
+ node = knot_node_new(rr->owner, NULL, 0);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node.\n");
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+ dbg_xfrin("Created new node for the record.\n");
+
+ // insert the RRSet to the node
+ ret = knot_node_add_rrset(node, rr, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to node (%s"
+ ")\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ // should not happen, this is new node
+ assert(0);
+// knot_rrset_deep_free(&rr, 1, 0, 0);
+ }
+
+ // insert the node into the zone
+ ret = add_node(zone, node, 1, 0, 1);
+ assert(node != NULL);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add node to zone (%s)"
+ ".\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR;
+ }
+
+ in_zone = 1;
+ } else {
+ assert(in_zone);
+
+ ret = knot_zone_contents_add_rrset(zone, rr, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to zone:"
+ "%s.\n", knot_strerror(ret));
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ // merged, free the RRSet
+// knot_rrset_deep_free(&rr, 1, 0, 0);
+ knot_rrset_free(&rr);
+ }
+
+ }
+
+ rr = NULL;
+
+ // parse next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ }
+
+ assert(ret != KNOT_EOK || rr == NULL);
+
+ if (ret < 0) {
+ // some error in parsing
+ dbg_xfrin("Could not parse next RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ assert(ret == KNOT_EOK);
+ assert(rr == NULL);
+
+ // if the last node is not yet in the zone, insert
+ if (!in_zone) {
+ assert(node != NULL);
+ ret = knot_zone_contents_add_node(zone, node, 1, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add last node into zone (%s)"
+ ".\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0);
+ return KNOT_ERROR; /*! \todo Other error */
+ }
+ }
+
+ /*! \note [TSIG] Now check if there is not a TSIG record at the end of
+ * the packet.
+ */
+ ret = xfrin_check_tsig(packet, xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ ++xfr->packet_nr;
+
+ knot_packet_free(&packet);
+ dbg_xfrin("Processed one AXFR packet successfully.\n");
+
+ /*! \note [TSIG] TSIG errors are propagated and reported in a standard
+ * manner, as we're in response processing, no further error response
+ * should be sent.
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt,
+ size_t size, knot_rrset_t **rr)
+{
+ *packet = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (packet == NULL) {
+ dbg_xfrin("Could not create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_parse_from_wire(*packet, pkt, size, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse packet: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ // check if the TC bit is set (it must not be)
+ if (knot_wire_get_tc(pkt)) {
+ dbg_xfrin("IXFR response has TC bit set.\n");
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ ret = knot_packet_parse_next_rr_answer(*packet, rr);
+
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse first Answer RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
+ knot_changesets_t **chs*/knot_ns_xfr_t *xfr)
+{
+ size_t size = xfr->wire_size;
+ const uint8_t *pkt = xfr->wire;
+ knot_changesets_t **chs = (knot_changesets_t **)(&xfr->data);
+
+ if (pkt == NULL || chs == NULL) {
+ dbg_xfrin("Wrong parameters supported.\n");
+ return KNOT_EBADARG;
+ }
+
+ // check if the response is OK
+ if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ return KNOT_EXFRREFUSED;
+ }
+
+ knot_packet_t *packet = NULL;
+// knot_rrset_t *soa1 = NULL;
+// knot_rrset_t *soa2 = NULL;
+ knot_rrset_t *rr = NULL;
+
+ int ret;
+
+ if ((ret = xfrin_parse_first_rr(&packet, pkt, size, &rr)) != KNOT_EOK) {
+ return ret;
+ }
+
+ assert(packet != NULL);
+
+ // state of the transfer
+ // -1 .. a SOA is expected to create a new changeset
+ int state = 0;
+
+ if (rr == NULL) {
+ dbg_xfrin("No RRs in the packet.\n");
+ knot_packet_free(&packet);
+ /*! \todo Some other action??? */
+ return KNOT_EMALF;
+ }
+
+ if (*chs == NULL) {
+ dbg_xfrin("Changesets empty, creating new.\n");
+
+ ret = knot_changeset_allocate(chs);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ knot_packet_free(&packet);
+ return ret;
+ }
+
+ // 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, 1);
+ ret = KNOT_EMALF;
+ goto cleanup;
+ }
+
+ // just store the first SOA for later use
+ (*chs)->first_soa = rr;
+ state = -1;
+
+ dbg_xfrin("First SOA of IXFR saved, state set to -1.\n");
+
+ // parse the next one
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /*
+ * If there is no other records in the response than the SOA, it
+ * means one of these two cases:
+ *
+ * 1) The server does not have newer zone than ours.
+ * This is indicated by serial equal to the one of our zone.
+ * 2) The server wants to send the transfer but is unable to fit
+ * it in the packet. This is indicated by serial different
+ * (newer) from the one of our zone.
+ *
+ * The serials must be compared in other parts of the server, so
+ * just indicate that the answer contains only one SOA.
+ */
+ if (rr == NULL) {
+ dbg_xfrin("Response containing only SOA,\n");
+ 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, 1);
+ dbg_xfrin("Fallback to AXFR.\n");
+ ret = XFRIN_RES_FALLBACK;
+ knot_free_changesets(chs);
+ xfr->data = 0;
+ return ret;
+ }
+ } else {
+ if ((*chs)->first_soa == NULL) {
+ dbg_xfrin("Changesets don't contain frist SOA!\n");
+ ret = KNOT_EBADARG;
+ goto cleanup;
+ }
+ dbg_xfrin("Changesets present.\n");
+ }
+
+ /*
+ * Process the next RR. Different requirements are in place in
+ * different cases:
+ *
+ * 1) Last changeset has both soa_from and soa_to.
+ * a) The next RR is a SOA.
+ * i) The next RR is equal to the first_soa saved in changesets.
+ * This denotes the end of the transfer. It may be dropped and
+ * the end should be signalised by returning positive value.
+ *
+ * ii) The next RR is some other SOA.
+ * This means a start of new changeset - create it and add it
+ * to the list.
+ *
+ * b) The next RR is not a SOA.
+ * Put the RR into the ADD part of the last changeset as this is
+ * not finished yet. Continue while SOA is not encountered. Then
+ * jump to 1-a.
+ *
+ * 2) Last changeset has only the soa_from and does not have soa_to.
+ * a) The next RR is a SOA.
+ * This means start of the ADD section. Put the SOA to the
+ * changeset. Continue adding RRs to the ADD section while SOA
+ * is not encountered. This is identical to 1-b.
+ *
+ * b) The next RR is not a SOA.
+ * This means the REMOVE part is not finished yet. Add the RR to
+ * the REMOVE part. Continue adding next RRs until a SOA is
+ * encountered. Then jump to 2-a.
+ */
+
+ // first, find out in what state we are
+ /*! \todo It would be more elegant to store the state in the
+ * changesets structure, or in some place persistent between
+ * calls to this function.
+ */
+ if (state != -1) {
+ dbg_xfrin("State is not -1, deciding...\n");
+ // there should be at least one started changeset right now
+ if ((*chs)->count <= 0) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ ret = KNOT_EMALF;
+ goto cleanup;
+ }
+
+ // a changeset should be created only when there is a SOA
+ assert((*chs)->sets[(*chs)->count - 1].soa_from != NULL);
+
+ if ((*chs)->sets[(*chs)->count - 1].soa_to == NULL) {
+ state = XFRIN_CHANGESET_REMOVE;
+ } else {
+ state = XFRIN_CHANGESET_ADD;
+ }
+ }
+
+ dbg_xfrin("State before the loop: %d\n", state);
+
+ /*! \todo This may be implemented with much less IFs! */
+
+ while (ret == KNOT_EOK && rr != NULL) {
+dbg_xfrin_exec(
+ dbg_xfrin("Next loop, state: %d\n", state);
+ char *name = knot_dname_to_str(knot_rrset_owner(rr));
+ dbg_xfrin("Actual RR: %s, type %s.\n", name,
+ knot_rrtype_to_string(knot_rrset_type(rr)));
+ free(name);
+);
+ switch (state) {
+ case -1:
+ // a SOA is expected
+ // this may be either a start of a changeset or the
+ // last SOA (in case the transfer was empty, but that
+ // is quite weird in fact
+ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ dbg_xfrin("First RR is not a SOA RR!\n");
+ dbg_xfrin("RR type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rr)));
+ ret = KNOT_EMALF;
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ if (knot_rdata_soa_serial(knot_rrset_rdata(rr))
+ == knot_rdata_soa_serial(
+ knot_rrset_rdata((*chs)->first_soa))) {
+
+ /*! \note [TSIG] Check TSIG, we're at the end of
+ * transfer.
+ */
+ ret = xfrin_check_tsig(packet, xfr, 1);
+
+ // last SOA, discard and end
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ knot_packet_free(&packet);
+
+ /*! \note [TSIG] If TSIG validates, consider
+ * transfer complete. */
+ if (ret == KNOT_EOK) {
+ ret = XFRIN_RES_COMPLETE;
+ }
+
+ return ret;
+ } else {
+ // normal SOA, start new changeset
+ (*chs)->count++;
+ if ((ret = knot_changesets_check_size(*chs))
+ != KNOT_EOK) {
+ (*chs)->count--;
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ ret = knot_changeset_add_soa(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_REMOVE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ // change state to REMOVE
+ state = XFRIN_CHANGESET_REMOVE;
+ }
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ // if the next RR is SOA, store it and change state to
+ // ADD
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ // we should not be here if soa_from is not set
+ assert((*chs)->sets[(*chs)->count - 1].soa_from
+ != NULL);
+
+ ret = knot_changeset_add_soa(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_ADD);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ state = XFRIN_CHANGESET_ADD;
+ } else {
+ // just add the RR to the REMOVE part and
+ // continue
+ if ((ret = knot_changeset_add_new_rr(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_REMOVE)) != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+ }
+ break;
+ case XFRIN_CHANGESET_ADD:
+ // if the next RR is SOA change to state -1 and do not
+ // parse next RR
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ state = -1;
+ continue;
+ } else {
+
+ // just add the RR to the ADD part and continue
+ if ((ret = knot_changeset_add_new_rr(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_ADD)) != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ // parse the next RR
+ dbg_xfrin("Parsing next RR..\n");
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ dbg_xfrin("Returned %d, %p.\n", ret, rr);
+ }
+
+ /*! \note Check TSIG, we're at the end of packet. It may not be
+ * required.
+ */
+ ret = xfrin_check_tsig(packet, xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ dbg_xfrin_detail("xfrin_check_tsig() returned %d\n", ret);
+ ++xfr->packet_nr;
+
+ /*! \note [TSIG] Cleanup and propagate error if TSIG validation fails.*/
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ // here no RRs remain in the packet but the transfer is not finished
+ // yet, return EOK
+ knot_packet_free(&packet);
+ return KNOT_EOK;
+
+cleanup:
+ /* We should go here only if some error occured. */
+ assert(ret < 0);
+
+ dbg_xfrin("Cleanup after processing IXFR/IN packet.\n");
+ knot_free_changesets(chs);
+ knot_packet_free(&packet);
+ xfr->data = 0;
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Applying changesets to zone */
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ /*!
+ * Deleted (without owners and RDATA) after successful update.
+ */
+ knot_rrset_t **old_rrsets;
+ int old_rrsets_count;
+ int old_rrsets_allocated;
+
+ /*!
+ * Deleted after successful update.
+ */
+ knot_rdata_t **old_rdata;
+ uint *old_rdata_types;
+ int old_rdata_count;
+ int old_rdata_allocated;
+
+ /*!
+ * \brief Copied RRSets (i.e. modified by the update).
+ *
+ * Deleted (without owners and RDATA) after failed update.
+ */
+ knot_rrset_t **new_rrsets;
+ int new_rrsets_count;
+ int new_rrsets_allocated;
+
+ /*!
+ * Deleted (without contents) after successful update.
+ */
+ knot_node_t **old_nodes;
+ int old_nodes_count;
+ int old_nodes_allocated;
+
+ /*!
+ * Deleted (without contents) after failed update.
+ */
+ knot_node_t **new_nodes;
+ int new_nodes_count;
+ int new_nodes_allocated;
+
+ ck_hash_table_item_t **old_hash_items;
+ int old_hash_items_count;
+ int old_hash_items_allocated;
+} xfrin_changes_t;
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_changes_free(xfrin_changes_t **changes)
+{
+ free((*changes)->old_nodes);
+ free((*changes)->old_rrsets);
+ free((*changes)->old_rdata);
+ free((*changes)->old_rdata_types);
+ free((*changes)->new_rrsets);
+ free((*changes)->new_nodes);
+ free((*changes)->old_hash_items);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_rrsets(knot_rrset_t ***rrsets,
+ int *count, int *allocated, int to_add)
+{
+ /* Ensure at least requested size is allocated. */
+ int new_count = (*count + to_add);
+ assert(new_count >= 0);
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate new memory block. */
+ knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *));
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* 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;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_nodes(knot_node_t ***nodes,
+ int *count, int *allocated)
+{
+ assert(nodes != NULL);
+ assert(count != NULL);
+ assert(allocated != 0);
+
+ /* Ensure at least count and some reserve is allocated. */
+ int new_count = *count + 2;
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* 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;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types,
+ int count, int *allocated, int to_add)
+{
+ /* Ensure at least requested size is allocated. */
+ int new_count = (count + to_add);
+ assert(new_count >= 0);
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate new memory block. */
+ knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *));
+ if (rdatas_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ uint *types_new = malloc(new_count * sizeof(uint));
+ if (types_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Initialize new memory and copy old data. */
+ memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *));
+ memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *));
+
+ memset(types_new, 0, new_count * sizeof(uint));
+ memcpy(types_new, *types, (*allocated) * sizeof(uint));
+
+ /* Free old rdatas and switch pointers. */
+ free(*rdatas);
+ free(*types);
+ *rdatas = rdatas_new;
+ *types = types_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_hash_items(ck_hash_table_item_t ***items,
+ int *count, int *allocated)
+{
+ /* Prevent infinite loop in case of allocated = 0. */
+ int new_count = 0;
+ if (*allocated == 0) {
+ new_count = *count + 1;
+ } else {
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+ }
+
+ const size_t item_len = sizeof(ck_hash_table_item_t *);
+ ck_hash_table_item_t **items_new = malloc(new_count * item_len);
+ if (items_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(items_new, 0, new_count * item_len);
+ memcpy(items_new, *items, (*count) * item_len);
+ free(*items);
+ *items = items_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
+{
+ /*! \todo This should be all in some API!! */
+
+ if ((*contents)->table != NULL) {
+// ck_destroy_table(&(*contents)->table, NULL, 0);
+ ck_table_free(&(*contents)->table);
+ }
+
+ // free the zone tree, but only the structure
+ // (nodes are already destroyed)
+ dbg_zone("Destroying zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nodes);
+ dbg_zone("Destroying NSEC3 zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nsec3_nodes);
+
+ knot_nsec3_params_free(&(*contents)->nsec3_params);
+
+ knot_dname_table_deep_free(&(*contents)->dname_table);
+
+ free(*contents);
+ *contents = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_rollback_update(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ /*
+ * This function is called only when no references were actually set to
+ * the new nodes, just the new nodes reference other.
+ * We thus do not need to fix any references, just from the old nodes
+ * to the new ones.
+ */
+
+ // discard new nodes, but do not remove RRSets from them
+ for (int i = 0; i < changes->new_nodes_count; ++i) {
+ knot_node_free(&changes->new_nodes[i], 0, 0);
+ }
+
+ // set references from old nodes to new nodes to NULL and remove the
+ // old flag
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ knot_node_set_new_node(changes->old_nodes[i], NULL);
+ knot_node_clear_old(changes->old_nodes[i]);
+ }
+
+ // discard new RRSets
+ for (int i = 0; i < changes->old_rrsets_count; ++i) {
+ knot_rrset_deep_free(&changes->new_rrsets[i], 0, 1, 0);
+ }
+
+ // destroy the shallow copy of zone
+ xfrin_zone_contents_free(&contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
+ const knot_rrset_t *what)
+{
+ knot_rdata_t *old = NULL;
+ knot_rdata_t *old_actual = NULL;
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(what);
+
+ while (rdata != NULL) {
+ old_actual = knot_rrset_remove_rdata(from, rdata);
+ if (old_actual != NULL) {
+ old_actual->next = old;
+ old = old_actual;
+ }
+ rdata = knot_rrset_rdata_next(what, rdata);
+ }
+
+ return old;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_get_node_copy(knot_node_t **node, xfrin_changes_t *changes)
+{
+ knot_node_t *new_node =
+ knot_node_get_new_node(*node);
+ if (new_node == NULL) {
+ dbg_xfrin("Creating copy of node.\n");
+ int ret = knot_node_shallow_copy(*node, &new_node);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create node copy.\n");
+ return KNOT_ENOMEM;
+ }
+
+ dbg_xfrin_detail("Created copy of old node %p to new node %p\n",
+ *node, new_node);
+
+ assert(changes);
+
+// changes->new_nodes_allocated = 0;
+
+ // save the copy of the node
+ ret = xfrin_changes_check_nodes(
+ &changes->new_nodes,
+ &changes->new_nodes_count,
+ &changes->new_nodes_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new node to list.\n");
+ knot_node_free(&new_node, 0, 0);
+ return ret;
+ }
+
+// changes->old_nodes_allocated = 0;
+
+ // save the old node to list of old nodes
+ ret = xfrin_changes_check_nodes(
+ &changes->old_nodes,
+ &changes->old_nodes_count,
+ &changes->old_nodes_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old node to list.\n");
+ knot_node_free(&new_node, 0, 0);
+ return ret;
+ }
+
+ assert(changes->new_nodes);
+ assert(changes->old_nodes);
+
+ changes->new_nodes[changes->new_nodes_count++] = new_node;
+ changes->old_nodes[changes->old_nodes_count++] = *node;
+
+ // mark the old node as old
+ knot_node_set_old(*node);
+
+ knot_node_set_new(new_node);
+ knot_node_set_new_node(*node, new_node);
+ }
+
+ *node = new_node;
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
+ xfrin_changes_t *changes)
+{
+ // create new RRSet by copying the old one
+ int ret = knot_rrset_shallow_copy(old, copy);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create RRSet copy.\n");
+ return KNOT_ENOMEM;
+ }
+
+ // add the RRSet to the list of new RRSets
+ ret = xfrin_changes_check_rrsets(&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");
+ knot_rrset_free(copy);
+ return ret;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = *copy;
+
+ // add the old RRSet to the list of old RRSets
+ ret = xfrin_changes_check_rrsets(&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;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = old;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
+ knot_rrset_t **rrset, xfrin_changes_t *changes)
+{
+ knot_rrset_t *old = knot_node_remove_rrset(node, type);
+
+ if (old == NULL) {
+ dbg_xfrin("RRSet not found for RR to be removed.\n");
+ return 1;
+ }
+
+ int ret = xfrin_copy_old_rrset(old, rrset, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_xfrin_detail("Copied old rrset %p to new %p.\n",
+ old, *rrset);
+
+ // replace the RRSet in the node copy by the new one
+ ret = knot_node_add_rrset(node, *rrset, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet copy to node\n");
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_rrsigs(xfrin_changes_t *changes,
+ const knot_rrset_t *remove,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(remove != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+ assert(knot_rrset_type(remove) == KNOT_RRTYPE_RRSIG);
+
+ /*! \todo These optimalizations may be useless as there may be only
+ * one RRSet of each type and owner in the changeset.
+ */
+
+ int ret;
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove))) {
+ // find RRSet based on the Type Covered
+ knot_rr_type_t type = knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove));
+
+ // copy the rrset
+ ret = xfrin_copy_rrset(node, type, rrset, changes);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to copy rrset from changeset.\n");
+ return ret;
+ }
+ } else {
+ // we should have the right RRSIG RRSet in *rrset
+ assert(knot_rrset_type(*rrset)
+ == knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove)));
+ // this RRSet should be the already copied RRSet so we may
+ // update it right away
+ }
+
+ // get the old rrsigs
+ knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset);
+ if (old == NULL) {
+ return 1;
+ }
+
+ // copy the RRSIGs
+ /*! \todo This may be done unnecessarily more times. */
+ knot_rrset_t *rrsigs;
+ ret = xfrin_copy_old_rrset(old, &rrsigs, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // set the RRSIGs to the new RRSet copy
+ if (knot_rrset_set_rrsigs(*rrset, rrsigs) != KNOT_EOK) {
+ dbg_xfrin("Failed to set rrsigs.\n");
+ return KNOT_ERROR;
+ }
+
+
+
+ // now in '*rrset' we have a copy of the RRSet which holds the RRSIGs
+ // and in 'rrsigs' we have the copy of the RRSIGs
+
+ knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove);
+ if (rdata == NULL) {
+ dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
+ knot_strerror(ret));
+ return 1;
+ }
+
+ // 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
+ if (knot_rrset_rdata(rrsigs) == NULL) {
+ // remove the RRSIGs from the RRSet
+ knot_rrset_set_rrsigs(*rrset, NULL);
+
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ 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(&rrsigs);
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
+
+ // now check if the RRSet is not totally empty
+ if (knot_rrset_rdata(*rrset) == NULL) {
+ 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 = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ 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;
+ }
+ }
+
+ // connect the RDATA to the list of old RDATA
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ changes->old_rdata[changes->old_rdata_count] = rdata;
+ changes->old_rdata_types[changes->old_rdata_count] =
+ knot_rrset_type(remove);
+ ++changes->old_rdata_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_normal(xfrin_changes_t *changes,
+ const knot_rrset_t *remove,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(remove != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ dbg_xfrin_detail("Removing RRSet: \n");
+ knot_rrset_dump(remove, 0);
+
+ // now we have the copy of the node, so lets get the right RRSet
+ // check if we do not already have it
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset)
+ != knot_rrset_type(remove)) {
+ /*!
+ * \todo This may happen also with already
+ * copied RRSet. In that case it would be
+ * an unnecesary overhead but will
+ * probably not cause problems. TEST!!
+ */
+ ret = xfrin_copy_rrset(node,
+ knot_rrset_type(remove), rrset, changes);
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (*rrset == NULL) {
+ dbg_xfrin("RRSet not found for RR to be removed.\n");
+ return 1;
+ }
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Updating RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+
+ // remove the specified RRs from the RRSet (de facto difference of
+ // sets)
+ knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove);
+ if (rdata == NULL) {
+ dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
+ knot_strerror(ret));
+ return 1;
+ }
+
+dbg_xfrin_exec_detail(
+ dbg_xfrin_detail("Removed rdata: \n");
+ knot_rdata_t *r = rdata;
+ if (r != NULL) {
+ do {
+ dbg_xfrin_detail("pointer: %p\n", r);
+ knot_rdata_dump(r, knot_rrset_type(remove), 0);
+ r = r->next;
+ } while (r != NULL && r != rdata);
+ }
+);
+
+ // 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
+ if (knot_rrset_rdata(*rrset) == NULL
+ && knot_rrset_rrsigs(*rrset) == NULL) {
+
+ knot_rrset_t *tmp = knot_node_remove_rrset(node,
+ knot_rrset_type(*rrset));
+ dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp);
+
+ // add the removed RRSet to list of old RRSets
+
+ assert(tmp == *rrset);
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ 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;
+ }
+
+ // connect the RDATA to the list of old RDATA
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ changes->old_rdata[changes->old_rdata_count] = rdata;
+ changes->old_rdata_types[changes->old_rdata_count] =
+ knot_rrset_type(remove);
+ ++changes->old_rdata_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_all_rrsets(xfrin_changes_t *changes,
+ knot_node_t *node, uint16_t type)
+{
+ /*! \todo Implement. */
+ int ret;
+
+ if (type == KNOT_RRTYPE_ANY) {
+ // put all the RRSets to the changes structure
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ knot_node_rrset_count(node));
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to check changeset rrsets.\n");
+ return ret;
+ }
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ knot_rrset_t **place = changes->old_rrsets
+ + changes->old_rrsets_count;
+ /*! \todo Test this!!! */
+ memcpy(place, rrsets, knot_node_rrset_count(node) * sizeof(knot_rrset_t *));
+
+ // remove all RRSets from the node
+ knot_node_remove_all_rrsets(node);
+ } else {
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to check changeset rrsets.\n");
+ return ret;
+ }
+ // remove only RRSet with the given type
+ knot_rrset_t *rrset = knot_node_remove_rrset(node, type);
+ changes->old_rrsets[changes->old_rrsets_count++] = rrset;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ xfrin_changes_t *changes)
+{
+ /*
+ * Iterate over removed RRSets, copy appropriate nodes and remove
+ * the rrsets from them. By default, the RRSet should be copied so that
+ * RDATA may be removed from it.
+ */
+ int ret = 0;
+ knot_node_t *node = NULL;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < chset->remove_count; ++i) {
+ // check if the old node is not the one we should use
+ if (!node || knot_rrset_owner(chset->remove[i])
+ != knot_node_owner(node)) {
+ node = knot_zone_contents_get_node(contents,
+ knot_rrset_owner(chset->remove[i]));
+ if (node == NULL) {
+ dbg_xfrin("Node not found for RR to be removed"
+ "!\n");
+ continue;
+ }
+ }
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ ret = xfrin_get_node_copy(&node, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ assert(node != NULL);
+ assert(knot_node_is_new(node));
+
+ // first check if all RRSets should be removed
+ if (knot_rrset_class(chset->remove[i]) == KNOT_CLASS_ANY) {
+ ret = xfrin_apply_remove_all_rrsets(
+ changes, node,
+ knot_rrset_type(chset->remove[i]));
+ } else if (knot_rrset_type(chset->remove[i])
+ == KNOT_RRTYPE_RRSIG) {
+ // this should work also for UPDATE
+ ret = xfrin_apply_remove_rrsigs(changes,
+ chset->remove[i],
+ node, &rrset);
+ } else {
+ // this should work also for UPDATE
+ ret = xfrin_apply_remove_normal(changes,
+ chset->remove[i],
+ node, &rrset);
+ }
+
+ dbg_xfrin("xfrin_apply_remove() ret = %d\n", ret);
+
+ if (ret > 0) {
+ continue;
+ } else if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
+ knot_rrset_t *rrset)
+{
+ /*! \todo Why is the function disabled? */
+ //return NULL;
+
+ knot_node_t *node = knot_node_new(knot_rrset_get_owner(rrset),
+ NULL, KNOT_NODE_FLAGS_NEW);
+ 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
+// dbg_xfrin("Adding new node to zone. From owner: %s type %s\n",
+// knot_dname_to_str(node->owner),
+// knot_rrtype_to_string(rrset->type));
+// getchar();
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) {
+ ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0,
+ 1);
+ } else {
+ ret = knot_zone_contents_add_node(contents, node, 1,
+ KNOT_NODE_FLAGS_NEW, 1);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new node to zone contents.\n");
+ return NULL;
+ }
+
+ // find previous node and connect the new one to it
+ knot_node_t *prev = NULL;
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) {
+ prev = knot_zone_contents_get_previous_nsec3(contents,
+ knot_rrset_owner(rrset));
+ } else {
+ prev = knot_zone_contents_get_previous(contents,
+ knot_rrset_owner(rrset));
+ }
+
+ // fix prev and next pointers
+ if (prev != NULL) {
+ knot_node_set_previous(node, prev);
+ }
+
+// printf("contents owned by: %s (%p)\n",
+// knot_dname_to_str(contents->apex->owner),
+// contents);
+ assert(contents->zone != NULL);
+ knot_node_set_zone(node, contents->zone);
+
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add_normal(xfrin_changes_t *changes,
+ knot_rrset_t *add,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(add != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ dbg_xfrin("applying rrset:\n");
+ knot_rrset_dump(add, 0);
+// getchar();
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset)
+ != knot_rrset_type(add)) {
+ dbg_xfrin("Removing rrset!\n");
+ *rrset = knot_node_remove_rrset(node, knot_rrset_type(add));
+ }
+
+ dbg_xfrin("Removed RRSet: \n");
+ knot_rrset_dump(*rrset, 1);
+
+ if (*rrset == NULL) {
+dbg_xfrin_exec_verb(
+ char *name = knot_dname_to_str(add->owner);
+ dbg_xfrin_verb("RRSet to be added not found in zone.\n");
+ dbg_xfrin_verb("owner: %s type: %s\n", name,
+ knot_rrtype_to_string(add->type));
+ free(name);
+);
+// getchar();
+ // 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??
+ */
+ ret = knot_node_add_rrset(node, add, 0);
+// ret = knot_zone_contents_add_rrset(node->zone->contents,
+// rrset, node,
+// KNOT_RRSET_DUPL_MERGE,
+// 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+ return 1; // return 1 to indicate the add RRSet was used
+ }
+
+ knot_rrset_t *old = *rrset;
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+ knot_rrset_dump(*rrset, 1);
+ ret = xfrin_copy_old_rrset(old, rrset, changes);
+ if (ret != KNOT_EOK) {
+ assert(0);
+ return ret;
+ }
+
+// dbg_xfrin("After copy: Found RRSet with owner %s, type %s\n",
+// knot_dname_to_str((*rrset)->owner),
+// knot_rrtype_to_string(knot_rrset_type(*rrset)));
+
+ // merge the changeset RRSet to the copy
+ /* What if the update fails?
+ * The changesets will be destroyed - that will destroy 'add',
+ * and the copied RRSet will be destroyed because it is in the new
+ * rrsets list.
+ *
+ * If the update is successfull, the old RRSet will be destroyed,
+ * but the one from the changeset will be not!!
+ *
+ * TODO: add the 'add' rrset to list of old RRSets?
+ */
+ dbg_xfrin("Merging RRSets with owners: %s %s types: %d %d\n",
+ (*rrset)->owner->name, add->owner->name, (*rrset)->type,
+ add->type);
+ dbg_xfrin_detail("RDATA in RRSet1: %p, RDATA in RRSet2: %p\n",
+ (*rrset)->rdata, add->rdata);
+ ret = knot_rrset_merge((void **)rrset, (void **)&add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to merge changeset RRSet to copy.\n");
+ return KNOT_ERROR;
+ }
+ dbg_xfrin("Merge returned: %d\n", ret);
+ knot_rrset_dump(*rrset, 1);
+ ret = knot_node_add_rrset(node, *rrset, 0);
+
+ // return 2 so that the add RRSet is removed from
+ // the changeset (and thus not deleted)
+ // and put to list of new RRSets (is this ok?)
+ // and deleted
+ return 2;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add_rrsig(xfrin_changes_t *changes,
+ knot_rrset_t *add,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(add != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+ assert(knot_rrset_type(add) == KNOT_RRTYPE_RRSIG);
+
+ int ret;
+
+ knot_rr_type_t type = knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(add));
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(add))) {
+ // copy the rrset
+ ret = xfrin_copy_rrset(node, type, rrset, changes);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ // we should have the right RRSIG RRSet in *rrset
+ assert(knot_rrset_type(*rrset) == type);
+ // this RRSet should be the already copied RRSet so we may
+ // update it right away
+ }
+
+ if (*rrset == NULL) {
+ dbg_xfrin("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_rrset_class(add),
+ knot_rrset_ttl(add));
+ if (*rrset == NULL) {
+ dbg_xfrin("Failed to create new RRSet for RRSIGs.\n");
+ return KNOT_ENOMEM;
+ }
+
+ // add the RRSet from the changeset to the node
+ ret = knot_node_add_rrset(node, *rrset, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+ }
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+
+ if (knot_rrset_rrsigs(*rrset) == NULL) {
+ ret = knot_rrset_set_rrsigs(*rrset, add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSIGs to the RRSet.\n");
+ return KNOT_ERROR;
+ }
+
+ return 1;
+ } else {
+ knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset);
+ assert(old != NULL);
+ knot_rrset_t *rrsig;
+
+ ret = xfrin_copy_old_rrset(old, &rrsig, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // replace the old RRSIGs with the new ones
+ knot_rrset_set_rrsigs(*rrset, rrsig);
+
+ // merge the changeset RRSet to the copy
+ /*! \todo What if the update fails?
+ *
+ */
+ ret = knot_rrset_merge((void **)&rrsig, (void **)&add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to merge changeset RRSet to copy.\n");
+ return KNOT_ERROR;
+ }
+
+ return 2;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ xfrin_changes_t *changes)
+{
+ // iterate over removed RRSets, copy appropriate nodes and remove
+ // the rrsets from them
+ int ret = 0;
+ knot_node_t *node = NULL;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < chset->add_count; ++i) {
+ dbg_xfrin_detail("Adding RRSet:\n");
+ knot_rrset_dump(chset->add[i], 0);
+ // check if the old node is not the one we should use
+ if (!node || knot_rrset_owner(chset->add[i])
+ != knot_node_owner(node)) {
+ node = knot_zone_contents_get_node(contents,
+ knot_rrset_owner(chset->add[i]));
+ if (node == NULL) {
+ // create new node, connect it properly to the
+ // zone nodes
+ dbg_xfrin("Creating new node from.\n");
+ node = xfrin_add_new_node(contents,
+ chset->add[i]);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node "
+ "in zone.\n");
+ return KNOT_ERROR;
+ }
+// continue; // continue with another RRSet
+ }
+ }
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ xfrin_get_node_copy(&node, changes);
+ }
+
+ assert(node != NULL);
+ assert(knot_node_is_new(node));
+
+ if (knot_rrset_type(chset->add[i]) == KNOT_RRTYPE_RRSIG) {
+ ret = xfrin_apply_add_rrsig(changes, chset->add[i],
+ node, &rrset);
+ } else {
+ ret = xfrin_apply_add_normal(changes, chset->add[i],
+ node, &rrset);
+ }
+
+ dbg_xfrin("xfrin_apply_..() returned %d, rrset: %p\n", ret,
+ rrset);
+
+ if (ret == 1) {
+ // the ADD RRSet was used, i.e. it should be removed
+ // from the changeset and saved in the list of new
+ // RRSets
+ ret = xfrin_changes_check_rrsets(
+ &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");
+ return ret;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] =
+ chset->add[i];
+
+ chset->add[i] = NULL;
+ } 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
+ // as these were merged to the copied RRSet
+ knot_rrset_free(&chset->add[i]);
+ } else if (ret != KNOT_EOK) {
+
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \todo This must be tested!! Simulate failure somehow.
+ */
+static void xfrin_clean_changes_after_fail(xfrin_changes_t *changes)
+{
+ /* 1) Delete copies of RRSets created because they were updated.
+ * Do not delete their RDATA or owners.
+ */
+ for (int i = 0; i < changes->new_rrsets_count; ++i) {
+ knot_rrset_free(&changes->new_rrsets[i]);
+ }
+
+ /* 2) Delete copies of nodes created because they were updated.
+ * Do not delete their RRSets.
+ */
+ for (int i = 0; i < changes->new_nodes_count; ++i) {
+ knot_node_free(&changes->new_nodes[i], 0, 1);
+ }
+
+ // changesets will be deleted elsewhere
+ // so just delete the changes structure
+ xfrin_changes_free(&changes);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_replace_soa(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes,
+ knot_changeset_t *chset)
+{
+ knot_node_t *node = knot_zone_contents_get_apex(contents);
+ assert(node != NULL);
+
+ int ret = 0;
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ ret = xfrin_get_node_copy(&node, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ assert(knot_node_is_new(node));
+
+ // set the node copy as the apex of the contents
+ contents->apex = node;
+
+ // remove the SOA RRSet from the apex
+ knot_rrset_t *rrset = knot_node_remove_rrset(node, KNOT_RRTYPE_SOA);
+ assert(rrset != NULL);
+
+ // add the old RRSet to the list of old RRSets
+ ret = xfrin_changes_check_rrsets(&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 SOA RDATA, because RDATA are not deleted with the
+ // RRSet
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RDATA to list.\n");
+ return ret;
+ }
+
+ // save the SOA to the new RRSet, so that it is deleted if the
+ // apply fails
+ ret = xfrin_changes_check_rrsets(&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");
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrset;
+
+ /*! \todo Maybe check if the SOA does not have more RDATA? */
+ changes->old_rdata[changes->old_rdata_count] = rrset->rdata;
+ changes->old_rdata_types[changes->old_rdata_count] = KNOT_RRTYPE_SOA;
+ ++changes->old_rdata_count;
+
+ // insert the new SOA RRSet to the node
+ dbg_xfrin_verb("Adding SOA.\n");
+ ret = knot_node_add_rrset(node, chset->soa_to, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = chset->soa_to;
+
+ // remove the SOA from the changeset, so it will not be deleted after
+ // successful apply
+ chset->soa_to = NULL;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_changeset(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes,
+ knot_changeset_t *chset)
+{
+ /*
+ * Applies one changeset to the zone. Checks if the changeset may be
+ * applied (i.e. the origin SOA (soa_from) has the same serial as
+ * SOA in the zone apex.
+ */
+
+ // check if serial matches
+ /*! \todo Only if SOA is present? */
+ const knot_rrset_t *soa = knot_node_rrset(contents->apex,
+ KNOT_RRTYPE_SOA);
+ if (soa == NULL || knot_rdata_soa_serial(knot_rrset_rdata(soa))
+ != chset->serial_from) {
+ dbg_xfrin("SOA serials do not match!!\n");
+ return KNOT_ERROR;
+ }
+
+ int ret = xfrin_apply_remove(contents, chset, changes);
+ if (ret != KNOT_EOK) {
+ xfrin_clean_changes_after_fail(changes);
+ return ret;
+ }
+
+ ret = xfrin_apply_add(contents, chset, changes);
+ if (ret != KNOT_EOK) {
+ xfrin_clean_changes_after_fail(changes);
+ return ret;
+ }
+
+ /*! \todo Only if SOA is present? */
+ return xfrin_apply_replace_soa(contents, changes, chset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_check_node_in_tree(knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(data != NULL);
+ assert(tnode->node != NULL);
+
+ xfrin_changes_t *changes = (xfrin_changes_t *)data;
+
+ knot_node_t *node = knot_node_get_new_node(tnode->node);
+
+ if (node == NULL) {
+ // no RRSets were removed from this node, thus it cannot be
+ // empty
+ return;
+ }
+
+ dbg_xfrin("xfrin_check_node_in_tree: children of old node: %u, "
+ "children of new node: %u.\n",
+ knot_node_children(node),
+ knot_node_children(tnode->node));
+
+
+ // check if the node is empty and has no children
+ // to be sure, check also the count of children of the old node
+ if (knot_node_rrset_count(node) == 0
+ && knot_node_children(node) == 0
+ && knot_node_children(tnode->node) == 0) {
+ // in this case the new node copy should be removed
+ // but it cannot be deleted because if a rollback happens,
+ // the node must be in the new nodes list
+ // just add it to the old nodes list so that it is deleted
+ // after successful update
+
+ // set the new node of the old node to NULL
+ knot_node_set_new_node(tnode->node, NULL);
+
+ // if the parent has a new copy, decrease the number of
+ // children of that copy
+ if (knot_node_new_node(knot_node_parent(node, 0))) {
+ /*! \todo Replace by some API. */
+ --node->parent->new_node->children;
+ }
+
+ // put the new node to te list of old nodes
+ if (xfrin_changes_check_nodes(&changes->old_nodes,
+ &changes->old_nodes_count,
+ &changes->old_nodes_allocated)
+ != KNOT_EOK) {
+ /*! \todo Notify about the error!!! */
+ return;
+ }
+
+ changes->old_nodes[changes->old_nodes_count++] = node;
+
+ // leave the old node in the old node list, we will delete
+ // it later
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_finalize_remove_nodes(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ assert(contents != NULL);
+ assert(changes != NULL);
+
+ knot_node_t *node;
+ knot_zone_tree_node_t *removed;
+ ck_hash_table_item_t *rem_hash;
+ int ret;
+
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ node = changes->old_nodes[i];
+
+ // if the node is marked as old and has no new node copy
+ // remove it from the zone structure but do not delete it
+ // that may be done only after the grace period
+ if (knot_node_is_old(node)
+ && knot_node_new_node(node) == NULL) {
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_NSEC3)
+ != NULL) {
+ ret = knot_zone_contents_remove_nsec3_node(
+ contents, node, &removed);
+ } else {
+ ret = knot_zone_contents_remove_node(
+ contents, node, &removed, &rem_hash);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to remove node from zone"
+ "!\n");
+ return KNOT_ENONODE;
+ }
+
+ assert(removed != NULL);
+ assert(removed->node == node);
+ // delete the tree node (not needed)
+ free(removed);
+
+ if (rem_hash != NULL) {
+ // save the removed hash table item
+ ret = xfrin_changes_check_hash_items(
+ &changes->old_hash_items,
+ &changes->old_hash_items_count,
+ &changes->old_hash_items_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to save the hash"
+ " table item to list of "
+ "old items.\n");
+ return ret;
+ }
+ changes->old_hash_items[
+ changes->old_hash_items_count++]
+ = rem_hash;
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_finalize_contents(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ // don't know what should have been done here, except for one thing:
+ // walk through the zone and remove empty nodes (save them in the
+ // old nodes list). But only those having no children!!!
+
+ /*
+ * Walk through the zone and remove empty nodes.
+ * We must walk backwards, so that children are processed before
+ * their parents. This will allow to remove chain of parent-children
+ * nodes.
+ * We cannot remove the nodes right away as it would modify the very
+ * structure used for walking through the zone. Just put the nodes
+ * to the list of old nodes to be removed.
+ * We must also decrease the node's parent's children count now
+ * and not when deleting the node, so that the chain of parent-child
+ * nodes may be removed.
+ */
+ knot_zone_tree_t *t = knot_zone_contents_get_nodes(contents);
+ assert(t != NULL);
+
+ // walk through the zone and select nodes to be removed
+ knot_zone_tree_reverse_apply_postorder(t, xfrin_check_node_in_tree,
+ (void *)changes);
+
+ // Do the same with NSEC3 nodes.
+ t = knot_zone_contents_get_nsec3_nodes(contents);
+ assert(t != NULL);
+
+ knot_zone_tree_reverse_apply_postorder(t, xfrin_check_node_in_tree,
+ (void *)changes);
+
+ // remove the nodes one by one
+ return xfrin_finalize_remove_nodes(contents, changes);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_refs_in_node(knot_zone_tree_node_t *tnode, void *data)
+{
+ /*! \todo Passed data is always seto to NULL. */
+ assert(tnode != NULL);
+ //assert(data != NULL);
+
+ //xfrin_changes_t *changes = (xfrin_changes_t *)data;
+
+ // 1) Fix the reference to the node to the new one if there is some
+ knot_node_t *node = tnode->node;
+
+ knot_node_t *new_node = knot_node_get_new_node(node);
+ if (new_node != NULL) {
+ //assert(knot_node_rrset_count(new_node) > 0);
+ node = new_node;
+ tnode->node = new_node;
+ }
+
+ // 2) fix references from the node remaining in the zone
+ knot_node_update_refs(node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_gen_in_node(knot_zone_tree_node_t *tnode, void *data)
+{
+ /*! \todo Passed data is always seto to NULL. */
+ assert(tnode != NULL);
+
+ knot_node_t *node = tnode->node;
+
+ knot_node_set_old(node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_hash_refs(ck_hash_table_item_t *item, void *data)
+{
+ if (item == NULL) {
+ return;
+ }
+
+ knot_node_t *new_node = knot_node_get_new_node(
+ (knot_node_t *)item->value);
+ if (new_node != NULL) {
+ assert(item->key_length
+ == knot_dname_size(knot_node_owner(new_node)));
+ assert(strncmp(item->key, (const char *)knot_dname_name(
+ knot_node_owner(new_node)), item->key_length) == 0);
+ item->value = (void *)new_node;
+ item->key = (const char *)knot_dname_name(
+ knot_node_owner(new_node));
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_dname_refs(knot_dname_t *dname, void *data)
+{
+ UNUSED(data);
+ knot_dname_update_node(dname);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_fix_references(knot_zone_contents_t *contents)
+{
+ /*! \todo This function must not fail!! */
+
+ /*
+ * Now the contents are already switched, and we should update all
+ * references not updated yet, so that the old contents may be removed.
+ *
+ * Walk through the zone tree, so that each node will be checked
+ * and updated.
+ */
+ // fix references in normal nodes
+ knot_zone_tree_t *tree = knot_zone_contents_get_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_refs_in_node,
+ NULL);
+
+ // fix refereces in NSEC3 nodes
+ tree = knot_zone_contents_get_nsec3_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_refs_in_node,
+ NULL);
+
+ // fix references in hash table
+ ck_hash_table_t *table = knot_zone_contents_get_hash_table(contents);
+ ck_apply(table, xfrin_fix_hash_refs, NULL);
+
+ // fix references dname table
+ int ret = knot_zone_contents_dname_table_apply(contents,
+ xfrin_fix_dname_refs, NULL);
+ assert(ret == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_fix_generation(knot_zone_contents_t *contents)
+{
+ assert(contents != NULL);
+
+ knot_zone_tree_t *tree = knot_zone_contents_get_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_gen_in_node,
+ NULL);
+
+ tree = knot_zone_contents_get_nsec3_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_gen_in_node,
+ NULL);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_cleanup_update(xfrin_changes_t *changes)
+{
+ // free old nodes but do not destroy their RRSets
+ // remove owners also, because of reference counting
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ dbg_xfrin_detail("Deleting old node: %p\n", changes->old_nodes[i]);
+ knot_node_dump(changes->old_nodes[i], 0);
+ knot_node_free(&changes->old_nodes[i], 1, 0);
+ }
+
+ // free old RRSets, and destroy also domain names in them
+ // because of reference counting
+
+ // check if there are not some duplicate RRSets
+// for (int i = 0; i < changes->old_rrsets_count; ++i) {
+// for (int j = i + 1; j < changes->old_rrsets_count; ++j) {
+// if (changes->old_rrsets[i] == changes->old_rrsets[j]) {
+// assert(0);
+// }
+// if (changes->old_rrsets[i]->rdata != NULL
+// && changes->old_rrsets[i]->rdata
+// == changes->old_rrsets[j]->rdata) {
+// assert(0);
+// }
+// }
+// }
+
+ for (int i = 0; i < changes->old_rrsets_count; ++i) {
+// knot_rrset_deep_free(&changes->old_rrsets[i], 1, 1, 1);
+ dbg_xfrin_detail("Deleting old RRSet: %p\n", changes->old_rrsets[i]);
+ knot_rrset_dump(changes->old_rrsets[i], 0);
+ knot_rrset_free(&changes->old_rrsets[i]);
+ }
+
+ // delete old RDATA
+ for (int i = 0; i < changes->old_rdata_count; ++i) {
+ dbg_xfrin_detail("Deleting old RDATA: %p, type: %s\n",
+ changes->old_rdata[i],
+ knot_rrtype_to_string(changes->old_rdata_types[i]));
+ knot_rdata_dump(changes->old_rdata[i], changes->old_rdata_types[i], 0);
+ knot_rdata_t *rdata = changes->old_rdata[i];
+ assert(rdata != NULL);
+ do {
+ knot_rdata_t *tmp = rdata->next;
+ knot_rdata_deep_free(&rdata,
+ changes->old_rdata_types[i], 1);
+ rdata = tmp;
+ } while (rdata != NULL && rdata != changes->old_rdata[i]);
+ changes->old_rdata[i] = NULL;
+ }
+
+ // free old hash table items, but do not touch their contents
+ for (int i = 0; i < changes->old_hash_items_count; ++i) {
+ free(changes->old_hash_items[i]);
+ }
+ free(changes->old_hash_items);
+
+ // free allocated arrays of nodes and rrsets
+ free(changes->new_nodes);
+ free(changes->old_nodes);
+ free(changes->new_rrsets);
+ free(changes->old_rrsets);
+ free(changes->old_rdata);
+ free(changes->old_rdata_types);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_apply_changesets_to_zone(knot_zone_t *zone,
+ knot_changesets_t *chsets)
+{
+ if (zone == NULL || chsets == NULL || chsets->count == 0) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_contents_t *old_contents = knot_zone_get_contents(zone);
+ if (!old_contents) {
+ return KNOT_EBADARG;
+ }
+
+// dbg_xfrin("\nOLD ZONE CONTENTS:\n\n");
+// knot_zone_contents_dump(old_contents, 1);
+
+ /*
+ * Ensure that the zone generation is set to 0.
+ */
+ if (!knot_zone_contents_gen_is_old(old_contents)) {
+ // this would mean that a previous update was not completed
+ // abort
+ dbg_zone("Trying to apply changesets to zone that is "
+ "being updated. Aborting.\n");
+ return KNOT_EAGAIN;
+ }
+
+ /*
+ * Create a shallow copy of the zone, so that the structures may be
+ * updated.
+ *
+ * This will create new zone contents structures (normal nodes' tree,
+ * NSEC3 tree, hash table, domain name table), but fill them with the
+ * data from the old contents.
+ */
+ knot_zone_contents_t *contents_copy = NULL;
+
+ int ret = knot_zone_contents_shallow_copy(old_contents,
+ &contents_copy);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create shallow copy of zone: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Now, apply one changeset after another until all are applied.
+ * Changesets may be either from IXFR or from a dynamic update.
+ * Dynamic updates use special TYPE and CLASS values to distinguish
+ * requests, such as "remove all RRSets from a node", "remove all RRs
+ * with the specified type from a node", etc.
+ *
+ * When updating anything within some node (removing RR, adding RR),
+ * the node structure is copied, but the RRSets within are not.
+ *
+ * 1) When removing RRs from node, The affected RRSet is copied. This
+ * it also a 'shallow copy', i.e. the RDATA remain the exact same.
+ * The specified RRs (i.e. RDATA) are then removed from the copied
+ * RRSet.
+ * 2) When adding RRs to node, there are two cases:
+ * a) If there is a RRSet that should contain these RRs
+ * this RRSet is copied (shallow copy) and the RRs are added to
+ * it (rrset_merge()).
+ * b) If there is not such a RRSet, the whole RRSet from the
+ * changeset is added to the new node (thus this RRSet must not
+ * be deleted afterwards).
+ *
+ * A special case are RRSIG RRs. These functions assume that they
+ * are grouped together in knot_rrset_t structures according to
+ * their header (owner, type, class) AND their 'type covered', i.e.
+ * there may be more RRSIG RRSets in one changeset (while there
+ * should not be more RRSets of any other type).
+ * 3) When removing RRSIG RRs from node, the appropriate RRSet holding
+ * them must be found (according to the 'type covered' field). This
+ * RRSet is then copied (shallow copy), Its RRSIGs are also copied
+ * and the RRSIG RRs are added to the RRSIG copy.
+ * 4) When adding RRSIG RRs to node, the same process is done - the
+ * proper RRSet holding them is found, copied, its RRSIGs are
+ * copied (if there are some) and the RRs are added to the copy.
+ *
+ * When a node is copied, reference to the copy is stored within the
+ * old node (node_t.old_node). This is important, because when the
+ * zone contents are switched to the new ones, references from old nodes
+ * that should point to new nodes are not yet set (it would influence
+ * replying from the old zone contents). While all these references
+ * (such as node_t.prev, node_t.next, node_t.parent, etc.) are properly
+ * modified, the search functions use old or new nodes accordingly
+ * (old nodes while old contents are used, new nodes when new contents
+ * are used). The 'check_version' parameter turns on this behaviour in
+ * search functions.
+ *
+ * In case of error, we must remove all data created by the update, i.e.
+ * - new nodes,
+ * - new RRSets,
+ * and remove the references to the new nodes from old nodes.
+ *
+ * In case of success, the RRSet structures from the changeset
+ * structures must not be deleted, as they are either already used by
+ * the server (stored within the new zone contents) or deleted when
+ * cleaning up the temporary 'changes' structure.
+ */
+ xfrin_changes_t changes;
+ memset(&changes, 0, sizeof(xfrin_changes_t));
+
+ for (int i = 0; i < chsets->count; ++i) {
+ if ((ret = xfrin_apply_changeset(contents_copy, &changes,
+ &chsets->sets[i])) != KNOT_EOK) {
+ xfrin_rollback_update(contents_copy, &changes);
+ dbg_xfrin("Failed to apply changesets to zone: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+ }
+
+ /*
+ * When all changesets are applied, set generation 1 to the copy of
+ * the zone so that new nodes are used instead of old ones.
+ */
+// knot_zone_contents_switch_generation(contents_copy);
+ //contents_copy->generation = 1;
+ knot_zone_contents_set_gen_new(contents_copy);
+
+ /*
+ * Finalize the zone contents.
+ */
+ ret = xfrin_finalize_contents(contents_copy, &changes);
+ if (ret != KNOT_EOK) {
+ xfrin_rollback_update(contents_copy, &changes);
+ dbg_xfrin("Failed to finalize new zone contents: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Switch the zone contents
+ */
+ knot_zone_contents_t *old =
+ knot_zone_switch_contents(zone, contents_copy);
+ assert(old == old_contents);
+
+ /*
+ * From now on, the new contents of the zone are being used.
+ * References to nodes may be updated in the meantime. However, we must
+ * traverse the zone and fix all references that were not.
+ */
+ /*! \todo This operation must not fail!!! .*/
+ ret = xfrin_fix_references(contents_copy);
+ assert(ret == KNOT_EOK);
+
+ // set generation to finished
+ knot_zone_contents_set_gen_new_finished(contents_copy);
+
+ // set generation of all nodes to the old one
+ // now it is safe (no old nodes should be referenced)
+ ret = xfrin_fix_generation(contents_copy);
+ assert(ret == KNOT_EOK);
+
+ /*
+ * Now we may also set the generation back to 0 so that another
+ * update is possible.
+ */
+ knot_zone_contents_set_gen_old(contents_copy);
+
+ /*
+ * Wait until all readers finish reading
+ */
+ synchronize_rcu();
+
+ /*
+ * Delete all old and unused data.
+ */
+ xfrin_zone_contents_free(&old_contents);
+ xfrin_cleanup_update(&changes);
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
new file mode 100644
index 0000000..8a7c64b
--- /dev/null
+++ b/src/libknot/updates/xfr-in.h
@@ -0,0 +1,184 @@
+/*!
+ * \file xfr-in.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief XFR client API.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_XFR_IN_H_
+#define _KNOT_XFR_IN_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "zone/zone.h"
+#include "packet/packet.h"
+#include "nameserver/name-server.h"
+#include "updates/changesets.h"
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct xfrin_orphan_rrsig {
+ knot_rrset_t *rrsig;
+ struct xfrin_orphan_rrsig *next;
+} xfrin_orphan_rrsig_t;
+
+typedef struct xfrin_constructed_zone {
+ knot_zone_contents_t *contents;
+ xfrin_orphan_rrsig_t *rrsigs;
+} xfrin_constructed_zone_t;
+
+typedef enum xfrin_transfer_result {
+ XFRIN_RES_COMPLETE = 1,
+ XFRIN_RES_SOA_ONLY = 2,
+ XFRIN_RES_FALLBACK = 3
+} xfrin_transfer_result_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates normal query for the given zone name and the SOA type.
+ *
+ * \param owner Zone owner.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size);
+
+/*!
+ * \brief Checks if a zone transfer is required by comparing the zone's SOA with
+ * the one received from master server.
+ *
+ * \param zone Zone to check.
+ * \param soa_response Response to SOA query received from master server.
+ *
+ * \retval < 0 if an error occured.
+ * \retval 1 if the transfer is needed.
+ * \retval 0 if the transfer is not needed.
+ */
+int xfrin_transfer_needed(const knot_zone_contents_t *zone,
+ knot_packet_t *soa_response);
+
+/*!
+ * \brief Creates normal query for the given zone name and the AXFR type.
+ *
+ * \param owner Zone owner.
+ * \param xfr Data structure holding important data for the query, namely
+ * pointer to the buffer for wireformat and TSIG data.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ * \param use_tsig If TSIG should be used.
+ *
+ * \todo Parameter use_tsig probably not needed.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_axfr_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size, int use_tsig);
+
+/*!
+ * \brief Creates normal query for the given zone name and the IXFR type.
+ *
+ * \param zone Zone contents.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ * \param use_tsig If TSIG should be used.
+ *
+ * \todo Parameter use_tsig probably not needed.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_ixfr_query(const knot_zone_contents_t *zone,
+ knot_ns_xfr_t *xfr, size_t *size, int use_tsig);
+
+/*!
+ * \brief Processes the newly created transferred zone.
+ *
+ * \param nameserver Name server to update.
+ * \param zone Zone build from transfer.
+ *
+ * \retval KNOT_ENOTSUP
+ */
+int xfrin_zone_transferred(knot_nameserver_t *nameserver,
+ knot_zone_contents_t *zone);
+
+/*!
+ * \brief Processes one incoming packet of AXFR transfer by updating the given
+ * zone.
+ *
+ * \param pkt Incoming packet in wire format.
+ * \param size Size of the packet in bytes.
+ * \param zone Zone being built. If there is no such zone (i.e. this is the
+ * first packet, \a *zone may be set to NULL, in which case a new
+ * zone structure is created).
+ *
+ * \retval KNOT_EOK
+ *
+ * \todo Refactor!!!
+ */
+int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
+ xfrin_constructed_zone_t **zone*/
+ knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Destroys the whole changesets structure.
+ *
+ * Frees all RRSets present in the changesets and all their data. Also frees
+ * the changesets structure and sets the parameter to NULL.
+ *
+ * \param changesets Changesets to destroy.
+ */
+void xfrin_free_changesets(knot_changesets_t **changesets);
+
+/*!
+ * \brief Parses IXFR reply packet and fills in the changesets structure.
+ *
+ * \param pkt Packet containing the IXFR reply in wire format.
+ * \param size Size of the packet in bytes.
+ * \param changesets Changesets to be filled in.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \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_apply_changesets_to_zone(knot_zone_t *zone,
+ knot_changesets_t *chsets);
+
+#endif /* _KNOTXFR_IN_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/debug.c b/src/libknot/util/debug.c
new file mode 100644
index 0000000..0ca67c9
--- /dev/null
+++ b/src/libknot/util/debug.c
@@ -0,0 +1,233 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "util/utils.h"
+#include "util/debug.h"
+#include "libknot.h"
+#include "common/print.h"
+
+void knot_rdata_dump(knot_rdata_t *rdata, uint32_t type, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_RDATA_DEBUG)
+ fprintf(stderr, " ------- RDATA -------\n");
+ if (rdata == NULL) {
+ fprintf(stderr, " There are no rdata in this RRset!\n");
+ fprintf(stderr, " ------- RDATA -------\n");
+ return;
+ }
+ knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+ char *name;
+
+ for (int i = 0; i < rdata->count; i++) {
+ if (rdata->items[i].raw_data == NULL) {
+ continue;
+ }
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ) {
+ assert(rdata->items[i].dname != NULL);
+ name = knot_dname_to_str(rdata->items[i].dname);
+ fprintf(stderr, " DNAME: %d: %s\n",
+ i, name);
+ free(name);
+ if (loaded_zone) {
+ if (rdata->items[i].dname->node) {
+ name =
+ knot_dname_to_str(rdata->items[i].dname->node->owner);
+ fprintf(stderr, " Has node owner: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, " No node set\n");
+ }
+ }
+ fprintf(stderr, " labels: ");
+ hex_print((char *)rdata->items[i].dname->labels,
+ rdata->items[i].dname->label_count);
+
+ } else {
+ assert(rdata->items[i].raw_data != NULL);
+ fprintf(stderr, " %d: raw_data: length: %d\n", i,
+ *(rdata->items[i].raw_data));
+ fprintf(stderr, " ");
+ hex_print(((char *)(
+ rdata->items[i].raw_data + 1)),
+ rdata->items[i].raw_data[0]);
+ }
+ }
+ fprintf(stderr, " ------- RDATA -------\n");
+#endif
+}
+
+void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_RRSET_DEBUG)
+ fprintf(stderr, " ------- RRSET -------\n");
+ fprintf(stderr, " %p\n", rrset);
+ if (!rrset) {
+ return;
+ }
+ char *name = knot_dname_to_str(rrset->owner);
+ fprintf(stderr, " owner: %s\n", name);
+ free(name);
+ fprintf(stderr, " type: %s\n", knot_rrtype_to_string(rrset->type));
+ fprintf(stderr, " class: %d\n", rrset->rclass);
+ fprintf(stderr, " ttl: %d\n", rrset->ttl);
+
+ fprintf(stderr, " RRSIGs:\n");
+ if (rrset->rrsigs != NULL) {
+ knot_rrset_dump(rrset->rrsigs, loaded_zone);
+ } else {
+ fprintf(stderr, " none\n");
+ }
+
+ if (rrset->rdata == NULL) {
+ fprintf(stderr, " NO RDATA!\n");
+ fprintf(stderr, " ------- RRSET -------\n");
+ return;
+ }
+
+ fprintf(stderr, " rdata count: %d\n", rrset->rdata->count);
+ knot_rdata_t *tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata && tmp->next != NULL) {
+ knot_rdata_dump(tmp, rrset->type, loaded_zone);
+ tmp = tmp->next;
+ }
+
+ knot_rdata_dump(tmp, rrset->type, loaded_zone);
+
+ fprintf(stderr, " ------- RRSET -------\n");
+#endif
+}
+
+void knot_node_dump(knot_node_t *node, void *loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_NODE_DEBUG)
+ //char loaded_zone = *((char*) data);
+ char *name;
+
+ fprintf(stderr, "------- NODE --------\n");
+ name = knot_dname_to_str(node->owner);
+ fprintf(stderr, "owner: %s\n", name);
+ free(name);
+ fprintf(stderr, "labels: ");
+ hex_print((char *)node->owner->labels, node->owner->label_count);
+ fprintf(stderr, "node: %p\n", node);
+ fprintf(stderr, "node (in node's owner): %p\n", node->owner->node);
+ if (loaded_zone && node->prev != NULL) {
+ name = knot_dname_to_str(node->prev->owner);
+ fprintf(stderr, "previous node: %s\n", name);
+ free(name);
+ }
+
+ if (knot_node_is_deleg_point(node)) {
+ fprintf(stderr, "delegation point\n");
+ }
+
+ if (knot_node_is_non_auth(node)) {
+ fprintf(stderr, "non-authoritative node\n");
+ }
+
+ if (node->parent != NULL) {
+ /*! \todo This causes segfault when parent was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->parent->owner);
+ fprintf(stderr, "parent: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "no parent\n");
+ }
+
+ if (node->prev != NULL) {
+ fprintf(stderr, "previous node: %p\n", node->prev);
+ /*! \todo This causes segfault when prev was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->prev->owner);
+ fprintf(stderr, "previous node: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "previous node: none\n");
+ }
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+
+ fprintf(stderr, "Wildcard child: ");
+
+ if (node->wildcard_child != NULL) {
+ /*! \todo This causes segfault when wildcard child was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->wildcard_child->owner);
+ fprintf(stderr, "%s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "none\n");
+ }
+
+ fprintf(stderr, "NSEC3 node: ");
+
+ if (node->nsec3_node != NULL) {
+ /*! \todo This causes segfault when nsec3_node was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->nsec3_node->owner);
+ fprintf(stderr, "%s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "none\n");
+ }
+
+ fprintf(stderr, "RRSet count: %d\n", node->rrset_count);
+
+ for (int i = 0; i < node->rrset_count; i++) {
+ knot_rrset_dump(rrsets[i], (int) loaded_zone);
+ }
+ free(rrsets);
+ //assert(node->owner->node == node);
+ fprintf(stderr, "------- NODE --------\n");
+#endif
+}
+
+void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG)
+ if (!zone) {
+ fprintf(stderr, "------- STUB ZONE --------\n");
+ return;
+ }
+
+ fprintf(stderr, "------- ZONE --------\n");
+
+ knot_zone_contents_tree_apply_inorder(zone, knot_node_dump, (void *)&loaded_zone);
+
+ fprintf(stderr, "------- ZONE --------\n");
+
+ fprintf(stderr, "------- NSEC 3 tree -\n");
+
+ knot_zone_contents_nsec3_apply_inorder(zone, knot_node_dump, (void *)&loaded_zone);
+
+ fprintf(stderr, "------- NSEC 3 tree -\n");
+#endif
+}
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
new file mode 100644
index 0000000..2f9f5fd
--- /dev/null
+++ b/src/libknot/util/debug.h
@@ -0,0 +1,755 @@
+/*!
+ * \file debug.h
+ *
+ * \author Jan Kadlec <jan.kadlec.@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Functions for debug output of structures.
+ *
+ * \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_DEBUG_H_
+#define _KNOT_DEBUG_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config.h" /* autoconf generated */
+
+#include "rdata.h"
+#include "rrset.h"
+#include "zone/node.h"
+#include "zone/zone.h"
+#include "util/utils.h"
+#include "common/print.h"
+
+/*
+ * Debug macros
+ */
+/*! \todo Set these during configure. */
+//#define KNOT_ZONE_DEBUG
+//#define KNOT_RESPONSE_DEBUG
+//#define KNOT_ZONEDB_DEBUG
+//#define KNOT_DNAME_DEBUG
+//#define KNOT_NODE_DEBUG
+//#define KNOT_PACKET_DEBUG
+//#define KNOT_EDNS_DEBUG
+//#define KNOT_RRSET_DEBUG
+//#define KNOT_RDATA_DEBUG
+//#define KNOT_NSEC3_DEBUG
+//#define CUCKOO_DEBUG
+//#define CUCKOO_DEBUG_HASH
+//#define KNOT_NS_DEBUG
+//#define KNOT_XFRIN_DEBUG
+//#define KNOT_DDNS_DEBUG
+//#define KNOT_TSIG_DEBUG
+
+/*!
+ * \brief Dumps RDATA of the given type.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_RDATA_DEBUG
+ * is defined.
+ *
+ * \param rdata RDATA to dump.
+ * \param type Type of the RDATA (needed to properly parse the RDATA).
+ * \param loaded_zone Set to <> 0 if the RDATA is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_rdata_dump(knot_rdata_t *rdata, uint32_t type, char loaded_zone);
+
+/*!
+ * \brief Dumps RRSet.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_RRSET_DEBUG
+ * is defined.
+ *
+ * \param rrset RRSet to dump.
+ * \param loaded_zone Set to <> 0 if the RRSet is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone);
+
+/*!
+ * \brief Dumps zone node.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_NODE_DEBUG
+ * is defined.
+ *
+ * \param node Node to dump.
+ * \param loaded_zone Set to <> 0 if the node is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_node_dump(knot_node_t *node, void *loaded_zone);
+
+/*!
+ * \brief Dumps the whole zone.
+ *
+ * This function is empty if KNOT_ZONE_DEBUG is not defined.
+ *
+ * \param zone Zone to dump.
+ * \param loaded_zone Set to <> 0 if the node is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
+
+/******************************************************************************/
+
+#ifdef KNOT_NS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ns(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex(data, len) hex_print((data), (len))
+#define dbg_ns_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_ns(msg...)
+#define dbg_ns_hex(data, len)
+#define dbg_ns_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ns_verb(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex_verb(data, len) hex_print((data), (len))
+#define dbg_ns_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_ns_verb(msg...)
+#define dbg_ns_hex_verb(data, len)
+#define dbg_ns_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ns_detail(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex_detail(data, len) hex_print((data), (len))
+#define dbg_ns_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_ns_detail(msg...)
+#define dbg_ns_hex_detail(data, len)
+#define dbg_ns_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ns(msg...)
+#define dbg_ns_hex(data, len)
+#define dbg_ns_exec(cmds)
+#define dbg_ns_verb(msg...)
+#define dbg_ns_hex_verb(data, len)
+#define dbg_ns_exec_verb(cmds)
+#define dbg_ns_detail(msg...)
+#define dbg_ns_hex_detail(data, len)
+#define dbg_ns_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_DNAME_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_dname(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex(data, len) hex_print((data), (len))
+#define dbg_dname_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_dname(msg...)
+#define dbg_dname_hex(data, len)
+#define dbg_dname_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_dname_verb(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex_verb(data, len) hex_print((data), (len))
+#define dbg_dname_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_dname_verb(msg...)
+#define dbg_dname_hex_verb(data, len)
+#define dbg_dname_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_dname_detail(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex_detail(data, len) hex_print((data), (len))
+#define dbg_dname_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_dname_detail(msg...)
+#define dbg_dname_hex_detail(data, len)
+#define dbg_dname_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_dname(msg...)
+#define dbg_dname_hex(data, len)
+#define dbg_dname_exec(cmds)
+#define dbg_dname_verb(msg...)
+#define dbg_dname_hex_verb(data, len)
+#define dbg_dname_exec_verb(cmds)
+#define dbg_dname_detail(msg...)
+#define dbg_dname_hex_detail(data, len)
+#define dbg_dname_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_NODE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_node(msg...) fprintf(stderr, msg)
+#define dbg_node_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_node(msg...)
+#define dbg_node_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_node_verb(msg...) fprintf(stderr, msg)
+#define dbg_node_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_node_verb(msg...)
+#define dbg_node_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_node_detail(msg...) fprintf(stderr, msg)
+#define dbg_node_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_node_detail(msg...)
+#define dbg_node_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_node(msg...)
+#define dbg_node_hex(data, len)
+#define dbg_node_verb(msg...)
+#define dbg_node_hex_verb(data, len)
+#define dbg_node_detail(msg...)
+#define dbg_node_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_ZONE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zone(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex(data, len) hex_print((data), (len))
+#define dbg_zone_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_zone(msg...)
+#define dbg_zone_hex(data, len)
+#define dbg_zone_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zone_verb(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex_verb(data, len) hex_print((data), (len))
+#define dbg_zone_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_zone_verb(msg...)
+#define dbg_zone_hex_verb(data, len)
+#define dbg_zone_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zone_detail(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex_detail(data, len) hex_print((data), (len))
+#define dbg_zone_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zone_detail(msg...)
+#define dbg_zone_hex_detail(data, len)
+#define dbg_zone_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zone(msg...)
+#define dbg_zone_hex(data, len)
+#define dbg_zone_exec(cmds)
+#define dbg_zone_verb(msg...)
+#define dbg_zone_hex_verb(data, len)
+#define dbg_zone_exec_verb(cmds)
+#define dbg_zone_detail(msg...)
+#define dbg_zone_hex_detail(data, len)
+#define dbg_zone_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_ZONEDB_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zonedb(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb(msg...)
+#define dbg_zonedb_hex(data, len)
+#define dbg_zonedb_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zonedb_verb(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex_verb(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb_verb(msg...)
+#define dbg_zonedb_hex_verb(data, len)
+#define dbg_zonedb_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zonedb_detail(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex_detail(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb_detail(msg...)
+#define dbg_zonedb_hex_detail(data, len)
+#define dbg_zonedb_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zonedb(msg...)
+#define dbg_zonedb_hex(data, len)
+#define dbg_zonedb_exec(cmds)
+#define dbg_zonedb_verb(msg...)
+#define dbg_zonedb_hex_verb(data, len)
+#define dbg_zonedb_exec_verb(cmds)
+#define dbg_zonedb_detail(msg...)
+#define dbg_zonedb_hex_detail(data, len)
+#define dbg_zonedb_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_RESPONSE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_response(msg...) fprintf(stderr, msg)
+#define dbg_response_hex(data, len) hex_print((data), (len))
+#define dbg_response_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_response(msg...)
+#define dbg_response_hex(data, len)
+#define dbg_response_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_response_verb(msg...) fprintf(stderr, msg)
+#define dbg_response_hex_verb(data, len) hex_print((data), (len))
+#define dbg_response_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_response_verb(msg...)
+#define dbg_response_hex_verb(data, len)
+#define dbg_response_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_response_detail(msg...) fprintf(stderr, msg)
+#define dbg_response_hex_detail(data, len) hex_print((data), (len))
+#define dbg_response_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_response_detail(msg...)
+#define dbg_response_hex_detail(data, len)
+#define dbg_response_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_response(msg...)
+#define dbg_response_hex(data, len)
+#define dbg_response_exec(cmds)
+#define dbg_response_verb(msg...)
+#define dbg_response_hex_verb(data, len)
+#define dbg_response_exec_verb(cmds)
+#define dbg_response_detail(msg...)
+#define dbg_response_hex_detail(data, len)
+#define dbg_response_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_PACKET_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_packet(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex(data, len) hex_print((data), (len))
+#define dbg_packet_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_packet(msg...)
+#define dbg_packet_hex(data, len)
+#define dbg_packet_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_packet_verb(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex_verb(data, len) hex_print((data), (len))
+#define dbg_packet_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_packet_verb(msg...)
+#define dbg_packet_hex_verb(data, len)
+#define dbg_packet_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_packet_detail(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex_detail(data, len) hex_print((data), (len))
+#define dbg_packet_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_packet_detail(msg...)
+#define dbg_packet_hex_detail(data, len)
+#define dbg_packet_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_packet(msg...)
+#define dbg_packet_hex(data, len)
+#define dbg_packet_exec(cmds)
+#define dbg_packet_verb(msg...)
+#define dbg_packet_hex_verb(data, len)
+#define dbg_packet_exec_verb(cmds)
+#define dbg_packet_detail(msg...)
+#define dbg_packet_hex_detail(data, len)
+#define dbg_packet_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_EDNS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_edns(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_edns(msg...)
+#define dbg_edns_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_edns_verb(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_edns_verb(msg...)
+#define dbg_edns_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_edns_detail(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_edns_detail(msg...)
+#define dbg_edns_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_edns(msg...)
+#define dbg_edns_hex(data, len)
+#define dbg_edns_verb(msg...)
+#define dbg_edns_hex_verb(data, len)
+#define dbg_edns_detail(msg...)
+#define dbg_edns_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_NSEC3_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_nsec3(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3(msg...)
+#define dbg_nsec3_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_nsec3_verb(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3_verb(msg...)
+#define dbg_nsec3_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_nsec3_detail(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3_detail(msg...)
+#define dbg_nsec3_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_nsec3(msg...)
+#define dbg_nsec3_hex(data, len)
+#define dbg_nsec3_verb(msg...)
+#define dbg_nsec3_hex_verb(data, len)
+#define dbg_nsec3_detail(msg...)
+#define dbg_nsec3_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef CUCKOO_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ck(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ck(msg...)
+#define dbg_ck_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ck_verb(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_verb(msg...)
+#define dbg_ck_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ck_detail(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_detail(msg...)
+#define dbg_ck_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ck(msg...)
+#define dbg_ck_hex(data, len)
+#define dbg_ck_verb(msg...)
+#define dbg_ck_hex_verb(data, len)
+#define dbg_ck_detail(msg...)
+#define dbg_ck_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef CUCKOO_DEBUG_HASH
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ck_hash(msg...) fprintf(stderr, msg)
+#define dbg_ck_rehash(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash(msg...)
+#define dbg_ck_rehash(msg...)
+#define dbg_ck_hash_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ck_hash_verb(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash_verb(msg...)
+#define dbg_ck_hash_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ck_hash_detail(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash_detail(msg...)
+#define dbg_ck_hash_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ck_hash(msg...)
+#define dbg_ck_rehash(msg...)
+#define dbg_ck_hash_hex(data, len)
+#define dbg_ck_hash_verb(msg...)
+#define dbg_ck_hash_hex_verb(data, len)
+#define dbg_ck_hash_detail(msg...)
+#define dbg_ck_hash_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_XFRIN_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_xfrin(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin(msg...)
+#define dbg_xfrin_hex(data, len)
+#define dbg_xfrin_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_xfrin_verb(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex_verb(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin_verb(msg...)
+#define dbg_xfrin_hex_verb(data, len)
+#define dbg_xfrin_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_xfrin_detail(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex_detail(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin_detail(msg...)
+#define dbg_xfrin_hex_detail(data, len)
+#define dbg_xfrin_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_xfrin(msg...)
+#define dbg_xfrin_hex(data, len)
+#define dbg_xfrin_exec(cmds)
+#define dbg_xfrin_verb(msg...)
+#define dbg_xfrin_hex_verb(data, len)
+#define dbg_xfrin_exec_verb(cmds)
+#define dbg_xfrin_detail(msg...)
+#define dbg_xfrin_hex_detail(data, len)
+#define dbg_xfrin_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_DDNS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ddns(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns(msg...)
+#define dbg_ddns_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ddns_verb(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns_verb(msg...)
+#define dbg_ddns_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ddns_detail(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns_detail(msg...)
+#define dbg_ddns_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ddns(msg...)
+#define dbg_ddns_hex(data, len)
+#define dbg_ddns_verb(msg...)
+#define dbg_ddns_hex_verb(data, len)
+#define dbg_ddns_detail(msg...)
+#define dbg_ddns_hex_detail(data, len)
+#endif
+
+#ifdef KNOT_TSIG_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_tsig(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig(msg...)
+#define dbg_tsig_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_tsig_verb(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex_verb(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig_verb(msg...)
+#define dbg_tsig_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_tsig_detail(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex_detail(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig_detail(msg...)
+#define dbg_tsig_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_tsig(msg...)
+#define dbg_tsig_hex(data, len)
+#define dbg_tsig_verb(msg...)
+#define dbg_tsig_hex_verb(data, len)
+#define dbg_tsig_detail(msg...)
+#define dbg_tsig_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#endif /* _KNOT_DEBUG_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c
new file mode 100644
index 0000000..fa94c24
--- /dev/null
+++ b/src/libknot/util/descriptor.c
@@ -0,0 +1,501 @@
+/*!
+ * \file descriptor.c
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \note Most of the constants and functions were taken from NSD's dns.c.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "libknot.h"
+
+enum desclen { KNOT_RRTYPE_DESCRIPTORS_LENGTH = 32770 }; // used to be 101
+
+/*!
+ * \brief Table for linking RR class constants to their textual representation.
+ */
+static knot_lookup_table_t dns_rrclasses[] = {
+ { KNOT_CLASS_IN, "IN" }, /* the Internet */
+ { KNOT_CLASS_CS, "CS" }, /* the CSNET class (Obsolete) */
+ { KNOT_CLASS_CH, "CH" }, /* the CHAOS class */
+ { KNOT_CLASS_HS, "HS" }, /* Hesiod */
+ { 0, NULL }
+};
+
+/*! \brief RR type descriptors. */
+static knot_rrtype_descriptor_t
+ knot_rrtype_descriptors[KNOT_RRTYPE_DESCRIPTORS_LENGTH] = {
+ /* 0 */
+ { 0, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 1 */
+ { KNOT_RRTYPE_A, "A", 1, { KNOT_RDATA_WF_A }, { KNOT_RDATA_ZF_A }, true },
+ /* 2 */
+ { KNOT_RRTYPE_NS, "NS", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 3 */
+ { KNOT_RRTYPE_MD, "MD", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 4 */
+ { KNOT_RRTYPE_MF, "MF", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 5 */
+ { KNOT_RRTYPE_CNAME, "CNAME", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 6 */
+ { KNOT_RRTYPE_SOA, "SOA", 7,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME, KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD },
+ true },
+ /* 7 */
+ { KNOT_RRTYPE_MB, "MB", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 8 */
+ { KNOT_RRTYPE_MG, "MG", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 9 */
+ { KNOT_RRTYPE_MR, "MR", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 10 */
+ { KNOT_RRTYPE_NULL, NULL, 1,
+ { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 11 */
+ { KNOT_RRTYPE_WKS, "WKS", 2,
+ { KNOT_RDATA_WF_A, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_A, KNOT_RDATA_ZF_SERVICES }, true },
+ /* 12 */
+ { KNOT_RRTYPE_PTR, "PTR", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME }, true },
+ /* 13 */
+ { KNOT_RRTYPE_HINFO, "HINFO", 2,
+ { KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT }, true },
+ /* 14 */
+ { KNOT_RRTYPE_MINFO, "MINFO", 2,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 15 */
+ { KNOT_RRTYPE_MX, "MX", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 16 */ /* This is obscure, but I guess there's no other way */
+ { KNOT_RRTYPE_TXT, "TXT", 1,
+ { KNOT_RDATA_WF_TEXT },
+ { KNOT_RDATA_ZF_TEXT },
+ false },
+ /* 17 */
+ { KNOT_RRTYPE_RP, "RP", 2,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 18 */
+ { KNOT_RRTYPE_AFSDB, "AFSDB", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 19 */
+ { KNOT_RRTYPE_X25, "X25", 1,
+ { KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT }, true },
+ /* 20 */
+ { KNOT_RRTYPE_ISDN, "ISDN", 2,
+ { KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT }, false },
+ /* 21 */
+ { KNOT_RRTYPE_RT, "RT", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 22 */
+ { KNOT_RRTYPE_NSAP, "NSAP", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_NSAP }, true },
+ /* 23 */
+ { 23, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 24 */
+ { KNOT_RRTYPE_SIG, "SIG", 9,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_SHORT,KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_RRTYPE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME,
+ KNOT_RDATA_ZF_BASE64 },
+ true },
+ /* 25 */
+ { KNOT_RRTYPE_KEY, "KEY", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 26 */
+ { KNOT_RRTYPE_PX, "PX", 3,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 27 */
+ { 27, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 28 */
+ { KNOT_RRTYPE_AAAA, "AAAA", 1,
+ { KNOT_RDATA_WF_AAAA },
+ { KNOT_RDATA_ZF_AAAA }, true },
+ /* 29 */
+ { KNOT_RRTYPE_LOC, "LOC", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_LOC }, true },
+ /* 30 */
+ { KNOT_RRTYPE_NXT, "NXT", 2,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_NXT }, true },
+ /* 31 */
+ { 31, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 32 */
+ { 32, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 33 */
+ { KNOT_RRTYPE_SRV, "SRV", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME },
+ true },
+ /* 34 */
+ { 34, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 35 */
+ { KNOT_RRTYPE_NAPTR, "NAPTR", 6,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_TEXT_SINGLE,
+ KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT,
+ KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 36 */
+ { KNOT_RRTYPE_KX, "KX", 2,
+ { KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 37 */
+ { KNOT_RRTYPE_CERT, "CERT", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_CERTIFICATE_TYPE, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 38 */
+ { KNOT_RRTYPE_A6, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 39 */
+ { KNOT_RRTYPE_DNAME, "DNAME", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME }, true },
+ /* 40 */
+ { 40, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 41 */
+ /* OPT has its parser token, but should never be in zone file... */
+ { KNOT_RRTYPE_OPT, "OPT", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 42 */
+ { KNOT_RRTYPE_APL, "APL", 1,
+ { KNOT_RDATA_WF_APL },
+ { KNOT_RDATA_ZF_APL }, false },
+ /* 43 */
+ { KNOT_RRTYPE_DS, "DS", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX }, true },
+ /* 44 */
+ { KNOT_RRTYPE_SSHFP, "SSHFP", 3,
+ { KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX },
+ true },
+ /* 45 */
+ { KNOT_RRTYPE_IPSECKEY, "IPSECKEY", 5,
+ { KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_IPSECGATEWAY,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_IPSECGATEWAY,
+ KNOT_RDATA_ZF_BASE64 }, false },
+ /* 46 */
+ { KNOT_RRTYPE_RRSIG, "RRSIG", 9,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_LITERAL_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_RRTYPE, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_TIME,
+ KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_LITERAL_DNAME,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 47 */
+ { KNOT_RRTYPE_NSEC, "NSEC", 2,
+ { KNOT_RDATA_WF_LITERAL_DNAME, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_LITERAL_DNAME, KNOT_RDATA_ZF_NSEC },
+ true },
+ /* 48 */
+ { KNOT_RRTYPE_DNSKEY, "DNSKEY", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_BYTE,
+ KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BASE64 }, true },
+ /* 49 */
+ { KNOT_RRTYPE_DHCID, "DHCID", 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BASE64 }, true },
+ /* 50 */
+ { KNOT_RRTYPE_NSEC3, "NSEC3", 6,
+ { KNOT_RDATA_WF_BYTE, /* hash type */
+ KNOT_RDATA_WF_BYTE, /* flags */
+ KNOT_RDATA_WF_SHORT, /* iterations */
+ KNOT_RDATA_WF_BINARYWITHLENGTH, /* salt */
+ KNOT_RDATA_WF_BINARYWITHLENGTH, /* next hashed name */
+ KNOT_RDATA_WF_BINARY /* type bitmap */ },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_HEX_LEN,
+ KNOT_RDATA_ZF_BASE32, KNOT_RDATA_ZF_NSEC },
+ true },
+ /* 51 */
+ { KNOT_RRTYPE_NSEC3PARAM, "NSEC3PARAM", 4,
+ { KNOT_RDATA_WF_BYTE, /* hash type */
+ KNOT_RDATA_WF_BYTE, /* flags */
+ KNOT_RDATA_WF_SHORT, /* iterations */
+ KNOT_RDATA_WF_BINARYWITHLENGTH /* salt */ },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE,
+ KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_HEX_LEN }, true },
+ /* 52 */
+
+
+ /* In NSD they have indices between 52 and 99 filled with
+ unknown types. TODO add here if it's really needed? */
+ /* it is indeed needed, in rrtype_from_string */
+
+ /* There's a GNU extension that works like this: [first ... last] = value */
+
+ /* 99 */
+ [99] = { KNOT_RRTYPE_SPF, "SPF", 1,
+ { KNOT_RDATA_WF_TEXT },
+ { KNOT_RDATA_ZF_TEXT }, false },
+ /* TSIG pseudo RR. */
+ [250] = { KNOT_RRTYPE_TSIG, "TSIG", 7,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME, KNOT_RDATA_WF_UINT48,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BINARYWITHSHORT,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_BINARYWITHSHORT },
+ /* Zoneformat not needed. */
+ {0, 0, 0, 0, 0}, true },
+ /* 32769 */
+ [32769] = { KNOT_RRTYPE_DLV, "DLV", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX },
+ true },
+};
+
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_type(uint16_t type)
+{
+ if (type < KNOT_RRTYPE_LAST + 1) {
+ return &knot_rrtype_descriptors[type];
+ } else if (type == KNOT_RRTYPE_DLV) {
+ return &knot_rrtype_descriptors[KNOT_RRTYPE_DLV];
+ }
+ return &knot_rrtype_descriptors[0];
+}
+
+/* I see a lot of potential here to speed up zone parsing - this is O(n) *
+ * could be better */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < KNOT_RRTYPE_DLV + 1; ++i) {
+ if (knot_rrtype_descriptors[i].name &&
+ strcasecmp(knot_rrtype_descriptors[i].name, name) == 0) {
+ return &knot_rrtype_descriptors[i];
+ }
+ }
+
+ if (knot_rrtype_descriptors[KNOT_RRTYPE_DLV].name &&
+ strcasecmp(knot_rrtype_descriptors[KNOT_RRTYPE_DLV].name,
+ name) == 0) {
+ return &knot_rrtype_descriptors[KNOT_RRTYPE_DLV];
+ }
+
+ return NULL;
+}
+
+const char *knot_rrtype_to_string(uint16_t rrtype)
+{
+ static char buf[20];
+ knot_rrtype_descriptor_t *descriptor =
+ knot_rrtype_descriptor_by_type(rrtype);
+ if (descriptor->name) {
+ return descriptor->name;
+ } else {
+ snprintf(buf, sizeof(buf), "TYPE%d", (int) rrtype);
+ return buf;
+ }
+}
+
+uint16_t knot_rrtype_from_string(const char *name)
+{
+ char *end;
+ long rrtype;
+ knot_rrtype_descriptor_t *entry;
+
+ entry = knot_rrtype_descriptor_by_name(name);
+ if (entry) {
+ return entry->type;
+ }
+
+ if (strlen(name) < 5) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "TYPE", 4) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[4])) {
+ return 0;
+ }
+
+ /* The rest from the string must be a number. */
+ rrtype = strtol(name + 4, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrtype < 0 || rrtype > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrtype;
+}
+
+const char *knot_rrclass_to_string(uint16_t rrclass)
+{
+ static char buf[20];
+ knot_lookup_table_t *entry = knot_lookup_by_id(dns_rrclasses,
+ rrclass);
+ if (entry) {
+ assert(strlen(entry->name) < sizeof(buf));
+ knot_strlcpy(buf, entry->name, sizeof(buf));
+ } else {
+ snprintf(buf, sizeof(buf), "CLASS%d", (int) rrclass);
+ }
+ return buf;
+}
+
+uint16_t knot_rrclass_from_string(const char *name)
+{
+ char *end;
+ long rrclass;
+ knot_lookup_table_t *entry;
+
+ entry = knot_lookup_by_name(dns_rrclasses, name);
+ if (entry) {
+ return (uint16_t) entry->id;
+ }
+
+ if (strlen(name) < 6) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "CLASS", 5) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[5])) {
+ return 0;
+ }
+
+ // The rest from the string must be a number.
+ rrclass = strtol(name + 5, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrclass < 0 || rrclass > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrclass;
+}
+
+size_t knot_wireformat_size(unsigned int wire_type)
+{
+ switch(wire_type) {
+ case KNOT_RDATA_WF_BYTE:
+ return 1;
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ return 2;
+ break;
+ case KNOT_RDATA_WF_LONG:
+ return 4;
+ break;
+ case KNOT_RDATA_WF_A:
+ return 4;
+ break;
+ default: /* unknown size */
+ return 0;
+ break;
+ } /* switch */
+}
+
+int knot_rrtype_is_metatype(uint16_t type)
+{
+ /*! \todo Check if there are some other metatypes. */
+ return (type == KNOT_RRTYPE_ANY
+ || type == KNOT_RRTYPE_AXFR
+ || type == KNOT_RRTYPE_IXFR
+ || type == KNOT_RRTYPE_MAILA
+ || type == KNOT_RRTYPE_MAILB
+ || type == KNOT_RRTYPE_OPT);
+}
+
diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h
new file mode 100644
index 0000000..10d3d20
--- /dev/null
+++ b/src/libknot/util/descriptor.h
@@ -0,0 +1,332 @@
+/*!
+ * \file descriptor.h
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet Labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \note Most of the constants and functions were taken from NSD's dns.h.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOT_DESCRIPTOR_H_
+#define _KNOT_DESCRIPTOR_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+enum knot_mxrdtln {
+ /*! \brief Maximum items in RDATA wireformat. */
+ KNOT_MAX_RDATA_ITEMS = 64,
+ /*! \brief Maximum size of one item in RDATA wireformat. */
+ KNOT_MAX_RDATA_ITEM_SIZE = 65534,
+ /*! \brief Maximum wire size of one RDATA. */
+ KNOT_MAX_RDATA_WIRE_SIZE =
+ KNOT_MAX_RDATA_ITEMS * KNOT_MAX_RDATA_ITEM_SIZE
+};
+
+typedef enum knot_mxrdtln knot_mxrdtln_t;
+//#define MAXRDATALEN 64
+
+/* 64 is in NSD. Seems a little too much, but I'd say it's not a real issue. */
+
+/*!
+ * \brief Resource record class codes.
+ */
+enum knot_rr_class {
+ KNOT_CLASS_IN = 1,
+ KNOT_CLASS_CS,
+ KNOT_CLASS_CH,
+ KNOT_CLASS_HS,
+ KNOT_CLASS_NONE = 254,
+ KNOT_CLASS_ANY = 255
+};
+
+typedef enum knot_rr_class knot_rr_class_t;
+
+/*!
+ * \brief Resource record type constants.
+ * \todo Not all indices can be used for indexing.
+ */
+enum knot_rr_type {
+ KNOT_RRTYPE_UNKNOWN, /*!< 0 - an unknown type */
+ KNOT_RRTYPE_A, /*!< 1 - a host address */
+ KNOT_RRTYPE_NS, /*!< 2 - an authoritative name server */
+ KNOT_RRTYPE_MD, /*!< 3 - a mail destination (Obsolete - use MX) */
+ KNOT_RRTYPE_MF, /*!< 4 - a mail forwarder (Obsolete - use MX) */
+ KNOT_RRTYPE_CNAME, /*!< 5 - the canonical name for an alias */
+ KNOT_RRTYPE_SOA, /*!< 6 - marks the start of a zone of authority */
+ KNOT_RRTYPE_MB, /*!< 7 - a mailbox domain name (EXPERIMENTAL) */
+ KNOT_RRTYPE_MG, /*!< 8 - a mail group member (EXPERIMENTAL) */
+ KNOT_RRTYPE_MR, /*!< 9 - a mail rename domain name (EXPERIMENTAL) */
+ KNOT_RRTYPE_NULL, /*!< 10 - a null RR (EXPERIMENTAL) */
+ KNOT_RRTYPE_WKS, /*!< 11 - a well known service description */
+ KNOT_RRTYPE_PTR, /*!< 12 - a domain name pointer */
+ KNOT_RRTYPE_HINFO, /*!< 13 - host information */
+ KNOT_RRTYPE_MINFO, /*!< 14 - mailbox or mail list information */
+ KNOT_RRTYPE_MX, /*!< 15 - mail exchange */
+ KNOT_RRTYPE_TXT, /*!< 16 - text strings */
+ KNOT_RRTYPE_RP, /*!< 17 - RFC1183 */
+ KNOT_RRTYPE_AFSDB, /*!< 18 - RFC1183 */
+ KNOT_RRTYPE_X25, /*!< 19 - RFC1183 */
+ KNOT_RRTYPE_ISDN, /*!< 20 - RFC1183 */
+ KNOT_RRTYPE_RT, /*!< 21 - RFC1183 */
+ KNOT_RRTYPE_NSAP, /*!< 22 - RFC1706 */
+
+ KNOT_RRTYPE_SIG = 24, /*!< 24 - 2535typecode */
+ KNOT_RRTYPE_KEY, /*!< 25 - 2535typecode */
+ KNOT_RRTYPE_PX, /*!< 26 - RFC2163 */
+
+ KNOT_RRTYPE_AAAA = 28, /*!< 28 - ipv6 address */
+ KNOT_RRTYPE_LOC, /*!< 29 - LOC record RFC1876 */
+ KNOT_RRTYPE_NXT, /*!< 30 - 2535typecode */
+
+ KNOT_RRTYPE_SRV = 33, /*!< 33 - SRV record RFC2782 */
+
+ KNOT_RRTYPE_NAPTR = 35, /*!< 35 - RFC2915 */
+ KNOT_RRTYPE_KX, /*!< 36 - RFC2230 Key Exchange Delegation Record */
+ KNOT_RRTYPE_CERT, /*!< 37 - RFC2538 */
+ KNOT_RRTYPE_A6, /*!< 38 - RFC2874 */
+ KNOT_RRTYPE_DNAME, /*!< 39 - RFC2672 */
+
+ KNOT_RRTYPE_OPT = 41, /*!< 41 - Pseudo OPT record... */
+ KNOT_RRTYPE_APL, /*!< 42 - RFC3123 */
+ KNOT_RRTYPE_DS, /*!< 43 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_SSHFP, /*!< 44 - SSH Key Fingerprint */
+ KNOT_RRTYPE_IPSECKEY, /*!< 45 - public key for ipsec use. RFC 4025 */
+ KNOT_RRTYPE_RRSIG, /*!< 46 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_NSEC, /*!< 47 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_DNSKEY, /*!< 48 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_DHCID, /*!< 49 - RFC4701 DHCP information */
+ /*!
+ * \brief 50 - NSEC3, secure denial, prevents zonewalking
+ */
+ KNOT_RRTYPE_NSEC3,
+ /*!
+ * \brief 51 - NSEC3PARAM at zone apex nsec3 parameters
+ */
+ KNOT_RRTYPE_NSEC3PARAM,
+
+ /* TODO consider some better way of doing this, indices too high */
+
+ KNOT_RRTYPE_SPF = 99, /*!< RFC 4408 */
+
+ // not designating any RRs
+ KNOT_RRTYPE_TSIG = 250, /*!< TSIG - RFC2845. */
+ KNOT_RRTYPE_IXFR = 251, /*!< IXFR (not an actual RR). */
+ KNOT_RRTYPE_AXFR = 252, /*!< AXFR (not an actual RR). */
+ /*!
+ * \brief A request for mailbox-related records (MB, MG or MR)
+ */
+ KNOT_RRTYPE_MAILB = 253,
+ /*!
+ * \brief A request for mail agent RRs (Obsolete - see MX)
+ */
+ KNOT_RRTYPE_MAILA = 254,
+ KNOT_RRTYPE_ANY = 255, /*!< any type (wildcard) */
+
+ // totally weird numbers (cannot use for indexing)
+ KNOT_RRTYPE_TA = 32768, /*!< DNSSEC Trust Authorities */
+ KNOT_RRTYPE_DLV = 32769, /*!< RFC 4431 */
+
+ /*! \brief Last normal RR type. */
+ KNOT_RRTYPE_LAST = KNOT_RRTYPE_TSIG
+ /*! \todo [TSIG] Is it allright to include all <= RR TSIG?
+ * Because TSIG is normal RR type. */
+};
+
+typedef enum knot_rr_type knot_rr_type_t;
+
+/*! \brief Constants characterising the wire format of RDATA items. */
+enum knot_rdata_wireformat {
+ /*!
+ * \brief Possibly compressed domain name.
+ */
+ KNOT_RDATA_WF_COMPRESSED_DNAME = 50,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME = 51, /*!< Uncompressed domain name. */
+ KNOT_RDATA_WF_LITERAL_DNAME = 52, /*!< Literal (not downcased) dname. */
+ KNOT_RDATA_WF_BYTE = 1, /*!< 8-bit integer. */
+ KNOT_RDATA_WF_SHORT = 2, /*!< 16-bit integer. */
+ KNOT_RDATA_WF_LONG = 4, /*!< 32-bit integer. */
+ KNOT_RDATA_WF_UINT48 = 8, /*!< 48-bit integer. */
+ KNOT_RDATA_WF_TEXT = 53, /*!< Text string. */
+ KNOT_RDATA_WF_A = 58, /*!< 32-bit IPv4 address. */
+ KNOT_RDATA_WF_AAAA = 16, /*!< 128-bit IPv6 address. */
+ KNOT_RDATA_WF_BINARY = 54, /*!< Binary data (unknown length). */
+ /*!
+ * \brief Binary data preceded by 1 byte length
+ */
+ KNOT_RDATA_WF_BINARYWITHLENGTH = 55,
+ KNOT_RDATA_WF_APL = 56, /*!< APL data. */
+ KNOT_RDATA_WF_IPSECGATEWAY = 57, /*!< IPSECKEY gateway ip4, ip6 or dname. */
+ KNOT_RDATA_WF_BINARYWITHSHORT = 59,
+ KNOT_RDATA_WF_TEXT_SINGLE = 60 /*!< Text string. */
+};
+
+/*! \brief Constants characterising the format of RDATA items in zone file. */
+enum knot_rdata_zoneformat
+{
+ KNOT_RDATA_ZF_DNAME, /* Domain name. */
+ KNOT_RDATA_ZF_LITERAL_DNAME, /* DNS name (not lowercased domain name). */
+ KNOT_RDATA_ZF_TEXT, /* Text string. */
+ KNOT_RDATA_ZF_BYTE, /* 8-bit integer. */
+ KNOT_RDATA_ZF_SHORT, /* 16-bit integer. */
+ KNOT_RDATA_ZF_LONG, /* 32-bit integer. */
+ KNOT_RDATA_ZF_A, /* 32-bit IPv4 address. */
+ KNOT_RDATA_ZF_AAAA, /* 128-bit IPv6 address. */
+ KNOT_RDATA_ZF_RRTYPE, /* RR type. */
+ KNOT_RDATA_ZF_ALGORITHM, /* Cryptographic algorithm. */
+ KNOT_RDATA_ZF_CERTIFICATE_TYPE,
+ KNOT_RDATA_ZF_PERIOD, /* Time period. */
+ KNOT_RDATA_ZF_TIME,
+ KNOT_RDATA_ZF_BASE64, /* Base-64 binary data. */
+ KNOT_RDATA_ZF_BASE32, /* Base-32 binary data. */
+ KNOT_RDATA_ZF_HEX, /* Hexadecimal binary data. */
+ KNOT_RDATA_ZF_HEX_LEN, /* Hexadecimal binary data. Skip initial length byte. */
+ KNOT_RDATA_ZF_NSAP, /* NSAP. */
+ KNOT_RDATA_ZF_APL, /* APL. */
+ KNOT_RDATA_ZF_IPSECGATEWAY, /* IPSECKEY gateway ip4, ip6 or dname. */
+ KNOT_RDATA_ZF_SERVICES, /* Protocol and port number bitmap. */
+ KNOT_RDATA_ZF_NXT, /* NXT type bitmap. */
+ KNOT_RDATA_ZF_NSEC, /* NSEC type bitmap. */
+ KNOT_RDATA_ZF_LOC, /* Location data. */
+ KNOT_RDATA_ZF_UNKNOWN /* Unknown data. */
+};
+
+/*! \brief Constants characterising the wire format of RDATA items. */
+typedef enum knot_rdata_zoneformat knot_rdata_zoneformat_t;
+
+/*! \brief Enum containing wireformat codes. */
+typedef enum knot_rdatawireformat knot_rdata_wireformat_t;
+
+/*! \brief Structure holding RR descriptor. */
+struct knot_rrtype_descriptor {
+ uint16_t type; /*!< RR type */
+ const char *name; /*!< Textual name. */
+ uint8_t length; /*!< Maximum number of RDATA items. */
+
+ /*! \brief Wire format specification for the RDATA. */
+ uint8_t wireformat[KNOT_MAX_RDATA_ITEMS];
+
+ /*! \brief Zone file format specification for the RDATA. */
+ uint8_t zoneformat[KNOT_MAX_RDATA_ITEMS];
+
+ bool fixed_items; /*!< Has fixed number of RDATA items? */
+};
+
+/*! \brief Structure holding RR descriptor. */
+typedef struct knot_rrtype_descriptor knot_rrtype_descriptor_t;
+
+/*!
+ * \brief Gets RR descriptor for given RR type.
+ *
+ * \param type Code of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given type code, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_type(uint16_t type);
+
+/*!
+ * \brief Gets RR descriptor for given RR name.
+ *
+ * \param name Mnemonic of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given name, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_name(const char *name);
+
+/*!
+ * \brief Converts numeric type representation to mnemonic string.
+ *
+ * \param rrtype Type RR type code to be converted.
+ *
+ * \return Mnemonic string if found, str(TYPE[rrtype]) otherwise.
+ */
+const char *knot_rrtype_to_string(uint16_t rrtype);
+
+/*!
+ * \brief Converts mnemonic string representation of a type to numeric one.
+ *
+ * \param name Mnemonic string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t knot_rrtype_from_string(const char *name);
+
+/*!
+ * \brief Converts numeric class representation to string one.
+ *
+ * \param rrclass Class code to be converted.
+ *
+ * \return String represenation of class if found,
+ * str(CLASS[rrclass]) otherwise.
+ */
+const char *knot_rrclass_to_string(uint16_t rrclass);
+
+/*!
+ * \brief Converts string representation of a class to numeric one.
+ *
+ * \param name Class string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t knot_rrclass_from_string(const char *name);
+
+/*!
+ * \brief Returns size of wireformat type in bytes.
+ *
+ * \param wire_type Wireformat type.
+ *
+ * \retval Size of given type on success.
+ * \retval 0 on unknown type or type that has no length.
+ */
+size_t knot_wireformat_size(unsigned int wire_type);
+
+int knot_rrtype_is_metatype(uint16_t type);
+
+#endif /* _KNOT_DESCRIPTOR_H_ */
+
+/*! @} */
+
diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h
new file mode 100644
index 0000000..da45151
--- /dev/null
+++ b/src/libknot/util/error.h
@@ -0,0 +1,87 @@
+/*!
+ * \file error.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Error codes and function for getting error message.
+ *
+ * \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_ERROR_H_
+#define _KNOT_ERROR_H_
+
+#include "common/errors.h"
+
+/*! \brief Error codes used in the library. */
+enum knot_error {
+ KNOT_EOK = 0, /*!< OK */
+
+ /* TSIG errors. */
+ KNOT_TSIG_EBADSIG = -16, /*!< Failed to verify TSIG MAC. */
+ KNOT_TSIG_EBADKEY = -17, /*!< TSIG key not recognized or invalid. */
+ KNOT_TSIG_EBADTIME = -18,/*!< TSIG signing time out of range. */
+
+ /* General errors. */
+ KNOT_ERROR = -10000, /*!< General error. */
+ KNOT_ENOMEM, /*!< Not enough memory. */
+ KNOT_ENOTSUP, /*!< Operation not supported. */
+ KNOT_EAGAIN, /*!< OS lacked necessary resources. */
+ KNOT_ERANGE, /*!< Value is out of range. */
+ KNOT_EBADARG, /*!< Wrong argument supported. */
+ KNOT_EFEWDATA, /*!< Not enough data to parse. */
+ KNOT_ESPACE, /*!< Not enough space provided. */
+ KNOT_EMALF, /*!< Malformed data. */
+ KNOT_ECRYPTO, /*!< Error in crypto library. */
+ KNOT_ENSEC3PAR, /*!< Missing or wrong NSEC3PARAM record. */
+ KNOT_EBADZONE, /*!< Domain name does not belong to the zone. */
+ KNOT_EHASH, /*!< Error in hash table. */
+ KNOT_EZONEIN, /*!< Error inserting zone. */
+ KNOT_ENOZONE, /*!< No such zone found. */
+ KNOT_ENONODE, /*!< No such node in zone found. */
+ KNOT_ENORRSET, /*!< No such RRSet found. */
+ KNOT_EDNAMEPTR, /*!< Domain name pointer larger than allowed. */
+ KNOT_EPAYLOAD, /*!< Payload in OPT RR larger than max wire size. */
+ KNOT_ECRC, /*!< Wrong dump CRC. */
+ KNOT_EPREREQ, /*!< UPDATE prerequisity not met. */
+ KNOT_ENOXFR, /*!< Transfer was not sent. */
+ KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */
+ KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */
+ KNOT_ECONN, /*!< Connection reset. */
+ KNOT_ERROR_COUNT = 30
+};
+
+/*! \brief Table linking error messages to error codes. */
+extern const error_table_t knot_error_msgs[KNOT_ERROR_COUNT];
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+static inline const char *knot_strerror(int code)
+{
+ return error_to_str((const error_table_t*)knot_error_msgs, code);
+}
+
+#endif /* _KNOT_ERROR_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c
new file mode 100644
index 0000000..bc2bed2
--- /dev/null
+++ b/src/libknot/util/libknot_error.c
@@ -0,0 +1,53 @@
+/* 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 "util/error.h"
+#include "util/utils.h"
+
+#include "common/errors.h"
+
+const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
+ {KNOT_EOK, "OK"},
+ {KNOT_ERROR, "General error."},
+ {KNOT_ENOMEM, "Not enough memory."},
+ {KNOT_ENOTSUP, "Operation not supported."},
+ {KNOT_EAGAIN, "OS lacked necessary resources."},
+ {KNOT_ERANGE, "Value is out of range."},
+ {KNOT_EBADARG, "Wrong argument supported."},
+ {KNOT_EFEWDATA, "Not enough data to parse."},
+ {KNOT_ESPACE, "Not enough space provided."},
+ {KNOT_EMALF, "Malformed data."},
+ {KNOT_ECRYPTO, "Error in crypto library."},
+ {KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record."},
+ {KNOT_EBADZONE, "Domain name does not belong to the given zone."},
+ {KNOT_EHASH, "Error in hash table."},
+ {KNOT_EZONEIN, "Error inserting zone."},
+ {KNOT_ENOZONE, "No such zone found."},
+ {KNOT_ENONODE, "No such node in zone found."},
+ {KNOT_ENORRSET, "No such RRSet found."},
+ {KNOT_EDNAMEPTR, "Domain name pointer larger than allowed."},
+ {KNOT_EPAYLOAD, "Payload in OPT RR larger than max wire size."},
+ {KNOT_ECRC, "CRC check failed."},
+ {KNOT_EPREREQ, "UPDATE prerequisity not met."},
+ {KNOT_ENOXFR, "Transfer was not sent."},
+ {KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)."},
+ {KNOT_EXFRREFUSED, "Zone transfer refused by the server."},
+ {KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC." },
+ {KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." },
+ {KNOT_TSIG_EBADTIME, "TSIG signing time out of range." },
+ {KNOT_ECONN, "Connection reset."},
+ {KNOT_ERROR, 0}
+};
diff --git a/src/libknot/util/tolower.c b/src/libknot/util/tolower.c
new file mode 100644
index 0000000..d71c467
--- /dev/null
+++ b/src/libknot/util/tolower.c
@@ -0,0 +1,276 @@
+/* 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 "util/tolower.h"
+
+const uint8_t char_table[CHAR_TABLE_SIZE] = {
+ '\x00',
+ '\x01',
+ '\x02',
+ '\x03',
+ '\x04',
+ '\x05',
+ '\x06',
+ '\x07',
+ '\x08',
+ '\x09',
+ '\x0A',
+ '\x0B',
+ '\x0C',
+ '\x0D',
+ '\x0E',
+ '\x0F',
+ '\x10',
+ '\x11',
+ '\x12',
+ '\x13',
+ '\x14',
+ '\x15',
+ '\x16',
+ '\x17',
+ '\x18',
+ '\x19',
+ '\x1A',
+ '\x1B',
+ '\x1C',
+ '\x1D',
+ '\x1E',
+ '\x1F',
+ '\x20',
+ '\x21', /* ! */
+ '\x22', /* " */
+ '\x23', /* # */
+ '\x24', /* $ */
+ '\x25', /* % */
+ '\x26', /* & */
+ '\x27', /* ' */
+ '\x28', /* ( */
+ '\x29', /* ) */
+ '\x2A', /* * */
+ '\x2B', /* + */
+ '\x2C', /* , */
+ '\x2D', /* - */
+ '\x2E', /* . */
+ '\x2F', /* / */
+ '\x30', /* 0 */
+ '\x31', /* 1 */
+ '\x32', /* 2 */
+ '\x33', /* 3 */
+ '\x34', /* 4 */
+ '\x35', /* 5 */
+ '\x36', /* 6 */
+ '\x37', /* 7 */
+ '\x38', /* 8 */
+ '\x39', /* 9 */
+ '\x3A', /* : */
+ '\x3B', /* ; */
+ '\x3C', /* < */
+ '\x3D', /* = */
+ '\x3E', /* > */
+ '\x3F', /* ? */
+ '\x40', /* @ */
+ '\x61', /* A */
+ '\x62', /* B */
+ '\x63', /* C */
+ '\x64', /* D */
+ '\x65', /* E */
+ '\x66', /* F */
+ '\x67', /* G */
+ '\x68', /* H */
+ '\x69', /* I */
+ '\x6A', /* J */
+ '\x6B', /* K */
+ '\x6C', /* L */
+ '\x6D', /* M */
+ '\x6E', /* N */
+ '\x6F', /* O */
+ '\x70', /* P */
+ '\x71', /* Q */
+ '\x72', /* R */
+ '\x73', /* S */
+ '\x74', /* T */
+ '\x75', /* U */
+ '\x76', /* V */
+ '\x77', /* W */
+ '\x78', /* X */
+ '\x79', /* Y */
+ '\x7A', /* Z */
+ '\x5B', /* [ */
+ '\x5C', /* \ */
+ '\x5D', /* ] */
+ '\x5E', /* ^ */
+ '\x5F', /* _ */
+ '\x60', /* ` */
+ '\x61', /* a */
+ '\x62', /* b */
+ '\x63', /* c */
+ '\x64', /* d */
+ '\x65', /* e */
+ '\x66', /* f */
+ '\x67', /* g */
+ '\x68', /* h */
+ '\x69', /* i */
+ '\x6A', /* j */
+ '\x6B', /* k */
+ '\x6C', /* l */
+ '\x6D', /* m */
+ '\x6E', /* n */
+ '\x6F', /* o */
+ '\x70', /* p */
+ '\x71', /* q */
+ '\x72', /* r */
+ '\x73', /* s */
+ '\x74', /* t */
+ '\x75', /* u */
+ '\x76', /* v */
+ '\x77', /* w */
+ '\x78', /* x */
+ '\x79', /* y */
+ '\x7A', /* z */
+ '\x7B', /* { */
+ '\x7C', /* | */
+ '\x7D', /* } */
+ '\x7E', /* ~ */
+ '\x7F',
+ '\x80',
+ '\x81',
+ '\x82',
+ '\x83',
+ '\x84',
+ '\x85',
+ '\x86',
+ '\x87',
+ '\x88',
+ '\x89',
+ '\x8A',
+ '\x8B',
+ '\x8C',
+ '\x8D',
+ '\x8E',
+ '\x8F',
+ '\x90',
+ '\x91',
+ '\x92',
+ '\x93',
+ '\x94',
+ '\x95',
+ '\x96',
+ '\x97',
+ '\x98',
+ '\x99',
+ '\x9A',
+ '\x9B',
+ '\x9C',
+ '\x9D',
+ '\x9E',
+ '\x9F',
+ '\xA0',
+ '\xA1',
+ '\xA2',
+ '\xA3',
+ '\xA4',
+ '\xA5',
+ '\xA6',
+ '\xA7',
+ '\xA8',
+ '\xA9',
+ '\xAA',
+ '\xAB',
+ '\xAC',
+ '\xAD',
+ '\xAE',
+ '\xAF',
+ '\xB0',
+ '\xB1',
+ '\xB2',
+ '\xB3',
+ '\xB4',
+ '\xB5',
+ '\xB6',
+ '\xB7',
+ '\xB8',
+ '\xB9',
+ '\xBA',
+ '\xBB',
+ '\xBC',
+ '\xBD',
+ '\xBE',
+ '\xBF',
+ '\xC0',
+ '\xC1',
+ '\xC2',
+ '\xC3',
+ '\xC4',
+ '\xC5',
+ '\xC6',
+ '\xC7',
+ '\xC8',
+ '\xC9',
+ '\xCA',
+ '\xCB',
+ '\xCC',
+ '\xCD',
+ '\xCE',
+ '\xCF',
+ '\xD0',
+ '\xD1',
+ '\xD2',
+ '\xD3',
+ '\xD4',
+ '\xD5',
+ '\xD6',
+ '\xD7',
+ '\xD8',
+ '\xD9',
+ '\xDA',
+ '\xDB',
+ '\xDC',
+ '\xDD',
+ '\xDE',
+ '\xDF',
+ '\xE0',
+ '\xE1',
+ '\xE2',
+ '\xE3',
+ '\xE4',
+ '\xE5',
+ '\xE6',
+ '\xE7',
+ '\xE8',
+ '\xE9',
+ '\xEA',
+ '\xEB',
+ '\xEC',
+ '\xED',
+ '\xEE',
+ '\xEF',
+ '\xF0',
+ '\xF1',
+ '\xF2',
+ '\xF3',
+ '\xF4',
+ '\xF5',
+ '\xF6',
+ '\xF7',
+ '\xF8',
+ '\xF9',
+ '\xFA',
+ '\xFB',
+ '\xFC',
+ '\xFD',
+ '\xFE',
+ '\xFF',
+};
diff --git a/src/libknot/util/tolower.h b/src/libknot/util/tolower.h
new file mode 100644
index 0000000..6b9e98c
--- /dev/null
+++ b/src/libknot/util/tolower.h
@@ -0,0 +1,57 @@
+/*!
+ * \file tolower.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Table for converting ASCII characters to lowercase.
+ *
+ * \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_TOLOWER_H_
+#define _KNOT_TOLOWER_H_
+
+#include <stdint.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
+};
+
+/*! \brief Character table mapping uppercase letters to lowercase. */
+extern const uint8_t char_table[CHAR_TABLE_SIZE];
+
+/*!
+ * \brief Converts ASCII character to lowercase.
+ *
+ * \param c ASCII 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];
+}
+
+#endif /* _KNOT_TOLOWER_H_ */
diff --git a/src/libknot/util/utils.c b/src/libknot/util/utils.c
new file mode 100644
index 0000000..17b33a7
--- /dev/null
+++ b/src/libknot/util/utils.c
@@ -0,0 +1,127 @@
+/* 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 <string.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util/utils.h"
+#include "common/WELL1024a.h"
+
+/*----------------------------------------------------------------------------*/
+
+knot_lookup_table_t *knot_lookup_by_name(knot_lookup_table_t *table,
+ const char *name)
+{
+ while (table->name != NULL) {
+ if (strcasecmp(name, table->name) == 0) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_lookup_table_t *knot_lookup_by_id(knot_lookup_table_t *table,
+ int id)
+{
+ while (table->name != NULL) {
+ if (table->id == id) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_strlcpy(char *dst, const char *src, size_t size)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = size;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0) {
+ break;
+ }
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (size != 0) {
+ *d = '\0'; /* NUL-terminate dst */
+ }
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+/*! \brief TLS key for rand seed. */
+static pthread_key_t _qr_key;
+static pthread_once_t _qr_once = PTHREAD_ONCE_INIT;
+
+/*! \brief TLS key initializer. */
+static void _qr_init()
+{
+ (void) pthread_key_create(&_qr_key, NULL);
+ (void) pthread_setspecific(_qr_key, (void*)time(0));
+}
+
+size_t knot_quick_rand()
+{
+ (void) pthread_once(&_qr_once, _qr_init);
+ size_t x = (size_t)pthread_getspecific(_qr_key);
+
+ /* Numerical Recipes in C.
+ * The Art of Scientific Computing, 2nd Edition,
+ * 1992, ISBN 0-521-43108-5.
+ * Page 284.
+ */
+ x = 1664525L * x + 1013904223L;
+ (void) pthread_setspecific(_qr_key, (void*)x);
+ return x;
+}
+
+uint16_t knot_random_id()
+{
+ return (uint16_t)(tls_rand() * ((uint16_t)~0));
+}
+
+struct flock* knot_file_lock(short type, short whence)
+{
+ static struct flock ret;
+ ret.l_type = type;
+ ret.l_start = 0;
+ ret.l_whence = whence;
+ ret.l_len = 0;
+ ret.l_pid = getpid();
+ return &ret;
+}
+
diff --git a/src/libknot/util/utils.h b/src/libknot/util/utils.h
new file mode 100644
index 0000000..f43b8f0
--- /dev/null
+++ b/src/libknot/util/utils.h
@@ -0,0 +1,196 @@
+/*!
+ * \file utils.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Various utilities.
+ *
+ * \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_UTILS_H_
+#define _KNOT_UTILS_H_
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*!
+ * \brief A general purpose lookup table.
+ *
+ * \note Taken from NSD.
+ */
+struct knot_lookup_table {
+ int id;
+ const char *name;
+};
+
+typedef struct knot_lookup_table knot_lookup_table_t;
+
+/*!
+ * \brief Looks up the given name in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param name Name to look up.
+ *
+ * \return Item in the lookup table with the given name or NULL if no such is
+ * present.
+ */
+knot_lookup_table_t *knot_lookup_by_name(knot_lookup_table_t *table,
+ const char *name);
+
+/*!
+ * \brief Looks up the given id in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param id ID to look up.
+ *
+ * \return Item in the lookup table with the given id or NULL if no such is
+ * present.
+ */
+knot_lookup_table_t *knot_lookup_by_id(knot_lookup_table_t *table,
+ int id);
+
+/*!
+ * \brief Strlcpy - safe string copy function, based on FreeBSD implementation.
+ *
+ * http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/
+ *
+ * \param dst Destination string.
+ * \param src Source string.
+ * \param size How many characters to copy - 1.
+ *
+ * \return strlen(src), if retval >= siz, truncation occurred.
+ */
+size_t knot_strlcpy(char *dst, const char *src, size_t size);
+
+/*
+ * Writing / reading arbitrary data to / from wireformat.
+ */
+
+/*!
+ * \brief Reads 2 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 2 bytes from.
+ *
+ * \return The 2 bytes read, in inverse endian.
+ */
+static inline uint16_t knot_wire_read_u16(const uint8_t *pos)
+{
+ return (pos[0] << 8) | pos[1];
+}
+
+/*!
+ * \brief Reads 4 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 4 bytes from.
+ *
+ * \return The 4 bytes read, in inverse endian.
+ */
+static inline uint32_t knot_wire_read_u32(const uint8_t *pos)
+{
+ return (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];
+}
+
+/*!
+ * \brief Reads 6 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 6 bytes from.
+ *
+ * \return The 6 bytes read, in inverse endian.
+ */
+static inline uint64_t knot_wire_read_u48(const uint8_t *pos)
+{
+ return ((uint64_t)(pos[0]) << 40) | ((uint64_t)(pos[1]) << 32) | (pos[2] << 24) |
+ (pos[3] << 16) | (pos[4] << 8) | pos[5];
+}
+
+/*!
+ * \brief Writes 2 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 2 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u16(uint8_t *pos, uint16_t data)
+{
+ pos[0] = (uint8_t)((data >> 8) & 0xff);
+ pos[1] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Writes 4 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u32(uint8_t *pos, uint32_t data)
+{
+ pos[0] = (uint8_t)((data >> 24) & 0xff);
+ pos[1] = (uint8_t)((data >> 16) & 0xff);
+ pos[2] = (uint8_t)((data >> 8) & 0xff);
+ pos[3] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Writes 6 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u48(uint8_t *pos, uint64_t data)
+{
+ pos[0] = (uint8_t)((data >> 40) & 0xff);
+ pos[1] = (uint8_t)((data >> 32) & 0xff);
+ pos[2] = (uint8_t)((data >> 24) & 0xff);
+ pos[3] = (uint8_t)((data >> 16) & 0xff);
+ pos[4] = (uint8_t)((data >> 8) & 0xff);
+ pos[5] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Linear congruential generator.
+ *
+ * Simple pseudorandom generator for general purpose.
+ * \warning Do not use for cryptography.
+ * \return Random number <0, (size_t)~0>
+ */
+size_t knot_quick_rand();
+
+uint16_t knot_random_id();
+
+/*!
+ * \brief Helper function for simple locking.
+ *
+ * \param type Type of lock.
+ * \param type Starting position of lock.
+ *
+ * \return Locking structure.
+ */
+struct flock* knot_file_lock(short type, short whence);
+
+#endif /* _KNOT_UTILS_H_ */
+
+/*! @} */
+
diff --git a/src/libknot/util/wire.h b/src/libknot/util/wire.h
new file mode 100644
index 0000000..0a24ff1
--- /dev/null
+++ b/src/libknot/util/wire.h
@@ -0,0 +1,926 @@
+/*!
+ * \file wire.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for manipulating and parsing raw data in DNS packets.
+ *
+ * \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_WIRE_H_
+#define _KNOT_WIRE_H_
+
+#include <stdint.h>
+#include <assert.h>
+
+#include "util/utils.h"
+
+/*! \brief Offset of DNS header fields in wireformat. */
+enum knot_wire_offsets {
+ KNOT_WIRE_OFFSET_ID = 0,
+ KNOT_WIRE_OFFSET_FLAGS1 = 2,
+ KNOT_WIRE_OFFSET_FLAGS2 = 3,
+ KNOT_WIRE_OFFSET_QDCOUNT = 4,
+ KNOT_WIRE_OFFSET_ANCOUNT = 6,
+ KNOT_WIRE_OFFSET_NSCOUNT = 8,
+ KNOT_WIRE_OFFSET_ARCOUNT = 10
+};
+
+/*! \brief Minimum size for some parts of the DNS packet. */
+enum knot_wire_sizes {
+ KNOT_WIRE_HEADER_SIZE = 12,
+ KNOT_WIRE_QUESTION_MIN_SIZE = 5,
+ KNOT_WIRE_RR_MIN_SIZE = 11
+};
+
+/*
+ * Packet header manipulation functions.
+ */
+
+/*!
+ * \brief Returns the ID from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return DNS packet ID.
+ */
+static inline uint16_t knot_wire_get_id(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ID);
+}
+
+/*!
+ * \brief Sets the ID to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param id DNS packet ID.
+ */
+static inline void knot_wire_set_id(uint8_t *packet, uint16_t id)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ID, id);
+}
+
+/*!
+ * \brief Returns the first byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return First byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags1(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1);
+}
+
+/*!
+ * \brief Sets the first byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags1 First byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags1(uint8_t *packet, uint8_t flags1)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) = flags1;
+}
+
+/*!
+ * \brief Returns the second byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Second byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags2(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2);
+}
+
+/*!
+ * \brief Sets the second byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags2 Second byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags2(uint8_t *packet, uint8_t flags2)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) = flags2;
+}
+
+/*!
+ * \brief Returns the QDCOUNT (count of Question entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return QDCOUNT (count of Question entries in the packet).
+ */
+static inline uint16_t knot_wire_get_qdcount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT);
+}
+
+/*!
+ * \brief Sets the QDCOUNT (count of Question entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param qdcount QDCOUNT (count of Question entries in the packet).
+ */
+static inline void knot_wire_set_qdcount(uint8_t *packet, uint16_t qdcount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, qdcount);
+}
+
+/*!
+ * \brief Returns the ANCOUNT (count of Answer entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ANCOUNT (count of Answer entries in the packet).
+ */
+static inline uint16_t knot_wire_get_ancount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT);
+}
+
+/*!
+ * \brief Sets the ANCOUNT (count of Answer entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param ancount ANCOUNT (count of Answer entries in the packet).
+ */
+static inline void knot_wire_set_ancount(uint8_t *packet, uint16_t ancount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, ancount);
+}
+
+/*!
+ * \brief Returns the NSCOUNT (count of Authority entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return NSCOUNT (count of Authority entries in the packet).
+ */
+static inline uint16_t knot_wire_get_nscount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT);
+}
+
+/*!
+ * \brief Sets the NSCOUNT (count of Authority entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param nscount NSCOUNT (count of Authority entries in the packet).
+ */
+static inline void knot_wire_set_nscount(uint8_t *packet, uint16_t nscount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, nscount);
+}
+
+/*!
+ * \brief Returns the ARCOUNT (count of Additional entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ARCOUNT (count of Additional entries in the packet).
+ */
+static inline uint16_t knot_wire_get_arcount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT);
+}
+
+/*!
+ * \brief Sets the ARCOUNT (count of Additional entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param arcount ARCOUNT (count of Additional entries in the packet).
+ */
+static inline void knot_wire_set_arcount(uint8_t *packet, uint16_t arcount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, arcount);
+}
+
+/*
+ * Packet header flags manipulation functions.
+ */
+/*! \brief Constants for DNS header flags in the first flags byte. */
+enum knot_wire_flags1_consts {
+ KNOT_WIRE_RD_MASK = (uint8_t)0x01U, /*!< RD bit mask. */
+ KNOT_WIRE_RD_SHIFT = 0, /*!< RD bit shift. */
+ KNOT_WIRE_TC_MASK = (uint8_t)0x02U, /*!< TC bit mask. */
+ KNOT_WIRE_TC_SHIFT = 1, /*!< TC bit shift. */
+ KNOT_WIRE_AA_MASK = (uint8_t)0x04U, /*!< AA bit mask. */
+ KNOT_WIRE_AA_SHIFT = 2, /*!< AA bit shift. */
+ KNOT_WIRE_OPCODE_MASK = (uint8_t)0x78U, /*!< OPCODE mask. */
+ KNOT_WIRE_OPCODE_SHIFT = 3, /*!< OPCODE shift. */
+ KNOT_WIRE_QR_MASK = (uint8_t)0x80U, /*!< QR bit mask. */
+ KNOT_WIRE_QR_SHIFT = 7 /*!< QR bit shift. */
+};
+
+/*! \brief Constants for DNS header flags in the second flags byte. */
+enum knot_wire_flags2_consts {
+ KNOT_WIRE_RCODE_MASK = (uint8_t)0x0fU, /*!< RCODE mask. */
+ KNOT_WIRE_RCODE_SHIFT = 0, /*!< RCODE shift. */
+ KNOT_WIRE_CD_MASK = (uint8_t)0x10U, /*!< CD bit mask. */
+ KNOT_WIRE_CD_SHIFT = 4, /*!< CD bit shift. */
+ KNOT_WIRE_AD_MASK = (uint8_t)0x20U, /*!< AD bit mask. */
+ KNOT_WIRE_AD_SHIFT = 5, /*!< AD bit shift. */
+ KNOT_WIRE_Z_MASK = (uint8_t)0x40U, /*!< Zero bit mask. */
+ KNOT_WIRE_Z_SHIFT = 6, /*!< Zero bit shift. */
+ KNOT_WIRE_RA_MASK = (uint8_t)0x80U, /*!< RA bit mask. */
+ KNOT_WIRE_RA_SHIFT = 7 /*!< RA bit shift. */
+};
+
+/*
+ * Functions for getting / setting / clearing flags and codes directly in packet
+ */
+
+/*!
+ * \brief Returns the RD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_rd(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_rd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_flags_clear_rd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the TC bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_tc(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_tc(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_tc(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_aa(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_aa(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_aa(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return OPCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_opcode(const uint8_t *packet)
+{
+ return (*(packet + KNOT_WIRE_OFFSET_FLAGS1)
+ & KNOT_WIRE_OPCODE_MASK) >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_set_opcode(uint8_t *packet, short opcode)
+{
+ uint8_t *flags1 = packet + KNOT_WIRE_OFFSET_FLAGS1;
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the QR bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_qr(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_qr(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_qr(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return RCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_rcode(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2)
+ & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_set_rcode(uint8_t *packet, short rcode)
+{
+ uint8_t *flags2 = packet + KNOT_WIRE_OFFSET_FLAGS2;
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the CD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_cd(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_cd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_cd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ad(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ad(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ad(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the Zero bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_z(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_z(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_z(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ra(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ra(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ra(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Functions for getting / setting / clearing flags in flags variable
+ */
+
+/*!
+ * \brief Returns the RD bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the RD bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_rd(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_rd(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_flags_clear_rd(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the TC bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_tc(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_tc(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_tc(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the AA bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_aa(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_aa(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_aa(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return OPCODE
+ */
+static inline uint8_t knot_wire_flags_get_opcode(uint8_t flags1)
+{
+ return (flags1 & KNOT_WIRE_OPCODE_MASK)
+ >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_flags_set_opcode(uint8_t *flags1, short opcode)
+{
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the QR bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_qr(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_qr(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_qr(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from the second byte of flags.
+ *
+ * \param flags2 First byte of DNS header flags.
+ *
+ * \return RCODE
+ */
+static inline uint8_t knot_wire_flags_get_rcode(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_flags_set_rcode(uint8_t *flags2, short rcode)
+{
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the CD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_cd(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_cd(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_cd(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the AD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ad(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ad(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ad(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the Zero bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_z(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_z(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_z(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the RA bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ra(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ra(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ra(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Pointer manipulation
+ */
+
+enum knot_wire_pointer_consts {
+ /*! \brief DNS packet pointer designation (first two bits set to 1). */
+ KNOT_WIRE_PTR = (uint8_t)0xc0U
+};
+
+/*!
+ * \brief Creates a DNS packet pointer and stores it in wire format.
+ *
+ * \param pos Position where tu put the pointer.
+ * \param ptr Relative position of the item to which the pointer should point in
+ * the wire format of the packet.
+ */
+static inline void knot_wire_put_pointer(uint8_t *pos, size_t ptr)
+{
+ uint16_t p = ptr;
+ knot_wire_write_u16(pos, p);
+ assert((pos[0] & KNOT_WIRE_PTR) == 0);
+ pos[0] |= KNOT_WIRE_PTR;
+}
+
+static inline int knot_wire_is_pointer(const uint8_t *pos)
+{
+ return ((pos[0] & KNOT_WIRE_PTR) != 0);
+}
+
+static inline size_t knot_wire_get_pointer(const uint8_t *pos)
+{
+ /*! \todo memcpy() is not needed, may be directly assigned. */
+ uint16_t p = 0;
+ memcpy(&p, pos, 2);
+ p &= ~KNOT_WIRE_PTR;
+
+ uint16_t p2 = knot_wire_read_u16((uint8_t *)&p);
+ return p2;
+}
+
+#endif /* _KNOT_WIRE_H_ */
+
+/*! @} */
diff --git a/src/libknot/zone/dname-table.c b/src/libknot/zone/dname-table.c
new file mode 100644
index 0000000..c41b4bd
--- /dev/null
+++ b/src/libknot/zone/dname-table.c
@@ -0,0 +1,310 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "zone/dname-table.h"
+#include "util/error.h"
+
+/*!< Tree functions. */
+TREE_DEFINE(dname_table_node, avl);
+
+struct knot_dname_table_fnc_data {
+ void (*func)(knot_dname_t *dname, void *data);
+ void *data;
+};
+
+static void knot_dname_table_apply(struct dname_table_node *node, void *data)
+{
+ assert(data != NULL);
+ assert(node != NULL);
+ struct knot_dname_table_fnc_data *d =
+ (struct knot_dname_table_fnc_data *)data;
+ d->func(node->dname, d->data);
+}
+
+/*!
+ * \brief Comparison function to be used with tree.
+ *
+ * \param n1 First dname to be compared.
+ * \param n2 Second dname to be compared.
+ *
+ * \return strncmp of dname's wireformats.
+ */
+static int compare_dname_table_nodes(struct dname_table_node *n1,
+ struct dname_table_node *n2)
+{
+ assert(n1 && n2);
+ return (strncmp((char *)n1->dname->name, (char *)n2->dname->name,
+ (n1->dname->size < n2->dname->size) ?
+ (n1->dname->size):(n2->dname->size)));
+}
+
+/*!
+ * \brief Deletes tree node along with its domain name.
+ *
+ * \param node Node to be deleted.
+ * \param data If <> 0, dname in the node will be freed as well.
+ */
+static void delete_dname_table_node(struct dname_table_node *node, void *data)
+{
+ if ((ssize_t)data == 1) {
+ knot_dname_release(node->dname);
+ } else if ((ssize_t)data == 2) {
+ knot_dname_free(&node->dname);
+ }
+
+ /*!< \todo it would be nice to set pointers to NULL. */
+ free(node);
+}
+
+static void knot_dname_table_delete_subtree(struct dname_table_node *root)
+{
+ if (root == NULL) {
+ return;
+ }
+
+ knot_dname_table_delete_subtree(root->avl.avl_left);
+ knot_dname_table_delete_subtree(root->avl.avl_right);
+ free(root);
+}
+
+static int knot_dname_table_copy_node(const struct dname_table_node *from,
+ struct dname_table_node **to)
+{
+ if (from == NULL) {
+ return KNOT_EOK;
+ }
+
+ *to = (struct dname_table_node *)
+ malloc(sizeof(struct dname_table_node));
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(*to, 0, sizeof(struct dname_table_node));
+
+ (*to)->dname = from->dname;
+ knot_dname_retain((*to)->dname);
+ (*to)->avl.avl_height = from->avl.avl_height;
+
+ int ret = knot_dname_table_copy_node(from->avl.avl_left,
+ &(*to)->avl.avl_left);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_dname_table_copy_node(from->avl.avl_right,
+ &(*to)->avl.avl_right);
+ if (ret != KNOT_EOK) {
+ knot_dname_table_delete_subtree((*to)->avl.avl_left);
+ (*to)->avl.avl_left = NULL;
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+knot_dname_table_t *knot_dname_table_new()
+{
+ knot_dname_table_t *ret = malloc(sizeof(knot_dname_table_t));
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ ret->tree = malloc(sizeof(table_tree_t));
+ if (ret->tree == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret);
+ return NULL;
+ }
+
+ TREE_INIT(ret->tree, compare_dname_table_nodes);
+
+ ret->id_counter = 1;
+
+ return ret;
+}
+
+knot_dname_t *knot_dname_table_find_dname(const knot_dname_table_t *table,
+ knot_dname_t *dname)
+{
+ if (table == NULL || dname == NULL) {
+ return NULL;
+ }
+
+ struct dname_table_node *node = NULL;
+ struct dname_table_node sought;
+ sought.dname = dname;
+
+ node = TREE_FIND(table->tree, dname_table_node, avl, &sought);
+
+ if (node == NULL) {
+ return NULL;
+ } else {
+ /* Increase reference counter. */
+ knot_dname_retain(node->dname);
+
+ return node->dname;
+ }
+}
+
+int knot_dname_table_add_dname(knot_dname_table_t *table,
+ knot_dname_t *dname)
+{
+ if (dname == NULL || table == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Node for insertion has to be created */
+ struct dname_table_node *node =
+ malloc(sizeof(struct dname_table_node));
+ CHECK_ALLOC_LOG(node, KNOT_ENOMEM);
+
+ // convert the dname to lowercase
+ knot_dname_to_lower(dname);
+
+ node->dname = dname;
+ node->avl.avl_height = 0;
+ node->avl.avl_left = NULL;
+ node->avl.avl_right = NULL;
+
+ node->dname->id = table->id_counter++;
+ assert(node->dname->id != 0);
+
+ /* Increase reference counter. */
+ knot_dname_retain(dname);
+
+ TREE_INSERT(table->tree, dname_table_node, avl, node);
+ return KNOT_EOK;
+}
+
+int knot_dname_table_add_dname_check(knot_dname_table_t *table,
+ knot_dname_t **dname)
+{
+ knot_dname_t *found_dname = NULL;
+
+ if (table == NULL || dname == NULL || *dname == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Fetch dname, need to release it later. */
+ found_dname = knot_dname_table_find_dname(table ,*dname);
+
+ if (!found_dname) {
+ /* Store reference in table. */
+ return knot_dname_table_add_dname(table, *dname);
+ } else {
+ /*! \todo Remove the check for equality. */
+ if (found_dname != *dname) {
+ /* Replace dname with found. */
+ knot_dname_release(*dname);
+ *dname = found_dname;
+ return 1; /*! \todo Error code? */
+
+ } else {
+
+ /* If the dname is already in the table, there is already
+ * a reference to it.
+ */
+ knot_dname_release(found_dname);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_dname_table_shallow_copy(knot_dname_table_t *from,
+ knot_dname_table_t *to)
+{
+ to->id_counter = from->id_counter;
+
+ if (to->tree == NULL) {
+ to->tree = malloc(sizeof(table_tree_t));
+ if (to->tree == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ TREE_INIT(to->tree, compare_dname_table_nodes);
+ }
+
+ return knot_dname_table_copy_node(from->tree->th_root,
+ &to->tree->th_root);
+}
+
+void knot_dname_table_free(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but not the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, 0);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_deep_free(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but free the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, (void *) 1);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_destroy(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but free the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, (void *) 2);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_tree_inorder_apply(const knot_dname_table_t *table,
+ void (*applied_function)(knot_dname_t *node,
+ void *data),
+ void *data)
+{
+ struct knot_dname_table_fnc_data d;
+ d.data = data;
+ d.func = applied_function;
+
+ TREE_FORWARD_APPLY(table->tree, dname_table_node, avl,
+ knot_dname_table_apply, &d);
+}
+
diff --git a/src/libknot/zone/dname-table.h b/src/libknot/zone/dname-table.h
new file mode 100644
index 0000000..dd86eaf
--- /dev/null
+++ b/src/libknot/zone/dname-table.h
@@ -0,0 +1,168 @@
+/*!
+ * \file dname-table.h
+ *
+ * \author Jan Kadlec <jan.kadlec.@nic.cz>
+ *
+ * \brief Structures representing dname table and functions for
+ * manipulating these structures.
+ *
+ * \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_DNAME_TABLE_H_
+#define _KNOT_DNAME_TABLE_H_
+
+#include <config.h>
+
+#include "common/tree.h"
+
+#include "dname.h"
+#include "common.h"
+
+
+/*!
+ * \brief Structure encapsulating
+ */
+struct dname_table_node {
+ knot_dname_t *dname; /*!< Dname stored in node. */
+ TREE_ENTRY(dname_table_node) avl; /*!< Tree variables. */
+};
+
+/*!
+ * \brief Tree structure.
+ */
+typedef TREE_HEAD(avl, dname_table_node) table_tree_t;
+
+/*!
+ * \brief Structure holding tree together with dname ID counter.
+ */
+struct knot_dname_table {
+ unsigned int id_counter; /*!< ID counter (starts from 1) */
+ table_tree_t *tree; /*!< AVL tree */
+};
+
+typedef struct knot_dname_table knot_dname_table_t;
+
+/*!
+ * \brief Creates new empty domain name table.
+ *
+ * \retval Created table on success.
+ * \retval NULL on memory error.
+ */
+knot_dname_table_t *knot_dname_table_new();
+
+/*!
+ * \brief Finds name in the domain name table.
+ *
+ * \note Reference count to dname will be incremented, caller is responsible
+ * for releasing it.
+ *
+ * \param table Domain name table to be searched.
+ * \param dname Dname to be searched.
+ *
+ * \retval Pointer to found dname when dname is present in the table.
+ * \retval NULL when dname is not present.
+ */
+knot_dname_t *knot_dname_table_find_dname(const knot_dname_table_t *table,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Adds domain name to domain name table.
+ *
+ * \param table Domain name table to be added to.
+ * \param dname Domain name to be added.
+ *
+ * \warning Function does not check for duplicates!
+ *
+ * \note This function encapsulates dname in a structure and saves it to a tree.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM when memory runs out.
+ */
+int knot_dname_table_add_dname(knot_dname_table_t *table,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Adds domain name to domain name table and checks for duplicates.
+ *
+ * \param table Domain name table to be added to.
+ * \param dname Domain name to be added.
+ *
+ * \note This function encapsulates dname in a structure and saves it to a tree.
+ * \note If a duplicate is found, \a dname is replaced by the name from table.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM when memory runs out.
+ */
+int knot_dname_table_add_dname_check(knot_dname_table_t *table,
+ knot_dname_t **dname);
+
+/*!
+ * \brief Creates a shallow copy of the domain name table.
+ *
+ * Expects an existing knot_dname_table_t structure to be passed via \a to,
+ * and fills it with the same data (domain names) as the original. Actual
+ * tree nodes are created, but domain names are not copied (just referenced).
+ *
+ * \param from Original domain name table.
+ * \param to Copy of the domain name table.
+ */
+int knot_dname_table_shallow_copy(knot_dname_table_t *from,
+ knot_dname_table_t *to);
+
+/*!
+ * \brief Frees dname table without its nodes. Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_free(knot_dname_table_t **table);
+
+/*!
+ * \brief Frees dname table and all its nodes (and release dnames in the nodes)
+ * Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_deep_free(knot_dname_table_t **table);
+
+/*!
+ * \brief Frees dname table and all its nodes (including dnames in the nodes)
+ * Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_destroy(knot_dname_table_t **table);
+
+/*!
+ * \brief Encapsulation of domain name table tree traversal function.
+ *
+ * \param table Table containing tree to be traversed.
+ * \param applied_function Function to be used to process nodes.
+ * \param data Data to be passed to processing function.
+ */
+void knot_dname_table_tree_inorder_apply(const knot_dname_table_t *table,
+ void (*applied_function)(knot_dname_t *dname,
+ void *data),
+ void *data);
+
+
+#endif // _KNOT_DNAME_TABLE_H_
+
+/*! @} */
+
diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c
new file mode 100644
index 0000000..1d2f938
--- /dev/null
+++ b/src/libknot/zone/node.c
@@ -0,0 +1,906 @@
+/* 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>
+#include <assert.h>
+#include <stdio.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/node.h"
+#include "rrset.h"
+#include "util/error.h"
+#include "common/skip-list.h"
+#include "common/tree.h"
+#include "util/debug.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the delegation point flag
+ *
+ * \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.
+ */
+static inline uint8_t knot_node_flags_get_deleg(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_DELEG;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the delegation point flag.
+ *
+ * \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 Returns the non-authoritative node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the non-authoritative node flag set if it was set in
+ * \a flags.
+ */
+static inline uint8_t knot_node_flags_get_nonauth(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_NONAUTH;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the non-authoritative node flag.
+ *
+ * \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 Returns the old node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the old node flag set if it was set in \a flags.
+ */
+static inline uint8_t knot_node_flags_get_old(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the old node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_new(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the new node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the new node flag set if it was set in \a flags.
+ */
+static inline uint8_t knot_node_flags_get_new(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the new node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_old(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline void knot_node_flags_clear_new(uint8_t *flags)
+{
+ *flags &= ~KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline void knot_node_flags_clear_old(uint8_t *flags)
+{
+ *flags &= ~KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the two keys as RR types.
+ *
+ * \note This function may be used in data structures requiring generic
+ * comparation function.
+ *
+ * \param key1 First RR type.
+ * \param key2 Second RR type.
+ *
+ * \retval 0 if \a key1 is equal to \a key2.
+ * \retval < 0 if \a key1 is lower than \a key2.
+ * \retval > 0 if \a key1 is higher than \a key2.
+ */
+static int compare_rrset_types(void *rr1, void *rr2)
+{
+ knot_rrset_t *rrset1 = (knot_rrset_t *)rr1;
+ knot_rrset_t *rrset2 = (knot_rrset_t *)rr2;
+ return ((rrset1->type > rrset2->type) ? 1 :
+ (rrset1->type == rrset2->type) ? 0 : -1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_node_zone_gen_is_new(const knot_node_t *node)
+{
+ assert(node->zone != NULL);
+ knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
+ assert(cont != NULL);
+ return knot_zone_contents_gen_is_new(cont);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_node_zone_gen_is_old(const knot_node_t *node)
+{
+ assert(node->zone != NULL);
+ knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
+ assert(cont != NULL);
+ return knot_zone_contents_gen_is_old(cont);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+ uint8_t flags)
+{
+ knot_node_t *ret = (knot_node_t *)calloc(1, sizeof(knot_node_t));
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ /* Store reference to owner. */
+ knot_dname_retain(owner);
+ ret->owner = owner;
+ knot_node_set_parent(ret, parent);
+ ret->rrset_tree = gen_tree_new(compare_rrset_types);
+ ret->flags = flags;
+
+ assert(ret->children == 0);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
+ int merge)
+{
+ int ret = 0;
+
+ if ((ret = (gen_tree_add(node->rrset_tree, rrset,
+ (merge) ? knot_rrset_merge : NULL))) < 0) {
+ dbg_node("Failed to add rrset to node->rrset_tree.\n");
+ return KNOT_ERROR;
+ }
+
+ if (ret >= 0) {
+ node->rrset_count += (ret > 0 ? 0 : 1);
+ return ret;
+ } else {
+ return KNOT_ERROR;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_node_rrset(const knot_node_t *node,
+ uint16_t type)
+{
+ assert(node != NULL);
+ assert(node->rrset_tree != NULL);
+ knot_rrset_t rrset;
+ rrset.type = type;
+ return (const knot_rrset_t *)gen_tree_find(node->rrset_tree, &rrset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_node_get_rrset(knot_node_t *node, uint16_t type)
+{
+ knot_rrset_t rrset;
+ rrset.type = type;
+ return (knot_rrset_t *)gen_tree_find(node->rrset_tree, &rrset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type)
+{
+ knot_rrset_t dummy_rrset;
+ dummy_rrset.type = type;
+ knot_rrset_t *rrset =
+ (knot_rrset_t *)gen_tree_find(node->rrset_tree, &dummy_rrset);
+ if (rrset != NULL) {
+ gen_tree_remove(node->rrset_tree, rrset);
+ node->rrset_count--;
+ }
+ return rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_remove_all_rrsets(knot_node_t *node)
+{
+ // remove RRSets but do not delete them
+ gen_tree_clear(node->rrset_tree);
+ node->rrset_count = 0;
+
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_node_rrset_count(const knot_node_t *node)
+{
+ return node->rrset_count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+struct knot_node_save_rrset_arg {
+ knot_rrset_t **array;
+ size_t count;
+};
+
+static void save_rrset_to_array(void *node, void *data)
+{
+ knot_rrset_t *rrset = (knot_rrset_t *)node;
+ struct knot_node_save_rrset_arg *args =
+ (struct knot_node_save_rrset_arg *)data;
+ args->array[args->count++] = rrset;
+}
+
+knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
+{
+// knot_node_dump(node, 1);
+ if (node->rrset_count == 0) {
+ return NULL;
+ }
+ knot_rrset_t **rrsets = (knot_rrset_t **)malloc(
+ node->rrset_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(rrsets, NULL);
+ struct knot_node_save_rrset_arg args;
+ args.array = rrsets;
+ args.count = 0;
+
+ gen_tree_apply_inorder(node->rrset_tree, save_rrset_to_array,
+ &args);
+
+ assert(args.count == node->rrset_count);
+
+ return rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t **knot_node_rrsets(const knot_node_t *node)
+{
+ //knot_node_dump((knot_node_t *)node, (void*)1);
+ if (node->rrset_count == 0) {
+ return NULL;
+ }
+
+ knot_rrset_t **rrsets = (knot_rrset_t **)malloc(
+ node->rrset_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(rrsets, NULL);
+ struct knot_node_save_rrset_arg args;
+ args.array = rrsets;
+ args.count = 0;
+
+ gen_tree_apply_inorder(node->rrset_tree, save_rrset_to_array,
+ &args);
+
+ assert(args.count == node->rrset_count);
+ assert(args.count);
+
+ return (const knot_rrset_t **)rrsets;
+
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_parent(const knot_node_t *node,
+ int check_version)
+{
+// assert(!check_version
+// || (node->zone != NULL && node->zone->contents != NULL));
+
+ knot_node_t *parent = node->parent;
+
+ if (check_version && node->zone != NULL) {
+ int new_gen = knot_node_zone_gen_is_new(node);
+// short ver = knot_node_zone_generation(node);
+
+ /*! \todo Remove, this will not be true during the reference
+ * fixing.
+ */
+// assert(new_gen || parent == NULL
+// || !knot_node_is_new(parent));
+
+ if (new_gen && parent != NULL) {
+ // we want the new node
+ assert(node->parent->new_node != NULL);
+ parent = parent->new_node;
+ }
+ }
+
+ return parent;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_parent(knot_node_t *node, knot_node_t *parent)
+{
+ // decrease number of children of previous parent
+ if (node->parent != NULL) {
+ --parent->children;
+ }
+ // set the parent
+ node->parent = parent;
+
+ // increase the count of children of the new parent
+ if (parent != NULL) {
+ ++parent->children;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_node_children(const knot_node_t *node)
+{
+ return node->children;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_previous(const knot_node_t *node,
+ int check_version)
+{
+ return knot_node_get_previous(node, check_version);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_get_previous(const knot_node_t *node,
+ int check_version)
+{
+ assert(!check_version
+ || (node->zone != NULL && node->zone->contents != NULL));
+
+ knot_node_t *prev = node->prev;
+
+ if (check_version && prev != NULL) {
+ 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) { // we want old node
+ while (knot_node_is_new(prev)) {
+ prev = prev->prev;
+ }
+ assert(!knot_node_is_new(prev));
+ } else if (new_gen) { // we want new node
+ while (knot_node_is_old(prev)) {
+ if (prev->new_node) {
+ prev = prev->new_node;
+ } else {
+ prev = prev;
+ }
+ }
+ assert(knot_node_is_new(prev));
+ }
+ }
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_previous(knot_node_t *node, knot_node_t *prev)
+{
+ node->prev = prev;
+ if (prev != NULL) {
+ // set the prev pointer of the next node to the given node
+ if (prev->next != NULL) {
+ assert(prev->next->prev == prev);
+ prev->next->prev = node;
+ }
+ node->next = prev->next;
+ prev->next = node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_nsec3_node(const knot_node_t *node,
+ int check_version)
+{
+ knot_node_t *nsec3_node = node->nsec3_node;
+ if (nsec3_node == NULL) {
+ return NULL;
+ }
+
+ if (check_version) {
+ 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);
+ assert(new_gen || !knot_node_is_new(nsec3_node));
+ if (old_gen && knot_node_is_new(nsec3_node)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(nsec3_node)) {
+ nsec3_node = nsec3_node->new_node;
+ }
+ }
+
+ return nsec3_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_nsec3_node(knot_node_t *node, knot_node_t *nsec3_node)
+{
+ node->nsec3_node = nsec3_node;
+ if (nsec3_node != NULL) {
+ nsec3_node->nsec3_referer = node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_node_owner(const knot_node_t *node)
+{
+ return node->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_node_get_owner(const knot_node_t *node)
+{
+ return node->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+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;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_wildcard_child(const knot_node_t *node,
+ int check_version)
+{
+ knot_node_t *w = node->wildcard_child;
+
+ if (check_version && w != 0) {
+ 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(w)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(w)) {
+ assert(w->new_node != NULL);
+ w = w->new_node;
+ }
+ }
+
+ return w;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_wildcard_child(knot_node_t *node,
+ knot_node_t *wildcard_child)
+{
+ node->wildcard_child = wildcard_child;
+// assert(wildcard_child->parent == node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+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;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_new_node(const knot_node_t *node)
+{
+ return node->new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_get_new_node(const knot_node_t *node)
+{
+ return node->new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_new_node(knot_node_t *node,
+ knot_node_t *new_node)
+{
+ node->new_node = new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone)
+{
+ node->zone = zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_update_ref(knot_node_t **ref)
+{
+ if (*ref != NULL && knot_node_is_old(*ref)) {
+ *ref = (*ref)->new_node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_update_refs(knot_node_t *node)
+{
+ /* CLEANUP */
+ /* OMG! */
+ // reference to previous node
+ knot_node_update_ref(&node->prev);
+// if (node->prev && knot_node_is_old(node->prev)) {
+// assert(node->prev->new_node != NULL);
+// node->prev = node->prev->new_node;
+// }
+
+ // reference to next node
+ knot_node_update_ref(&node->next);
+// if (node->next && knot_node_is_old(node->next)) {
+// assert(node->next->new_node != NULL);
+// node->next = node->next->new_node;
+// }
+
+ // reference to parent
+// if (node->parent && knot_node_is_old(node->parent)) {
+// assert(node->parent->new_node != NULL);
+// // do not use the API function to set parent, so that children count
+// // is not changed
+// //knot_node_set_parent(node, node->parent->new_node);
+// node->parent = node->parent->new_node;
+// }
+ knot_node_update_ref(&node->parent);
+
+ // reference to wildcard child
+ knot_node_update_ref(&node->wildcard_child);
+// if (node->wildcard_child && knot_node_is_old(node->wildcard_child)) {
+// assert(node->wildcard_child->new_node != NULL);
+// node->wildcard_child = node->wildcard_child->new_node;
+// }
+
+ // reference to NSEC3 node
+ knot_node_update_ref(&node->nsec3_node);
+// if (node->nsec3_node && knot_node_is_old(node->nsec3_node)) {
+// assert(node->nsec3_node->new_node != NULL);
+// node->nsec3_node = node->nsec3_node->new_node;
+// }
+
+ // reference to NSEC3 referrer
+ knot_node_update_ref(&node->nsec3_referer);
+// if (node->nsec3_referer && knot_node_is_old(node->nsec3_referer)) {
+// assert(node->nsec3_referer->new_node != NULL);
+// node->nsec3_referer = node->nsec3_referer->new_node;
+// }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_deleg_point(knot_node_t *node)
+{
+ knot_node_flags_set_deleg(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_deleg_point(const knot_node_t *node)
+{
+ return knot_node_flags_get_deleg(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_non_auth(knot_node_t *node)
+{
+ knot_node_flags_set_nonauth(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_non_auth(const knot_node_t *node)
+{
+ return knot_node_flags_get_nonauth(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_auth(const knot_node_t *node)
+{
+ return (node->flags == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+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);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_node_free_rrsets_from_tree(void *item, void *data)
+{
+ if (item == NULL) {
+ return;
+ }
+
+ knot_rrset_t *rrset = (knot_rrset_t *)(item);
+ knot_rrset_deep_free(&rrset, 0, 1, *((int *)data));
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames)
+{
+ /* CLEANUP */
+// knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+// for (int i = 0; i < node->rrset_count; i++) {
+// knot_rrset_deep_free(&(rrsets[i]), 0, 1, free_rdata_dnames);
+// }
+
+// free(rrsets);
+
+ char *name = knot_dname_to_str(node->owner);
+ free(name);
+
+ gen_tree_destroy(&node->rrset_tree, knot_node_free_rrsets_from_tree,
+ (void *)&free_rdata_dnames);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_free(knot_node_t **node, int free_owner, int fix_refs)
+{
+ if (node == NULL || *node == NULL) {
+ return;
+ }
+
+ dbg_node("Freeing node.\n");
+ if ((*node)->rrset_tree != NULL) {
+ dbg_node("Freeing RRSets.\n");
+ gen_tree_destroy(&(*node)->rrset_tree, NULL, NULL);
+ }
+
+ /*! \todo Always release owner? */
+ //if (free_owner) {
+ dbg_node("Releasing owner.\n");
+ knot_dname_release((*node)->owner);
+ //}
+
+ // check nodes referencing this node and fix the references
+
+ if (fix_refs) {
+ // previous node
+ dbg_node("Checking previous.\n");
+ if ((*node)->prev && (*node)->prev->next == (*node)) {
+ (*node)->prev->next = (*node)->next;
+ }
+
+ dbg_node("Checking next.\n");
+ if ((*node)->next && (*node)->next->prev == (*node)) {
+ (*node)->next->prev = (*node)->prev;
+ }
+
+ // NSEC3 node
+ dbg_node("Checking NSEC3.\n");
+ if ((*node)->nsec3_node
+ && (*node)->nsec3_node->nsec3_referer == (*node)) {
+ (*node)->nsec3_node->nsec3_referer = NULL;
+ }
+
+ dbg_node("Checking NSEC3 ref.\n");
+ if ((*node)->nsec3_referer
+ && (*node)->nsec3_referer->nsec3_node == (*node)) {
+ (*node)->nsec3_referer->nsec3_node = NULL;
+ }
+
+ // wildcard child node
+ dbg_node("Checking parent's wildcard child.\n");
+ if ((*node)->parent
+ && (*node)->parent->wildcard_child == (*node)) {
+ (*node)->parent->wildcard_child = NULL;
+ }
+
+ // fix parent's children count
+ if ((*node)->parent) {
+ --(*node)->parent->children;
+ }
+ }
+
+ free(*node);
+ *node = NULL;
+
+ dbg_node("Done.\n");
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_compare(knot_node_t *node1, knot_node_t *node2)
+{
+ return knot_dname_compare(node1->owner, node2->owner);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
+{
+ // create new node
+ *to = knot_node_new(from->owner, from->parent, from->flags);
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Free old rrset_tree, as it will be replaced by shallow copy. */
+ gen_tree_destroy(&(*to)->rrset_tree, 0, 0);
+
+ // 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));
+
+ // copy RRSets
+ // copy the skip list with the old references
+ /* CLEANUP */
+ (*to)->rrset_tree = gen_tree_shallow_copy(from->rrset_tree);
+// assert((*to)->rrset_tree != from->rrset_tree);
+// (*to)->rrsets = skip_copy_list(from->rrsets);
+ if ((*to)->rrset_tree == NULL) {
+ free(*to);
+ *to = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h
new file mode 100644
index 0000000..fcb612d
--- /dev/null
+++ b/src/libknot/zone/node.h
@@ -0,0 +1,436 @@
+/*!
+ * \file node.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure representing one node in domain name tree and API for
+ * manipulating it.
+ *
+ * \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_NODE_H_
+#define _KNOT_NODE_H_
+
+#include "dname.h"
+#include "common/skip-list.h"
+#include "rrset.h"
+#include "common/tree.h"
+#include "common/general-tree.h"
+
+struct knot_zone;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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.
+ */
+struct knot_node {
+ knot_dname_t *owner; /*!< Domain name being the owner of this node. */
+ struct knot_node *parent; /*!< Parent node in the name hierarchy. */
+
+ /*! \brief Type-ordered list of RRSets belonging to this node. */
+ general_tree_t *rrset_tree;
+
+ unsigned short rrset_count; /*!< Number of RRSets stored in the node. */
+
+ /*! \brief Wildcard node being the direct descendant of this node. */
+ struct knot_node *wildcard_child;
+
+ /*!
+ * \brief Previous node in canonical order.
+ *
+ * Only authoritative nodes or delegation points are referenced by this,
+ * as only they may contain NSEC records needed for authenticating
+ * negative answers.
+ */
+ struct knot_node *prev;
+
+ struct knot_node *next;
+
+ /*!
+ * \brief NSEC3 node corresponding to this node.
+ *
+ * Such NSEC3 node has owner in form of the hashed domain name of this
+ * node prepended as a single label to the zone name.
+ */
+ struct knot_node *nsec3_node;
+
+ struct knot_node *nsec3_referer;
+
+ /*!
+ * \brief Various flags.
+ *
+ * Currently only two:
+ * 0x01 - node is a delegation point
+ * 0x02 - node is non-authoritative (under a delegation point)
+ * 0x80 - node is old and will be removed (during update)
+ * 0x40 - node is new, should not be used while zone is old
+ */
+ uint8_t flags;
+
+ struct knot_node *new_node;
+
+ unsigned int children;
+
+ /*!
+ * \brief Generation of node to be used.
+ *
+ * If set to 0, the old node will be used. Otherwise new nodes will
+ * be used. This applies when getting some referenced node.
+
+ */
+// short **generation;
+
+ struct knot_zone *zone;
+};
+
+typedef struct knot_node knot_node_t;
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Flags used to mark nodes with some property. */
+typedef enum {
+ /*! \brief Node is a delegation point (i.e. marking a zone cut). */
+ 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)0x80,
+ /*! \brief Node is new and should not be used while zoen is old. */
+ KNOT_NODE_FLAGS_NEW = (uint8_t)0x40
+} knot_node_flags_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates and initializes new node structure.
+ *
+ * \todo Owner reference counter will be increased.
+ *
+ * \param owner Owner of the created node.
+ * \param parent Parent of the created node.
+ * \param flags Document me.
+ *
+ * \todo Document missing parameters.
+ *
+ * \return Newly created node or NULL if an error occured.
+ */
+knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+ uint8_t flags);
+
+/*!
+ * \brief Adds an RRSet to the node.
+ *
+ * \param node Node to add the RRSet to.
+ * \param rrset RRSet to add.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ERROR if the RRSet could not be inserted.
+ */
+int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
+ int merge);
+
+/*!
+ * \brief Returns the RRSet of the given type from the node.
+ *
+ * \param node Node to get the RRSet from.
+ * \param type Type of the RRSet to retrieve.
+ *
+ * \return RRSet from node \a node having type \a type, or NULL if no such
+ * RRSet exists in this node.
+ */
+const knot_rrset_t *knot_node_rrset(const knot_node_t *node,
+ uint16_t type);
+
+/*!
+ * \brief Returns the RRSet of the given type from the node (non-const version).
+ *
+ * \param node Node to get the RRSet from.
+ * \param type Type of the RRSet to retrieve.
+ *
+ * \return RRSet from node \a node having type \a type, or NULL if no such
+ * RRSet exists in this node.
+ */
+knot_rrset_t *knot_node_get_rrset(knot_node_t *node, uint16_t type);
+
+knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type);
+
+void knot_node_remove_all_rrsets(knot_node_t *node);
+
+/*!
+ * \brief Returns number of RRSets in the node.
+ *
+ * \param node Node to get the RRSet count from.
+ *
+ * \return Number of RRSets in \a node.
+ */
+short knot_node_rrset_count(const knot_node_t *node);
+
+/*!
+ * \brief Returns all RRSets from the node.
+ *
+ * \param node Node to get the RRSets from.
+ *
+ * \return Newly allocated array of RRSets or NULL if an error occured.
+ */
+knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node);
+
+/*!
+ * \brief Returns all RRSets from the node.
+ *
+ * \note This function is identical to knot_node_get_rrsets(), only it returns
+ * non-modifiable data.
+ *
+ * \param node Node to get the RRSets from.
+ *
+ * \return Newly allocated array of RRSets or NULL if an error occured.
+ */
+const knot_rrset_t **knot_node_rrsets(const knot_node_t *node);
+
+/*!
+ * \brief Returns the parent of the node.
+ *
+ * \param node Node to get the parent of.
+ *
+ * \return Parent node of the given node or NULL if no parent has been set (e.g.
+ * node in a zone apex has no parent).
+ */
+const knot_node_t *knot_node_parent(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the parent of the node.
+ *
+ * \param node Node to set the parent of.
+ * \param parent Parent to set to the node.
+ */
+void knot_node_set_parent(knot_node_t *node, knot_node_t *parent);
+
+unsigned int knot_node_children(const knot_node_t *node);
+
+/*!
+ * \brief Returns the previous authoritative node or delegation point in
+ * canonical order or the first node in zone.
+ *
+ * \param node Node to get the previous node of.
+ *
+ * \return Previous authoritative node or delegation point in canonical order or
+ * the first node in zone if \a node is the last node in zone.
+ * \retval NULL if previous node is not set.
+ */
+const knot_node_t *knot_node_previous(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Returns the previous authoritative node or delegation point in
+ * canonical order or the first node in zone.
+ *
+ * \note This function is identical to knot_node_previous() except that it
+ * returns non-const node.
+ *
+ * \param node Node to get the previous node of.
+ *
+ * \return Previous authoritative node or delegation point in canonical order or
+ * the first node in zone if \a node is the last node in zone.
+ * \retval NULL if previous node is not set.
+ */
+knot_node_t *knot_node_get_previous(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the previous node of the given node.
+ *
+ * \param node Node to set the previous node to.
+ * \param prev Previous node to set.
+ */
+void knot_node_set_previous(knot_node_t *node, knot_node_t *prev);
+
+/*!
+ * \brief Returns the NSEC3 node corresponding to the given node.
+ *
+ * \param node Node to get the NSEC3 node for.
+ *
+ * \return NSEC3 node corresponding to \a node (i.e. node with owner name
+ * created by concatenating the hash of owner domain name of \a node
+ * and the name of the zone \a node belongs to).
+ * \retval NULL if the NSEC3 node is not set.
+ */
+const knot_node_t *knot_node_nsec3_node(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the corresponding NSEC3 node of the given node.
+ *
+ * \param node Node to set the NSEC3 node to.
+ * \param nsec3_node NSEC3 node to set.
+ */
+void knot_node_set_nsec3_node(knot_node_t *node, knot_node_t *nsec3_node);
+
+/*!
+ * \brief Returns the owner of the node.
+ *
+ * \param node Node to get the owner of.
+ *
+ * \return Owner of the given node.
+ */
+const knot_dname_t *knot_node_owner(const knot_node_t *node);
+
+/*!
+ * \todo Document me.
+ */
+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.
+ *
+ * \param node Node to get the owner of.
+ *
+ * \return Wildcard child of the given node or NULL if it has none.
+ */
+const knot_node_t *knot_node_wildcard_child(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the wildcard child of the node.
+ *
+ * \param node Node to set the wildcard child of.
+ * \param wildcard_child Wildcard child of the node.
+ */
+void knot_node_set_wildcard_child(knot_node_t *node,
+ knot_node_t *wildcard_child);
+
+const knot_node_t *knot_node_current(const knot_node_t *node);
+
+knot_node_t *knot_node_get_current(knot_node_t *node);
+
+const knot_node_t *knot_node_new_node(const knot_node_t *node);
+
+knot_node_t *knot_node_get_new_node(const knot_node_t *node);
+
+void knot_node_set_new_node(knot_node_t *node,
+ knot_node_t *new_node);
+
+void knot_node_set_zone(knot_node_t *node, struct knot_zone *zone);
+
+void knot_node_update_ref(knot_node_t **ref);
+
+void knot_node_update_refs(knot_node_t *node);
+
+/*!
+ * \brief Mark the node as a delegation point.
+ *
+ * \param node Node to mark as a delegation point.
+ */
+void knot_node_set_deleg_point(knot_node_t *node);
+
+/*!
+ * \brief Checks if the node is a delegation point.
+ *
+ * \param node Node to check.
+ *
+ * \retval <> 0 if \a node is marked as delegation point.
+ * \retval 0 otherwise.
+ */
+int knot_node_is_deleg_point(const knot_node_t *node);
+
+/*!
+ * \brief Mark the node as non-authoritative.
+ *
+ * \param node Node to mark as non-authoritative.
+ */
+void knot_node_set_non_auth(knot_node_t *node);
+
+/*!
+ * \brief Checks if the node is non-authoritative.
+ *
+ * \param node Node to check.
+ *
+ * \retval <> 0 if \a node is marked as non-authoritative.
+ * \retval 0 otherwise.
+ */
+int knot_node_is_non_auth(const 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);
+
+void knot_node_set_new(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);
+
+/*!
+ * \brief Destroys the RRSets within the node structure.
+ *
+ * \param node Node to be destroyed.
+ * \param free_rdata_dnames Set to <> 0 if you want to delete ALL domain names
+ * present in RDATA. Set to 0 otherwise. (See
+ * knot_rdata_deep_free().)
+ */
+void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames);
+
+/*!
+ * \brief Destroys the node structure.
+ *
+ * Does not destroy the RRSets within the node.
+ * Also sets the given pointer to NULL.
+ *
+ * \param node Node to be destroyed.
+ * \param free_owner Set to 0 if you do not want the owner domain name to be
+ * destroyed also. Set to <> 0 otherwise.
+ * \param fix_refs
+ *
+ * \todo Document missing parameters.
+ */
+void knot_node_free(knot_node_t **node, int free_owner, int fix_refs);
+
+/*!
+ * \brief Compares two nodes according to their owner.
+ *
+ * \param node1 First node.
+ * \param node2 Second node.
+ *
+ * \retval < 0 if \a node1 goes before \a node2 according to canonical order
+ * of their owner names.
+ * \retval 0 if they are equal.
+ * \retval > 0 if \a node1 goes after \a node2.
+ */
+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);
+
+#endif /* _KNOT_NODE_H_ */
+
+/*! @} */
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
new file mode 100644
index 0000000..d550728
--- /dev/null
+++ b/src/libknot/zone/zone-contents.c
@@ -0,0 +1,2396 @@
+/* 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 "zone/zone-contents.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common/base32hex.h"
+#include "consts.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ void (*func)(knot_node_t *, void *);
+ void *data;
+} knot_zone_tree_func_t;
+
+typedef struct {
+ knot_node_t *first_node;
+ knot_zone_contents_t *zone;
+ knot_node_t *previous_node;
+ int check_ver;
+} knot_zone_adjust_arg_t;
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_apply(knot_zone_tree_node_t *node,
+ void *data)
+{
+ if (node == NULL || data == NULL) {
+ return;
+ }
+
+ knot_zone_tree_func_t *f = (knot_zone_tree_func_t *)data;
+ f->func(node->node, f->data);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Checks if the given node can be inserted into the given zone.
+ *
+ * Checks if both the arguments are non-NULL and if the owner of the node
+ * belongs to the zone (i.e. is a subdomain of the zone apex).
+ *
+ * \param zone Zone to which the node is going to be inserted.
+ * \param node Node to check.
+ *
+ * \retval KNOT_EOK if both arguments are non-NULL and the node belongs to the
+ * zone.
+ * \retval KNOT_EBADARG if either of the arguments is NULL.
+ * \retval KNOT_EBADZONE if the node does not belong to the zone.
+ */
+static int knot_zone_contents_check_node(
+ const knot_zone_contents_t *contents, const knot_node_t *node)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // assert or just check??
+ assert(contents->apex != NULL);
+
+ if (!knot_dname_is_subdomain(node->owner,
+ knot_node_owner(contents->apex))) {
+dbg_zone_exec(
+ char *node_owner = knot_dname_to_str(knot_node_owner(node));
+ char *apex_owner = knot_dname_to_str(contents->apex->owner);
+ dbg_zone("zone: Trying to insert foreign node to a "
+ "zone. Node owner: %s, zone apex: %s\n",
+ node_owner, apex_owner);
+ free(node_owner);
+ free(apex_owner);
+);
+ return KNOT_EBADZONE;
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys all RRSets in a node.
+ *
+ * This function is designed to be used in the tree-iterating functions.
+ *
+ * \param node Node to destroy RRSets from.
+ * \param data Unused parameter.
+ */
+static void knot_zone_contents_destroy_node_rrsets_from_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ int free_rdata_dnames = (int)((intptr_t)data);
+ knot_node_free_rrsets(tnode->node, free_rdata_dnames);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys node owner.
+ *
+ * This function is designed to be used in the tree-iterating functions.
+ *
+ * \param node Node to destroy the owner of.
+ * \param data Unused parameter.
+ */
+static void knot_zone_contents_destroy_node_owner_from_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ UNUSED(data);
+ /*!< \todo change completely! */
+ knot_node_free(&tnode->node, 0, 0);
+}
+
+/*!
+ * \brief Finds and sets wildcard child for given node's owner.
+ *
+ * \param zone Current zone.
+ * \param node Node to be used.
+ */
+static void find_and_set_wildcard_child(knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ knot_dname_t *chopped = knot_dname_left_chop(node->owner);
+ assert(chopped);
+ knot_node_t *wildcard_parent;
+ wildcard_parent =
+ knot_zone_contents_get_node(zone, chopped);
+
+ knot_dname_free(&chopped);
+
+ assert(wildcard_parent); /* it *has* to be there */
+
+ knot_node_set_wildcard_child(wildcard_parent, node);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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_item(knot_rdata_t *rdata,
+ knot_zone_contents_t *zone,
+ knot_node_t *node,
+ int pos)
+{
+ return;
+ const knot_rdata_item_t *dname_item
+ = knot_rdata_item(rdata, pos);
+
+ assert(dname_item);
+
+ if (dname_item != NULL) {
+ knot_dname_t *dname = dname_item->dname;
+ const knot_node_t *n = NULL;
+ const knot_node_t *closest_encloser = NULL;
+ const knot_node_t *prev = NULL;
+
+ if (knot_dname_is_wildcard(dname)) {
+ find_and_set_wildcard_child(zone, node);
+ }
+
+ int ret = knot_zone_contents_find_dname(zone, dname, &n,
+ &closest_encloser, &prev);
+
+ // n = knot_zone_find_node(zone, dname);
+
+ if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) {
+ // TODO: do some cleanup if needed
+ return;
+ }
+
+ assert(ret != KNOT_ZONE_NAME_FOUND
+ || n == closest_encloser);
+
+ if (ret != KNOT_ZONE_NAME_FOUND
+ && (closest_encloser != NULL)) {
+ dbg_zone("Saving closest encloser to RDATA.\n");
+ // save pointer to the closest encloser
+ knot_rdata_item_t *item =
+ knot_rdata_get_item(rdata, pos);
+ assert(item->dname != NULL);
+ assert(item->dname->node == NULL);
+ //skip_insert(list, (void *)item->dname,
+ // (void *)closest_encloser->owner, NULL);
+ item->dname->node = closest_encloser->owner->node;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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,
+ knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ uint16_t type = knot_rrset_type(rrset);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc);
+
+ knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset);
+ knot_rdata_t *rdata = rdata_first;
+
+ if (rdata == NULL) {
+ return;
+ }
+
+ while (rdata->next != rdata_first) {
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata,
+ zone,
+ node,
+ i);
+ }
+ }
+ rdata = rdata->next;
+ }
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata, zone,
+ node, i);
+ }
+ }
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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 void knot_zone_contents_adjust_rrsets(knot_node_t *node,
+ knot_zone_contents_t *zone)
+{
+ //return;
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ short count = knot_node_rrset_count(node);
+
+ assert(count == 0 || rrsets != NULL);
+
+ for (int r = 0; r < count; ++r) {
+ assert(rrsets[r] != NULL);
+ dbg_zone("Adjusting next RRSet.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
+ node);
+ knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
+ if (rrsigs != NULL) {
+ dbg_zone("Adjusting next RRSIGs.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsigs,
+ zone,
+ node);
+ }
+ }
+
+ free(rrsets);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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.
+ */
+static void knot_zone_contents_adjust_node(knot_node_t *node,
+ knot_zone_contents_t *zone,
+ int check_ver)
+{
+
+dbg_zone_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_zone("----- Adjusting node %s -----\n", name);
+ free(name);
+);
+
+ // adjust domain names in RDATA
+ knot_zone_contents_adjust_rrsets(node, zone);
+
+dbg_zone_exec(
+ if (knot_node_parent(node, 1)) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_node_parent(node, check_ver)));
+ dbg_zone("Parent: %s\n", name);
+ dbg_zone("Parent is delegation point: %s\n",
+ knot_node_is_deleg_point(knot_node_parent(node, check_ver))
+ ? "yes" : "no");
+ dbg_zone("Parent is non-authoritative: %s\n",
+ knot_node_is_non_auth(knot_node_parent(node, check_ver))
+ ? "yes" : "no");
+ free(name);
+ } else {
+ dbg_zone("No parent!\n");
+ }
+);
+ // delegation point / non-authoritative node
+ if (knot_node_parent(node, check_ver)
+ && (knot_node_is_deleg_point(knot_node_parent(node, check_ver))
+ || knot_node_is_non_auth(knot_node_parent(node, check_ver)))) {
+ knot_node_set_non_auth(node);
+ } else if (knot_node_rrset(node, KNOT_RRTYPE_NS) != NULL
+ && node != zone->apex) {
+ knot_node_set_deleg_point(node);
+ }
+
+ // authorative node?
+// if (!knot_node_is_non_auth(node)) {
+ zone->node_count++;
+// }
+
+ // assure that owner has proper node
+ if (knot_dname_node(knot_node_owner(node), 0) == NULL) {
+ knot_dname_set_node(knot_node_get_owner(node), node);
+ knot_dname_set_node(knot_node_get_owner(node), node);
+ }
+
+ // NSEC3 node (only if NSEC3 tree is not empty)
+ const knot_node_t *prev;
+ const knot_node_t *nsec3;
+ int match = knot_zone_contents_find_nsec3_for_name(zone,
+ knot_node_owner(node),
+ &nsec3, &prev, check_ver);
+ if (match != KNOT_ZONE_NAME_FOUND) {
+ nsec3 = NULL;
+ }
+
+ knot_node_set_nsec3_node(node, (knot_node_t *)nsec3);
+
+ dbg_zone("Set flags to the node: \n");
+ dbg_zone("Delegation point: %s\n",
+ knot_node_is_deleg_point(node) ? "yes" : "no");
+ dbg_zone("Non-authoritative: %s\n",
+ knot_node_is_non_auth(node) ? "yes" : "no");
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts zone node for faster query processing.
+ *
+ * This function is just a wrapper over knot_zone_adjust_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_node_in_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
+ knot_node_set_previous(node, args->previous_node);
+ args->previous_node = node;
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+ knot_zone_contents_t *zone = args->zone;
+
+ knot_zone_contents_adjust_node(node, zone, args->check_ver);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
+ knot_node_set_previous(node, args->previous_node);
+ args->previous_node = node;
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+
+ /* Not needed anymore. */
+// knot_zone_contents_t *zone = args->zone;
+// knot_zone_contents_adjust_nsec3_node(node, zone, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \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.
+ */
+static 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(
+ char *n = knot_dname_to_str(name);
+ dbg_zone("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");
+
+ char *name_b32 = NULL;
+ size_t size = base32hex_encode_alloc((char *)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);
+ if (name_b32 != NULL) {
+ free(name_b32);
+ }
+ return KNOT_ECRYPTO;
+ }
+
+ assert(name_b32 != NULL);
+ free(hashed_name);
+
+ dbg_zone("Base32-encoded hash: %s\n", name_b32);
+
+ /* Will be returned to caller, make sure it is released after use. */
+ *nsec3_name = knot_dname_new_from_str(name_b32, size, NULL);
+
+ free(name_b32);
+
+ if (*nsec3_name == NULL) {
+ dbg_zone("Error while creating domain name for hashed"
+ " name.\n");
+ return KNOT_ERROR;
+ }
+
+ 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);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find the given domain name in the zone tree.
+ *
+ * \param zone Zone to search in.
+ * \param name Domain name to find.
+ * \param node Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a name in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval <> 0 if the domain name was found. In such case \a node holds the
+ * zone node with \a name as its owner. \a previous is set
+ * properly.
+ * \retval 0 if the domain name was not found. \a node may hold any (or none)
+ * node. \a previous is set properly.
+ */
+static int knot_zone_contents_find_in_tree(knot_zone_tree_t *tree,
+ const knot_dname_t *name,
+ knot_node_t **node,
+ knot_node_t **previous)
+{
+ assert(tree != NULL);
+ assert(name != NULL);
+ assert(node != NULL);
+ assert(previous != NULL);
+
+ knot_node_t *found = NULL, *prev = NULL;
+// knot_node_t *found2 = NULL, *prev2 = NULL;
+
+ int exact_match = knot_zone_tree_get_less_or_equal(
+ tree, name, &found, &prev, 1);
+
+// assert(prev != NULL);
+ assert(exact_match >= 0);
+ *node = found;
+ *previous = prev;
+
+// if (prev == NULL) {
+// // either the returned node is the root of the tree, or it is
+// // the leftmost node in the tree; in both cases node was found
+// // set the previous node of the found node
+// assert(exact_match);
+// assert(found != NULL);
+// *previous = knot_node_get_previous(found, 1);
+// } else {
+// // otherwise check if the previous node is not an empty
+// // non-terminal
+// *previous = (knot_node_rrset_count(prev) == 0)
+// ? knot_node_get_previous(prev, 1)
+// : prev;
+// }
+
+ return exact_match;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_node_to_hash(knot_zone_tree_node_t *tnode,
+ void *data)
+{
+ assert(tnode != NULL && tnode->node != NULL
+ && tnode->node->owner != NULL && data != NULL);
+
+ knot_node_t *node = tnode->node;
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)data;
+ /*
+ * By the original approach, only authoritative nodes and delegation
+ * points should be added to the hash table, but currently, all nodes
+ * are being added when the zone is created (don't know why actually:),
+ * so we will do no distinction here neither.
+ */
+
+#ifdef USE_HASH_TABLE
+//dbg_zone_exec(
+// char *name = knot_dname_to_str(node->owner);
+// dbg_zone("Adding node with owner %s to hash table.\n", name);
+// free(name);
+//);
+ //assert(zone->table != NULL);
+ // add the node also to the hash table if authoritative, or deleg. point
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)node->owner->name,
+ node->owner->size, (void *)node) != 0) {
+ dbg_zone("Error inserting node into hash table!\n");
+ }
+#endif
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_rdata_to_table(
+ knot_dname_table_t *table, knot_rdata_t *rdata,
+ knot_rrtype_descriptor_t *d)
+{
+ unsigned int count = knot_rdata_item_count(rdata);
+ int rc = 0;
+ assert(count <= d->length);
+ // for each RDATA item
+ for (unsigned int j = 0; j < count; ++j) {
+ if (d->wireformat[j]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || d->wireformat[j]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || d->wireformat[j]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Saving dname from "
+ "rdata to dname table"
+ ".\n");
+ rc = knot_dname_table_add_dname_check(table,
+ &knot_rdata_get_item(rdata, j)->dname);
+ if (rc < 0) {
+ dbg_zone("Error: %s\n",
+ knot_strerror(rc));
+ return rc;
+ }
+ }
+ }
+
+ dbg_zone("RDATA OK.\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_rrset_to_table(
+ knot_dname_table_t *table, knot_rrset_t *rrset, int replace_owner,
+ knot_dname_t *owner)
+{
+ assert(table != NULL && rrset != NULL && owner != NULL);
+
+ if (replace_owner) {
+ // discard the old owner and replace it with the new
+ knot_rrset_set_owner(rrset, owner);
+ }
+ dbg_zone("RRSet owner: %p\n", rrset->owner);
+
+ knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(
+ knot_rrset_type(rrset));
+ if (desc == NULL) {
+ // not recognized RR type, ignore
+ dbg_zone("RRSet type not recognized.\n");
+ return KNOT_EOK;
+ }
+ // for each RDATA in RRSet
+ knot_rdata_t *rdata = knot_rrset_get_rdata(rrset);
+ while (rdata != NULL) {
+ int rc = knot_zone_contents_dnames_from_rdata_to_table(table,
+ rdata, desc);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+
+ rdata = knot_rrset_rdata_get_next(rrset, rdata);
+ }
+
+ dbg_zone("RRSet OK.\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_node_to_table(
+ knot_dname_table_t *table, knot_node_t *node)
+{
+ /*
+ * Assuming that all the RRSets have the same owner as the node.
+ */
+
+ // insert owner
+ char *name = knot_dname_to_str(node->owner);
+ dbg_zone("Node owner before inserting to dname table: %p.\n",
+ node->owner);
+ dbg_zone("Node owner before inserting to dname table: %s.\n",
+ name);
+ free(name);
+ //knot_dname_t *old_owner = node->owner;
+ int rc = knot_dname_table_add_dname_check(table, &node->owner);
+ if (rc < 0) {
+ dbg_zone("Failed to add dname to dname table.\n");
+ return rc;
+ }
+ int replace_owner = (rc > 0);
+ dbg_zone("Node owner after inserting to dname table: %p.\n",
+ node->owner);
+ name = knot_dname_to_str(node->owner);
+ dbg_zone("Node owner after inserting to dname table: %s.\n",
+ name);
+ free(name);
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ // for each RRSet
+ for (int i = 0; i < knot_node_rrset_count(node); ++i) {
+ dbg_zone("Inserting RRSets from node to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(table,
+ rrsets[i], replace_owner, node->owner);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ }
+
+ free(rrsets);
+
+ dbg_zone("Node OK\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
+ uint node_count,
+ int use_domain_table,
+ struct knot_zone *zone)
+{
+ knot_zone_contents_t *contents = (knot_zone_contents_t *)
+ calloc(1, sizeof(knot_zone_contents_t));
+ if (contents == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+// printf("created cont: %p (%s)\n",
+// contents, knot_dname_to_str(apex->owner));
+
+ contents->apex = apex;
+ contents->zone = zone;
+ knot_node_set_zone(apex, zone);
+
+ dbg_zone("Creating tree for normal nodes.\n");
+ contents->nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ dbg_zone("Creating tree for NSEC3 nodes.\n");
+ contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nsec3_nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ if (use_domain_table) {
+ dbg_zone("Creating domain name table.\n");
+ contents->dname_table = knot_dname_table_new();
+ if (contents->dname_table == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+ } else {
+ contents->dname_table = NULL;
+ }
+
+ contents->node_count = node_count;
+
+ /* Initialize NSEC3 params */
+ dbg_zone("Initializing NSEC3 parameters.\n");
+ contents->nsec3_params.algorithm = 0;
+ contents->nsec3_params.flags = 0;
+ contents->nsec3_params.iterations = 0;
+ contents->nsec3_params.salt_length = 0;
+ contents->nsec3_params.salt = NULL;
+
+ dbg_zone("Initializing zone trees.\n");
+ if (knot_zone_tree_init(contents->nodes) != KNOT_EOK
+ || knot_zone_tree_init(contents->nsec3_nodes) != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ dbg_zone("Inserting apex into the zone tree.\n");
+ if (knot_zone_tree_insert(contents->nodes, apex) != KNOT_EOK) {
+ dbg_zone("Failed to insert apex to the zone tree.\n");
+ goto cleanup;
+ }
+
+#ifdef USE_HASH_TABLE
+ if (contents->node_count > 0) {
+ dbg_zone("Creating hash table.\n");
+ contents->table = ck_create_table(contents->node_count);
+ if (contents->table == NULL) {
+ goto cleanup;
+ }
+
+ // insert the apex into the hash table
+ dbg_zone("Inserting apex into the hash table.\n");
+ if (ck_insert_item(contents->table,
+ (const char *)knot_dname_name(
+ knot_node_owner(apex)),
+ knot_dname_size(knot_node_owner(apex)),
+ (void *)apex) != 0) {
+ ck_destroy_table(&contents->table, NULL, 0);
+ goto cleanup;
+ }
+ } else {
+ contents->table = NULL;
+ }
+#endif
+
+ // insert names from the apex to the domain table
+ if (use_domain_table) {
+ dbg_zone("Inserting names from apex to table.\n");
+ int rc = knot_zone_contents_dnames_from_node_to_table(
+ contents->dname_table, apex);
+ if (rc != KNOT_EOK) {
+ ck_destroy_table(&contents->table, NULL, 0);
+ goto cleanup;
+ }
+ }
+
+ return contents;
+
+cleanup:
+ dbg_zone("Cleaning up.\n");
+ free(contents->dname_table);
+ free(contents->nodes);
+ free(contents->nsec3_nodes);
+ free(contents);
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//short knot_zone_contents_generation(const knot_zone_contents_t *zone)
+//{
+// return zone->generation;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == -1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+//void knot_zone_contents_switch_generation(knot_zone_contents_t *zone)
+//{
+// zone->generation = 1 - zone->generation;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents)
+{
+ contents->generation = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents)
+{
+ contents->generation = 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_new_finished(knot_zone_contents_t *contents)
+{
+ contents->generation = -1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_zone_contents_class(const knot_zone_contents_t *contents)
+{
+ if (contents == NULL || contents->apex == NULL
+ || knot_node_rrset(contents->apex, KNOT_RRTYPE_SOA) == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_rrset_class(knot_node_rrset(contents->apex,
+ KNOT_RRTYPE_SOA));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_node(knot_zone_contents_t *zone,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table)
+{
+ if (zone == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = 0;
+ if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
+ return ret;
+ }
+
+ ret = knot_zone_tree_insert(zone->nodes, node);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert node into zone tree.\n");
+ return ret;
+ }
+
+#ifdef USE_HASH_TABLE
+ char *name = knot_dname_to_str(node->owner);
+// dbg_zone("Adding node with owner %s to hash table.\n", name);
+ free(name);
+ //assert(zone->table != NULL);
+ // add the node also to the hash table if authoritative, or deleg. point
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)node->owner->name,
+ node->owner->size, (void *)node) != 0) {
+ dbg_zone("Error inserting node into hash table!\n");
+ /*! \todo Remove the node from the tree. */
+ return KNOT_EHASH;
+ }
+#endif
+ assert(knot_zone_contents_find_node(zone, node->owner));
+
+ if (use_domain_table) {
+ ret = knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Remove node from the tree and hash table.*/
+ dbg_zone("Failed to add dnames into table.\n");
+ return ret;
+ }
+ }
+
+ knot_node_set_zone(node, zone->zone);
+
+ if (!create_parents) {
+ return KNOT_EOK;
+ }
+
+ dbg_zone("Creating parents of the node.\n");
+
+ knot_dname_t *chopped =
+ knot_dname_left_chop(knot_node_owner(node));
+ if (knot_dname_compare(knot_node_owner(zone->apex), chopped) == 0) {
+ dbg_zone("Zone apex is the parent.\n");
+ knot_node_set_parent(node, zone->apex);
+ } else {
+ knot_node_t *next_node;
+ while ((next_node
+ = knot_zone_contents_get_node(zone, chopped)) == NULL) {
+ /* Adding new dname to zone + add to table. */
+ dbg_zone("Creating new node.\n");
+ next_node = knot_node_new(chopped, NULL, flags);
+ if (next_node == NULL) {
+ /* Directly discard. */
+ knot_dname_free(&chopped);
+ return KNOT_ENOMEM;
+ }
+ if (use_domain_table) {
+ ret =
+ knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, next_node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Will next_node leak? */
+ knot_dname_release(chopped);
+ return ret;
+ }
+ }
+
+ if (next_node->owner != chopped) {
+ /* Node owner was in RDATA */
+ chopped = next_node->owner;
+ }
+
+ assert(knot_zone_contents_find_node(zone, chopped)
+ == NULL);
+ assert(knot_node_owner(next_node) == chopped);
+
+ dbg_zone("Inserting new node to zone tree.\n");
+// TREE_INSERT(zone->tree, knot_node, avl, next_node);
+
+ 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);
+ return ret;
+ }
+
+#ifdef USE_HASH_TABLE
+dbg_zone_exec(
+ char *name = knot_dname_to_str(
+ knot_node_owner(next_node));
+ dbg_zone("Adding new node with owner %s to "
+ "hash table.\n", name);
+ free(name);
+);
+
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)knot_dname_name(
+ knot_node_owner(next_node)),
+ knot_dname_size(knot_node_owner(next_node)),
+ (void *)next_node) != 0) {
+ dbg_zone("Error inserting node into "
+ "hash table!\n");
+ /*! \todo Delete the node?? */
+ /* Directly discard. */
+ knot_dname_release(chopped);
+ return KNOT_EHASH;
+ }
+
+ // set parent
+ 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))) {
+ knot_node_set_wildcard_child(next_node, node);
+ }
+#endif
+ dbg_zone("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);
+
+ }
+ // set the found parent (in the zone) as the parent of the last
+ // inserted node
+ assert(knot_node_parent(node, 0) == NULL);
+ knot_node_set_parent(node, next_node);
+
+ dbg_zone("Created all parents.\n");
+ }
+
+ /* Directly discard. */
+ /*! \todo This may be double-release. */
+ knot_dname_release(chopped);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
+ knot_rrset_t *rrset, knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrset == NULL || zone->apex == NULL
+ || zone->apex->owner == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // 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)) {
+ return KNOT_EBADZONE;
+ }
+
+ if ((*node) == NULL
+ && (*node = knot_zone_contents_get_node(zone,
+ knot_rrset_owner(rrset))) == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+
+ /*! \todo REMOVE RRSET */
+ rc = knot_node_add_rrset(*node, rrset,
+ dupl == KNOT_RRSET_DUPL_MERGE);
+ if (rc < 0) {
+ dbg_zone("Failed to add RRSet to node.\n");
+ return rc;
+ }
+
+ int ret = rc;
+
+ if (use_domain_table) {
+ dbg_zone("Saving RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrset, 0, (*node)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ /*! \todo Do even if domain table is not used?? */
+ if (ret == KNOT_EOK && rrset->owner != (*node)->owner) {
+ knot_rrset_set_owner(rrset, (*node)->owner);
+ }
+
+ dbg_zone("RRSet OK.\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_rrsigs(knot_zone_contents_t *zone,
+ knot_rrset_t *rrsigs,
+ knot_rrset_t **rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrsigs == NULL || rrset == NULL || node == NULL
+ || zone->apex == NULL || zone->apex->owner == NULL) {
+dbg_zone_exec(
+ dbg_zone("Parameters: zone=%p, rrsigs=%p, rrset=%p, "
+ "node=%p\n", zone, rrsigs, rrset, node);
+ if (zone != NULL) {
+ dbg_zone("zone->apex=%p\n", zone->apex);
+ if (zone->apex != NULL) {
+ dbg_zone("zone->apex->owner=%p\n",
+ zone->apex->owner);
+ }
+ }
+);
+ return KNOT_EBADARG;
+ }
+
+ // check if the RRSet belongs to the zone
+ if (*rrset != NULL
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ zone->apex->owner) != 0
+ && !knot_dname_is_subdomain(knot_rrset_owner(*rrset),
+ zone->apex->owner)) {
+ return KNOT_EBADZONE;
+ }
+
+ // check if the RRSIGs belong to the RRSet
+ if (*rrset != NULL
+ && (knot_dname_compare(knot_rrset_owner(rrsigs),
+ knot_rrset_owner(*rrset)) != 0)) {
+ dbg_zone("RRSIGs does not belong to the given RRSet.\n");
+ return KNOT_EBADARG;
+ }
+
+ // if no RRSet given, try to find the right RRSet
+ if (*rrset == NULL) {
+ // even no node given
+ // find proper node
+ knot_node_t *(*get_node)(const knot_zone_contents_t *,
+ const knot_dname_t *)
+ = (knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs)) == KNOT_RRTYPE_NSEC3)
+ ? knot_zone_contents_get_nsec3_node
+ : knot_zone_contents_get_node;
+
+ if (*node == NULL
+ && (*node = get_node(
+ zone, knot_rrset_owner(rrsigs))) == NULL) {
+ dbg_zone("Failed to find node for RRSIGs.\n");
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // find the RRSet in the node
+ // take only the first RDATA from the RRSIGs
+ dbg_zone("Finding RRSet for type %s\n",
+ knot_rrtype_to_string(
+ knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs))));
+ *rrset = knot_node_get_rrset(
+ *node, knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs)));
+ if (*rrset == NULL) {
+ dbg_zone("Failed to find RRSet for RRSIGs.\n");
+ return KNOT_ENORRSET;
+ }
+ }
+
+ assert(*rrset != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+ int ret = KNOT_EOK;
+
+ rc = knot_rrset_add_rrsigs(*rrset, rrsigs, dupl);
+ if (rc < 0) {
+ dbg_dname("Failed to add RRSIGs to RRSet.\n");
+ return rc;
+ } else if (rc > 0) {
+ assert(dupl == KNOT_RRSET_DUPL_MERGE);
+ ret = 1;
+ }
+
+ if (use_domain_table) {
+ dbg_zone("Saving RRSIG RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrsigs, 0, (*rrset)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ if ((*rrset)->owner != (*rrset)->rrsigs->owner) {
+ knot_rrset_set_owner((*rrset)->rrsigs, (*rrset)->owner);
+ }
+
+ dbg_zone("RRSIGs OK\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table)
+{
+ UNUSED(create_parents);
+ UNUSED(flags);
+
+ if (zone == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = 0;
+ if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
+ return ret;
+ }
+
+ // how to know if this is successfull??
+// TREE_INSERT(zone->nsec3_nodes, knot_node, avl, node);
+ knot_zone_tree_insert(zone->nsec3_nodes, node);
+
+ if (use_domain_table) {
+ ret = knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Remove the node from the tree. */
+ dbg_zone("Failed to add dnames into table.\n");
+ return ret;
+ }
+ }
+
+ // no parents to be created, the only parent is the zone apex
+ // set the apex as the parent of the node
+ knot_node_set_parent(node, zone->apex);
+
+ // set the zone to the node
+ knot_node_set_zone(node, zone->zone);
+
+ // cannot be wildcard child, so nothing to be done
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
+ knot_rrset_t *rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrset == NULL || zone->apex == NULL
+ || zone->apex->owner == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // 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)) {
+ return KNOT_EBADZONE;
+ }
+
+ if ((*node) == NULL
+ && (*node = knot_zone_contents_get_nsec3_node(
+ zone, knot_rrset_owner(rrset))) == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+
+ /*! \todo REMOVE RRSET */
+ rc = knot_node_add_rrset(*node, rrset,
+ dupl == KNOT_RRSET_DUPL_MERGE);
+ if (rc < 0) {
+ return rc;
+ }
+
+ int ret = rc;
+
+ if (use_domain_table) {
+ dbg_zone("Saving NSEC3 RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrset, 0, (*node)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ /*! \todo Do even if domain table is not used? */
+ if (rrset->owner != (*node)->owner) {
+ knot_rrset_set_owner(rrset, (*node)->owner);
+ }
+
+ dbg_zone("NSEC3 OK\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_remove_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed_tree,
+ ck_hash_table_item_t **removed_hash)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_dname_t *owner = knot_node_owner(node);
+
+ // 1) remove the node from hash table
+ *removed_hash = NULL;
+ *removed_hash = ck_remove_item(contents->table,
+ (const char *)knot_dname_name(owner),
+ knot_dname_size(owner));
+// int ret = ck_detete_item(contents->table,
+// (const char *)knot_dname_name(owner),
+// knot_dname_size(owner), NULL, 0);
+ if (*removed_hash == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ // 2) remove the node from the zone tree
+ *removed_tree = NULL;
+ int ret = knot_zone_tree_remove(contents->nodes, owner, removed_tree);
+ if (ret != KNOT_EOK) {
+ return KNOT_ENONODE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_dname_t *owner = knot_node_owner(node);
+
+ // remove the node from the zone tree
+ *removed = NULL;
+ int ret = knot_zone_tree_remove(contents->nsec3_nodes, owner, removed);
+ if (ret != KNOT_EOK) {
+ return KNOT_ENONODE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_create_and_fill_hash_table(
+ knot_zone_contents_t *zone)
+{
+ if (zone == NULL || zone->apex == NULL || zone->apex->owner == NULL) {
+ return KNOT_EBADARG;
+ }
+ /*
+ * 1) Create hash table.
+ */
+#ifdef USE_HASH_TABLE
+ if (zone->node_count > 0) {
+ zone->table = ck_create_table(zone->node_count);
+ if (zone->table == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // insert the apex into the hash table
+ if (ck_insert_item(zone->table,
+ (const char *)zone->apex->owner->name,
+ zone->apex->owner->size,
+ (void *)zone->apex) != 0) {
+ return KNOT_EHASH;
+ }
+ } else {
+ zone->table = NULL;
+ return KNOT_EOK; // OK?
+ }
+
+ /*
+ * 2) Fill in the hash table.
+ *
+ * In this point, the nodes in the zone must be adjusted, so that only
+ * relevant nodes (authoritative and delegation points are inserted.
+ *
+ * TODO: how to know if this was successful??
+ */
+ /*! \todo Replace by zone tree. */
+ int ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_contents_node_to_hash, zone);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert nodes to hash table.\n");
+ return ret;
+ }
+
+#endif
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_node(const knot_zone_contents_t *zone,
+ const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ // create dummy node to use for lookup
+// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
+// knot_node_t *n = TREE_FIND(zone->tree, knot_node, avl, tmp);
+// knot_node_free(&tmp, 0);
+
+ knot_node_t *n;
+ int ret = knot_zone_tree_get(zone->nodes, name, &n);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to find name in the zone tree.\n");
+ return NULL;
+ }
+
+ return n;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_nsec3_node(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ // create dummy node to use for lookup
+// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
+// knot_node_t *n = TREE_FIND(zone->nsec3_nodes, knot_node, avl, tmp);
+// knot_node_free(&tmp, 0);
+ knot_node_t *n;
+ int ret = knot_zone_tree_get(zone->nsec3_nodes, name, &n);
+
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to find NSEC3 name in the zone tree."
+ "\n");
+ return NULL;
+ }
+
+ return n;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_node(
+ const knot_zone_contents_t *zone,const knot_dname_t *name)
+{
+ return knot_zone_contents_get_node(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_find_dname(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser,
+ const knot_node_t **previous)
+{
+ if (zone == NULL || name == NULL || node == NULL
+ || closest_encloser == NULL || previous == NULL
+ || zone->apex == NULL || zone->apex->owner == NULL) {
+ return KNOT_EBADARG;
+ }
+
+dbg_zone_exec(
+ char *name_str = knot_dname_to_str(name);
+ char *zone_str = knot_dname_to_str(zone->apex->owner);
+ dbg_zone("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
+ free(name_str);
+ free(zone_str);
+);
+
+ if (knot_dname_compare(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)) {
+ *node = NULL;
+ *closest_encloser = NULL;
+ return KNOT_EBADZONE;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nodes, name,
+ &found, &prev);
+ assert(exact_match >= 0);
+ *node = found;
+ *previous = prev;
+
+dbg_zone_exec(
+ char *name_str = (*node) ? knot_dname_to_str((*node)->owner)
+ : "(nil)";
+ char *name_str2 = (*previous != NULL)
+ ? knot_dname_to_str((*previous)->owner)
+ : "(nil)";
+ dbg_zone("Search function returned %d, node %s and prev: %s\n",
+ exact_match, name_str, name_str2);
+
+ if (*node) {
+ free(name_str);
+ }
+ if (*previous != NULL) {
+ free(name_str2);
+ }
+);
+
+ *closest_encloser = *node;
+
+ // there must be at least one node with domain name less or equal to
+ // the searched name if the name belongs to the zone (the root)
+ if (*node == NULL) {
+ return KNOT_EBADZONE;
+ }
+
+ // TODO: this could be replaced by saving pointer to closest encloser
+ // in node
+
+ if (!exact_match) {
+ 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)))) {
+ (*closest_encloser) =
+ knot_node_parent((*closest_encloser), 1);
+ assert(*closest_encloser);
+ }
+ }
+dbg_zone_exec(
+ char *n = knot_dname_to_str(knot_node_owner((*closest_encloser)));
+ dbg_zone("Closest encloser: %s\n", n);
+ free(n);
+);
+
+ dbg_zone("find_dname() returning %d\n", exact_match);
+
+ return (exact_match)
+ ? KNOT_ZONE_NAME_FOUND
+ : KNOT_ZONE_NAME_NOT_FOUND;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_previous(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nodes, name,
+ &found, &prev);
+ assert(exact_match >= 0);
+ assert(prev != NULL);
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_previous(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_previous(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_previous_nsec3(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nsec3_nodes,
+ name, &found, &prev);
+ assert(exact_match >= 0);
+ assert(prev != NULL);
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_previous_nsec3(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_previous(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_left_chop(char *name, size_t *size)
+{
+ short label_size = name[0];
+
+ memmove(name, name + label_size + 1, *size -label_size - 1);
+ *size = *size - label_size - 1;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef USE_HASH_TABLE
+int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser)
+{
+ if (zone == NULL || name == NULL || node == NULL
+ || closest_encloser == NULL) {
+ return KNOT_EBADARG;
+ }
+
+dbg_zone_exec(
+ char *name_str = knot_dname_to_str(name);
+ char *zone_str = knot_dname_to_str(zone->apex->owner);
+ dbg_zone("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
+ free(name_str);
+ free(zone_str);
+);
+
+ if (knot_dname_compare(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)) {
+ *node = NULL;
+ *closest_encloser = NULL;
+ return KNOT_EBADZONE;
+ }
+
+ // temporary name used for hashing
+ char name_tmp[KNOT_MAX_DNAME_LENGTH];
+ size_t name_size = name->size;
+ if (knot_dname_to_lower_copy(name, name_tmp, KNOT_MAX_DNAME_LENGTH)
+ != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ const ck_hash_table_item_t *item = ck_find_item(zone->table,
+ name_tmp, name_size);
+
+ if (item != NULL) {
+ *node = (const knot_node_t *)item->value;
+ *closest_encloser = *node;
+
+ dbg_zone("Found node in hash table: %p (owner %p, "
+ "labels: %d)\n", *node, (*node)->owner,
+ knot_dname_label_count((*node)->owner));
+ assert(*node != NULL);
+ assert(*closest_encloser != NULL);
+ return KNOT_ZONE_NAME_FOUND;
+ }
+
+ *node = NULL;
+
+ // chop leftmost labels until some node is found
+ // copy the name for chopping
+ /* Local allocation, will be discarded. */
+ //knot_dname_t *name_copy = knot_dname_deep_copy(name);
+dbg_zone_exec(
+ //char *n = knot_dname_to_str(name_copy);
+ dbg_zone("Finding closest encloser..\nStarting with: %.*s\n",
+ (int)name_size, name_tmp);
+ //free(n);
+);
+
+ while (item == NULL) {
+ //knot_dname_left_chop_no_copy(name_copy);
+ knot_zone_contents_left_chop(name_tmp, &name_size);
+dbg_zone_exec(
+ //char *n = knot_dname_to_str(name_copy);
+ dbg_zone("Chopped leftmost label: %.*s\n",
+ (int)name_size, name_tmp);
+ //free(n);
+);
+ // not satisfied in root zone!!
+ //assert(name_copy->label_count > 0);
+ assert(name_size > 0);
+
+ item = ck_find_item(zone->table, name_tmp, name_size);
+ }
+
+ /* Directly discard. */
+ //knot_dname_free(&name_copy);
+
+ assert(item != NULL);
+ *closest_encloser = (const knot_node_t *)item->value;
+
+ return KNOT_ZONE_NAME_NOT_FOUND;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_nsec3_node(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_nsec3_node(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_find_nsec3_for_name(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **nsec3_node,
+ const knot_node_t **nsec3_previous,
+ int check_ver)
+{
+ if (zone == NULL || name == NULL
+ || nsec3_node == NULL || nsec3_previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_t *nsec3_name = NULL;
+ int ret = knot_zone_contents_nsec3_name(zone, name, &nsec3_name);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+dbg_zone_exec(
+ char *n = knot_dname_to_str(nsec3_name);
+ dbg_zone("NSEC3 node name: %s.\n", n);
+ free(n);
+);
+
+ const knot_node_t *found = NULL, *prev = NULL;
+
+ // create dummy node to use for lookup
+ int exact_match = knot_zone_tree_find_less_or_equal(
+ zone->nsec3_nodes, nsec3_name, &found, &prev, check_ver);
+ assert(exact_match >= 0);
+
+ knot_dname_release(nsec3_name);
+
+dbg_zone_exec(
+ if (found) {
+ char *n = knot_dname_to_str(found->owner);
+ dbg_zone("Found NSEC3 node: %s.\n", n);
+ free(n);
+ } else {
+ dbg_zone("Found no NSEC3 node.\n");
+ }
+
+ if (prev) {
+ assert(prev->owner);
+ char *n = knot_dname_to_str(prev->owner);
+ dbg_zone("Found previous NSEC3 node: %s.\n", n);
+ free(n);
+ } else {
+ dbg_zone("Found no previous NSEC3 node.\n");
+ }
+);
+ *nsec3_node = found;
+
+ if (prev == NULL) {
+ // either the returned node is the root of the tree, or it is
+ // the leftmost node in the tree; in both cases node was found
+ // set the previous node of the found node
+ assert(exact_match);
+ assert(*nsec3_node != NULL);
+ *nsec3_previous = knot_node_previous(*nsec3_node, check_ver);
+ } else {
+ *nsec3_previous = prev;
+ }
+
+ dbg_zone("find_nsec3_for_name() returning %d\n", exact_match);
+
+ return (exact_match)
+ ? KNOT_ZONE_NAME_FOUND
+ : KNOT_ZONE_NAME_NOT_FOUND;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_apex(
+ const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return zone->apex;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_apex(const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return zone->apex;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//knot_dname_t *knot_zone_contents_name(const knot_zone_contents_t *zone)
+//{
+// if (zone == NULL) {
+// return NULL;
+// }
+
+// return zone->name;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_adjust(knot_zone_contents_t *zone, int check_ver)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // load NSEC3PARAM (needed on adjusting function)
+ knot_zone_contents_load_nsec3param(zone);
+
+ knot_zone_adjust_arg_t adjust_arg;
+ adjust_arg.zone = zone;
+ adjust_arg.first_node = NULL;
+ adjust_arg.previous_node = NULL;
+ adjust_arg.check_ver = check_ver;
+
+ dbg_zone("Adjusting normal nodes.\n");
+ int ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_contents_adjust_node_in_tree,
+ &adjust_arg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ dbg_zone("Done.\n");
+
+ assert(zone->apex == adjust_arg.first_node);
+ knot_node_set_previous(zone->apex, adjust_arg.previous_node);
+
+ adjust_arg.first_node = NULL;
+ adjust_arg.previous_node = NULL;
+
+ dbg_zone("Adjusting NSEC3 nodes.\n");
+ ret = knot_zone_tree_forward_apply_inorder(
+ zone->nsec3_nodes,
+ knot_zone_contents_adjust_nsec3_node_in_tree,
+ &adjust_arg);
+
+ dbg_zone("Done.\n");
+ if (adjust_arg.first_node) {
+ knot_node_set_previous(adjust_arg.first_node,
+ adjust_arg.previous_node);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_load_nsec3param(knot_zone_contents_t *zone)
+{
+ if (zone == NULL || zone->apex == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_rrset_t *rrset = knot_node_rrset(zone->apex,
+ KNOT_RRTYPE_NSEC3PARAM);
+
+ if (rrset != NULL) {
+ knot_nsec3_params_from_wire(&zone->nsec3_params, rrset);
+ } else {
+ memset(&zone->nsec3_params, 0, sizeof(knot_nsec3_params_t));
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ //return (zone->nsec3_params.algorithm != 0);
+ return (zone->nsec3_nodes->th_root != NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_nsec3_params_t *knot_zone_contents_nsec3params(
+ const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ return &zone->nsec3_params;
+ } else {
+ return NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_postorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_postorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_inorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_inorder_reverse(
+ knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data), void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_reverse_apply_inorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_postorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_postorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_inorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_inorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_inorder_reverse(
+ knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data), void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_reverse_apply_inorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_tree_t *knot_zone_contents_get_nodes(
+ knot_zone_contents_t *contents)
+{
+ return contents->nodes;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_tree_t *knot_zone_contents_get_nsec3_nodes(
+ knot_zone_contents_t *contents)
+{
+ return contents->nsec3_nodes;
+}
+
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_t *knot_zone_contents_get_hash_table(
+ knot_zone_contents_t *contents)
+{
+ return contents->table;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_dname_table_apply(knot_zone_contents_t *contents,
+ void (*function)(knot_dname_t *,
+ void *),
+ void *data)
+{
+ if (contents == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_table_tree_inorder_apply(contents->dname_table,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
+ knot_zone_contents_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Copy to same destination as source. */
+ if (from == *to) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = KNOT_EOK;
+
+ knot_zone_contents_t *contents = (knot_zone_contents_t *)calloc(
+ 1, sizeof(knot_zone_contents_t));
+ if (contents == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ contents->apex = from->apex;
+
+ contents->nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+
+ contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nsec3_nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+
+ if (from->dname_table != NULL) {
+ contents->dname_table = knot_dname_table_new();
+ if (contents->dname_table == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+ if ((ret = knot_dname_table_shallow_copy(from->dname_table,
+ contents->dname_table)) != KNOT_EOK) {
+ goto cleanup;
+ }
+ } else {
+ contents->dname_table = NULL;
+ }
+
+ contents->node_count = from->node_count;
+ contents->generation = from->generation;
+
+ contents->zone = from->zone;
+
+ /* Initialize NSEC3 params */
+ memcpy(&contents->nsec3_params, &from->nsec3_params,
+ sizeof(knot_nsec3_params_t));
+
+ if ((ret = knot_zone_tree_shallow_copy(from->nodes,
+ contents->nodes)) != KNOT_EOK
+ || (ret = knot_zone_tree_shallow_copy(from->nsec3_nodes,
+ contents->nsec3_nodes)) != KNOT_EOK) {
+ goto cleanup;
+ }
+
+#ifdef USE_HASH_TABLE
+ if (from->table != NULL) {
+// ret = ck_copy_table(from->table, &contents->table);
+ ret = ck_shallow_copy(from->table, &contents->table);
+ if (ret != 0) {
+ dbg_zone("knot_zone_contents_shallow_copy: "
+ "hash table copied\n");
+ ret = KNOT_ERROR;
+ goto cleanup;
+ }
+ }
+#endif
+
+ dbg_zone("knot_zone_contents_shallow_copy: "
+ "finished OK\n");
+
+ *to = contents;
+ return KNOT_EOK;
+
+cleanup:
+ knot_zone_tree_free(&contents->nodes);
+ knot_zone_tree_free(&contents->nsec3_nodes);
+ free(contents->dname_table);
+ free(contents);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_free(knot_zone_contents_t **contents)
+{
+ if (contents == NULL || *contents == NULL) {
+ return;
+ }
+
+ // free the zone tree, but only the structure
+ knot_zone_tree_free(&(*contents)->nodes);
+ knot_zone_tree_free(&(*contents)->nsec3_nodes);
+
+#ifdef USE_HASH_TABLE
+ if ((*contents)->table != NULL) {
+ ck_destroy_table(&(*contents)->table, NULL, 0);
+ }
+#endif
+ knot_nsec3_params_free(&(*contents)->nsec3_params);
+
+ knot_dname_table_free(&(*contents)->dname_table);
+
+ free(*contents);
+ *contents = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_deep_free(knot_zone_contents_t **contents,
+ int destroy_dname_table)
+{
+ if (contents == NULL || *contents == NULL) {
+ return;
+ }
+
+ if ((*contents) != NULL) {
+
+#ifdef USE_HASH_TABLE
+ if ((*contents)->table != NULL) {
+ ck_destroy_table(&(*contents)->table, NULL, 0);
+ }
+#endif
+ /* has to go through zone twice, rdata may contain references to
+ node owners earlier in the zone which may be already freed */
+ /* NSEC3 tree is deleted first as it may contain references to
+ the normal tree. */
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nsec3_nodes,
+ knot_zone_contents_destroy_node_rrsets_from_tree,
+ (void*)1);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nsec3_nodes,
+ knot_zone_contents_destroy_node_owner_from_tree, 0);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nodes,
+ knot_zone_contents_destroy_node_rrsets_from_tree,
+ (void*)1);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nodes,
+ knot_zone_contents_destroy_node_owner_from_tree, 0);
+
+ // free the zone tree, but only the structure
+ // (nodes are already destroyed)
+ dbg_zone("Destroying zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nodes);
+ dbg_zone("Destroying NSEC3 zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nsec3_nodes);
+
+ knot_nsec3_params_free(&(*contents)->nsec3_params);
+
+ if (destroy_dname_table) {
+ /*
+ * Hack, used in zcompile - destroys the table using
+ * dname_free() instead of dname_retain().
+ */
+ knot_dname_table_destroy(&(*contents)->dname_table);
+ } else {
+ knot_dname_table_deep_free(&(*contents)->dname_table);
+ }
+ }
+
+ free((*contents));
+ *contents = NULL;
+}
+
diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h
new file mode 100644
index 0000000..2856f76
--- /dev/null
+++ b/src/libknot/zone/zone-contents.h
@@ -0,0 +1,556 @@
+/*!
+ * \file zone-contents.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone contents structure and API for manipulating it.
+ *
+ * \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_ZONE_CONTENTS_H_
+#define _KNOT_ZONE_CONTENTS_H_
+
+//#include <time.h>
+
+#include "zone/node.h"
+#include "dname.h"
+#include "nsec3.h"
+#include "zone/dname-table.h"
+#include "common/tree.h"
+#include "hash/cuckoo-hash-table.h"
+
+#include "zone-tree.h"
+
+struct knot_zone;
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct knot_zone_contents_t {
+ knot_node_t *apex; /*!< Apex node of the zone (holding SOA) */
+
+ ck_hash_table_t *table; /*!< Hash table for holding zone nodes. */
+ knot_zone_tree_t *nodes;
+ knot_zone_tree_t *nsec3_nodes;
+
+ /*!
+ * \todo Unify the use of this field - authoritative nodes vs. all.
+ */
+ uint node_count;
+
+ knot_dname_table_t *dname_table;
+
+ knot_nsec3_params_t nsec3_params;
+
+ /*! \brief Generation of the zone during update.
+ *
+ * Possible values:
+ * - 0 - Original version of the zone. Old nodes should be used.
+ * - 1 - New (updated) zone. New nodes should be used.
+ * - -1 - New (updated) zone, but exactly the stored nodes should be
+ * used, no matter their generation.
+ */
+ short generation;
+
+ struct knot_zone *zone;
+} knot_zone_contents_t;
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
+ uint node_count,
+ int use_domain_table,
+ struct knot_zone *zone);
+
+time_t knot_zone_contents_version(const knot_zone_contents_t *contents);
+
+void knot_zone_contents_set_version(knot_zone_contents_t *contents,
+ time_t version);
+
+//short knot_zone_contents_generation(const knot_zone_contents_t *contents);
+
+int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents);
+int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents);
+int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents);
+
+//void knot_zone_contents_switch_generation(knot_zone_contents_t *contents);
+
+void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents);
+void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents);
+void knot_zone_contents_set_gen_new_finished(knot_zone_contents_t *contents);
+
+uint16_t knot_zone_contents_class(const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Adds a node to the given zone.
+ *
+ * Checks if the node belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. It thus also forbids adding node with the same name as the
+ * zone apex.
+ *
+ * \warning This function may destroy domain names saved in the node, that
+ * are already present in the zone.
+ *
+ * \param zone Zone to add the node into.
+ * \param node Node to add into the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ * \retval KNOT_EHASH
+ */
+int knot_zone_contents_add_node(knot_zone_contents_t *contents,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table);
+
+/*!
+ * \brief Adds a RRSet to the given zone.
+ *
+ * Checks if the RRSet belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. The RRSet is inserted only if the node is given, or if
+ * a node where the RRSet should belong is found in the zone.
+ *
+ * \warning The function does not check if the node is already inserted in the
+ * zone, just assumes that it is.
+ * \warning This function may destroy domain names saved in the RRSet, that
+ * are already present in the zone.
+ *
+ * \param zone Zone to add the node into.
+ * \param rrset RRSet to add into the zone.
+ * \param node Node the RRSet should be inserted into. (Should be a node of the
+ * given zone.) If set to NULL, the function will find proper node
+ * and set it to this parameter.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+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,
+ int use_domain_table);
+
+int knot_zone_contents_add_rrsigs(knot_zone_contents_t *contents,
+ knot_rrset_t *rrsigs,
+ knot_rrset_t **rrset, knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table);
+
+/*!
+ * \brief Adds a node holding NSEC3 records to the given zone.
+ *
+ * Checks if the node belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. It does not check if the node really contains any NSEC3
+ * records, nor if the name is a hash (as there is actually no way of
+ * determining this).
+ *
+ * \param zone Zone to add the node into.
+ * \param node Node to add into the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *contents,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table);
+
+int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *contents,
+ knot_rrset_t *rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table);
+
+int knot_zone_contents_remove_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed_tree,
+ ck_hash_table_item_t **removed_hash);
+
+//knot_zone_tree_node_t *knot_zone_contents_remove_node(
+// knot_zone_contents_t *contents, const knot_node_t *node);
+
+int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed);
+
+/*!
+ * \warning Always call knot_zone_adjust_dnames() prior to calling this
+ * function. Otherwise the node count would not be set.
+ *
+ * \note Currently, all nodes (even non-authoritative) are inserted into the
+ * hash table.
+ */
+int knot_zone_contents_create_and_fill_hash_table(
+ knot_zone_contents_t *contents);
+
+/*!
+ * \brief Tries to find a node with the specified name in the zone.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+knot_node_t *knot_zone_contents_get_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find a node with the specified name among the NSEC3 nodes
+ * of the zone.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+knot_node_t *knot_zone_contents_get_nsec3_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find a node with the specified name in the zone.
+ *
+ * \note This function is identical to knot_zone_contents_get_node(), only it returns
+ * constant reference.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const knot_node_t *knot_zone_contents_find_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find domain name in the given zone using AVL tree.
+ *
+ * \param[in] zone Zone to search for the name.
+ * \param[in] name Domain name to search for.
+ * \param[out] node The found node (if it was found, otherwise it may contain
+ * arbitrary node).
+ * \param[out] closest_encloser Closest encloser of the given name in the zone.
+ * \param[out] previous Previous domain name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if node with owner \a name was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_find_dname(const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser,
+ const knot_node_t **previous);
+
+/*!
+ * \brief Finds previous name in canonical order to the given name in the zone.
+ *
+ * \param zone Zone to search for the name.
+ * \param name Domain name to find the previous domain name of.
+ *
+ * \return Previous node in canonical order, or NULL if some parameter is wrong.
+ */
+const knot_node_t *knot_zone_contents_find_previous(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+knot_node_t *knot_zone_contents_get_previous(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+const knot_node_t *knot_zone_contents_find_previous_nsec3(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+knot_node_t *knot_zone_contents_get_previous_nsec3(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+#ifdef USE_HASH_TABLE
+/*!
+ * \brief Tries to find domain name in the given zone using the hash table.
+ *
+ * \param[in] zone Zone to search for the name.
+ * \param[in] name Domain name to search for.
+ * \param[out] node The found node (if it was found, otherwise it may contain
+ * arbitrary node).
+ * \param[out] closest_encloser Closest encloser of the given name in the zone.
+ * \param[out] previous Previous domain name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if node with owner \a name was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser);
+#endif
+
+/*!
+ * \brief Tries to find a node with the specified name among the NSEC3 nodes
+ * of the zone.
+ *
+ * \note This function is identical to knot_zone_contents_get_nsec3_node(), only it
+ * returns constant reference.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const knot_node_t *knot_zone_contents_find_nsec3_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Finds NSEC3 node and previous NSEC3 node in canonical order,
+ * corresponding to the given domain name.
+ *
+ * This functions creates a NSEC3 hash of \a name and tries to find NSEC3 node
+ * with the hashed domain name as owner.
+ *
+ * \param[in] zone Zone to search in.
+ * \param[in] name Domain name to get the corresponding NSEC3 nodes for.
+ * \param[out] nsec3_node NSEC3 node corresponding to \a name (if found,
+ * otherwise this may be an arbitrary NSEC3 node).
+ * \param[out] nsec3_previous The NSEC3 node immediately preceding hashed domain
+ * name corresponding to \a name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if the corresponding NSEC3 node was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENSEC3PAR
+ * \retval KNOT_ECRYPTO
+ * \retval KNOT_ERROR
+ */
+int knot_zone_contents_find_nsec3_for_name(
+ const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **nsec3_node,
+ const knot_node_t **nsec3_previous,
+ int check_ver);
+/*!
+ * \brief Returns the apex node of the zone.
+ *
+ * \param zone Zone to get the apex of.
+ *
+ * \return Zone apex node.
+ */
+const knot_node_t *knot_zone_contents_apex(
+ const knot_zone_contents_t *contents);
+
+knot_node_t *knot_zone_contents_get_apex(
+ const knot_zone_contents_t *contents);
+
+//knot_dname_t *knot_zone_contents_name(
+// const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Optimizes zone by replacing domain names in RDATA with references to
+ * domain names present in zone (as node owners).
+ *
+ * \param zone Zone to adjust domain names in.
+ */
+int knot_zone_contents_adjust(knot_zone_contents_t *contents, int check_ver);
+
+/*!
+ * \brief Parses the NSEC3PARAM record stored in the zone.
+ *
+ * This function properly fills in the nsec3_params field of the zone structure
+ * according to data stored in the NSEC3PARAM record. This is necessary to do
+ * before any NSEC3 operations on the zone are requested, otherwise they will
+ * fail (error KNOT_ENSEC3PAR).
+ *
+ * \note If there is no NSEC3PARAM record in the zone, this function clears
+ * the nsec3_params field of the zone structure (fills it with zeros).
+ *
+ * \param zone Zone to get the NSEC3PARAM record from.
+ */
+int knot_zone_contents_load_nsec3param(knot_zone_contents_t *contents);
+
+/*!
+ * \brief Checks if the zone uses NSEC3.
+ *
+ * This function will return 0 if the NSEC3PARAM record was not parse prior to
+ * calling it.
+ *
+ * \param zone Zone to check.
+ *
+ * \retval <> 0 if the zone uses NSEC3.
+ * \retval 0 if it does not.
+ *
+ * \see knot_zone_contents_load_nsec3param()
+ */
+int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Returns the parsed NSEC3PARAM record of the zone.
+ *
+ * \note You must parse the NSEC3PARAM record prior to calling this function
+ * (knot_zone_contents_load_nsec3param()).
+ *
+ * \param zone Zone to get the NSEC3PARAM record from.
+ *
+ * \return Parsed NSEC3PARAM from the zone or NULL if the zone does not use
+ * NSEC3 or the record was not parsed before.
+ *
+ * \see knot_zone_contents_load_nsec3param()
+ */
+const knot_nsec3_params_t *knot_zone_contents_nsec3params(
+ const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_postorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_inorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_inorder_reverse(
+ knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data), void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_postorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_inorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_inorder_reverse(
+ knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data), void *data);
+
+knot_zone_tree_t *knot_zone_contents_get_nodes(
+ knot_zone_contents_t *contents);
+
+knot_zone_tree_t *knot_zone_contents_get_nsec3_nodes(
+ knot_zone_contents_t *contents);
+
+ck_hash_table_t *knot_zone_contents_get_hash_table(
+ knot_zone_contents_t *contents);
+
+int knot_zone_contents_dname_table_apply(knot_zone_contents_t *contents,
+ void (*function)(knot_dname_t *,
+ void *),
+ void *data);
+
+/*!
+ * \brief Creates a shallow copy of the zone (no stored data are copied).
+ *
+ * This function creates a new zone structure in \a to, creates new trees for
+ * regular nodes and for NSEC3 nodes, creates new hash table and a new domain
+ * table. It also fills these structures with the exact same data as the
+ * original zone is - no copying of stored data is done, just pointers are
+ * copied.
+ *
+ * \param from Original zone.
+ * \param to Copy of the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
+ knot_zone_contents_t **to);
+
+void knot_zone_contents_free(knot_zone_contents_t **contents);
+
+void knot_zone_contents_deep_free(knot_zone_contents_t **contents,
+ int destroy_dname_table);
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c
new file mode 100644
index 0000000..cdf128e
--- /dev/null
+++ b/src/libknot/zone/zone-tree.c
@@ -0,0 +1,470 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+
+#include "zone-tree.h"
+#include "zone/node.h"
+#include "util/error.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+// AVL tree functions
+TREE_DEFINE(knot_zone_tree_node, avl);
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_tree_node_compare(knot_zone_tree_node_t *node1,
+ knot_zone_tree_node_t *node2)
+{
+ assert(node1 != NULL);
+ assert(node2 != NULL);
+ assert(node1->node != NULL);
+ assert(node2->node != NULL);
+ assert(knot_node_owner(node1->node) != NULL);
+ assert(knot_node_owner(node2->node) != NULL);
+
+ return knot_node_compare(node1->node, node2->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_delete_subtree(knot_zone_tree_node_t *root)
+{
+ if (root == NULL) {
+ return;
+ }
+
+ knot_zone_tree_delete_subtree(root->avl.avl_left);
+ knot_zone_tree_delete_subtree(root->avl.avl_right);
+ free(root);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_tree_copy_node(knot_zone_tree_node_t *from,
+ knot_zone_tree_node_t **to)
+{
+ if (from == NULL) {
+ *to = NULL;
+ return KNOT_EOK;
+ }
+
+ *to = (knot_zone_tree_node_t *)
+ malloc(sizeof(knot_zone_tree_node_t));
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ (*to)->node = from->node;
+ (*to)->avl.avl_height = from->avl.avl_height;
+
+ int ret = knot_zone_tree_copy_node(from->avl.avl_left,
+ &(*to)->avl.avl_left);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_zone_tree_copy_node(from->avl.avl_right,
+ &(*to)->avl.avl_right);
+ if (ret != KNOT_EOK) {
+ knot_zone_tree_delete_subtree((*to)->avl.avl_left);
+ (*to)->avl.avl_left = NULL;
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_free_node(knot_zone_tree_node_t *node,
+ int free_data, int free_owner)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ knot_zone_tree_free_node(node->avl.avl_left, free_data, free_owner);
+
+ knot_zone_tree_free_node(node->avl.avl_right, free_data, free_owner);
+
+ if (free_data) {
+ knot_node_free(&node->node, free_owner, 0);
+ }
+
+ free(node);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_init(knot_zone_tree_t *tree)
+{
+ if (tree == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_INIT(tree, knot_zone_tree_node_compare);
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_insert(knot_zone_tree_t *tree, knot_node_t *node)
+{
+ if (tree == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_node_t *znode = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (znode == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ znode->node = node;
+ znode->avl.avl_left = NULL;
+ znode->avl.avl_right = NULL;
+ znode->avl.avl_height = 0;
+
+ /*! \todo How to know if this was successful? */
+ TREE_INSERT(tree, knot_zone_tree_node, avl, znode);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_find(knot_zone_tree_t *tree, const knot_dname_t *owner,
+ const knot_node_t **found)
+{
+ if (tree == NULL || owner == NULL || found == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_node_t *f = NULL;
+ int ret = knot_zone_tree_get(tree, owner, &f);
+ *found = f;
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_get(knot_zone_tree_t *tree, const knot_dname_t *owner,
+ knot_node_t **found)
+{
+ if (tree == NULL || owner == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *found = NULL;
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl,
+ tmp);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+ if (n != NULL) {
+ *found = n->node;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_find_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found,
+ const knot_node_t **previous,
+ int check_version)
+{
+ if (tree == NULL || owner == NULL || found == NULL || previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_node_t *f, *p;
+ int ret = knot_zone_tree_get_less_or_equal(tree, owner, &f, &p, check_version);
+
+ *found = f;
+ *previous = p;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found,
+ knot_node_t **previous,
+ int check_version)
+{
+ if (tree == NULL || owner == NULL || found == NULL
+ || previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_node_t *f = NULL, *prev = NULL;
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ int exact_match = TREE_FIND_LESS_EQUAL(
+ tree, knot_zone_tree_node, avl, tmp, &f, &prev);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+ *found = (exact_match > 0) ? f->node : NULL;
+
+ if (exact_match < 0) {
+ // previous is not really previous but should be the leftmost
+ // node in the tree; take it's previous
+ assert(prev != NULL);
+ *previous = knot_node_get_previous(prev->node, check_version);
+ exact_match = 0;
+ } else if (prev == NULL) {
+ if (!exact_match) {
+ printf("Searched for owner %s in zone tree.\n",
+ knot_dname_to_str(owner));
+ printf("Exact match: %d\n", exact_match);
+ printf("Found node: %p: %s.\n", f, (f)
+ ? knot_dname_to_str(knot_node_owner(f->node))
+ : "none");
+ printf("Previous node: %p: %s.\n", prev, (prev)
+ ? knot_dname_to_str(knot_node_owner(prev->node))
+ : "none");
+ }
+
+ // either the returned node is the root of the tree, or
+ // it is the leftmost node in the tree; in both cases
+ // node was found set the previous node of the found
+ // node
+ assert(exact_match > 0);
+ assert(f != NULL);
+ *previous = knot_node_get_previous(f->node, check_version);
+ } else {
+ // otherwise check if the previous node is not an empty
+ // non-terminal
+ /*! \todo Here we assume that the 'prev' pointer always points
+ * to an empty non-terminal.
+ */
+ *previous = (knot_node_rrset_count(prev->node) == 0)
+ ? knot_node_get_previous(prev->node, check_version)
+ : prev->node;
+ }
+
+ assert(exact_match >= 0);
+
+ return exact_match;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_remove(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_zone_tree_node_t **removed)
+{
+ if (tree == NULL || owner == NULL || removed == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ // we must first find the node, so that it may be destroyed
+ knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl,
+ tmp);
+
+ /*! \todo How to know if this was successful? */
+ TREE_REMOVE(tree, knot_zone_tree_node, avl, tmp);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+// *removed = (n) ? n->node : NULL;
+// free(n);
+ *removed = n;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_forward_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_FORWARD_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_forward_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_POST_ORDER_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_reverse_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_REVERSE_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_reverse_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_REVERSE_APPLY_POST(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
+ knot_zone_tree_t *to)
+{
+ if (to == NULL || from == NULL) {
+ return KNOT_EBADARG;
+ }
+ /*
+ * This function will copy the tree by hand, so that the nodes
+ * do not have to be inserted the normal way. It should be substantially
+ * faster.
+ */
+
+ to->th_cmp = from->th_cmp;
+
+ return knot_zone_tree_copy_node(from->th_root, &to->th_root);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_tree_free(knot_zone_tree_t **tree)
+{
+ if (tree == NULL || *tree == NULL) {
+ return;
+ }
+ knot_zone_tree_free_node((*tree)->th_root, 0, 0);
+ free(*tree);
+ *tree = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_tree_deep_free(knot_zone_tree_t **tree, int free_owners)
+{
+ if (tree == NULL || *tree == NULL) {
+ return;
+ }
+ knot_zone_tree_free_node((*tree)->th_root, 1, free_owners);
+ free(*tree);
+ *tree = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/zone/zone-tree.h b/src/libknot/zone/zone-tree.h
new file mode 100644
index 0000000..0971749
--- /dev/null
+++ b/src/libknot/zone/zone-tree.h
@@ -0,0 +1,300 @@
+/*!
+ * \file zone-tree.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone tree structure and API for manipulating it.
+ *
+ * Implemented as AVL tree.
+ *
+ * \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_ZONE_TREE_H_
+#define _KNOT_ZONE_TREE_H_
+
+#include "common/tree.h"
+#include "zone/node.h"
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct knot_zone_tree_node {
+ /*! \brief Structure for connecting this node to an AVL tree. */
+ TREE_ENTRY(knot_zone_tree_node) avl;
+ /*! \brief Zone tree data. */
+ knot_node_t *node;
+ /*! \brief Owner of the node. */
+// knot_dname_t *owner;
+} knot_zone_tree_node_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef TREE_HEAD(knot_zone_tree, knot_zone_tree_node) knot_zone_tree_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes the zone tree.
+ *
+ * Does not allocate the structure. Must be called before any use of the tree.
+ *
+ * \param tree Zone tree structure to initialize.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_init(knot_zone_tree_t *tree);
+
+/*!
+ * \brief Inserts the given node into the zone tree.
+ *
+ * \param tree Zone tree to insert the node into.
+ * \param node Node to insert.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_insert(knot_zone_tree_t *tree, knot_node_t *node);
+
+/*!
+ * \brief Finds node with the given owner in the zone tree.
+ *
+ * \param tree Zone tree to search in.
+ * \param owner Owner of the node to find.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_find(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found);
+
+/*!
+ * \brief Finds node with the given owner in the zone tree.
+ *
+ * \note This function is identical to knot_zone_tree_find() except that it
+ * returns non-const node.
+ *
+ * \param tree Zone tree to search in.
+ * \param owner Owner of the node to find.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_get(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found);
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree and returns the
+ * associated node and previous node in canonical order.
+ *
+ * \param zone Zone to search in.
+ * \param owner Owner of the node to find.
+ * \param found Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a owner in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval > 0 if the domain name was found. In such case \a found holds the
+ * zone node with \a owner as its owner.
+ * \a previous is set properly.
+ * \retval 0 if the domain name was not found. \a found may hold any (or none)
+ * node. \a previous is set properly.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_find_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found,
+ const knot_node_t **previous,
+ int check_version);
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree and returns the
+ * associated node and previous node in canonical order.
+ *
+ * \note This function is identical to knot_zone_tree_find_less_or_equal()
+ * except that it returns non-const nodes.
+ *
+ * \param zone Zone to search in.
+ * \param owner Owner of the node to find.
+ * \param found Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a owner in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval > 0 if the domain name was found. In such case \a found holds the
+ * zone node with \a owner as its owner.
+ * \a previous is set properly.
+ * \retval 0 if the domain name was not found. \a found may hold any (or none)
+ * node. \a previous is set properly.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found,
+ knot_node_t **previous,
+ int check_version);
+
+/*!
+ * \brief Removes node with the given owner from the zone tree and returns it.
+ *
+ * \param tree Zone tree to remove the node from.
+ * \param owner Owner of the node to find.
+ * \param removed The removed node.
+ *
+ * \retval The removed node.
+ */
+int knot_zone_tree_remove(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_zone_tree_node_t **removed);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_forward_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_forward_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_reverse_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses post-order depth-first reverse traversal, i.e. the
+ * function is first recursively applied to right subtree, then to the
+ * left subtree and then to the root.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_reverse_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Copies the whole zone tree structure (but not the data contained
+ * within).
+ *
+ * \warning This function does not check if the target zone tree is empty,
+ * it just replaces the root pointer.
+ *
+ * \param from Original zone tree.
+ * \param to Zone tree to copy the original one into.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
+ knot_zone_tree_t *to);
+
+/*!
+ * \brief Destroys the zone tree, not touching the saved data.
+ *
+ * \param tree Zone tree to be destroyed.
+ */
+void knot_zone_tree_free(knot_zone_tree_t **tree);
+
+/*!
+ * \brief Destroys the zone tree, together with the saved data.
+ *
+ * \param tree Zone tree to be destroyed.
+ * \param free_owners Set to <> 0 if owners of the nodes should be destroyed
+ * as well. Set to 0 otherwise.
+ */
+void knot_zone_tree_deep_free(knot_zone_tree_t **tree, int free_owners);
+
+/*----------------------------------------------------------------------------*/
+
+#endif // _KNOT_ZONE_TREE_H_
+
+/*! @} */
+
diff --git a/src/libknot/zone/zone.c b/src/libknot/zone/zone.c
new file mode 100644
index 0000000..9de1a53
--- /dev/null
+++ b/src/libknot/zone/zone.c
@@ -0,0 +1,246 @@
+/* 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>
+#include <assert.h>
+#include <string.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/zone.h"
+#include "zone/node.h"
+#include "dname.h"
+#include "consts.h"
+#include "util/descriptor.h"
+#include "nsec3.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "util/utils.h"
+#include "common/tree.h"
+#include "common/base32hex.h"
+#include "hash/cuckoo-hash-table.h"
+#include "zone/zone-contents.h"
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zone_new_empty(knot_dname_t *name)
+{
+ if (!name) {
+ return 0;
+ }
+
+ dbg_zone("Creating new zone!\n");
+
+ knot_zone_t *zone = malloc(sizeof(knot_zone_t));
+ if (zone == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ memset(zone, 0, sizeof(knot_zone_t));
+
+ // save the zone name
+ dbg_zone("Setting zone name.\n");
+ zone->name = name;
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zone_new(knot_node_t *apex, uint node_count,
+ int use_domain_table)
+{
+ knot_zone_t *zone = knot_zone_new_empty(
+ knot_dname_deep_copy(knot_node_owner(apex)));
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ dbg_zone("Creating zone contents.\n");
+ zone->contents = knot_zone_contents_new(apex, node_count,
+ use_domain_table, zone);
+ if (zone->contents == NULL) {
+ knot_dname_release(zone->name);
+ free(zone);
+ return NULL;
+ }
+
+ zone->contents->zone = zone;
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_get_contents(
+ const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return rcu_dereference(zone->contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_zone_contents_t *knot_zone_contents(
+ const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return rcu_dereference(zone->contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+time_t knot_zone_version(const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return zone->version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_version(knot_zone_t *zone, time_t version)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ zone->version = version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_zone_is_master(const knot_zone_t *zone)
+{
+ return zone->master;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_master(knot_zone_t *zone, short master)
+{
+ zone->master = master;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const void *knot_zone_data(const knot_zone_t *zone)
+{
+ return zone->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_data(knot_zone_t *zone, void *data)
+{
+ zone->data = data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_zone_name(const knot_zone_t *zone)
+{
+ return zone->name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone,
+ knot_zone_contents_t *new_contents)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ knot_zone_contents_t *old_contents =
+ rcu_xchg_pointer(&zone->contents, new_contents);
+ return old_contents;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_free(knot_zone_t **zone)
+{
+ if (zone == NULL || *zone == NULL) {
+ return;
+ }
+
+ dbg_zone("zone_free().\n");
+
+ if ((*zone)->contents
+ && !knot_zone_contents_gen_is_old((*zone)->contents)) {
+ // zone is in the middle of an update, report
+ dbg_zone("Destroying zone that is in the middle of an "
+ "update.\n");
+ }
+
+ knot_dname_release((*zone)->name);
+
+ /* Call zone data destructor if exists. */
+ if ((*zone)->dtor) {
+ (*zone)->dtor(*zone);
+ }
+
+ knot_zone_contents_free(&(*zone)->contents);
+ free(*zone);
+ *zone = NULL;
+
+ dbg_zone("Done.\n");
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table)
+{
+ if (zone == NULL || *zone == NULL) {
+ return;
+ }
+
+ if ((*zone)->contents
+ && !knot_zone_contents_gen_is_old((*zone)->contents)) {
+ // zone is in the middle of an update, report
+ dbg_zone("Destroying zone that is in the middle of an "
+ "update.\n");
+ }
+
+dbg_zone_exec(
+ char *name = knot_dname_to_str((*zone)->name);
+ dbg_zone("Destroying zone %p, name: %s.\n", *zone, name);
+ free(name);
+);
+
+ knot_dname_release((*zone)->name);
+
+ /* Call zone data destructor if exists. */
+ if ((*zone)->dtor) {
+ (*zone)->dtor(*zone);
+ }
+
+ knot_zone_contents_deep_free(&(*zone)->contents, destroy_dname_table);
+ free(*zone);
+ *zone = NULL;
+}
diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h
new file mode 100644
index 0000000..331ef1f
--- /dev/null
+++ b/src/libknot/zone/zone.h
@@ -0,0 +1,157 @@
+/*!
+ * \file zone.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone structure and API for manipulating it.
+ *
+ * \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_ZONE_H_
+#define _KNOT_ZONE_H_
+
+#include <time.h>
+
+#include "zone/node.h"
+#include "dname.h"
+#include "nsec3.h"
+#include "zone/dname-table.h"
+#include "common/tree.h"
+#include "hash/cuckoo-hash-table.h"
+
+#include "zone-tree.h"
+
+#include "zone/zone-contents.h"
+
+/*----------------------------------------------------------------------------*/
+
+//typedef TREE_HEAD(avl_tree, knot_node) avl_tree_t;
+//struct event_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Return values for search functions.
+ *
+ * Used in knot_zone_find_dname() and knot_zone_find_dname_hash().
+ */
+enum knot_zone_retvals {
+ KNOT_ZONE_NAME_FOUND = 1,
+ KNOT_ZONE_NAME_NOT_FOUND = 0
+};
+
+typedef enum knot_zone_retvals knot_zone_retvals_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Structure for holding DNS zone.
+ *
+ * \warning Make sure not to insert the same nodes using both the normal and
+ * NSEC3 functions. Although this will be successfull, it will produce
+ * double-free errors when destroying the zone.
+ */
+struct knot_zone {
+ knot_dname_t *name;
+
+ knot_zone_contents_t *contents;
+
+ time_t version;
+
+ /*! \todo Set when loading zone. */
+ short master;
+
+ void *data; /*!< Pointer to generic zone-related data. */
+ int (*dtor)(struct knot_zone *); /*!< Data destructor. */
+};
+
+typedef struct knot_zone knot_zone_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates new empty DNS zone.
+ *
+ * \notice Zone will be created without contents.
+ *
+ * \param name Zone owner.
+ *
+ * \return The initialized zone structure or NULL if an error occured.
+ */
+knot_zone_t *knot_zone_new_empty(knot_dname_t *name);
+
+/*!
+ * \brief Creates new DNS zone.
+ *
+ * \param apex Node representing the zone apex.
+ * \param node_count Number of authorative nodes in the zone.
+ *
+ * \return The initialized zone structure or NULL if an error occured.
+ */
+knot_zone_t *knot_zone_new(knot_node_t *apex, uint node_count,
+ int use_domain_table);
+
+knot_zone_contents_t *knot_zone_get_contents(
+ const knot_zone_t *zone);
+
+const knot_zone_contents_t *knot_zone_contents(
+ const knot_zone_t *zone);
+
+
+time_t knot_zone_version(const knot_zone_t *zone);
+
+void knot_zone_set_version(knot_zone_t *zone, time_t version);
+
+short knot_zone_is_master(const knot_zone_t *zone);
+
+void knot_zone_set_master(knot_zone_t *zone, short master);
+
+const void *knot_zone_data(const knot_zone_t *zone);
+
+void knot_zone_set_data(knot_zone_t *zone, void *data);
+
+const knot_dname_t *knot_zone_name(const knot_zone_t *zone);
+
+knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone,
+ knot_zone_contents_t *new_contents);
+
+/*!
+ * \brief Correctly deallocates the zone structure, without deleting its nodes.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param zone Zone to be freed.
+ */
+void knot_zone_free(knot_zone_t **zone);
+
+/*!
+ * \brief Correctly deallocates the zone structure and all nodes within.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param zone Zone to be freed.
+ * \param free_rdata_dnames Set to <> 0 if you want to delete ALL domain names
+ * present in RDATA. Set to 0 otherwise. (See
+ * knot_rdata_deep_free().)
+ */
+void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table);
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c
new file mode 100644
index 0000000..8f07d45
--- /dev/null
+++ b/src/libknot/zone/zonedb.c
@@ -0,0 +1,389 @@
+/* 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>
+#include <assert.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/zone.h"
+#include "zone/zonedb.h"
+#include "dname.h"
+#include "zone/node.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common/general-tree.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the two arguments interpreted as zone names (domain names).
+ *
+ * Use this function with generic data structures (such as the skip list).
+ *
+ * \param d1 First zone name.
+ * \param d2 Second zone name.
+ *
+ * \retval 0 if the two zone names are equal.
+ * \retval < 0 if \a d1 is before \a d2 in canonical order.
+ * \retval > 0 if \a d1 is after \a d2 in canonical order.
+ */
+static int knot_zonedb_compare_zone_names(void *p1, void *p2)
+{
+ const knot_zone_t *zone1 = (const knot_zone_t *)p1;
+ const knot_zone_t *zone2 = (const knot_zone_t *)p2;
+
+ int ret = knot_dname_compare(zone1->name, zone2->name);
+
+dbg_zonedb_exec(
+ char *name1 = knot_dname_to_str(zone1->name);
+ char *name2 = knot_dname_to_str(zone2->name);
+ dbg_zonedb("Compared names %s and %s, result: %d.\n",
+ name1, name2, ret);
+ free(name1);
+ free(name2);
+);
+
+ return (ret);
+}
+
+/*----------------------------------------------------------------------------*/
+
+//static int knot_zonedb_replace_zone_in_list(void **list_item, void **new_zone)
+//{
+// assert(list_item != NULL);
+// assert(*list_item != NULL);
+// assert(new_zone != NULL);
+// assert(*new_zone != NULL);
+
+// dbg_zonedb("Replacing list item %p with new zone %p\n",
+// *list_item, *new_zone);
+
+// *list_item = *new_zone;
+
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zonedb_t *knot_zonedb_new()
+{
+ knot_zonedb_t *db =
+ (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t));
+ CHECK_ALLOC_LOG(db, NULL);
+
+ db->zone_tree = gen_tree_new(knot_zonedb_compare_zone_names);
+ if (db->zone_tree == NULL) {
+ free(db);
+ return NULL;
+ }
+
+ db->zone_count = 0;
+
+ return db;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone)
+{
+ if (db == NULL || zone == NULL) {
+ return KNOT_EBADARG;
+ }
+dbg_zonedb_exec(
+ char *name = knot_dname_to_str(zone->name);
+ dbg_zonedb("Inserting zone %s into zone db.\n", name);
+ free(name);
+);
+
+ int ret = KNOT_EOK;
+ if (knot_zone_contents(zone)) {
+ ret = knot_zone_contents_load_nsec3param(
+ knot_zone_get_contents(zone));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ ret = gen_tree_add(db->zone_tree, zone, NULL);
+
+ if (ret == 0) {
+ db->zone_count++;
+ }
+
+ return (ret != 0) ? KNOT_EZONEIN : KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ knot_zone_t dummy_zone;
+ memset(&dummy_zone, 0, sizeof(knot_zone_t));
+ dummy_zone.name = (knot_dname_t *)zone_name;
+
+ // add some lock to avoid multiple removals
+ knot_zone_t *z = (knot_zone_t *)gen_tree_find(db->zone_tree,
+ &dummy_zone);
+
+ if (z == NULL) {
+ return NULL;
+ }
+
+ // remove the zone from the skip list, but do not destroy it
+ gen_tree_remove(db->zone_tree, &dummy_zone);
+
+// if (destroy_zone) {
+// // properly destroy the zone and all its contents
+// knot_zone_deep_free(&z, 0);
+// }
+
+ db->zone_count--;
+
+ //return KNOT_EOK;
+ return z;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
+// knot_zone_t *zone)
+//{
+// knot_zone_t *z = knot_zonedb_find_zone(db,
+// knot_node_owner(knot_zone_apex(zone)));
+// if (z == NULL) {
+// return NULL;
+// }
+
+// /*! \todo The replace should be atomic!!! */
+
+// dbg_zonedb("Found zone: %p\n", z);
+
+// int ret = skip_remove(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(zone)),
+// NULL, NULL);
+// if (ret != 0) {
+// return NULL;
+// }
+
+// dbg_zonedb("Removed zone, return value: %d\n", ret);
+// dbg_zonedb("Old zone: %p\n", z);
+
+// ret = skip_insert(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(zone)),
+// (void *)zone, NULL);
+
+// dbg_zonedb("Inserted zone, return value: %d\n", ret);
+
+// if (ret != 0) {
+// // return the removed zone back
+// skip_insert(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(z)),
+// (void *)z, NULL);
+// /*! \todo There may be problems and the zone may remain
+// removed. */
+// return NULL;
+// }
+
+// return z;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ knot_zone_t dummy_zone;
+ dummy_zone.name = (knot_dname_t *)zone_name;
+ return (knot_zone_t *)gen_tree_find(db->zone_tree, &dummy_zone);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
+ const knot_dname_t *dname)
+{
+ if (db == NULL || dname == NULL) {
+ return NULL;
+ }
+
+ knot_zone_t dummy_zone;
+ dummy_zone.name = (knot_dname_t *)dname;
+ void *found = NULL;
+ int exact_match = gen_tree_find_less_or_equal(db->zone_tree,
+ &dummy_zone,
+ &found);
+ UNUSED(exact_match);
+
+ knot_zone_t *zone = (found) ? (knot_zone_t *)found : NULL;
+
+dbg_zonedb_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_zonedb("Found zone for name %s: %p\n", name, zone);
+ free(name);
+);
+ if (zone != NULL && zone->contents != NULL
+ && knot_dname_compare(zone->contents->apex->owner, dname) != 0
+ && !knot_dname_is_subdomain(dname, zone->contents->apex->owner)) {
+ zone = NULL;
+ }
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ if (db == NULL || zone_name == NULL) {
+ return NULL;
+ }
+
+ // Remove the contents from the zone, but keep the zone in the zonedb.
+
+ knot_zone_t *zone = knot_zonedb_find_zone(db, zone_name);
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return knot_zone_switch_contents(zone, NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zonedb_t *knot_zonedb_copy(const knot_zonedb_t *db)
+{
+ knot_zonedb_t *db_new =
+ (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t));
+ CHECK_ALLOC_LOG(db_new, NULL);
+
+ db_new->zone_tree = gen_tree_shallow_copy(db->zone_tree);
+ if (db_new->zone_tree == NULL) {
+ free(db_new);
+ return NULL;
+ }
+
+ return db_new;
+}
+
+size_t knot_zonedb_zone_count(const knot_zonedb_t *db)
+{
+ return db->zone_count;
+}
+
+struct knot_zone_db_tree_arg {
+ const knot_zone_t **zones;
+ size_t count;
+};
+
+static void save_zone_to_array(void *node, void *data)
+{
+ knot_zone_t *zone = (knot_zone_t *)node;
+ struct knot_zone_db_tree_arg *args =
+ (struct knot_zone_db_tree_arg *)data;
+ assert(data);
+ args->zones[args->count++] = zone;
+}
+
+const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db)
+{
+ struct knot_zone_db_tree_arg args;
+ args.zones = malloc(sizeof(knot_zone_t) * db->zone_count);
+ args.count = 0;
+ CHECK_ALLOC_LOG(args.zones, NULL);
+
+ gen_tree_apply_inorder(db->zone_tree, save_zone_to_array,
+ &args);
+ assert(db->zone_count == args.count);
+
+ return args.zones;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zonedb_free(knot_zonedb_t **db)
+{
+ gen_tree_destroy(&((*db)->zone_tree), NULL ,NULL);
+ free(*db);
+ *db = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void delete_zone_from_db(void *node, void *data)
+{
+ UNUSED(data);
+ knot_zone_t *zone = (knot_zone_t *)node;
+ assert(zone);
+ synchronize_rcu();
+ knot_zone_deep_free(&zone, 0);
+}
+
+void knot_zonedb_deep_free(knot_zonedb_t **db)
+{
+ dbg_zonedb("Deleting zone db (%p).\n", *db);
+// dbg_zonedb("Is it empty (%p)? %s\n",
+// (*db)->zones, skip_is_empty((*db)->zones) ? "yes" : "no");
+
+//dbg_zonedb_exec(
+// int i = 1;
+// char *name = NULL;
+// while (zn != NULL) {
+// dbg_zonedb("%d. zone: %p, key: %p\n", i, zn->value,
+// zn->key);
+// assert(zn->key == ((knot_zone_t *)zn->value)->apex->owner);
+// name = knot_dname_to_str((knot_dname_t *)zn->key);
+// dbg_zonedb(" zone name: %s\n", name);
+// free(name);
+
+// zn = skip_next(zn);
+// }
+
+// zn = skip_first((*db)->zones);
+//);
+
+// while (zn != NULL) {
+// zone = (knot_zone_t *)zn->value;
+// assert(zone != NULL);
+
+// // remove the zone from the database
+// skip_remove((*db)->zones, zn->key, NULL, NULL);
+// // wait for all readers to finish
+// synchronize_rcu;
+// // destroy the zone
+// knot_zone_deep_free(&zone, 0);
+
+// zn = skip_first((*db)->zones);
+// }
+
+// assert(skip_is_empty((*db)->zones));
+
+// skip_destroy_list(&(*db)->zones, NULL, NULL);
+ gen_tree_destroy(&((*db)->zone_tree), delete_zone_from_db, NULL);
+ assert((*db)->zone_tree == NULL);
+ free(*db);
+ *db = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h
new file mode 100644
index 0000000..d5a4992
--- /dev/null
+++ b/src/libknot/zone/zonedb.h
@@ -0,0 +1,145 @@
+/*!
+ * \file zonedb.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone database structure and API for manipulating it.
+ *
+ * Zone database groups several zones and provides functions for finding
+ * suitable zone for a domain name, for searching in a particular zone, etc.
+ *
+ * \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_ZONEDB_H_
+#define _KNOT_ZONEDB_H_
+
+#include "common/general-tree.h"
+#include "zone/zone.h"
+#include "zone/node.h"
+#include "dname.h"
+
+/*!
+ * \brief Zone database structure. Contains all zones managed by the server.
+ */
+struct knot_zonedb {
+ general_tree_t *zone_tree; /*!< AVL tree of zones. */
+ size_t zone_count;
+};
+
+typedef struct knot_zonedb knot_zonedb_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Allocates and initializes the zone database structure.
+ *
+ * \return Pointer to the created zone database structure or NULL if an error
+ * occured.
+ */
+knot_zonedb_t *knot_zonedb_new();
+
+/*!
+ * \brief Adds new zone to the database.
+ *
+ * \param db Zone database to store the zone.
+ * \param zone Parsed zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EZONEIN
+ */
+int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone);
+
+/*!
+ * \brief Removes the given zone from the database if it exists.
+ *
+ * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames().
+ * If it was not, it may leak some memory due to checks used in
+ * knot_rdata_deep_free().
+ *
+ * \param db Zone database to remove from.
+ * \param zone_name Name of the zone to be removed.
+ * \param destroy_zone Set to <> 0 if you do want the function to destroy the
+ * zone after removing from zone database. Set to 0
+ * otherwise.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOZONE
+ */
+knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
+// knot_zone_t *zone);
+
+/*!
+ * \brief Finds zone exactly matching the given zone name.
+ *
+ * \param db Zone database to search in.
+ * \param zone_name Domain name representing the zone name.
+ *
+ * \return Zone with \a zone_name being the owner of the zone apex or NULL if
+ * not found.
+ */
+knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+
+/*!
+ * \brief Finds zone the given domain name should belong to.
+ *
+ * \param db Zone database to search in.
+ * \param dname Domain name to find zone for.
+ *
+ * \retval Zone in which the domain name should be present or NULL if no such
+ * zone is found.
+ */
+const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
+ const knot_dname_t *dname);
+
+knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+size_t knot_zonedb_zone_count(const knot_zonedb_t *db);
+const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db);
+
+/*!
+ * \brief Destroys and deallocates the zone database structure (but not the
+ * zones within).
+ *
+ * \param db Zone database to be destroyed.
+ */
+void knot_zonedb_free(knot_zonedb_t **db);
+
+/*!
+ * \brief Destroys and deallocates the whole zone database including the zones.
+ *
+ * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames().
+ * If it was not, it may leak some memory due to checks used in
+ * knot_rdata_deep_free().
+ *
+ * \param db Zone database to be destroyed.
+ */
+void knot_zonedb_deep_free(knot_zonedb_t **db);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_ZONEDB_H_ */
+
+/*! @} */
diff --git a/src/tests/README b/src/tests/README
new file mode 100644
index 0000000..2f299ad
--- /dev/null
+++ b/src/tests/README
@@ -0,0 +1,10 @@
+Unit testing
+------------
+
+Make assembles "unittest" binary with all of the planned tests included.
+So far it is accepting the same parameters as the "cutedns",
+but an own parameter format is being developed.
+
+Example:
+bin/unittest samples/example.com.zone
+
diff --git a/src/tests/common/acl_tests.c b/src/tests/common/acl_tests.c
new file mode 100644
index 0000000..b4232ac
--- /dev/null
+++ b/src/tests/common/acl_tests.c
@@ -0,0 +1,111 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/socket.h>
+
+#include "tests/common/acl_tests.h"
+#include "common/sockaddr.h"
+#include "common/acl.h"
+
+static int acl_tests_count(int argc, char *argv[]);
+static int acl_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api acl_tests_api = {
+ "ACL", //! Unit name
+ &acl_tests_count, //! Count scheduled tests
+ &acl_tests_run //! Run scheduled tests
+};
+
+static int acl_tests_count(int argc, char *argv[])
+{
+ return 13;
+}
+
+static int acl_tests_run(int argc, char *argv[])
+{
+ // 1. Create an ACL
+ acl_t *acl = acl_new(ACL_DENY, "simple ACL");
+ ok(acl != 0, "acl: new");
+
+ // 2. Create IPv4 address
+ sockaddr_t test_v4;
+ int ret = sockaddr_set(&test_v4, AF_INET, "127.0.0.1", 12345);
+ ok(ret > 0, "acl: new IPv4 address");
+
+ // 3. Create IPv6 address
+ sockaddr_t test_v6;
+ ret = sockaddr_set(&test_v6, AF_INET6, "::1", 54321);
+ ok(ret > 0, "acl: new IPv6 address");
+
+ // 4. Create simple IPv4 rule
+ ret = acl_create(acl, &test_v4, ACL_ACCEPT);
+ ok(ret == ACL_ACCEPT, "acl: inserted IPv4 rule");
+
+ // 5. Create simple IPv6 rule
+ ret = acl_create(acl, &test_v6, ACL_ACCEPT);
+ ok(ret == ACL_ACCEPT, "acl: inserted IPv6 rule");
+
+ // 6. Create simple IPv4 'any port' rule
+ sockaddr_t test_v4a;
+ sockaddr_set(&test_v4a, AF_INET, "20.20.20.20", 0);
+ ret = acl_create(acl, &test_v4a, ACL_ACCEPT);
+ ok(ret == ACL_ACCEPT, "acl: inserted IPv4 'any port' rule");
+
+ // 7. Attempt to match unmatching address
+ sockaddr_t unmatch_v4;
+ sockaddr_set(&unmatch_v4, AF_INET, "10.10.10.10", 24424);
+ ret = acl_match(acl, &unmatch_v4);
+ ok(ret == ACL_DENY, "acl: matching non-existing address");
+
+ // 8. Attempt to match unmatching IPv6 address
+ sockaddr_t unmatch_v6;
+ sockaddr_set(&unmatch_v6, AF_INET6, "2001:db8::1428:57ab", 24424);
+ ret = acl_match(acl, &unmatch_v6);
+ ok(ret == ACL_DENY, "acl: matching non-existing IPv6 address");
+
+ // 9. Attempt to match matching address
+ ret = acl_match(acl, &test_v4);
+ ok(ret == ACL_ACCEPT, "acl: matching existing address");
+
+ // 10. Attempt to match matching address
+ ret = acl_match(acl, &test_v6);
+ ok(ret == ACL_ACCEPT, "acl: matching existing IPv6 address");
+
+ // 11. Attempt to match matching 'any port' address
+ sockaddr_t match_v4a;
+ sockaddr_set(&match_v4a, AF_INET, "20.20.20.20", 24424);
+ ret = acl_match(acl, &match_v4a);
+ ok(ret == ACL_ACCEPT, "acl: matching existing IPv4 'any port' address");
+
+ // 12. Attempt to match matching address without matching port
+ sockaddr_set(&unmatch_v4, AF_INET, "127.0.0.1", 54321);
+ ret = acl_match(acl, &unmatch_v4);
+ ok(ret == ACL_DENY, "acl: matching address without matching port");
+
+ // 13. Invalid parameters
+ lives_ok({
+ acl_delete(0);
+ acl_create(0, 0, ACL_ERROR);
+ acl_match(0, 0);
+ acl_truncate(0);
+ acl_name(0);
+ }, "acl: won't crash with NULL parameters");
+
+ // Return
+ return 0;
+}
diff --git a/src/tests/common/acl_tests.h b/src/tests/common/acl_tests.h
new file mode 100644
index 0000000..a928e2d
--- /dev/null
+++ b/src/tests/common/acl_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ACL_TESTS_H_
+#define _KNOTD_ACL_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api acl_tests_api;
+
+#endif /* _KNOTD_ACL_TESTS_H_ */
diff --git a/src/tests/common/da_tests.c b/src/tests/common/da_tests.c
new file mode 100644
index 0000000..627e8aa
--- /dev/null
+++ b/src/tests/common/da_tests.c
@@ -0,0 +1,330 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/common/da_tests.h"
+#include "common/dynamic-array.h"
+#include <unistd.h>
+#include <urcu.h>
+
+static int da_tests_count(int argc, char *argv[]);
+static int da_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api da_tests_api = {
+ "Dynamic array",
+ &da_tests_count,
+ &da_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+
+static const int DA_TEST_COUNT = 5;
+static const int RCU_THREADS = 3;
+static const int DA_FRAGMENT = 10;
+static const int DA_DEF_SIZE = 1000;
+static const int DA_OPERATIONS = 1000;
+enum Operations {
+ DA_RESERVE = 0,
+ DA_OCCUPY = 1,
+ DA_RELEASE = 2,
+ DA_OPCOUNT = 3
+};
+
+static int da_tests_count(int argc, char *argv[])
+{
+ return DA_TEST_COUNT;
+}
+
+static void do_something(int loops)
+{
+ int i;
+ int res = 1;
+
+ static const int LOOPS = 10000;
+
+ for (int j = 1; j <= LOOPS; ++j) {
+ for (i = 1; i <= loops; ++i) {
+ res *= i;
+ }
+ }
+}
+
+static void *test_rcu_routine(void *obj)
+{
+ rcu_register_thread();
+ rcu_read_lock();
+
+ do_something(1000);
+
+ rcu_read_unlock();
+ rcu_unregister_thread();
+
+ return NULL;
+}
+
+static int test_rcu_threads()
+{
+ // Create threads
+ pthread_t *threads = malloc(RCU_THREADS * sizeof(pthread_t));
+ for (int i = 0; i < RCU_THREADS; ++i) {
+ if (pthread_create(&threads[i], NULL, test_rcu_routine, NULL)) {
+ diag("rcu: failed to create thread %d", i);
+ free(threads);
+ return 0;
+ }
+ }
+
+ // Join threads
+ void *pret = NULL;
+ for (int i = 0; i < RCU_THREADS; ++i) {
+ if (pthread_join(threads[i], &pret)) {
+ diag("rcu: failed to join thread %d", i);
+ free(threads);
+ return 0;
+ }
+ }
+
+ synchronize_rcu();
+ free(threads);
+
+ return 1;
+}
+
+static int test_da_init(da_array_t *arr)
+{
+ return da_initialize(arr, DA_DEF_SIZE, sizeof(uint)) == 0;
+}
+
+static int test_da_random_op(da_array_t *arr)
+{
+ unsigned seed = (unsigned)time(0);
+ uint allocated = DA_DEF_SIZE;
+ uint size = 0;
+
+ for (int i = 0; i < DA_OPERATIONS; ++i) {
+ int r = rand_r(&seed) % DA_OPCOUNT;
+ int count = rand_r(&seed) % DA_FRAGMENT + 1;
+
+ switch (r) {
+
+ // Perform reserve operation
+ case DA_RESERVE:
+ if (da_reserve(arr, count) >= 0 &&
+ size <= allocated) {
+ if ((allocated - size) < count) {
+ allocated *= 2;
+ }
+ } else {
+ diag("dynamic-array: da_reserve(%p, %d) failed"
+ " (size %d, alloc'd %d)",
+ arr, count, size, allocated);
+ return 0;
+ }
+ break;
+
+ // Perform occupy operation
+ case DA_OCCUPY:
+ if (da_occupy(arr, count) == 0) {
+ uint *items = (uint *) da_get_items(arr);
+ for (int j = 0; j < da_get_count(arr); ++j) {
+ items[j] = rand_r(&seed);
+ }
+ if (size <= allocated &&
+ (allocated - size) >= count) {
+ size += count;
+ } else {
+ return 0;
+ }
+ } else {
+ diag("dynamic-array: da_occupy(%p, %d) failed"
+ " (size %d, alloc'd %d)",
+ arr, count, size, allocated);
+ return 0;
+ }
+ break;
+
+ // Perform release operation
+ case DA_RELEASE:
+ if (arr->count > 0) {
+ count = (rand_r(&seed) % DA_FRAGMENT) % arr->count;
+ da_release(arr, count);
+
+ if (size <= allocated && size >= count) {
+ size -= count;
+ } else {
+ return 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Check allocated / size
+ if (allocated != arr->allocated || size != arr->count) {
+ diag("dynamic-array: allocated memory %d (expected %d)"
+ " size %d (expected %d) mismatch",
+ arr->allocated, allocated, arr->count, size);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void *test_da_read(void *obj)
+{
+ rcu_register_thread();
+ rcu_read_lock();
+
+ unsigned seed = (unsigned)time(0);
+ da_array_t *arr = (da_array_t *) obj;
+ int index = rand_r(&seed) % da_get_count(arr);
+
+ note(" dynamic-array: read thread");
+ note(" read thread: saving pointer to %d. item", index);
+ uint *item = &((uint *) da_get_items(arr))[index];
+ note(" read thread: before: pointer: %p item: %u", item, *item);
+
+ do_something(100000);
+
+ note(" read thread after: pointer: %p item: %u", item, *item);
+ rcu_read_unlock();
+ note(" read thread unlocked: pointer: %p item: %u", item, *item);
+
+ do_something(10000);
+
+ note(" read thread: now the item should be deallocated");
+ //note(" read thread: pointer: %p item: %u", item, *item);
+
+ rcu_unregister_thread();
+
+ return NULL;
+}
+
+static int test_da_resize_holding(da_array_t *arr)
+{
+ int ret = 1;
+ rcu_register_thread();
+ pthread_t reader;
+
+ // Create thread for reading
+ note("dynamic-array: creating read threads");
+ if (pthread_create(&reader, NULL, test_da_read, (void *)arr)) {
+ diag("dynamic-array: failed to create reading thread",
+ __func__);
+ rcu_unregister_thread();
+ return 0;
+ }
+
+ // Wait some time, so the other thread gets the item for reading
+ do_something(5000);
+
+ // Force resize
+ note(" dynamic-array: array resized");
+ if (da_reserve(arr, arr->allocated - arr->count + 1) == -1) {
+ diag("dynamic-array: da_reserve(%p, %d) failed", arr,
+ arr->allocated - arr->count + 1);
+ ret = 0;
+ }
+
+ //Wait for the thread to finish
+ void *pret = NULL;
+ if (pthread_join(reader, &pret)) {
+ diag("dynamic-array: failed to join reading thread",
+ __func__);
+ ret = 0;
+ }
+
+ rcu_unregister_thread();
+ return ret;
+}
+
+static int test_da_resize(da_array_t *arr)
+{
+ unsigned seed = (unsigned)time(0);
+ int orig_count = da_get_count(arr);
+ note("dynamic-array: allocated: %d, items: %d", arr->allocated,
+ orig_count);
+ // store the items currently in the array
+ int *items = (int *)malloc(orig_count * sizeof(int));
+ for (int i = 0; i < orig_count; ++i) {
+ items[i] = ((int *)da_get_items(arr))[i];
+ }
+
+ // force resize
+ int res = 0;
+ while ((res = da_reserve(arr, 10)) == 0) {
+ int i = da_get_count(arr);
+ da_occupy(arr, 10);
+ for (; i < da_get_count(arr); ++i) {
+ ((int *)da_get_items(arr))[i] = rand_r(&seed);
+ }
+ }
+
+ if (res < 0) {
+ diag("dynamic-array: failed to reserve space");
+ return 0;
+ }
+
+ int errors = 0;
+ for (int i = 0; i < orig_count; ++i) {
+ if (items[i] != ((int *)da_get_items(arr))[i]) {
+ diag("dynamic-array: Wrong item on position %d."
+ "Should be: %d, "
+ "present value: %d", i, items[i],
+ ((int *)da_get_items(arr))[i]);
+ ++errors;
+ }
+ }
+
+ free(items);
+
+ return errors == 0;
+}
+
+static int da_tests_run(int argc, char *argv[])
+{
+ // Init
+ rcu_init();
+ da_array_t array;
+
+ // Test 1: test rcu
+ ok(test_rcu_threads(), "dynamic-array: rcu tests");
+
+ // Test 2: init
+ ok(test_da_init(&array), "dynamic-array: init");
+
+ // Test 3: reserve/occupy random operations
+ ok(test_da_random_op(&array),
+ "dynamic-array: randomized reserve/occupy/release");
+
+ // Test 4: resizing array while holding an item
+ ok(test_da_resize_holding(&array),
+ "dynamic-array: resize array while holding an item");
+
+ // Test 5: resize
+ ok(test_da_resize(&array), "dynamic-array: resize array");
+
+ // Cleanup
+ da_destroy(&array);
+ return 0;
+}
diff --git a/src/tests/common/da_tests.h b/src/tests/common/da_tests.h
new file mode 100644
index 0000000..d51b7be
--- /dev/null
+++ b/src/tests/common/da_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DA_TESTS_H_
+#define _KNOTD_DA_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api da_tests_api;
+
+#endif /* _KNOTD_DA_TESTS_H_ */
diff --git a/src/tests/common/events_tests.c b/src/tests/common/events_tests.c
new file mode 100644
index 0000000..d7702fe
--- /dev/null
+++ b/src/tests/common/events_tests.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 <stdlib.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "tests/common/events_tests.h"
+#include "common/evqueue.h"
+#include "common/evsched.h"
+
+static int events_tests_count(int argc, char *argv[]);
+static int events_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api events_tests_api = {
+ "Event queue and scheduler", //! Unit name
+ &events_tests_count, //! Count scheduled tests
+ &events_tests_run //! Run scheduled tests
+};
+
+void* term_thr(void *arg)
+{
+ evsched_t *s = (evsched_t *)arg;
+
+ /* Sleep for 100ms. */
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000; // 100ms
+ select(0, 0, 0, 0, &tv);
+
+ /* Issue termination event. */
+ evsched_schedule_term(s, 0);
+ return 0;
+}
+
+static int events_tests_count(int argc, char *argv[])
+{
+ return 9 + 11;
+}
+
+static int events_tests_run(int argc, char *argv[])
+{
+ /*
+ * Event queue tests.
+ */
+
+ // 1. Construct an event queue
+ evqueue_t *q = evqueue_new();
+ ok(q != 0, "evqueue: new");
+
+ // 2. Send integer through event queue
+ int ret = 0;
+ uint8_t sent = 0xaf, rcvd = 0;
+ ret = evqueue_write(q, &sent, sizeof(uint8_t));
+ ok(ret == sizeof(uint8_t), "evqueue: send byte through");
+
+ // 3. Receive byte from event queue
+ ret = evqueue_read(q, &rcvd, sizeof(uint8_t));
+ ok(ret == sizeof(uint8_t), "evqueue: received byte");
+
+ // 4. Received match
+ ok(sent == rcvd, "evqueue: received byte match");
+
+ // 5. Sending event
+ event_t ev, rev;
+ memset(&ev, 0, sizeof(event_t));
+ memset(&rev, 0, sizeof(event_t));
+ ev.type = 0xfa11;
+ ev.data = (void*)0xceed;
+ ret = evqueue_add(q, &ev);
+ ok(ret == 0, "evqueue: sent event to queue");
+
+ // 6. Poll for new events
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100 * 1000 * 1000; // 100ms
+ ret = evqueue_poll(q, &ts, 0);
+ ok(ret > 0, "evqueue: polling queue for events");
+
+ // 7. Compare received event
+ ret = evqueue_get(q, &rev);
+ /* Compare useful data, as event owner was changed in evqueue_get(). */
+ if (ev.type == rev.type && ev.data == rev.data) {
+ ret = 0;
+ }
+ ok(ret == 0, "evqueue: received event matches sent");
+
+ // 8. Invalid parameters
+ lives_ok({
+ evqueue_free(0);
+ evqueue_poll(0,0,0);
+ evqueue_read(0, 0, 0);
+ evqueue_write(0, 0, 0);
+ evqueue_read(0, 0, 0);
+ evqueue_get(0, 0);
+ evqueue_add(0, 0);
+ }, "evqueue: won't crash with NULL parameters");
+
+ // 9. Free event queue
+ lives_ok({evqueue_free(&q);}, "evqueue: delete");
+
+ /*
+ * Event scheduler tests.
+ */
+
+ // 1. Construct event scheduler
+ event_t *e = 0;
+ evsched_t *s = evsched_new();
+ ok(s != 0, "evsched: new");
+
+ // 2. Schedule event to happen after N ms
+ int msecs = 50;
+ struct timeval st, rt;
+ gettimeofday(&st, 0);
+ e = evsched_schedule_cb(s, 0, (void*)0xcafe, msecs);
+ ok(e != 0, "evsched: scheduled empty event after %dms", msecs);
+
+ // 3. Wait for next event
+ e = evsched_next(s);
+ evsched_event_finished(s);
+ gettimeofday(&rt, 0);
+ ok(e != 0, "evsched: received valid event");
+
+ // 4. Check receive time
+ double passed = (rt.tv_sec - st.tv_sec) * 1000;
+ passed += (rt.tv_usec - st.tv_usec) / 1000;
+ double margin = msecs * 0.2;
+ double lb = msecs - margin, ub = msecs + margin;
+ int in_bounds = (passed >= lb) && (passed <= ub);
+ ok(in_bounds, "evsched: receive time %.1lfms is in <%.1lf,%.1lf>",
+ passed, lb, ub);
+
+ // 5. Check data
+ ok(e->data == (void*)0xcafe, "evsched: received data is valid");
+
+ // 6. Delete event
+ lives_ok({evsched_event_free(s, e);}, "evsched: deleted event");
+
+ // 7. Insert and immediately cancel an event
+ e = evsched_schedule_cb(s, 0, (void*)0xdead, 1000);
+ ret = evsched_cancel(s, e);
+ ok(ret == 0, "evsched: inserted and cancelled an event");
+ if (e) {
+ evsched_event_free(s, e);
+ }
+
+ // 8. Start listener thread and block
+ pthread_t t;
+ pthread_create(&t, 0, term_thr, s);
+ e = evsched_next(s);
+ evsched_event_finished(s);
+ ok(e != 0, "evsched: received termination event");
+
+ // 9. Termination event is valid
+ ok(e->type == EVSCHED_TERM, "evsched: termination event is valid");
+ evsched_event_free(s, e);
+ pthread_join(t, 0);
+
+ // 10. Invalid parameters
+ lives_ok({
+ evsched_delete(0);
+ evsched_event_new(0, 0);
+ evsched_event_free(0, 0);
+ evsched_next(0);
+ evsched_schedule(0, 0, 0);
+ evsched_schedule_cb(0, 0, 0, 0);
+ evsched_schedule_term(0, 0);
+ evsched_cancel(0, 0);
+
+ }, "evsched: won't crash with NULL parameters");
+
+ // 11. Delete event scheduler
+ lives_ok({evsched_delete(&s);}, "evsched: delete");
+
+
+ return 0;
+}
diff --git a/src/tests/common/events_tests.h b/src/tests/common/events_tests.h
new file mode 100644
index 0000000..b54b6da
--- /dev/null
+++ b/src/tests/common/events_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD__EVENTS_TESTS_H_
+#define _KNOTD__EVENTS_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api events_tests_api;
+
+#endif /* _KNOTD__EVENTS_TESTS_H_ */
diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c
new file mode 100644
index 0000000..7dd95a1
--- /dev/null
+++ b/src/tests/common/fdset_tests.c
@@ -0,0 +1,177 @@
+/* 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 <stdlib.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+#include "tests/common/fdset_tests.h"
+#include "common/fdset.h"
+
+#define WRITE_PATTERN ((char) 0xde)
+#define WRITE_PATTERN_LEN sizeof(char)
+
+
+/* Subtract the `struct timeval' values X and Y,
+ storing the result in RESULT.
+ Return 1 if the difference is negative, otherwise 0.
+ Copyright http://www.delorie.com/gnu/docs/glibc/libc_428.html
+*/
+static int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval* y)
+{
+ /* Perform the carry for the later subtraction by updating y. */
+ if (x->tv_usec < y->tv_usec) {
+ int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
+ y->tv_usec -= 1000000 * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_usec - y->tv_usec > 1000000) {
+ int nsec = (x->tv_usec - y->tv_usec) / 1000000;
+ y->tv_usec += 1000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ tv_usec is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
+}
+
+static size_t timeval_diff(struct timeval *from, struct timeval *to) {
+ struct timeval res;
+ timeval_subtract(&res, to, from);
+ return res.tv_sec*1000 + res.tv_usec/1000;
+}
+
+static int fdset_tests_count(int argc, char *argv[]);
+static int fdset_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api fdset_tests_api = {
+ "Native fdset poll wrapper", //! Unit name
+ &fdset_tests_count, //! Count scheduled tests
+ &fdset_tests_run //! Run scheduled tests
+};
+
+void* thr_action(void *arg)
+{
+ int *fd = (int *)arg;
+
+ /* Sleep for 100ms. */
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000; // 100ms
+ select(0, 0, 0, 0, &tv);
+
+ /* Write pattern. */
+ char pattern = WRITE_PATTERN;
+ int ret = write(*fd, &pattern, WRITE_PATTERN_LEN);
+ ret = ret; /* Use variable. */
+
+ return 0;
+}
+
+static int fdset_tests_count(int argc, char *argv[])
+{
+ return 11;
+}
+
+static int fdset_tests_run(int argc, char *argv[])
+{
+ diag("fdset: implements '%s'", fdset_method());
+
+ /* 1. Create fdset. */
+ fdset_t *set = fdset_new();
+ ok(set != 0, "fdset: new");
+
+ /* 2. Create pipe. */
+ int fds[2], tmpfds[2];
+ int ret = pipe(fds);
+ ok(ret >= 0, "fdset: pipe() works");
+ ret = pipe(tmpfds);
+
+ /* 3. Add fd to set. */
+ ret = fdset_add(set, fds[0], OS_EV_READ);
+ ok(ret == 0, "fdset: add to set works");
+ fdset_add(set, tmpfds[0], OS_EV_READ);
+
+ /* Schedule write. */
+ struct timeval ts, te;
+ gettimeofday(&ts, 0);
+ pthread_t t;
+ pthread_create(&t, 0, thr_action, &fds[1]);
+
+ /* 4. Watch fdset. */
+ ret = fdset_wait(set);
+ gettimeofday(&te, 0);
+ size_t diff = timeval_diff(&ts, &te);
+
+ ok(ret > 0 && diff > 99 && diff < 10000,
+ "fdset: poll returned events in %zu ms", diff);
+
+ /* 5. Prepare event set. */
+ fdset_it_t it;
+ ret = fdset_begin(set, &it);
+ ok(ret == 0 && it.fd == fds[0], "fdset: begin is valid, ret=%d", ret);
+
+ /* 6. Receive data. */
+ char buf = 0x00;
+ ret = read(it.fd, &buf, WRITE_PATTERN_LEN);
+ ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data, fd=%d", it.fd);
+
+ /* 7. Iterate event set. */
+ ret = fdset_next(set, &it);
+ ok(ret < 0, "fdset: boundary check works");
+
+ /* 8. Remove from event set. */
+ ret = fdset_remove(set, fds[0]);
+ ok(ret == 0, "fdset: remove from fdset works");
+ close(fds[0]);
+ close(fds[1]);
+ ret = fdset_remove(set, tmpfds[0]);
+ close(tmpfds[1]);
+ close(tmpfds[1]);
+
+ /* 9. Poll empty fdset. */
+ ret = fdset_wait(set);
+ ok(ret <= 0, "fdset: polling empty fdset returns -1 (ret=%d)", ret);
+
+ /* 10. Crash test. */
+ lives_ok({
+ fdset_destroy(0);
+ fdset_add(0, -1, 0);
+ fdset_remove(0, -1);
+ fdset_wait(0);
+ fdset_begin(0, 0);
+ fdset_end(0, 0);
+ fdset_next(0, 0);
+ fdset_method();
+ }, "fdset: crash test successful");
+
+ /* 11. Destroy fdset. */
+ ret = fdset_destroy(set);
+ ok(ret == 0, "fdset: destroyed");
+
+ /* Cleanup. */
+ pthread_join(t, 0);
+
+ return 0;
+}
diff --git a/src/tests/common/fdset_tests.h b/src/tests/common/fdset_tests.h
new file mode 100644
index 0000000..d29e1a9
--- /dev/null
+++ b/src/tests/common/fdset_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_FDSET_TESTS_H_
+#define _KNOTD_FDSET_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api fdset_tests_api;
+
+#endif /* _KNOTD_FDSET_TESTS_H_ */
diff --git a/src/tests/common/skiplist_tests.c b/src/tests/common/skiplist_tests.c
new file mode 100644
index 0000000..4fe99ec
--- /dev/null
+++ b/src/tests/common/skiplist_tests.c
@@ -0,0 +1,198 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <time.h>
+
+#include "tests/common/skiplist_tests.h"
+#include "common/skip-list.h"
+
+static int skiplist_tests_count(int argc, char *argv[]);
+static int skiplist_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api skiplist_tests_api = {
+ "Skip list",
+ &skiplist_tests_count,
+ &skiplist_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+
+static const int SKIPLIST_TEST_COUNT = 5;
+
+static int skiplist_tests_count(int argc, char *argv[])
+{
+ return SKIPLIST_TEST_COUNT;
+}
+
+/* Comparing and merging limited to int keys used in test.
+ */
+int test_skip_compare_keys(void *key1, void *key2)
+{
+ return ((long)key1 < (long)key2) ?
+ -1 : (((long)key1 > (long)key2) ? 1 : 0);
+}
+
+int test_skip_merge_values(void **lvalue, void **rvalue)
+{
+ (*lvalue) = (void *)((long)(*lvalue) + (long)(*rvalue));
+ return 0;
+}
+
+int test_skiplist_create(skip_list_t **list)
+{
+ *list = skip_create_list(test_skip_compare_keys);
+ return *list != NULL;
+}
+
+int test_skiplist_fill(skip_list_t *list, long *uitems, int loops)
+{
+ int uitem_count = 0;
+ for (int i = 0; i < loops; ++i) {
+ long key = rand() % 100 + 1;
+ long value = rand() % 100 + 1;
+ int res = skip_insert(list, (void *)key, (void *)value,
+ test_skip_merge_values);
+ switch (res) {
+ case -2:
+ diag("skiplist: merging failed");
+ return 0;
+ break;
+ case -1:
+ diag("skiplist: insert failed");
+ return 0;
+ break;
+ case 0:
+ uitems[uitem_count++] = key;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return uitem_count;
+}
+
+int test_skiplist_lookup_seq(skip_list_t *list, long *uitems, int uitems_count)
+{
+ int errors = 0;
+
+ // Sequential lookup
+ for (int i = 0; i < uitems_count; ++i) {
+ void *found = skip_find(list, (void *) uitems[i]);
+ if (found == NULL) {
+ diag("skiplist: sequential "
+ "lookup failed, key: %d", uitems[i]);
+ ++errors;
+ }
+ }
+
+ if (errors) {
+ diag("skiplist: sequential lookup: %d found %d missed,"
+ " %.2f%% success rate",
+ uitems_count - errors, errors,
+ (uitems_count - errors) / (float) uitems_count * 100.0);
+ }
+
+ return errors == 0;
+}
+
+int test_skiplist_lookup_rand(skip_list_t *list, long *uitems, int uitems_count)
+{
+ int errors = 0;
+ srand((unsigned)time(NULL));
+
+ // Random lookup
+ for (int i = 0; i < uitems_count; ++i) {
+ long key = rand() % uitems_count + 1;
+ void *found = skip_find(list, (void *) key);
+ if (found == NULL) {
+ diag("skiplist: random lookup"
+ "failed, key: %d", uitems[i]);
+ ++errors;
+ }
+ }
+
+ if (errors) {
+ diag("skiplist: sequential lookup: "
+ "%d found %d missed, %.2f%% success rate",
+ uitems_count - errors, errors,
+ (uitems_count - errors) / (float) uitems_count * 100.0);
+ }
+ return errors == 0;
+}
+
+
+int test_skiplist_remove(skip_list_t *list, long *uitems, int uitems_count)
+{
+ int errors = 0;
+
+ // delete items
+ for (int i = 0; i < uitems_count; ++i) {
+ int res = skip_remove(list, (void *) uitems[i], NULL, NULL);
+ switch (res) {
+ case 0:
+ break;
+ default:
+ ++errors;
+ break;
+ }
+ }
+
+ if (errors) {
+ diag("skiplist: sequential lookup: %d found %d missed, "
+ "%.2f%% success rate",
+ uitems_count - errors, errors,
+ (uitems_count - errors) / (float) uitems_count * 100.0);
+ }
+ return errors == 0;
+}
+
+static int skiplist_tests_run(int argc, char *argv[])
+{
+ const int loops = 100;
+ int uitems_count = 0;
+ long *uitems = malloc(loops * sizeof(long));
+ skip_list_t *list = 0;
+
+ // Test 1: create
+ ok(test_skiplist_create(&list), "skiplist: create");
+
+ // Test 2: fill
+ ok(uitems_count = test_skiplist_fill(list, uitems, loops),
+ "skiplist: fill");
+
+ // Test 3: sequential lookup
+ ok(test_skiplist_lookup_seq(list, uitems, uitems_count),
+ "skiplist: sequential lookup");
+
+ // Test 4: sequential lookup
+ ok(test_skiplist_lookup_seq(list, uitems, uitems_count),
+ "skiplist: random lookup lookup");
+
+ // Test 5: remove items
+ ok(test_skiplist_remove(list, uitems, uitems_count),
+ "skiplist: random lookup lookup");
+
+ // Cleanup
+ skip_destroy_list(&list, NULL, NULL);
+ free(uitems);
+ return 0;
+}
diff --git a/src/tests/common/skiplist_tests.h b/src/tests/common/skiplist_tests.h
new file mode 100644
index 0000000..ff91706
--- /dev/null
+++ b/src/tests/common/skiplist_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_SKIPLIST_TESTS_H_
+#define _KNOTD_SKIPLIST_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api skiplist_tests_api;
+
+#endif /* _KNOTD_SKIPLIST_TESTS_H_ */
diff --git a/src/tests/common/slab_tests.c b/src/tests/common/slab_tests.c
new file mode 100644
index 0000000..f362ca0
--- /dev/null
+++ b/src/tests/common/slab_tests.c
@@ -0,0 +1,141 @@
+/* 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 <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdbool.h>
+
+#include "tests/common/slab_tests.h"
+#include "common/slab/slab.h"
+#include "knot/common.h"
+
+/* Explicitly ask for symbols,
+ * as the constructor and destructor
+ * aren't created for test modules.
+ */
+extern void slab_init();
+extern void slab_deinit();
+
+static int slab_tests_count(int argc, char *argv[]);
+static int slab_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api slab_tests_api = {
+ "SLAB allocator", //! Unit name
+ &slab_tests_count, //! Count scheduled tests
+ &slab_tests_run //! Run scheduled tests
+};
+
+static int slab_tests_count(int argc, char *argv[])
+{
+ return 7;
+}
+
+static int slab_tests_run(int argc, char *argv[])
+{
+ // 1. Create slab cache
+ srand(time(0));
+ const unsigned pattern = 0xdeadbeef;
+ slab_cache_t cache;
+ int ret = slab_cache_init(&cache, sizeof(int));
+ ok(ret == 0, "slab: created empty cache");
+
+ // 2. Couple alloc/free
+ bool valid_free = true;
+ lives_ok({
+ for(int i = 0; i < 100; ++i) {
+ int* data = (int*)slab_cache_alloc(&cache);
+ *data = pattern;
+ slab_free(data);
+ if (*data == pattern)
+ valid_free = false;
+ }
+ }, "slab: couple alloc/free");
+
+ // 5. Verify freed block
+ ok(valid_free, "slab: freed memory is correctly invalidated");
+
+ // 4. Reap memory
+ slab_t* slab = cache.slabs_free;
+ int free_count = 0;
+ while (slab) {
+ slab_t* next = slab->next;
+ if (slab_isempty(slab)) {
+ ++free_count;
+ }
+ slab = next;
+ }
+
+ int reaped = slab_cache_reap(&cache);
+ cmp_ok(reaped, "==", free_count, "slab: cache reaping works");
+
+ // Stress cache
+ int alloc_count = 73521;
+ void** ptrs = alloca(alloc_count * sizeof(void*));
+ int ptrs_i = 0;
+ for(int i = 0; i < alloc_count; ++i) {
+ double roll = rand() / (double) RAND_MAX;
+ if ((ptrs_i == 0) || (roll < 0.6)) {
+ int id = ptrs_i++;
+ ptrs[id] = slab_cache_alloc(&cache);
+ if (ptrs[id] == 0) {
+ ptrs_i--;
+ } else {
+ int* data = (int*)ptrs[id];
+ *data = pattern;
+ }
+ } else {
+ slab_free(ptrs[--ptrs_i]);
+ }
+ }
+
+ // 5. Delete cache
+ slab_cache_destroy(&cache);
+ ok(cache.bufsize == 0, "slab: freed cache");
+
+ // 6. Greate GP allocator
+ slab_alloc_t alloc;
+ ret = slab_alloc_init(&alloc);
+ ok(ret == 0, "slab: created GP allocator");
+
+ // 7. Stress allocator
+ unsigned ncount = 0;
+ ptrs_i = 0;
+ for(int i = 0; i < alloc_count; ++i) {
+ double roll = rand() / (double) RAND_MAX;
+ size_t bsize = roll * 2048;
+ bsize = MAX(bsize, 8);
+ if ((ptrs_i == 0) || (roll < 0.6)) {
+ void* m = slab_alloc_alloc(&alloc, bsize);
+ if (m == 0) {
+ ++ncount;
+ } else {
+ ptrs[ptrs_i++] = m;
+ }
+ } else {
+ slab_free(ptrs[--ptrs_i]);
+ }
+ }
+
+ cmp_ok(ncount, "==", 0, "slab: GP allocator alloc/free working");
+
+ // 7. Destroy allocator
+ slab_alloc_destroy(&alloc);
+
+ return 0;
+}
diff --git a/src/tests/common/slab_tests.h b/src/tests/common/slab_tests.h
new file mode 100644
index 0000000..4d45fb8
--- /dev/null
+++ b/src/tests/common/slab_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_SLAB_TESTS_H_
+#define _KNOTD_SLAB_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api slab_tests_api;
+
+#endif /* _KNOTD_SLAB_TESTS_H_ */
diff --git a/src/tests/files/sample_conf b/src/tests/files/sample_conf
new file mode 100644
index 0000000..6cd9e50
--- /dev/null
+++ b/src/tests/files/sample_conf
@@ -0,0 +1,59 @@
+# configuration file will follow bird (and juniper) type of configuration file
+# i.e. curly brackets will be used;
+
+# what to do with };
+# a) ignore ; if it follows }
+
+system {
+
+ identity "I have no mouth and must scream";
+ version "Infinitesimal";
+ storage "/var/run/knot/";
+}
+
+keys {
+ key0.example.net hmac-md5 "Wg=="; # key special for one remote
+ key1.example.net hmac-md5 "==gW"; # implicit key for whole zone
+}
+
+remotes {
+ remote0 { address 1.2.3.4; }
+}
+
+zones {
+ example.net {
+ file "/var/lib/knot/example.net";
+ xfr-out remote0;
+ }
+}
+
+interfaces {
+ interface0 {
+ address 10.10.1.1;
+ port 53531;
+ }
+
+ interface1 {
+ address ::0;
+ # port 53;
+ }
+}
+
+log {
+ syslog {
+ any notice, warning, error;
+ zone all;
+ }
+
+ file "/var/log/knot/server.err" {
+ server error;
+ }
+
+ stderr {
+ any warning, error;
+ }
+
+ stdout {
+ any info;
+ }
+}
diff --git a/src/tests/knot/conf_tests.c b/src/tests/knot/conf_tests.c
new file mode 100644
index 0000000..61520ea
--- /dev/null
+++ b/src/tests/knot/conf_tests.c
@@ -0,0 +1,141 @@
+/* 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 <stdio.h>
+
+#include "tests/knot/conf_tests.h"
+#include "knot/conf/conf.h"
+
+/* Resources. */
+#include "tests/sample_conf.rc"
+
+static int conf_tests_count(int argc, char *argv[]);
+static int conf_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api conf_tests_api = {
+ "Configuration parser", //! Unit name
+ &conf_tests_count, //! Count scheduled tests
+ &conf_tests_run //! Run scheduled tests
+};
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int conf_tests_count(int argc, char *argv[])
+{
+ return 21;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int conf_tests_run(int argc, char *argv[])
+{
+
+ // Test 1: Allocate new config
+ const char *config_fn = "rc:/sample_conf";
+ conf_t *conf = conf_new(config_fn);
+ ok(conf != 0, "config_new()");
+
+ // Test 2: Parse config
+ int ret = conf_parse_str(conf, sample_conf_rc);
+ ok(ret == 0, "parsing configuration file %s", config_fn);
+ skip(ret != 0, conf_tests_count(argc, argv) - 2);
+ {
+
+ // Test 3: Test server version (0-level depth)
+ is(conf->version, "Infinitesimal", "server version loaded ok");
+
+ // Test 4: Test interfaces (1-level depth)
+ ok(!EMPTY_LIST(conf->ifaces), "configured interfaces exist");
+
+ // Test 5,6,7,8: Interfaces content (2-level depth)
+ struct node *n = HEAD(conf->ifaces);
+ conf_iface_t *iface = (conf_iface_t*)n;
+ is(iface->address, "10.10.1.1", "interface0 address check");
+ cmp_ok(iface->port, "==", 53531, "interface0 port check");
+ n = n->next;
+ iface = (conf_iface_t*)n;
+ is(iface->address, "::0", "interface1 address check");
+ cmp_ok(iface->port, "==", 53, "interface1 default port check");
+
+ // Test 9,10: Check server key
+ if(conf->key_count <= 0) {
+ ok(0, "TSIG key algorithm check - NO KEY FOUND");
+ ok(0, "TSIG key secret check - NO KEY FOUND");
+ } else {
+ knot_key_t *k = &((conf_key_t *)HEAD(conf->keys))->k;
+ cmp_ok(k->algorithm, "==", KNOT_TSIG_ALG_HMAC_MD5,
+ "TSIG key algorithm check");
+ is(k->secret, "Wg==", "TSIG key secret check");
+ }
+
+ // Test 11,12,13,14,15,16,17,18: Check logging facilities
+ cmp_ok(conf->logs_count, "==", 4, "log facilites count check");
+ n = HEAD(conf->logs);
+ ok(!EMPTY_LIST(conf->logs), "log facilities not empty");
+
+ conf_log_t *log = (conf_log_t*)n;
+ node *nm = HEAD(log->map);
+ conf_log_map_t *m = (conf_log_map_t*)nm;
+ cmp_ok(log->type, "==", LOGT_SYSLOG, "log0 is syslog");
+
+ skip(EMPTY_LIST(log->map), 5);
+ {
+ cmp_ok(m->source, "==", LOG_ANY, "syslog first rule is ANY");
+ int mask = LOG_MASK(LOG_NOTICE)|LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR);
+ cmp_ok(m->prios, "==", mask, "syslog mask is equal");
+ nm = nm->next;
+ m = (conf_log_map_t*)nm;
+ ok(m != 0, "syslog has more than 1 rule");
+ skip(!m, 2);
+ {
+ cmp_ok(m->source, "==", LOG_ZONE, "syslog next rule is for zone");
+ cmp_ok(m->prios, "==", 0xff, "rule for zone is: any level");
+ }
+ endskip;
+ } endskip;
+
+ // Test 19,20: File facility checks
+ n = n->next;
+ log = (conf_log_t*)n;
+ ok(n != 0, "log has next facility");
+ skip(!n, 1);
+ {
+ is(log->file, "/var/log/knot/server.err", "log file matches");
+ } endskip;
+
+ // 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);
+ if (conf->key_count > 0) {
+ knot_key_t *k = &((conf_key_t *)HEAD(conf->keys))->k;
+ ok(knot_dname_compare(sample, k->name) == 0,
+ "TSIG key dname check");
+ } else {
+ ok(0, "TSIG key dname check - NO KEY FOUND");
+ }
+ knot_dname_free(&sample);
+
+ } endskip;
+
+ // Deallocating config
+ conf_free(conf);
+
+ return 0;
+}
diff --git a/src/tests/knot/conf_tests.h b/src/tests/knot/conf_tests.h
new file mode 100644
index 0000000..dfd2fd7
--- /dev/null
+++ b/src/tests/knot/conf_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_CONF_TESTS_H_
+#define _KNOTD_CONF_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api conf_tests_api;
+
+#endif /* _KNOTD_CONF_TESTS_H_ */
diff --git a/src/tests/knot/dthreads_tests.c b/src/tests/knot/dthreads_tests.c
new file mode 100644
index 0000000..d95fbed
--- /dev/null
+++ b/src/tests/knot/dthreads_tests.c
@@ -0,0 +1,392 @@
+/* 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 <pthread.h>
+#include <sched.h>
+#include <sys/select.h>
+#include <signal.h>
+
+#include "tests/knot/dthreads_tests.h"
+#include "knot/server/dthreads.h"
+
+static int dt_tests_count(int argc, char *argv[]);
+static int dt_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api dthreads_tests_api = {
+ "DThreads",
+ &dt_tests_count,
+ &dt_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+static const int DT_TEST_COUNT = 23;
+
+/* Unit runnable data. */
+static pthread_mutex_t _runnable_mx;
+static volatile int _runnable_i = 0;
+static const int _runnable_cycles = 10000;
+
+/*! \brief Unit runnable. */
+int runnable(struct dthread_t *thread)
+{
+ for (int i = 0; i < _runnable_cycles; ++i) {
+
+ // Increase counter
+ pthread_mutex_lock(&_runnable_mx);
+ ++_runnable_i;
+ pthread_mutex_unlock(&_runnable_mx);
+
+ // Cancellation point
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ // Yield
+ sched_yield();
+ }
+
+ return 0;
+}
+
+/*! \brief Unit blocking runnable. */
+int runnable_simio(struct dthread_t *thread)
+{
+ // Infinite blocking, must be interrupted
+ select(0, 0, 0, 0, 0);
+ return 0;
+}
+
+/*! \brief Create unit. */
+static inline dt_unit_t *dt_test_create(int size)
+{
+ return dt_create(size);
+}
+
+/*! \brief Assign a task. */
+static inline int dt_test_single(dt_unit_t *unit)
+{
+ return dt_repurpose(unit->threads[0], &runnable, NULL) == 0;
+}
+
+/*! \brief Assign task to all unit threads. */
+static inline int dt_test_coherent(dt_unit_t *unit)
+{
+ int ret = 0;
+ for (int i = 0; i < unit->size; ++i) {
+ ret += dt_repurpose(unit->threads[i], &runnable, NULL);
+ }
+
+ return ret == 0;
+}
+
+/*! \brief Repurpose single thread. */
+static inline int dt_test_repurpose(dt_unit_t *unit, int id)
+{
+ return dt_repurpose(unit->threads[id], &runnable_simio, NULL) == 0;
+}
+
+/*! \brief Cancel single thread. */
+static inline int dt_test_cancel(dt_unit_t *unit, int id)
+{
+ return dt_cancel(unit->threads[id]) == 0;
+}
+
+/*! \brief Reanimate dead threads. */
+static inline int dt_test_reanimate(dt_unit_t *unit)
+{
+ // Compact all threads
+ int ret = 0;
+ ret += dt_compact(unit);
+
+ // Remove purpose from all
+ for (int i = 0; i < unit->size; ++i) {
+ ret += dt_repurpose(unit->threads[i], 0, 0);
+ }
+
+ // Set single thread to purpose
+ ret += dt_repurpose(unit->threads[0], &runnable, 0);
+
+ // Restart
+ _runnable_i = 0;
+ ret += dt_start(unit);
+
+ // Wait for finish
+ ret += dt_join(unit);
+
+ // Verify
+ int expected = 1 * _runnable_cycles;
+ if (_runnable_i != expected) {
+ return 0;
+ }
+
+ // Check return codes
+ return ret == 0;
+}
+
+/*! \brief Resize unit. */
+static inline int dt_test_resize(dt_unit_t *unit, int size)
+{
+ // Resize
+ int ret = 0;
+ ret = dt_resize(unit, size);
+ if (ret < 0) {
+ return 0;
+ }
+
+ // Check outcome
+ if (unit->size != size) {
+ return 0;
+ }
+
+ // Repurpose all
+ _runnable_i = 0;
+ for (int i = 0; i < size; ++i) {
+ ret += dt_repurpose(unit->threads[i], &runnable, 0);
+ ret += dt_start_id(unit->threads[i]);
+ }
+
+ // Wait for finish
+ ret += dt_join(unit);
+
+ // Verify
+ int expected = size * _runnable_cycles;
+ note("resize test: %d threads, %d ticks, %d expected",
+ size, _runnable_i, expected);
+ if (_runnable_i != expected) {
+ return 0;
+ }
+
+ // Check return codes
+ return ret == 0;
+}
+
+/*! \brief Resize unit while threads are active. */
+static inline int dt_test_liveresize(dt_unit_t *unit)
+{
+ // Size
+ int size = unit->size;
+ int size_hi = size + 2;
+ int size_lo = size - 1;
+
+ // Expand
+ int ret = 0;
+ ret = dt_resize(unit, size_hi);
+ if (ret < 0) {
+ return 0;
+ }
+
+ // Repurpose all
+ for (int i = 0; i < unit->size; ++i) {
+ ret += dt_repurpose(unit->threads[i], &runnable, 0);
+ }
+
+ // Restart
+ _runnable_i = 0;
+ ret += dt_start(unit);
+
+ // Shrink
+ ret += dt_resize(unit, size_lo);
+
+ // Wait for finish
+ ret += dt_join(unit);
+
+ // Verify
+ int expected_hi = size_hi * _runnable_cycles;
+ int expected_lo = size_lo * _runnable_cycles;
+ note("resize test: %d->%d->%d threads, %d ticks, <%d,%d> expected",
+ size, size_hi, size_lo, _runnable_i, expected_lo, expected_hi);
+
+ if (_runnable_i > expected_hi || _runnable_i < expected_lo) {
+ return 0;
+ }
+
+ // Check return codes
+ return ret == 0;
+}
+
+/*! \brief Start unit. */
+static inline int dt_test_start(dt_unit_t *unit)
+{
+ return dt_start(unit) == 0;
+}
+
+/*! \brief Stop unit. */
+static inline int dt_test_stop(dt_unit_t *unit)
+{
+ return dt_stop(unit);
+}
+
+/*! \brief Join unit. */
+static inline int dt_test_join(dt_unit_t *unit)
+{
+ return dt_join(unit) == 0;
+}
+
+/*! API: return number of tests. */
+static int dt_tests_count(int argc, char *argv[])
+{
+ return DT_TEST_COUNT;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+static int dt_tests_run(int argc, char *argv[])
+{
+ // Register service and signal handler
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ /* Initialize */
+ srand(time(NULL));
+ struct timeval tv;
+ pthread_mutex_init(&_runnable_mx, NULL);
+
+ /* Test 1: Create unit */
+ dt_unit_t *unit = dt_test_create(dt_optimal_size());
+ ok(unit != 0, "dthreads: create unit (optimal size %d)", unit->size);
+ skip(unit == 0, DT_TEST_COUNT - 1);
+
+ /* Test 2: Assign a single task. */
+ ok(dt_test_single(unit), "dthreads: assign single task");
+
+ /* Test 3: Start tasks. */
+ _runnable_i = 0;
+ ok(dt_test_start(unit), "dthreads: start single task");
+
+ /* Test 4: Wait for tasks. */
+ ok(dt_test_join(unit), "dthreads: join threads");
+
+ /* Test 5: Compare counter. */
+ int expected = _runnable_cycles * 1;
+ cmp_ok(_runnable_i, "==", expected, "dthreads: result ok");
+
+ /* Test 6: Repurpose threads. */
+ _runnable_i = 0;
+ ok(dt_test_coherent(unit), "dthreads: repurpose to coherent");
+
+ /* Test 7: Restart threads. */
+ ok(dt_test_start(unit), "dthreads: start coherent unit");
+
+ /* Test 8: Repurpose single thread. */
+ tv.tv_sec = 0;
+ tv.tv_usec = 4000 + rand() % 1000; // 4-5ms
+ note("waiting for %dus to let thread do some work ...",
+ tv.tv_usec);
+ select(0, 0, 0, 0, &tv);
+ ok(dt_test_repurpose(unit, 0), "dthreads: repurpose on-the-fly");
+
+ /* Test 9: Cancel blocking thread. */
+ tv.tv_sec = 0;
+ tv.tv_usec = (250 + rand() % 500) * 1000; // 250-750ms
+ note("waiting for %dms to let thread pretend blocking I/O ...",
+ tv.tv_usec / 1000);
+ select(0, 0, 0, 0, &tv);
+ ok(dt_test_cancel(unit, 0), "dthreads: cancel blocking thread");
+
+ /* Test 10: Wait for tasks. */
+ ok(dt_test_join(unit), "dthreads: join threads");
+
+ /* Test 11: Compare counter. */
+ int expected_lo = _runnable_cycles * (unit->size - 1);
+ cmp_ok(_runnable_i, ">=", expected_lo,
+ "dthreads: result %d is => %d", _runnable_i, expected_lo);
+
+ /* Test 12: Compare counter #2. */
+ int expected_hi = _runnable_cycles * unit->size;
+ cmp_ok(_runnable_i, "<=", expected_hi,
+ "dthreads: result %d is <= %d", _runnable_i, expected_hi);
+
+ /* Test 13: Reanimate dead threads. */
+ ok(dt_test_reanimate(unit), "dthreads: reanimate dead threads");
+
+ /* Test 14: Expand unit by 100%. */
+ int size = unit->size * 2;
+ ok(dt_test_resize(unit, size),
+ "dthreads: expanding unit to size * 2 (%d threads)", size);
+
+ /* Test 15: Shrink unit to half. */
+ size = unit->size / 2;
+ ok(dt_test_resize(unit, size),
+ "dthreads: shrinking unit to size / 2 (%d threads)", size);
+
+ /* Test 16: Resize while threads are active. */
+ ok(dt_test_liveresize(unit), "dthreads: resizing unit while active");
+
+ /* Test 17: Deinitialize */
+ dt_delete(&unit);
+ ok(unit == 0, "dthreads: delete unit");
+ endskip;
+
+ /* Test 18: Wrong values. */
+ unit = dt_create(-1);
+ ok(unit == 0, "dthreads: create with negative count");
+ unit = dt_create_coherent(dt_optimal_size(), 0, 0);
+
+ /* Test 19: NULL runnable. */
+ cmp_ok(dt_start(unit), "==", 0, "dthreads: start with NULL runnable");
+
+ /* Test 20: resize to negative value. */
+ cmp_ok(dt_resize(unit, -19),
+ "<", 0, "dthreads: resize to negative size");
+
+ /* Test 21: resize to zero value. */
+ cmp_ok(dt_resize(unit, 0), "<", 0, "dthreads: resize to NULL size");
+ dt_join(unit);
+ dt_delete(&unit);
+
+ /* Test 22: NULL operations crashing. */
+ int op_count = 14;
+ int expected_min = op_count * -1;
+ // All functions must return -1 at least
+ int ret = 0;
+ lives_ok( {
+ ret += dt_activate(0); // -1
+ ret += dt_cancel(0); // -1
+ ret += dt_compact(0); // -1
+ dt_delete(0); //
+ ret += dt_is_cancelled(0); // 0
+ ret += dt_join(0); // -1
+ ret += dt_repurpose(0, 0, 0); // -1
+ ret += dt_resize(0, 0); // -1
+ ret += dt_setprio(0, 0); // -1
+ ret += dt_signalize(0, SIGALRM); // -1
+ ret += dt_start(0); // -1
+ ret += dt_start_id(0); // -1
+ ret += dt_stop(0); // -1
+ ret += dt_stop_id(0); // -1
+ ret += dt_unit_lock(0); // -1
+ ret += dt_unit_unlock(0); // -1
+ }, "dthreads: not crashed while executing functions on NULL context");
+
+ /* Test 23: expected results. */
+ cmp_ok(ret, "<=", expected_min,
+ "dthreads: correct values when passed NULL context "
+ "(%d, min: %d)", ret, expected_min);
+
+ pthread_mutex_destroy(&_runnable_mx);
+ return 0;
+}
diff --git a/src/tests/knot/dthreads_tests.h b/src/tests/knot/dthreads_tests.h
new file mode 100644
index 0000000..e41bdc5
--- /dev/null
+++ b/src/tests/knot/dthreads_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DTHREADS_TESTS_H_
+#define _KNOTD_DTHREADS_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api dthreads_tests_api;
+
+#endif /* _KNOTD_DTHREADS_TESTS_H_ */
diff --git a/src/tests/knot/journal_tests.c b/src/tests/knot/journal_tests.c
new file mode 100644
index 0000000..21c92fe
--- /dev/null
+++ b/src/tests/knot/journal_tests.c
@@ -0,0 +1,184 @@
+/* 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 <string.h>
+
+#include "tests/knot/journal_tests.h"
+#include "knot/server/journal.h"
+#include "knot/other/error.h"
+
+static int journal_tests_count(int argc, char *argv[]);
+static int journal_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api journal_tests_api = {
+ "Journal",
+ &journal_tests_count,
+ &journal_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+static const int JOURNAL_TEST_COUNT = 11;
+
+/*! \brief Generate random string with given length. */
+static int randstr(char* dst, size_t len)
+{
+ for (int i = 0; i < len - 1; ++i) {
+ dst[i] = '0' + (int) (('Z'-'0') * (rand() / (RAND_MAX + 1.0)));
+ }
+ dst[len - 1] = '\0';
+
+ return 0;
+}
+
+/*! \brief Walk journal of chars into buffer. */
+static int _wbi = 0;
+static char _walkbuf[7];
+static int walkchars_cmp(uint64_t k1, uint64_t k2) {
+ return k1 - k2;
+}
+
+static int walkchars(journal_t *j, journal_node_t *n) {
+ journal_read(j, n->id, walkchars_cmp, _walkbuf + _wbi);
+ ++_wbi;
+ return 0;
+}
+
+/*! API: return number of tests. */
+static int journal_tests_count(int argc, char *argv[])
+{
+ return JOURNAL_TEST_COUNT;
+}
+
+/*! API: run tests. */
+static int journal_tests_run(int argc, char *argv[])
+{
+ /* Test 1: Create tmpfile. */
+ int fsize = 8092;
+ int jsize = 6;
+ char jfn_buf[] = "/tmp/journal.XXXXXX";
+ int tmp_fd = mkstemp(jfn_buf);
+ ok(tmp_fd >= 0, "journal: create temporary file");
+ skip(tmp_fd < 0, JOURNAL_TEST_COUNT - 1);
+
+ /* Test 2: Create journal. */
+ const char *jfilename = jfn_buf;
+ int ret = journal_create(jfilename, jsize);
+ ok(ret == KNOTD_EOK, "journal: create journal '%s'", jfilename);
+
+ /* Test 3: Open journal. */
+ journal_t *j = journal_open(jfilename, fsize, 0);
+ ok(j != 0, "journal: open");
+
+ /* Test 4: Write entry to log. */
+ const char *sample = "deadbeef";
+ ret = journal_write(j, 0x0a, sample, strlen(sample));
+ ok(ret == KNOTD_EOK, "journal: write");
+
+ /* Test 5: Read entry from log. */
+ char tmpbuf[64] = {'\0'};
+ ret = journal_read(j, 0x0a, 0, tmpbuf);
+ ok(ret == KNOTD_EOK, "journal: read entry");
+
+ /* Test 6: Compare read data. */
+ ret = strncmp(sample, tmpbuf, strlen(sample));
+ ok(ret == 0, "journal: read data integrity check");
+
+ /* Append several characters. */
+ journal_write(j, 0, "X", 1); /* Dummy */
+ char word[7] = { 'w', 'o', 'r', 'd', '0', '\0', '\0' };
+ for (int i = 0; i < strlen(word); ++i) {
+ journal_write(j, i, word+i, 1);
+ }
+
+ /* Test 7: Compare journal_walk() result. */
+ _wbi = 0;
+ journal_walk(j, walkchars);
+ _walkbuf[_wbi] = '\0';
+ ret = strcmp(word, _walkbuf);
+ ok(ret == 0, "journal: read data integrity check 2 '%s'", _walkbuf);
+ _wbi = 0;
+
+ /* Test 8: Change single letter and compare. */
+ word[5] = 'X';
+ journal_write(j, 5, word+5, 1); /* append 'X', shifts out 'w' */
+ journal_walk(j, walkchars);
+ _walkbuf[_wbi] = '\0';
+ ret = strcmp(word + 1, _walkbuf);
+ ok(ret == 0, "journal: read data integrity check 3 '%s'", _walkbuf);
+ _wbi = 0;
+
+ /* Close journal. */
+ journal_close(j);
+
+ /* Recreate journal. */
+ remove(jfilename);
+ fsize = 8092;
+ jsize = 512;
+ ret = journal_create(jfilename, jsize);
+ j = journal_open(jfilename, fsize, 0);
+
+ /* Test 9: Write random data. */
+ int chk_key = 0;
+ char chk_buf[64] = {'\0'};
+ ret = 0;
+ const int itcount = 1;//jsize * 5 + 5;
+ for (int i = 0; i < itcount; ++i) {
+ int key = rand() % 65535;
+ randstr(tmpbuf, sizeof(tmpbuf));
+ if (journal_write(j, key, tmpbuf, sizeof(tmpbuf)) != KNOTD_EOK) {
+ ret = -1;
+ break;
+ }
+
+ /* Store some key on the end. */
+ if (i == itcount - 2) {
+ chk_key = key;
+ memcpy(chk_buf, tmpbuf, sizeof(chk_buf));
+ }
+ }
+ ok(ret == 0, "journal: sustained looped writes");
+
+ /* Test 10: Check data integrity. */
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ journal_read(j, chk_key, 0, tmpbuf);
+ ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
+ ok(ret == 0, "journal: read data integrity check");
+
+ /* Test 11: Reopen log and re-read value. */
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ journal_close(j);
+ j = journal_open(jfilename, fsize, 0);
+ journal_read(j, chk_key, 0, tmpbuf);
+ ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
+ ok(ret == 0, "journal: read data integrity check after close/open");
+
+ /* Close journal. */
+ journal_close(j);
+
+ /* Close temporary file fd. */
+ close(tmp_fd);
+
+ /* Delete journal. */
+ remove(jfilename);
+
+ endskip;
+
+ return 0;
+}
diff --git a/src/tests/knot/journal_tests.h b/src/tests/knot/journal_tests.h
new file mode 100644
index 0000000..beec8ca
--- /dev/null
+++ b/src/tests/knot/journal_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_JOURNAL_TESTS_H_
+#define _KNOTD_JOURNAL_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api journal_tests_api;
+
+#endif /* _KNOTD_JOURNAL_TESTS_H_ */
diff --git a/src/tests/knot/server_tests.c b/src/tests/knot/server_tests.c
new file mode 100644
index 0000000..5ae04d8
--- /dev/null
+++ b/src/tests/knot/server_tests.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "tests/knot/server_tests.h"
+#include "knot/server/server.h"
+
+static int server_tests_count(int argc, char *argv[]);
+static int server_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api server_tests_api = {
+ "Server",
+ &server_tests_count,
+ &server_tests_run
+};
+
+/*
+ * Unit implementation.
+ */
+
+static const int SERVER_TEST_COUNT = 4;
+
+/*! Test: create server. */
+server_t *test_server_create()
+{
+ return server_create();
+}
+
+/*! Test: start server. */
+int test_server_start(server_t *s)
+{
+ return server_start(s) == 0;
+}
+
+/*! Test: finish server. */
+int test_server_finish(server_t *s)
+{
+ return server_wait(s) == 0;
+}
+
+/*! Test: stop server. */
+int test_server_destroy(server_t *s)
+{
+ server_destroy(&s);
+ return s == 0;
+}
+
+/*! API: return number of tests. */
+static int server_tests_count(int argc, char *argv[])
+{
+ return SERVER_TEST_COUNT + 1;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+static int server_tests_run(int argc, char *argv[])
+{
+ server_t *server = 0;
+ int ret = 0;
+
+ // Register service and signal handler
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ //! Test server for correct initialization
+ server = test_server_create();
+ ok(server != 0, "server: initialized");
+
+ //! Test server startup
+ ret = 0;
+ lives_ok( {
+ ret = test_server_start(server);
+ }, "server: not crashing on runtime");
+
+ //! Test server exit code
+ ok(ret, "server: started ok");
+ if (ret) {
+ server_stop(server);
+ } else {
+ diag("server crashed, skipping deinit and destroy tests");
+ }
+
+ //! Test server waiting for finish
+ skip(!ret, 2);
+ ok(test_server_finish(server), "server: waiting for finish");
+
+ //! Test server for correct deinitialization
+ ok(test_server_destroy(server), "server: deinit");
+ endskip;
+
+ return 0;
+}
diff --git a/src/tests/knot/server_tests.h b/src/tests/knot/server_tests.h
new file mode 100644
index 0000000..43ad0c1
--- /dev/null
+++ b/src/tests/knot/server_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_SERVER_TESTS_H_
+#define _KNOTD_SERVER_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api server_tests_api;
+
+#endif /* _KNOTD_SERVER_TESTS_H_ */
diff --git a/src/tests/libknot/files/parsed_data b/src/tests/libknot/files/parsed_data
new file mode 100644
index 0000000..4027c92
--- /dev/null
+++ b/src/tests/libknot/files/parsed_data
Binary files differ
diff --git a/src/tests/libknot/files/parsed_data_queries b/src/tests/libknot/files/parsed_data_queries
new file mode 100644
index 0000000..5857c87
--- /dev/null
+++ b/src/tests/libknot/files/parsed_data_queries
Binary files differ
diff --git a/src/tests/libknot/files/raw_data b/src/tests/libknot/files/raw_data
new file mode 100644
index 0000000..f94236b
--- /dev/null
+++ b/src/tests/libknot/files/raw_data
Binary files differ
diff --git a/src/tests/libknot/files/raw_data_queries b/src/tests/libknot/files/raw_data_queries
new file mode 100644
index 0000000..9062d5a
--- /dev/null
+++ b/src/tests/libknot/files/raw_data_queries
Binary files differ
diff --git a/src/tests/libknot/libknot/cuckoo_tests.c b/src/tests/libknot/libknot/cuckoo_tests.c
new file mode 100644
index 0000000..c1306a3
--- /dev/null
+++ b/src/tests/libknot/libknot/cuckoo_tests.c
@@ -0,0 +1,382 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <time.h>
+#include <assert.h>
+
+#include "tests/libknot/libknot/cuckoo_tests.h"
+
+#include "libknot/hash/cuckoo-hash-table.h"
+
+//#define CK_TEST_DEBUG
+//#define CK_TEST_LOOKUP
+//#define CK_TEST_OUTPUT
+//#define CK_TEST_REMOVE
+//#define CK_TEST_COMPARE
+
+#ifdef CK_TEST_DEBUG
+#define CK_TEST_LOOKUP
+#define CK_TEST_OUTPUT
+#define CK_TEST_REMOVE
+#define CK_TEST_COMPARE
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+static int cuckoo_tests_count(int argc, char *argv[]);
+static int cuckoo_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api cuckoo_tests_api = {
+ "Cuckoo hashing", //! Unit name
+ &cuckoo_tests_count, //! Count scheduled tests
+ &cuckoo_tests_run //! Run scheduled tests
+};
+
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Unit implementation
+ */
+static const int CUCKOO_TESTS_COUNT = 13;
+static const int CUCKOO_MAX_ITEMS = 1000;
+static const int CUCKOO_TEST_MAX_KEY_SIZE = 10;
+
+typedef struct test_cuckoo_items {
+ char **keys;
+ size_t *key_sizes;
+ size_t *values;
+ size_t *deleted;
+ int count;
+ int total_count;
+} test_cuckoo_items;
+
+/*----------------------------------------------------------------------------*/
+
+static inline char rand_char()
+{
+ return (char)((rand() % 26) + 97);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline void rand_str(char *str, int size)
+{
+ for (int i = 0; i < size; ++i) {
+ str[i] = rand_char();
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int cuckoo_tests_count(int argc, char *argv[])
+{
+ return CUCKOO_TESTS_COUNT;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_create(ck_hash_table_t **table, uint items)
+{
+ *table = ck_create_table(items);
+ return (*table != NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_insert(ck_hash_table_t *table,
+ const test_cuckoo_items *items)
+{
+ assert(table != NULL);
+ int errors = 0;
+ for (int i = 0; i < items->count; ++i) {
+ assert(items->values[i] != 0);
+ if (ck_insert_item(table, items->keys[i], items->key_sizes[i],
+ (void *)items->values[i]) != 0) {
+ ++errors;
+ }
+ }
+ return errors == 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_lookup(ck_hash_table_t *table,
+ const test_cuckoo_items *items)
+{
+ int errors = 0;
+ for (int i = 0; i < items->count; ++i) {
+ const ck_hash_table_item_t *found = ck_find_item(
+ table, items->keys[i], items->key_sizes[i]);
+ if (!found) {
+ if (items->deleted[i] == 0) {
+ diag("Not found item with key %.*s\n",
+ items->key_sizes[i], items->keys[i]);
+ ++errors;
+ }
+ } else {
+ if (items->deleted[i] != 0
+ || found->key != items->keys[i]
+ || (size_t)(found->value) != items->values[i]) {
+ diag("Found item with key %.*s (size %u) "
+ "(should be %.*s (size %u)) and value %zu "
+ "(should be %d).\n",
+ found->key_length, found->key,
+ found->key_length, items->key_sizes[i],
+ items->keys[i], items->key_sizes[i],
+ (size_t)found->value, items->values[i]);
+ ++errors;
+ }
+ }
+ }
+
+ if (errors > 0) {
+ diag("Not found %d of %d items.\n", errors, items->count);
+ } else {
+ note("Found %d items.\n", items->count);
+ }
+
+ return errors == 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_delete(ck_hash_table_t *table, test_cuckoo_items *items)
+{
+ int errors = 0;
+ // delete approx. 1/10 items from the table
+ int count = rand() % (CUCKOO_MAX_ITEMS / 10) + 1;
+
+ for (int i = 0; i < count; ++i) {
+ int item = rand() % items->count;
+ if (items->deleted[item] == 0
+ && ck_delete_item(table, items->keys[item],
+ items->key_sizes[item], NULL, 0) != 0) {
+ ++errors;
+ } else {
+ items->deleted[item] = 1;
+ }
+ }
+
+ return errors == 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_modify(ck_hash_table_t *table, test_cuckoo_items *items)
+{
+ int errors = 0;
+ // modify approx. 1/10 items from the table
+ int count = rand() % (CUCKOO_MAX_ITEMS / 10) + 1;
+
+ for (int i = 0; i < count; ++i) {
+ int item = rand() % items->count;
+ int old_value = items->values[item];
+ items->values[item] = rand() + 1;
+ if (ck_update_item(table, items->keys[item],
+ items->key_sizes[item],
+ (void *)items->values[item], NULL) != 0
+ && items->deleted[item] == 1) {
+ ++errors;
+ items->values[item] = old_value;
+ }
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_rehash(ck_hash_table_t *table)
+{
+ return (ck_rehash(table) == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_resize(ck_hash_table_t *table)
+{
+ // test the resize explicitly
+ return (ck_resize_table(table) == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_cuckoo_full(ck_hash_table_t *table, test_cuckoo_items *items)
+{
+ // invoke the resize by inserting so much items that thay cannot
+ // fit into the table
+ int new_count = table->items;
+
+ while (new_count < hashsize(table->table_size_exp) * table->table_count) {
+ new_count += table->items;
+ }
+
+ note("Old item count: %d, new count: %d, capacity of the table: %d\n",
+ table->items, new_count,
+ hashsize(table->table_size_exp) * table->table_count);
+
+ assert(new_count <= items->total_count);
+
+ int errors = 0;
+
+ for (int i = items->count; i < new_count; ++i) {
+ assert(items->values[i] != 0);
+ if (ck_insert_item(table, items->keys[i], items->key_sizes[i],
+ (void *)items->values[i]) != 0) {
+ ++errors;
+ }
+ }
+
+ items->count = new_count;
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void create_random_items(test_cuckoo_items *items, int item_count)
+{
+ assert(items != NULL);
+
+ items->count = item_count;
+ items->total_count = item_count * 10;
+ items->values = (size_t *)malloc(items->total_count * sizeof(size_t));
+ items->key_sizes = (size_t *)malloc(items->total_count * sizeof(size_t));
+ items->deleted = (size_t *)malloc(items->total_count * sizeof(size_t));
+ items->keys = (char **)malloc(items->total_count * sizeof(char *));
+
+ for (int i = 0; i < items->total_count; ++i) {
+ int value = rand() + 1;
+ int key_size = rand() % CUCKOO_TEST_MAX_KEY_SIZE + 1;
+ char *key = malloc(key_size * sizeof(char));
+ assert(key != NULL);
+ rand_str(key, key_size);
+
+ // check if the key is not already in the table
+ int found = 0;
+ for (int j = 0; j < i; ++j) {
+ if (items->key_sizes[j] == key_size
+ && strncmp(items->keys[j], key, key_size) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ assert(value != 0);
+ items->values[i] = value;
+ items->key_sizes[i] = key_size;
+ items->keys[i] = key;
+ items->deleted[i] = 0;
+ } else {
+ free(key);
+ --i;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void delete_items(test_cuckoo_items *items)
+{
+ free(items->deleted);
+ free(items->key_sizes);
+ free(items->values);
+ for (int i = 0; i < items->total_count; ++i) {
+ free(items->keys[i]);
+ }
+ free(items->keys);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int cuckoo_tests_run(int argc, char *argv[])
+{
+ srand(time(NULL));
+ int res;
+
+ const int item_count = rand() % CUCKOO_MAX_ITEMS + 1;
+ test_cuckoo_items *items = (test_cuckoo_items *)
+ malloc(sizeof(test_cuckoo_items));
+
+ ck_hash_table_t *table = NULL;
+
+ // Test 1: create
+ ok(res = test_cuckoo_create(&table, item_count),
+ "cuckoo hashing: create");
+
+ create_random_items(items, item_count);
+
+ skip(!res, 10);
+ // Test 2: insert
+ ok(test_cuckoo_insert(table, items), "cuckoo hashing: insert");
+
+ // Test 3: lookup
+ ok(test_cuckoo_lookup(table, items), "cuckoo hashing: lookup");
+
+ // Test 4: delete
+ ok(test_cuckoo_delete(table, items), "cuckoo hashing: delete");
+
+ // Test 5: lookup 2
+ ok(test_cuckoo_lookup(table, items),
+ "cuckoo hashing: lookup after delete");
+
+ // Test 6: modify
+ ok(test_cuckoo_modify(table, items), "cuckoo hashing: modify");
+
+ // Test 7: lookup 3
+ ok(test_cuckoo_lookup(table, items),
+ "cuckoo hashing: lookup after modify");
+
+ // Test 8: rehash
+ ok(test_cuckoo_rehash(table), "cuckoo hashing: rehash");
+
+ // Test 9: lookup 4
+ ok(test_cuckoo_lookup(table, items),
+ "cuckoo hashing: lookup after rehash");
+
+ // Test 10: resize
+ ok(test_cuckoo_resize(table), "cuckoo hashing: resize");
+
+ // Test 11: lookup 5
+ ok(test_cuckoo_lookup(table, items),
+ "cuckoo hashing: lookup after resize");
+
+ // Test 12: owerflow the table
+ ok(test_cuckoo_full(table, items), "cuckoo hashing: overflow");
+
+ // Test 13: lookup 5
+ ok(test_cuckoo_lookup(table, items),
+ "cuckoo hashing: lookup after overflow");
+
+ endskip;
+
+ /**
+ * \note These last 2 tests found some major bug in the cuckoo hash
+ * table, so running them results in abort upon assertion.
+ * Disabled for now.
+ */
+
+ // Cleanup
+ ck_destroy_table(&table, NULL, 0);
+ delete_items(items);
+ free(items);
+
+ return 0;
+}
diff --git a/src/tests/libknot/libknot/cuckoo_tests.h b/src/tests/libknot/libknot/cuckoo_tests.h
new file mode 100644
index 0000000..b6b0db8
--- /dev/null
+++ b/src/tests/libknot/libknot/cuckoo_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_CUCKOO_TESTS_H_
+#define _KNOTD_CUCKOO_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api cuckoo_tests_api;
+
+#endif /* _KNOTD_CUCKOO_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/dname_table_tests.c b/src/tests/libknot/libknot/dname_table_tests.c
new file mode 100644
index 0000000..0d00a44
--- /dev/null
+++ b/src/tests/libknot/libknot/dname_table_tests.c
@@ -0,0 +1,393 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+
+#include "dname_table_tests.h"
+#include "libknot/util/error.h"
+#include "libknot/zone/dname-table.h"
+/* *test_t structures */
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+
+static int knot_dname_table_tests_count(int argc, char *argv[]);
+static int knot_dname_table_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api dname_table_tests_api = {
+ "Dname table", //! Unit name
+ &knot_dname_table_tests_count, //! Count scheduled tests
+ &knot_dname_table_tests_run //! Run scheduled tests
+};
+
+/* Helper functions. */
+static knot_dname_t *dname_from_test_dname_str(const test_dname_t *test_dname)
+{
+ assert(test_dname != NULL);
+ knot_dname_t *ret = knot_dname_new_from_str (test_dname->str,
+ strlen(test_dname->str),
+ NULL);
+ CHECK_ALLOC(ret, NULL);
+
+ return ret;
+}
+
+static int dname_compare_sort_wrapper(const void *ptr1, const void *ptr2)
+{
+ const knot_dname_t *dname1 =
+ dname_from_test_dname_str((const test_dname_t *)ptr1);
+ const knot_dname_t *dname2 =
+ dname_from_test_dname_str((const test_dname_t *)ptr2);
+ assert(dname1 && dname2);
+ return knot_dname_compare(dname1, dname2);
+}
+
+/* Unit implementation. */
+enum {DNAME_TABLE_DNAME_COUNT = 3};
+
+/* Strings are enough, we're not testing dname here ... */
+static test_dname_t DNAME_TABLE_DNAMES[DNAME_TABLE_DNAME_COUNT] = {
+ /* list ptr, string, wire, length, labels, label_count */
+ {NULL, NULL, ".", NULL, 1, NULL, 0},
+ {NULL, NULL, "a.ns.nic.cz.", NULL, 13, NULL, 0},
+ {NULL, NULL, "b.ns.nic.cz.", NULL, 13, NULL, 0}
+};
+
+static int test_dname_table_new()
+{
+ knot_dname_table_t *table = knot_dname_table_new();
+ if (table == NULL) {
+ return 0;
+ }
+
+ knot_dname_table_free(&table);
+ return 1;
+}
+
+struct test_dname_table_arg {
+ /* Times two - safety measure. */
+ knot_dname_t *array[DNAME_TABLE_DNAME_COUNT * 2];
+ uint count;
+};
+
+static void save_dname_to_array(knot_dname_t *node, void *data)
+{
+ assert(data);
+ struct test_dname_table_arg *arg = (struct test_dname_table_arg *)data;
+ arg->array[arg->count] = node;
+ arg->count++;
+}
+
+static int test_dname_table_adding()
+{
+ int errors = 0;
+ knot_dname_table_t *table = knot_dname_table_new();
+ CHECK_ALLOC(table, 0);
+
+ /* Add NULL */
+ if (knot_dname_table_add_dname(table, NULL) != KNOT_EBADARG) {
+ diag("Adding NULL dname did not result in an error!");
+ errors++;
+ }
+
+ /* Add to NULL table*/
+ if (knot_dname_table_add_dname(NULL, NULL) != KNOT_EBADARG) {
+ diag("Adding to NULL table did not result in an error!");
+ errors++;
+ }
+
+ /* Add NULL */
+ if (knot_dname_table_add_dname_check(table, NULL) != KNOT_EBADARG) {
+ diag("Adding NULL dname did not result in an error!");
+ errors++;
+ }
+
+ /* Add to NULL table*/
+ if (knot_dname_table_add_dname_check(NULL, NULL) != KNOT_EBADARG) {
+ diag("Adding to NULL table did not result in an error!");
+ errors++;
+ }
+
+
+ /* Add valid dnames. */
+ for (int i = 0; i < DNAME_TABLE_DNAME_COUNT; i++) {
+ knot_dname_t *dname =
+ dname_from_test_dname_str(&DNAME_TABLE_DNAMES[i]);
+ if (!dname) {
+ diag("Could not create dname from test dname!");
+ errors++;
+ continue;
+ }
+ if (knot_dname_table_add_dname(table, dname) != KNOT_EOK) {
+ diag("Could not add dname! (%s)",
+ DNAME_TABLE_DNAMES[i].str);
+ errors++;
+ }
+ }
+
+ /*
+ * Using inorder traversal of the table,
+ * create array containing dnames.
+ */
+
+ struct test_dname_table_arg arg;
+ arg.count = 0;
+
+ knot_dname_table_tree_inorder_apply(table, save_dname_to_array, &arg);
+
+ if (arg.count != DNAME_TABLE_DNAME_COUNT) {
+ diag("Table contains too many dnames!");
+ /* No sense in continuing. */
+ knot_dname_table_deep_free(&table);
+ return 0;
+ }
+
+ /*
+ * Check that inordered array is really sorted
+ * and contains valid dnames.
+ */
+ for (int i = 0; i < DNAME_TABLE_DNAME_COUNT; i++) {
+ assert(arg.array[i]);
+ const char *str = knot_dname_to_str(arg.array[i]);
+ if (str == NULL) {
+ diag("Wrong dname in table!");
+ errors++;
+ continue;
+ }
+
+ if (arg.array[i]->size !=
+ DNAME_TABLE_DNAMES[i].size) {
+ diag("Wrong dname size in table!");
+ diag("Is: %u should be %u.",
+ arg.array[i]->size,
+ DNAME_TABLE_DNAMES[i].size);
+ errors++;
+ continue;
+ }
+
+ if (strncmp(str, DNAME_TABLE_DNAMES[i].str,
+ DNAME_TABLE_DNAMES[i].size) != 0) {
+ diag("Wrong dname wire in table!");
+ errors++;
+ }
+ }
+
+ /* Now add one dname once again. It has to be the first item! */
+
+ if (knot_dname_table_add_dname(table,
+ dname_from_test_dname_str(&DNAME_TABLE_DNAMES[0])) !=
+ KNOT_EOK) {
+ diag("Could not add dname to table once it's already there!");
+ /* Next test would not make sense. */
+ knot_dname_table_deep_free(&table);
+ return 0;
+ }
+
+ /*
+ * After walking the table, there should now be
+ * DNAME_TABLE_DNAME_COUNT + 1 items, with 2 identical
+ * items at the beginning.
+ */
+
+ memset(arg.array, 0,
+ sizeof(knot_dname_t *) * DNAME_TABLE_DNAME_COUNT * 2);
+ arg.count = 0;
+ knot_dname_table_tree_inorder_apply(table, save_dname_to_array, &arg);
+
+ if (arg.count != DNAME_TABLE_DNAME_COUNT + 1) {
+ diag("Identical dname was not added!");
+ /* Again, next test would not make any sense. */
+ knot_dname_table_deep_free(&table);
+ return 0;
+ }
+
+ if (knot_dname_compare(arg.array[0], arg.array[1]) != 0) {
+ diag("First two dnames in table are not identical!");
+ errors++;
+ }
+
+ /* Delete table, wipe out array. */
+ knot_dname_table_deep_free(&table);
+ memset(arg.array, 0,
+ sizeof(knot_dname_t *) * DNAME_TABLE_DNAME_COUNT * 2);
+ arg.count = 0;
+
+ table = knot_dname_table_new();
+ assert(table);
+
+ /*
+ * Add dname with same content twice using knot_dname_table_add2 -
+ * table should now only contain one item.
+ */
+
+ knot_dname_t *tmp_dname =
+ dname_from_test_dname_str(&DNAME_TABLE_DNAMES[0]);
+ assert(tmp_dname);
+
+ if (knot_dname_table_add_dname_check(table, &tmp_dname) != KNOT_EOK) {
+ diag("Could not add dname using dname_table_add_dname2!");
+ knot_dname_table_deep_free(&table);
+ knot_dname_free(&tmp_dname);
+ return 0;
+ }
+
+ tmp_dname = dname_from_test_dname_str(&DNAME_TABLE_DNAMES[0]);
+ assert(tmp_dname);
+
+ knot_dname_t *dname_before_add = tmp_dname;
+
+ if (knot_dname_table_add_dname_check(table, &tmp_dname) != 1) {
+ diag("Could not add dname again using dname_table_add_dname2!");
+ knot_dname_table_deep_free(&table);
+ return 0;
+ }
+
+ if (tmp_dname == dname_before_add) {
+ diag("Dname was not freed after insertion!");
+ errors++;
+ }
+
+ knot_dname_table_tree_inorder_apply(table, save_dname_to_array, &arg);
+
+ if (arg.count != 1) {
+ diag("Add_dname2 has added dname when it shouldn't!");
+ errors++;
+ }
+
+ if (knot_dname_compare(tmp_dname, arg.array[0]) != 0) {
+ diag("Add_dname2 has added wrong dname!");
+ errors++;
+ }
+
+ knot_dname_table_deep_free(&table);
+ return (errors == 0);
+}
+
+static int test_dname_table_find()
+{
+ int errors = 0;
+ knot_dname_table_t *table = knot_dname_table_new();
+ assert(table);
+
+ if (knot_dname_table_find_dname(table, NULL) != NULL) {
+ diag("Dname table did not return NULL when searching NULL!");
+ errors++;
+ }
+
+ if (knot_dname_table_find_dname(NULL, NULL) != NULL) {
+ diag("Passing NULL instead of dname table did not "
+ "return NULL!");
+ errors++;
+ }
+
+ /* Add all dnames but the last one. */
+ for (int i = 0; i < DNAME_TABLE_DNAME_COUNT - 1; i++) {
+ knot_dname_t *dname =
+ dname_from_test_dname_str(&DNAME_TABLE_DNAMES[i]);
+ if (!dname) {
+ diag("Could not create dname from test dname!");
+ errors++;
+ continue;
+ }
+ if (knot_dname_table_add_dname(table, dname) != KNOT_EOK) {
+ diag("Could not add dname! (%s)",
+ DNAME_TABLE_DNAMES[i].str);
+ errors++;
+ }
+ }
+
+ /* Search for added dnames. */
+ for (int i = 0; i < DNAME_TABLE_DNAME_COUNT - 1; i++) {
+ knot_dname_t *dname =
+ dname_from_test_dname_str(&DNAME_TABLE_DNAMES[i]);
+ if (!dname) {
+ diag("Could not create dname from test dname!");
+ errors++;
+ continue;
+ }
+
+ knot_dname_t *found_dname =
+ knot_dname_table_find_dname(table, dname);
+
+ if (found_dname == NULL) {
+ diag("Dname table did not return "
+ "dname when it should!");
+ errors++;
+ continue;
+ }
+
+ if (knot_dname_compare(found_dname, dname) != 0) {
+ diag("Returned dname did not match!");
+ errors++;
+ continue;
+ }
+ }
+
+ /* Search for last dname, it should return NULL. */
+ knot_dname_t *dname =
+ dname_from_test_dname_str(
+ &DNAME_TABLE_DNAMES[DNAME_TABLE_DNAME_COUNT]);
+ assert(dname);
+
+ if (knot_dname_table_find_dname(table, dname) != NULL) {
+ diag("Dname table returned dname when it "
+ "should not be there!");
+ errors++;
+ }
+
+ knot_dname_free(&dname);
+ knot_dname_table_deep_free(&table);
+
+ return (errors == 0);
+}
+
+static const int KNOT_DNAME_TABLE_TEST_COUNT = 3;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_dname_table_tests_count(int argc, char *argv[])
+{
+ return KNOT_DNAME_TABLE_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_dname_table_tests_run(int argc, char *argv[])
+{
+ int final_res = 1;
+ int res = 0;
+
+ /* Sort array containing test dnames. */
+ qsort(DNAME_TABLE_DNAMES, DNAME_TABLE_DNAME_COUNT,
+ sizeof(test_dname_t), dname_compare_sort_wrapper);
+
+ ok((res = test_dname_table_new()), "dname table: new");
+ final_res *= res;
+
+ skip(!res, 2);
+
+ ok((res = test_dname_table_adding()), "dname table: adding");
+ final_res *= res;
+
+ ok((res = test_dname_table_find()), "dname table: searching");
+ final_res *= res;
+
+ endskip;
+
+ return final_res;
+}
diff --git a/src/tests/libknot/libknot/dname_table_tests.h b/src/tests/libknot/libknot/dname_table_tests.h
new file mode 100644
index 0000000..f3088e9
--- /dev/null
+++ b/src/tests/libknot/libknot/dname_table_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DNAME_TABLE_TESTS_H_
+#define _KNOTD_DNAME_TABLE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api dname_table_tests_api;
+
+#endif /* _KNOTD_DNAME_TABLE_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/dname_tests.c b/src/tests/libknot/libknot/dname_tests.c
new file mode 100644
index 0000000..9730756
--- /dev/null
+++ b/src/tests/libknot/libknot/dname_tests.c
@@ -0,0 +1,877 @@
+/* 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 <string.h>
+#include <assert.h>
+
+#include "tests/libknot/libknot/dname_tests.h"
+#include "libknot/dname.h"
+#include "libknot/zone/node.h"
+
+static int knot_dname_tests_count(int argc, char *argv[]);
+static int knot_dname_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api dname_tests_api = {
+ "DNS library - dname", //! Unit name
+ &knot_dname_tests_count, //! Count scheduled tests
+ &knot_dname_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+// C will not accept const int in other const definition
+enum { TEST_DOMAINS_OK = 8 };
+
+enum { TEST_DOMAINS_BAD = 4 };
+
+enum { TEST_DOMAINS_NON_FQDN = 6 };
+
+static knot_node_t *NODE_ADDRESS = (knot_node_t *)0xDEADBEEF;
+
+struct test_domain {
+ char *str;
+ char *wire;
+ uint size;
+ char *labels;
+ short label_count;
+};
+
+/*! \warning Do not change the order in those, if you want to test some other
+ * feature with new dname, add it at the end of these arrays.
+ */
+static const struct test_domain
+ test_domains_ok[TEST_DOMAINS_OK] = {
+ { "abc.test.domain.com.", "\3abc\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "xyz.test.domain.com.", "\3xyz\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "test.domain.com.", "\4test\6domain\3com", 17,
+ "\x0\x5\xC", 3 },
+ { ".", "\0", 1,
+ "", 0 },
+ { "foo.bar.net.", "\3foo\3bar\3net", 13,
+ "\x0\x4\x8", 3},
+ { "bar.net.", "\3bar\3net", 9,
+ "\x0\x4", 2}
+};
+
+static const struct test_domain // sizes are strlen()s here
+ test_domains_non_fqdn[TEST_DOMAINS_NON_FQDN] = {
+ { "www", "\3www", 4, "\x0", 1 },
+ { "example", "\7example", 8, "\x0", 1 },
+ { "com", "\3com", 4, "\x0", 1 },
+ { "www.example.com", "\3www\7example\3com", 16, "\x0\x4\xC",
+ 3 },
+ { "some", "\4some", 5, "\x0", 1 },
+ { "example.com", "\7example\3com", 12, "\x0\x8", 2 }
+ };
+
+static const struct test_domain
+ test_domains_bad[TEST_DOMAINS_BAD] = {
+ { NULL, "\2ex\3com", 0, "", 0 },
+ { "ex.com.", NULL, 0, "", 0 },
+ { "ex.com.\5", "\3ex\3com\0\5", 10, "", 0 },
+ { "example.com", "\3example\3com", 12, "\x0\x8", 2 }
+};
+
+static int test_dname_create()
+{
+ knot_dname_t *dname = knot_dname_new();
+ if (dname == NULL
+ || knot_dname_name(dname) != NULL
+ || knot_dname_size(dname) != 0
+ || knot_dname_node(dname, 0) != NULL) {
+ diag("New domain name not initialized properly!");
+ return 0;
+ }
+ knot_dname_free(&dname);
+ if (dname != NULL) {
+ diag("Pointer to the structure not set to"
+ "NULL when deallocating!");
+ return 0;
+ }
+ return 1;
+}
+
+static int test_dname_delete()
+{
+ // how to test this??
+ return 0;
+}
+
+static int check_domain_name(const knot_dname_t *dname,
+ const struct test_domain *test_domains, int i,
+ int check_node)
+{
+ int errors = 0;
+
+ if (dname == NULL) {
+ diag("Domain name #%d not created!", i);
+ return 1;
+ }
+
+ // check size
+ if (knot_dname_size(dname) != test_domains[i].size) {
+ diag("Bad size of the created domain name: %u (should be %u).",
+ knot_dname_size(dname), test_domains[i].size);
+ ++errors;
+ }
+ // check wire format
+ uint size = knot_dname_size(dname);
+ if (strncmp((char *)knot_dname_name(dname),
+ test_domains[i].wire, size) != 0) {
+ diag("The wire format of the created domain name is wrong:"
+ " '%.*s' (should be '%.*s').",
+ size, knot_dname_name(dname),
+ size, test_domains[i].wire);
+ ++errors;
+ }
+ // check labels
+ if (test_domains[i].label_count != dname->label_count) {
+ diag("Label count of the created domain name is wrong:"
+ " %d (should be %d)\n", dname->label_count,
+ test_domains[i].label_count);
+ ++errors;
+ }
+ if (strncmp((char *)dname->labels, test_domains[i].labels,
+ test_domains[i].label_count) != 0) {
+ diag("Label offsets of the created domain name are wrong.\n");
+ ++errors;
+ }
+
+ if (check_node) {
+ if (knot_dname_node(dname, 0) != NODE_ADDRESS) {
+ diag("Node pointer in the created domain name is wrong:"
+ "%p (should be %p)",
+ knot_dname_node(dname, 0), NODE_ADDRESS);
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+static int test_dname_create_from_str()
+{
+ int errors = 0;
+ knot_dname_t *dname = NULL;
+
+ for (int i = 0; i < TEST_DOMAINS_OK && errors == 0; ++i) {
+ //note("testing domain: %s", test_domains_ok[i].str);
+ dname = knot_dname_new_from_str(test_domains_ok[i].str,
+ strlen(test_domains_ok[i].str), NODE_ADDRESS);
+ errors += check_domain_name(dname, test_domains_ok, i, 1);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_create_from_str_non_fqdn()
+{
+ int errors = 0;
+ knot_dname_t *dname = NULL;
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; ++i) {
+ //note("testing domain: %s", test_domains_non_fqdn[i].str);
+ dname = knot_dname_new_from_str(test_domains_non_fqdn[i].str,
+ strlen(test_domains_non_fqdn[i].str), NULL);
+ errors += check_domain_name(dname, test_domains_non_fqdn, i, 0);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_cat()
+{
+ int errors = 0;
+
+ /*
+ * This uses three particular dnames from test_domains structure
+ * where the third dname is a concatenation of the first two dnames.
+ */
+
+ knot_dname_t *d1, *d2, *d3;
+
+ d1 = knot_dname_new_from_str(test_domains_non_fqdn[0].str,
+ strlen(test_domains_non_fqdn[0].str), NULL);
+ d2 = knot_dname_new_from_str(test_domains_non_fqdn[1].str,
+ strlen(test_domains_non_fqdn[1].str), NULL);
+ d3 = knot_dname_new_from_str(test_domains_non_fqdn[2].str,
+ strlen(test_domains_non_fqdn[2].str), NULL);
+
+ knot_dname_cat(d1, d2);
+ knot_dname_cat(d1, d3);
+
+ errors += check_domain_name(d1, test_domains_non_fqdn, 3, 0);
+
+ knot_dname_free(&d1);
+ knot_dname_free(&d2);
+ knot_dname_free(&d3);
+
+ /*
+ * Same thing as above, only different case.
+ */
+
+ d1 = knot_dname_new_from_str(test_domains_non_fqdn[4].str,
+ strlen(test_domains_non_fqdn[4].str),
+ NODE_ADDRESS);
+
+ d2 = knot_dname_new_from_str(test_domains_ok[4].str,
+ strlen(test_domains_ok[4].str),
+ NODE_ADDRESS);
+
+ knot_dname_cat(d1, d2);
+
+ errors += check_domain_name(d1, test_domains_ok, 1, 1);
+
+ knot_dname_free(&d1);
+ knot_dname_free(&d2);
+
+ return (errors == 0);
+}
+
+static int test_dname_left_chop()
+{
+ int errors = 0;
+
+ /* Uses same principle as test_dname_cat(), only reversed */
+
+ /* TODO this would maybe deserver separate structure */
+
+ knot_dname_t *d1;
+
+ d1 = knot_dname_new_from_str(test_domains_ok[1].str,
+ strlen(test_domains_ok[1].str),
+ NODE_ADDRESS);
+
+ knot_dname_t *chopped;
+
+ chopped = knot_dname_left_chop(d1);
+
+ errors += check_domain_name(chopped, test_domains_ok, 4, 0);
+
+ knot_dname_free(&d1);
+ knot_dname_free(&chopped);
+
+ d1 = knot_dname_new_from_str(test_domains_non_fqdn[3].str,
+ strlen(test_domains_non_fqdn[3].str),
+ NODE_ADDRESS);
+
+ chopped = knot_dname_left_chop(d1);
+
+ errors += check_domain_name(chopped, test_domains_non_fqdn, 5, 0);
+
+ knot_dname_free(&d1);
+ knot_dname_free(&chopped);
+
+ return (errors == 0);
+}
+
+static int test_dname_create_from_wire()
+{
+ int errors = 0;
+ knot_dname_t *dname = NULL;
+
+ for (int i = 0; i < TEST_DOMAINS_OK && errors == 0; ++i) {
+ assert(strlen(test_domains_ok[i].wire) + 1 ==
+ test_domains_ok[i].size);
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NODE_ADDRESS);
+ errors += check_domain_name(dname, test_domains_ok, i, 1);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_to_str()
+{
+ int errors = 0;
+
+ /*
+ * Converts dname wireformat to string represenation, which is compared
+ * with entries in test_domains structure.
+ */
+
+ knot_dname_t *dname = NULL;
+
+ for (int i = 0; i < TEST_DOMAINS_OK && errors == 0; ++i) {
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NODE_ADDRESS);
+ char *name_str = knot_dname_to_str(dname);
+ if (strcmp(name_str, test_domains_ok[i].str) != 0) {
+ diag("Presentation format of domain name wrong:"
+ " %s (should be %s)",
+ name_str, test_domains_ok[i].str);
+ ++errors;
+ }
+ free(name_str);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+/* called by lives_ok */
+static int test_faulty_data()
+{
+ knot_dname_t *dname = NULL;
+
+ /*
+ * This takes dnames from test_domains_bad array, which contains
+ * malformed dnames. TODO add something like: 2www3foo - it's gonna fail
+ */
+
+ for (int i = 0; i < TEST_DOMAINS_BAD; i++) {
+
+ if (test_domains_bad[i].str != NULL) {
+ dname = knot_dname_new_from_str(
+ test_domains_bad[i].str,
+ strlen(test_domains_bad[i].str),
+ NODE_ADDRESS);
+ } else {
+ dname = knot_dname_new_from_str(
+ test_domains_bad[i].str, 0, NODE_ADDRESS);
+ }
+
+ knot_dname_free(&dname);
+
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_bad[i].wire,
+ test_domains_bad[i].size, NODE_ADDRESS);
+
+ knot_dname_free(&dname);
+ }
+
+ return 1; //did it get here? success
+}
+
+static int test_dname_compare()
+{
+ knot_dname_t *dnames[TEST_DOMAINS_OK];
+
+ /* This uses particular dnames from TEST_DOMAINS_OK array */
+
+ for (int i = 0; i < TEST_DOMAINS_OK; ++i) {
+ dnames[i] = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NODE_ADDRESS);
+ }
+
+ int errors = 0;
+ /* abc < some */
+ if (knot_dname_compare(dnames[0], dnames[1]) >= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /* abc.test.domain.com. < foo.bar.net. */
+ if (knot_dname_compare(dnames[0], dnames[6]) >= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /* foo.bar.net. < . */
+ if (knot_dname_compare(dnames[5], dnames[0]) >= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /* bar.net. < foo.bar.net. */
+ if (knot_dname_compare(dnames[7], dnames[6]) >= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /* some == some */
+ if (knot_dname_compare(dnames[1], dnames[3]) != 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /*xyz > some */
+ if (knot_dname_compare(dnames[2], dnames[1]) <= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ /*foo.bar.net. > xyz.test.domain.com. */
+ if (knot_dname_compare(dnames[6], dnames[3]) <= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+// /* xyz.test.domain.com. > . */
+// if (knot_dname_compare(dnames[3], dnames[5]) <= 0) {
+// diag("Dname comparison error");
+// errors++;
+// }
+
+ /* bar.net. < foo.bar.net. */
+ if (knot_dname_compare(dnames[6], dnames[7]) <= 0) {
+ diag("Dname comparison error");
+ errors++;
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ knot_dname_free(&dnames[i]);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_is_fqdn()
+{
+ int errors = 0;
+
+ knot_dname_t *dname;
+
+ /* All dnames in TEST_DOMAINS_OK are fqdn */
+
+ for (int i = 0; i < TEST_DOMAINS_OK && !errors; ++i) {
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NODE_ADDRESS);
+ errors += !knot_dname_is_fqdn(dname);
+ knot_dname_free(&dname);
+ }
+
+ /* None of the following dnames should be fqdn */
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN && !errors; ++i) {
+ dname = knot_dname_new_from_str(test_domains_non_fqdn[i].str,
+ strlen(test_domains_non_fqdn[i].str), NULL);
+ errors += knot_dname_is_fqdn(dname);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_is_subdomain()
+{
+ int errors = 0;
+
+ knot_dname_t *dnames_fqdn[TEST_DOMAINS_OK];
+ knot_dname_t *dnames_non_fqdn[TEST_DOMAINS_NON_FQDN];
+
+ for (int i = 0; i < TEST_DOMAINS_OK; ++i) {
+ dnames_fqdn[i] = knot_dname_new_from_wire(
+ (const uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NULL);
+ assert(dnames_fqdn[i] != NULL);
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; ++i) {
+ dnames_non_fqdn[i] = knot_dname_new_from_str(
+ test_domains_non_fqdn[i].str,
+ test_domains_non_fqdn[i].size, NULL);
+ assert(dnames_non_fqdn[i] != NULL);
+ }
+
+ // fqdn names 0 - 3 should be subdomains of name 4
+ knot_dname_t *parent = dnames_fqdn[4];
+ for (int i = 0; i < 3; ++i) {
+ if (!knot_dname_is_subdomain(dnames_fqdn[i], parent)) {
+ diag("(fqdn 1-%d) "
+ "Name %s was not considered subdomain of %s", i,
+ knot_dname_name(dnames_fqdn[i]),
+ knot_dname_name(parent));
+ ++errors;
+ }
+ }
+
+ // fqdn names 0 - 4 should be subdomains of name 5 (root)
+ parent = dnames_fqdn[5];
+ for (int i = 0; i < 4; ++i) {
+ if (!knot_dname_is_subdomain(dnames_fqdn[i], parent)) {
+ diag("(fqdn 2-%d) "
+ "Name %s was not considered subdomain of %s", i,
+ knot_dname_name(dnames_fqdn[i]),
+ knot_dname_name(parent));
+ ++errors;
+ }
+ }
+
+ // non-fqdn names 3 and 5 should be subdomains of non-fqdn name 2
+ parent = dnames_non_fqdn[2];
+ if (!knot_dname_is_subdomain(dnames_non_fqdn[3], parent)) {
+ diag("(non-fqdn 1) "
+ "Name %.*s was not considered subdomain of %.*s",
+ knot_dname_size(dnames_non_fqdn[3]),
+ knot_dname_name(dnames_non_fqdn[3]),
+ knot_dname_size(parent),
+ knot_dname_name(parent));
+ ++errors;
+ }
+ if (!knot_dname_is_subdomain(dnames_non_fqdn[5], parent)) {
+ diag("(non-fqdn 2) "
+ "Name %.*s was not considered subdomain of %.*s",
+ knot_dname_size(dnames_non_fqdn[5]),
+ knot_dname_name(dnames_non_fqdn[5]),
+ knot_dname_size(parent),
+ knot_dname_name(parent));
+ ++errors;
+ }
+
+ // non-fqdn name 3 should be subdomain of non-fqdn name 5
+ parent = dnames_non_fqdn[5];
+ if (!knot_dname_is_subdomain(dnames_non_fqdn[3], parent)) {
+ diag("(non-fqdn 3) "
+ "Name %.*s was not considered subdomain of %.*s",
+ knot_dname_size(dnames_non_fqdn[3]),
+ knot_dname_name(dnames_non_fqdn[3]),
+ knot_dname_size(parent),
+ knot_dname_name(parent));
+ ++errors;
+ }
+
+ // identical names should not be considered subdomains
+ if (knot_dname_is_subdomain(dnames_fqdn[0], dnames_fqdn[0])) {
+ diag("(identical names) "
+ "Name %s was considered subdomain of itself",
+ knot_dname_name(dnames_fqdn[0]));
+ ++errors;
+ }
+ if (knot_dname_is_subdomain(dnames_fqdn[1], dnames_fqdn[3])) {
+ diag("(identical names) "
+ "Name %s was considered subdomain of %s",
+ knot_dname_name(dnames_fqdn[1]),
+ knot_dname_name(dnames_fqdn[3]));
+ ++errors;
+ }
+
+ // fqdn name should not be considered subdomain of non-fqdn name
+ if (knot_dname_is_subdomain(dnames_fqdn[1], dnames_non_fqdn[2])) {
+ diag("(fqdn subdomain of non-fqdn) "
+ "Name %s was considered subdomain of %.*s",
+ knot_dname_name(dnames_fqdn[1]),
+ knot_dname_size(dnames_non_fqdn[2]),
+ knot_dname_name(dnames_non_fqdn[2]));
+ ++errors;
+ }
+
+ // non-fqdn name should not be considered subdomain of fqdn name
+ if (knot_dname_is_subdomain(dnames_fqdn[1], dnames_non_fqdn[2])) {
+ diag("(non-fqdn subdomain of fqdn) "
+ "Name %s was considered subdomain of %.*s",
+ knot_dname_name(dnames_fqdn[1]),
+ knot_dname_size(dnames_non_fqdn[2]),
+ knot_dname_name(dnames_non_fqdn[2]));
+ ++errors;
+ }
+
+ // parent name should not be considered subdomain of its subdomain
+ if (knot_dname_is_subdomain(dnames_fqdn[4], dnames_fqdn[0])) {
+ diag("(ancestor subdomain of name) "
+ "Name %s was considered subdomain of %s",
+ knot_dname_name(dnames_fqdn[4]),
+ knot_dname_name(dnames_fqdn[0]));
+ ++errors;
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_OK; ++i) {
+ knot_dname_free(&dnames_fqdn[i]);
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; ++i) {
+ knot_dname_free(&dnames_non_fqdn[i]);
+ }
+
+ return (errors == 0);
+}
+
+static int check_wires(const uint8_t *wire1, uint size1,
+ uint8_t *wire2, uint size2)
+{
+ if (size1 != size2) {
+ return 0;
+ }
+
+ int i;
+
+ for (i = 0; (i < size1); i++) {
+ if (wire1[i] != wire2[i]) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*!< \note not to be run separately */
+static int test_dname_name(knot_dname_t **dnames_fqdn,
+ knot_dname_t **dnames_non_fqdn)
+{
+ assert(dnames_fqdn);
+ assert(dnames_non_fqdn);
+
+ int errors = 0;
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ const uint8_t *tmp_name;
+
+ tmp_name = knot_dname_name(dnames_fqdn[i]);
+ if (!check_wires(tmp_name, dnames_fqdn[i]->size,
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size)) {
+ diag("Got bad name value from structure: "
+ "%s, should be: %s. Sizes: %d and: %d",
+ tmp_name, test_domains_ok[i].wire,
+ dnames_fqdn[i]->size,
+ test_domains_ok[i].size);
+ errors++;
+ }
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+ const uint8_t *tmp_name;
+ tmp_name = knot_dname_name(dnames_non_fqdn[i]);
+ if (!check_wires(tmp_name, dnames_non_fqdn[i]->size - 1,
+ (uint8_t *)test_domains_non_fqdn[i].wire,
+ test_domains_non_fqdn[i].size)) {
+ diag("Got bad name value from structure: "
+ "%s, should be: %s. Sizes: %d and %d\n",
+ tmp_name, test_domains_non_fqdn[i].wire,
+ dnames_non_fqdn[i]->size,
+ test_domains_non_fqdn[i].size);
+// hex_print(dnames_non_fqdn[i]->name,
+// dnames_non_fqdn[i]->size);
+// hex_print(test_domains_non_fqdn[i].wire,
+// test_domains_non_fqdn[i].size);
+// diag("%s and %s\n",
+// knot_dname_to_str(dnames_non_fqdn[i]),
+// test_domains_non_fqdn[i]);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+/* \note not to be run separately */
+static int test_dname_size(knot_dname_t **dnames_fqdn,
+ knot_dname_t **dnames_non_fqdn)
+{
+ assert(dnames_fqdn);
+ assert(dnames_non_fqdn);
+
+ int errors = 0;
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ uint8_t tmp_size;
+ if ((tmp_size = knot_dname_size(dnames_fqdn[i])) !=
+ test_domains_ok[i].size) {
+ diag("Got bad size value from structure: "
+ "%u, should be: %u",
+ tmp_size, test_domains_ok[i].size);
+ errors++;
+ }
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+ uint8_t tmp_size;
+ if ((tmp_size = knot_dname_size(dnames_non_fqdn[i])) !=
+ test_domains_non_fqdn[i].size) {
+ diag("Got bad size value from structure: "
+ "%u, should be: %u",
+ tmp_size, test_domains_non_fqdn[i].size);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+/* \note not to be run separately */
+static int test_dname_node(knot_dname_t **dnames_fqdn,
+ knot_dname_t **dnames_non_fqdn)
+{
+ assert(dnames_fqdn);
+ assert(dnames_non_fqdn);
+
+ int errors = 0;
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ const knot_node_t *tmp_node;
+ if ((tmp_node = knot_dname_node(dnames_fqdn[i], 0)) !=
+ NODE_ADDRESS) {
+ diag("Got bad node value from structure: "
+ "%p, should be: %p",
+ tmp_node, NODE_ADDRESS);
+ errors++;
+ }
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+ const knot_node_t *tmp_node;
+ if ((tmp_node = knot_dname_node(dnames_non_fqdn[i], 0)) !=
+ NODE_ADDRESS) {
+ diag("Got bad node value from structure: "
+ "%s, should be: %s",
+ tmp_node, NODE_ADDRESS);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+static int test_dname_getters(uint type)
+{
+ int errors = 0;
+
+ knot_dname_t *dnames_fqdn[TEST_DOMAINS_OK];
+ knot_dname_t *dnames_non_fqdn[TEST_DOMAINS_NON_FQDN];
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ dnames_fqdn[i] = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[i].wire,
+ test_domains_ok[i].size, NODE_ADDRESS);
+ assert(dnames_fqdn[i] != NULL);
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+ printf("Creating dname: %s size: %d\n", test_domains_non_fqdn[i].wire, test_domains_non_fqdn[i].size);
+ dnames_non_fqdn[i] = knot_dname_new_from_str(
+ test_domains_non_fqdn[i].str,
+ test_domains_non_fqdn[i].size, NODE_ADDRESS);
+ assert(dnames_non_fqdn[i] != NULL);
+ }
+
+ switch (type) {
+ case 0: {
+ errors += test_dname_name(dnames_fqdn, dnames_non_fqdn);
+ break;
+ }
+
+ case 1: {
+ errors += test_dname_size(dnames_fqdn, dnames_non_fqdn);
+ break;
+ }
+
+ case 2: {
+ errors += test_dname_node(dnames_fqdn, dnames_non_fqdn);
+ break;
+ }
+ } /* switch */
+
+ for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+ knot_dname_free(&dnames_fqdn[i]);
+ }
+
+ for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+ knot_dname_free(&dnames_non_fqdn[i]);
+ }
+
+ return (errors == 0);
+}
+
+static const int KNOT_DNAME_TEST_COUNT = 15;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_dname_tests_count(int argc, char *argv[])
+{
+ return KNOT_DNAME_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_dname_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_str = 0,
+ res_wire = 0,
+ res_str_non_fqdn = 0,
+ res_final = 1;
+
+ res = test_dname_create();
+ ok(res, "dname: create empty");
+ res_final *= res;
+
+ skip(!res, 12);
+
+ todo();
+
+ ok((res = test_dname_delete()), "dname: delete");
+ //res_final *= res;
+
+ endtodo;
+
+ ok((res_str = test_dname_create_from_str()), "dname: create from str");
+ ok((res_wire = test_dname_create_from_wire()),
+ "dname: create from wire");
+ ok((res_str_non_fqdn = test_dname_create_from_str_non_fqdn()),
+ "dname: create from str non fqdn");
+ res_final *= res_str;
+ res_final *= res_wire;
+ res_final *= res_str_non_fqdn;
+
+ todo();
+ res = test_dname_getters(0);
+ ok(res, "dname: name");
+ endtodo;
+
+ todo();
+ res = test_dname_getters(1);
+ ok(res, "dname: size");
+ endtodo;
+
+ res = test_dname_getters(2);
+ ok(res, "dname: node");
+
+ skip(!res_str || !res_wire || !res_str_non_fqdn, 2);
+
+ ok((res = test_dname_to_str()), "dname: convert to str");
+ res_final *= res;
+
+ lives_ok(test_faulty_data(); , "dname: faulty data test");
+
+ endskip; /* !res_str || !res_wire */
+
+ ok((res = test_dname_compare()), "dname: compare");
+ res_final *= res;
+
+ ok((res = test_dname_cat()), "dname: cat");
+ res_final *= res;
+
+ ok((res = test_dname_is_fqdn()), "dname: fqdn");
+ res_final *= res;
+
+ ok((res = test_dname_left_chop()), "dname: left chop");
+ res_final *= res;
+
+ ok((res = test_dname_is_subdomain()), "dname: is subdomain");
+ res_final *= res;
+
+ endskip; /* create failed */
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/dname_tests.h b/src/tests/libknot/libknot/dname_tests.h
new file mode 100644
index 0000000..a7d75aa
--- /dev/null
+++ b/src/tests/libknot/libknot/dname_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DNAME_TESTS_H_
+#define _KNOTD_DNAME_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api dname_tests_api;
+
+#endif /* _KNOTD_DNAME_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/edns_tests.c b/src/tests/libknot/libknot/edns_tests.c
new file mode 100644
index 0000000..ac5d130
--- /dev/null
+++ b/src/tests/libknot/libknot/edns_tests.c
@@ -0,0 +1,596 @@
+/* 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 "tests/libknot/libknot/edns_tests.h"
+#include "libknot/common.h"
+#include "libknot/edns.h"
+
+static int knot_edns_tests_count(int argc, char *argv[]);
+static int knot_edns_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api edns_tests_api = {
+ "DNS library - EDNS", //! Unit name
+ &knot_edns_tests_count, //! Count scheduled tests
+ &knot_edns_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+enum { TEST_EDNS = 1, OPTION_COUNT = 3 };
+
+struct test_edns_options {
+ uint16_t code;
+ uint16_t length;
+ uint8_t *data;
+};
+
+struct test_edns {
+ struct test_edns_options *options;
+ uint16_t payload;
+ uint8_t ext_rcode;
+ uint8_t version;
+ uint16_t flags;
+ short option_count;
+ short options_max;
+ short size;
+};
+
+typedef struct test_edns test_edns_t;
+
+struct test_edns_options test_options_data[OPTION_COUNT] = {
+ {5, 7, (uint8_t *)"123456"},
+ {4, 3, (uint8_t *)"12"},
+ {1, 5, (uint8_t *)"13333"}
+};
+
+test_edns_t test_edns_data[TEST_EDNS] = {
+{ NULL, 4096, 2, 0, 0, 0, 10, 11}
+};
+
+enum edns_mask {
+ KNOT_EDNS_DO_MASK = (uint16_t)0x8000
+};
+
+/* Creates actual knot_opt_rr_t variable from test_edns_t variable */
+static knot_opt_rr_t *opt_rr_from_test_edns(test_edns_t *test_edns)
+{
+ knot_opt_rr_t *ret = knot_edns_new();
+
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ ret->flags = test_edns->flags;
+ ret->ext_rcode = test_edns->ext_rcode;
+ ret->payload = test_edns->payload;
+ ret->version = test_edns->version;
+
+ for (int i = 0; i < test_edns->option_count; i++) {
+ if (knot_edns_add_option(ret, test_edns->options[i].code,
+ test_edns->options[i].length,
+ test_edns->options[i].data) != 0) {
+ knot_edns_free(&ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+/* simple wire compare - 0 if same, 1 otherwise */
+static int edns_compare_wires(uint8_t *wire1,
+ uint8_t *wire2,
+ uint16_t length)
+{
+ for (uint i = 0; i < length; i++) {
+ if (wire1[i] != wire2[i]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int check_edns(const knot_opt_rr_t *edns,
+ const test_edns_t *test_edns)
+{
+ if (edns->option_count != test_edns->option_count) {
+ diag("Option count is wrong");
+ return -1;
+ }
+
+ for (int i = 0; i < edns->option_count; i++) {
+ /* check options */
+ if (edns->options[i].code != test_edns->options[i].code) {
+ diag("Code in options is wrong");
+ return -1;
+ }
+
+ if (edns->options[i].length != test_edns->options[i].length) {
+ diag("Length in options is wrong");
+ return -1;
+ }
+
+ if (edns_compare_wires(edns->options[i].data,
+ test_edns->options[i].data,
+ edns->options[i].length) != 0) {
+ diag("Data in options are wrong");
+ return -1;
+ }
+ }
+
+ if (edns->version != test_edns->version) {
+ diag("Version is wrong");
+ return -1;
+ }
+
+ if (edns->flags != test_edns->flags) {
+ diag("Flags are wrong");
+ return -1;
+ }
+
+ if (edns->size != test_edns->size) {
+ diag("Size is wrong");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_edns_get_payload(const knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ if (knot_edns_get_payload(edns) !=
+ test_edns->payload) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_get_ext_rcode(const knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ if (knot_edns_get_ext_rcode(edns) !=
+ test_edns->ext_rcode) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_get_flags(const knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ if (knot_edns_get_flags(edns) !=
+ test_edns->flags) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_get_version(const knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ if (knot_edns_get_version(edns) !=
+ test_edns->version) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_do(const knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ if (knot_edns_do(edns) !=
+ (test_edns->flags & KNOT_EDNS_DO_MASK)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_size(knot_opt_rr_t *edns, test_edns_t *test_edns)
+{
+ if (knot_edns_size(edns) !=
+ test_edns->size) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_set_payload(knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ knot_edns_set_payload(edns, test_edns->payload);
+
+ if (edns->payload !=
+ test_edns->payload) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_set_ext_rcode(knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ knot_edns_set_ext_rcode(edns, test_edns->ext_rcode);
+ if (edns->ext_rcode !=
+ test_edns->ext_rcode) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_set_version(knot_opt_rr_t *edns,
+ test_edns_t *test_edns)
+{
+ knot_edns_set_version(edns,
+ test_edns->version);
+
+ if (edns->version !=
+ test_edns->version) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_set_do(knot_opt_rr_t *edns)
+{
+ knot_edns_set_do(edns);
+
+ if (!knot_edns_do(edns)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int test_edns_getters(uint type)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_EDNS; i++) {
+ knot_opt_rr_t *edns =
+ opt_rr_from_test_edns(&(test_edns_data[i]));
+ if (edns == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ switch(type) {
+ case 0:
+ if (test_edns_get_payload(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong payload!");
+ errors++;
+ }
+ break;
+ case 1:
+ if (test_edns_get_ext_rcode(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong extended RCODE!");
+ errors++;
+ }
+ break;
+ case 2:
+ if (test_edns_get_flags(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong flags!");
+
+ errors++;
+ }
+ break;
+ case 3:
+ if (test_edns_get_version(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong version!");
+ errors++;
+ }
+ break;
+ case 4:
+ if (test_edns_do(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong DO bit!");
+ errors++;
+ }
+ break;
+ case 5:
+ if (test_edns_size(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Got wrong size!");
+ errors++;
+ }
+ break;
+ default:
+ diag("Unknown option");
+ errors++;
+ } /* switch */
+
+ knot_edns_free(&edns);
+ }
+
+ return (errors == 0);
+}
+
+static int test_edns_setters(uint type)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_EDNS; i++) {
+ knot_opt_rr_t *edns =
+ opt_rr_from_test_edns(&(test_edns_data[i]));
+ if (edns == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ switch(type) {
+ case 0:
+ if (test_edns_set_payload(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Set wrong payload!");
+ errors++;
+ }
+ break;
+ case 1:
+ if (test_edns_set_ext_rcode(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Set wrong ext_rcode");
+ errors++;
+ }
+ break;
+ case 2:
+ if (test_edns_set_version(edns,
+ &test_edns_data[i]) != 1) {
+ diag("Set wrong version!");
+ errors++;
+ }
+ break;
+ case 3:
+ if (test_edns_set_do(edns) != 1) {
+ diag("Set wrong DO bit!");
+ errors++;
+ }
+ break;
+ default:
+ diag("Unknown option");
+ errors++;
+ } /* switch */
+
+ knot_edns_free(&edns);
+ }
+
+ return (errors == 0);
+}
+
+static int test_edns_wire()
+{
+ /*
+ * Tests to_wire and from_wire in one test.
+ */
+ for (int i = 0; i < TEST_EDNS; i++) {
+ /* Creates instance from test_edns_t. */
+ knot_opt_rr_t *edns =
+ opt_rr_from_test_edns(&(test_edns_data[i]));
+ if (edns == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ uint8_t *wire = NULL;
+ wire = malloc(sizeof(uint8_t) * edns->size);
+ CHECK_ALLOC_LOG(wire, 0);
+
+ /* Converts EDNS to wire. */
+ short wire_size = knot_edns_to_wire(edns, wire, 100);
+
+ if (wire_size == -1) {
+ diag("Could not create EDNS wire");
+ return 0;
+ }
+
+ knot_opt_rr_t *edns_from_wire = knot_edns_new();
+ if (edns == NULL) {
+ return 0;
+ }
+
+ /* TODO use some constant */
+ /* Creates new EDNS from wire */
+ if (knot_edns_new_from_wire(edns_from_wire,
+ wire,
+ 100) <= 0) {
+ diag("Could not create from wire");
+ return 0;
+ }
+
+ /* Checks whether EDNS created from wire is the same */
+ if (check_edns(edns_from_wire,
+ &(test_edns_data[i])) != 0) {
+ diag("EDNS created from wire is different from the "
+ "original one");
+ }
+
+ free(wire);
+ knot_edns_free(&edns_from_wire);
+ knot_edns_free(&edns);
+ }
+ return 1;
+}
+
+static int test_edns_add_option()
+{
+ /*
+ * Create empty EDNS and add options one by one, testing their presence.
+ */
+ for (int i = 0; i < TEST_EDNS; i++) {
+ knot_opt_rr_t *edns = knot_edns_new();
+ assert(edns->option_count == 0);
+
+ if (edns == NULL) {
+ ERR_ALLOC_FAILED;
+ return 0;
+ }
+
+ for (int j = 0; j < test_edns_data[i].option_count; j++) {
+ if (knot_edns_add_option(edns,
+ test_edns_data[i].options[j].code,
+ test_edns_data[i].options[j].length,
+ test_edns_data[i].options[j].
+ data) != 0) {
+ diag("Could not add option");
+ return 0;
+ }
+
+ if (edns->options[j].code !=
+ test_edns_data[i].options[j].code) {
+ diag("Option code wrongly added!");
+ return 0;
+ }
+
+ if (edns->options[j].length !=
+ test_edns_data[i].options[j].length) {
+ diag("Option length wrongly added!");
+ return 0;
+ }
+
+ if (edns_compare_wires(edns->options[j].data,
+ test_edns_data[i].
+ options[j].data,
+ edns->options[j].length) != 0) {
+ diag("Option wire wrongly added!");
+ return 0;
+ }
+ }
+ knot_edns_free(&edns);
+ }
+ return 1;
+}
+
+static int test_edns_has_option()
+{
+ /*
+ * Create empty EDNS and add options one by one, testing their presence
+ */
+ for (int i = 0; i < TEST_EDNS; i++) {
+ knot_opt_rr_t *edns = knot_edns_new();
+ assert(edns->option_count == 0);
+
+ if (edns == NULL) {
+ ERR_ALLOC_FAILED;
+ return 0;
+ }
+
+ for (int j = 0; j < test_edns_data[i].option_count; j++) {
+ if (knot_edns_add_option(edns,
+ test_edns_data[i].options[j].code,
+ test_edns_data[i].options[j].length,
+ test_edns_data[i].options[j].
+ data) != 0) {
+ diag("Could not add option");
+ return 0;
+ }
+
+ if (knot_edns_has_option(edns,
+ test_edns_data[i].options[j].code) != 1) {
+ diag("Option not found!");
+ return 0;
+ }
+ }
+ knot_edns_free(&edns);
+ }
+ return 1;
+}
+
+static const int KNOT_EDNS_TESTS_COUNT = 12;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_edns_tests_count(int argc, char *argv[])
+{
+ return KNOT_EDNS_TESTS_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_edns_tests_run(int argc, char *argv[])
+{
+ int res = 0;
+ int res_final = 1;
+
+ res = test_edns_getters(0);
+ ok(res, "ends: get payload");
+ res_final *= res;
+
+ res = test_edns_getters(1);
+ ok(res, "ends: get extenden RCODE");
+ res_final *= res;
+
+ res = test_edns_getters(2);
+ ok(res, "ends: get flags");
+ res_final *= res;
+
+ res = test_edns_getters(3);
+ ok(res, "ends: get version");
+ res_final *= res;
+
+ res = test_edns_getters(4);
+ ok(res, "ends: do");
+ res_final *= res;
+
+ res = test_edns_getters(5);
+ ok(res, "ends: size");
+ res_final *= res;
+
+ res = test_edns_setters(0);
+ ok(res, "ends: set payload");
+ res_final *= res;
+
+ res = test_edns_setters(1);
+ ok(res, "ends: set extended RCODE");
+ res_final *= res;
+
+ res = test_edns_setters(2);
+ ok(res, "ends: set version");
+ res_final *= res;
+
+ res = test_edns_setters(3);
+ ok(res, "ends: set DO");
+ res_final *= res;
+
+ res = test_edns_add_option();
+ ok(res, "ends: add option");
+ res_final *= res;
+
+ res = test_edns_has_option();
+ ok(res, "ends: has option");
+ res_final *= res;
+
+ res = test_edns_wire();
+ ok(res, "ends: to_wire and from_wire");
+ res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/edns_tests.h b/src/tests/libknot/libknot/edns_tests.h
new file mode 100644
index 0000000..4553234
--- /dev/null
+++ b/src/tests/libknot/libknot/edns_tests.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/>.
+ */
+/*!
+ * \file edns_tests.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * Contains unit tests for ENDS API
+ *
+ * Contains tests for:
+ * - ENDS API
+ */
+#ifndef _KNOTD__EDNS_TESTS_H_
+#define _KNOTD__EDNS_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api edns_tests_api;
+
+#endif /* _KNOTD__EDNS_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/node_tests.c b/src/tests/libknot/libknot/node_tests.c
new file mode 100644
index 0000000..f04a202
--- /dev/null
+++ b/src/tests/libknot/libknot/node_tests.c
@@ -0,0 +1,344 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/libknot/libknot/node_tests.h"
+#include "libknot/dname.h"
+#include "libknot/zone/node.h"
+#include "libknot/util/descriptor.h"
+
+static int knot_node_tests_count(int argc, char *argv[]);
+static int knot_node_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api node_tests_api = {
+ "DNS library - node", //! Unit name
+ &knot_node_tests_count, //! Count scheduled tests
+ &knot_node_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+// C will not accept const int in other const definition
+enum { TEST_NODES = 2, RRSETS = 5};
+
+struct test_node {
+ knot_dname_t owner;
+ knot_node_t *parent;
+ uint size;
+};
+
+static knot_dname_t test_dnames[TEST_NODES] = {
+ {{}, (uint8_t *)"\3www\7example\3com", 17},
+ {{}, (uint8_t *)"\3www\7example\3com", 17}
+};
+
+static struct test_node test_nodes[TEST_NODES] = {
+ {{{}, (uint8_t *)"\3com", 4}, (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\3www\7example\3com", 17}, (knot_node_t *)NULL}
+};
+
+static knot_rrset_t rrsets[RRSETS] = {
+ {&test_dnames[0], 1, 1, 3600, NULL, NULL},
+ {&test_dnames[1], 2, 1, 3600, NULL, NULL},
+ {&test_dnames[1], 7, 1, 3600, NULL, NULL},
+ {&test_dnames[1], 3, 1, 3600, NULL, NULL},
+ {&test_dnames[1], 9, 1, 3600, NULL, NULL}
+};
+
+static int test_node_create()
+{
+ /* Tests creation of node by comparing with test_node struct */
+ knot_node_t *tmp;
+ int errors = 0;
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ tmp = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+ if (tmp == NULL ||
+ tmp->owner != &test_nodes[i].owner ||
+ tmp->parent != test_nodes[i].parent ||
+ tmp->rrset_tree == NULL) {
+ errors++;
+ diag("Failed to create node structure");
+ }
+ knot_node_free(&tmp, 0, 0);
+ }
+ return (errors == 0);
+}
+
+static int test_node_add_rrset()
+{
+ knot_node_t *tmp;
+ knot_rrset_t *rrset;
+ int errors = 0;
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ /* create node from test_node structure */
+ tmp = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+ rrset = &rrsets[i];
+ if (knot_node_add_rrset(tmp, rrset, 0) < 0) {
+ errors++;
+ diag("Failed to insert rrset into node");
+ }
+
+ /* check if rrset is really there */
+
+ const knot_rrset_t *rrset_from_node = NULL;
+ if ((rrset_from_node =
+ knot_node_rrset(tmp, rrset->type)) == NULL) {
+ errors++;
+ diag("Inserted rrset could not be found");
+ continue;
+ }
+
+ /* compare rrset from node with original rrset */
+
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ int cmp = 0;
+
+ if ((rrset_from_node->rdata == NULL) &&
+ (rrset->rdata == NULL)) {
+ cmp = 0;
+ } else if ((rrset_from_node->rdata != NULL) &&
+ (rrset->rdata != NULL)) {
+ cmp = knot_rdata_compare(rrset_from_node->rdata,
+ rrset->rdata,
+ desc->wireformat);
+ } else { /* one is not NULL and other is -> error */
+ cmp = 1;
+ }
+
+ if (!((rrset_from_node->type == rrset->type) &&
+ (rrset_from_node->rclass == rrset->rclass) &&
+ (rrset_from_node->ttl == rrset->ttl) &&
+ (rrset_from_node->rrsigs == rrset->rrsigs) &&
+ (cmp == 0))) {
+ errors++;
+ diag("Values in found rrset are wrong");
+ }
+
+ knot_node_free(&tmp, 0, 0);
+ }
+
+ return (errors == 0);
+}
+
+static int test_node_get_rrset()
+{
+ knot_node_t *tmp;
+ knot_rrset_t *rrset;
+ int errors = 0;
+
+ knot_node_t *nodes[TEST_NODES];
+
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ tmp = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+ nodes[i] = tmp;
+ for (int j = 0; j < RRSETS; j++) {
+ knot_node_add_rrset(tmp, &rrsets[j], 0);
+ }
+ }
+
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ for (int j = 0; j < RRSETS; j++) {
+ rrset = &rrsets[j];
+ if (knot_node_rrset(nodes[i], rrset->type)
+ != rrset) {
+ errors++;
+ diag("Failed to get proper rrset from node");
+ }
+ }
+ knot_node_free(&nodes[i], 0, 0);
+ }
+
+ return (errors == 0);
+}
+
+static int test_node_get_parent()
+{
+ knot_node_t *tmp;
+ knot_rrset_t *rrset;
+ int errors = 0;
+
+ knot_node_t *nodes[TEST_NODES];
+
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ tmp = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+ nodes[i] = tmp;
+ rrset = &rrsets[i];
+ knot_node_add_rrset(tmp, rrset, 0);
+ }
+
+ for (int i = 0; i < TEST_NODES && !errors; i++) {
+ rrset = &rrsets[i];
+ if (knot_node_parent(nodes[i], 0) != test_nodes[i].parent) {
+ errors++;
+ diag("Failed to get proper parent from node");
+ }
+ knot_node_free(&nodes[i], 0, 0);
+ }
+ return (errors == 0);
+}
+
+static int test_node_sorting()
+{
+ knot_node_t *tmp;
+ knot_rrset_t *rrset;
+ int errors = 0;
+
+ tmp = knot_node_new(&test_nodes[0].owner, test_nodes[0].parent, 0);
+
+ /* Will add rrsets to node. */
+
+ for (int i = 0; i < RRSETS && !errors; i++) {
+ rrset = &rrsets[i];
+ knot_node_add_rrset(tmp, rrset, 0);
+ }
+
+// const skip_node_t *node = skip_first(tmp->rrsets);
+
+// int last = *((uint16_t *)node->key);
+
+// /* TODO there is now an API function knot_node_rrsets ... */
+
+// /* Iterates through skip list and checks, whether it is sorted. */
+
+// while ((node = skip_next(node)) != NULL) {
+// if (last > *((uint16_t *)node->key)) {
+// errors++;
+// diag("RRset sorting error");
+// }
+// last = *((uint16_t *)node->key);
+// }
+
+ knot_node_free(&tmp, 0, 0);
+ return (errors == 0);
+}
+
+static int test_node_delete()
+{
+ int errors = 0;
+
+ knot_node_t *tmp_node;
+
+ for (int i = 0; i < TEST_NODES; i++) {
+ tmp_node = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+
+ knot_node_free(&tmp_node, 0, 0);
+
+ errors += (tmp_node != NULL);
+ }
+
+ return (errors == 0);
+}
+
+static int test_node_set_parent()
+{
+ knot_node_t *tmp_parent = knot_node_new(NULL, NULL, 0);
+ int errors = 0;
+
+ knot_node_t *tmp_node;
+
+ for (int i = 0; i < TEST_NODES; i++) {
+ tmp_node = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+
+ knot_node_set_parent(tmp_node, tmp_parent);
+
+ if (tmp_node->parent != tmp_node->parent) {
+ diag("Parent node is wrongly set.");
+ errors++;
+ }
+ knot_node_free(&tmp_node, 0, 0);
+ }
+ knot_node_free(&tmp_parent, 0, 0);
+ return (errors == 0);
+}
+
+static int test_node_free_rrsets()
+{
+ int errors = 0;
+
+ knot_node_t *tmp_node;
+
+ for (int i = 0; i < TEST_NODES; i++) {
+ tmp_node = knot_node_new(&test_nodes[i].owner,
+ test_nodes[i].parent, 0);
+
+ knot_node_free_rrsets(tmp_node, 0);
+
+// errors += (tmp_node->rrsets != NULL);
+
+ knot_node_free(&tmp_node, 0, 0);
+ }
+ return (errors == 0);
+}
+
+static const int KNOT_NODE_TEST_COUNT = 8;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_node_tests_count(int argc, char *argv[])
+{
+ return KNOT_NODE_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_node_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_final = 1;
+
+ res = test_node_create();
+ ok(res, "node: create");
+ res_final *= res;
+
+ skip(!res, 6)
+
+ ok((res = test_node_add_rrset()), "node: add");
+ res_final *= res;
+
+ ok((res = test_node_get_rrset()), "node: get");
+ res_final *= res;
+
+ ok((res = test_node_get_parent()), "node: get parent");
+ res_final *= res;
+
+ ok((res = test_node_set_parent()), "node: set parent");
+ res_final *= res;
+
+ ok((res = test_node_sorting()), "node: sort");
+ res_final *= res;
+
+ ok((res = test_node_free_rrsets()), "node: free rrsets");
+ res_final *= res;
+
+ endskip;
+
+ ok((res = test_node_delete()), "node: delete");
+ //res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/node_tests.h b/src/tests/libknot/libknot/node_tests.h
new file mode 100644
index 0000000..a90179f
--- /dev/null
+++ b/src/tests/libknot/libknot/node_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_NODE_TESTS_H_
+#define _KNOTD_NODE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api node_tests_api;
+
+#endif /* _KNOTD_NODE_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/nsec3_tests.c b/src/tests/libknot/libknot/nsec3_tests.c
new file mode 100644
index 0000000..7b95549
--- /dev/null
+++ b/src/tests/libknot/libknot/nsec3_tests.c
@@ -0,0 +1,252 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+
+#include "libknot/common.h"
+#include "libknot/util/error.h"
+#include "libknot/nsec3.h"
+#include "libknot/util/utils.h"
+#include "common/base32hex.h"
+#include "nsec3_tests.h"
+
+#ifdef TEST_WITH_LDNS
+#include "ldns/ldns.h"
+#endif
+
+static int knot_nsec3_tests_count(int argc, char *argv[]);
+static int knot_nsec3_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api nsec3_tests_api = {
+ "NSEC3", //! Unit name
+ &knot_nsec3_tests_count, //! Count scheduled tests
+ &knot_nsec3_tests_run //! Run scheduled tests
+};
+
+extern int compare_wires_simple(uint8_t *w1, uint8_t *w2, uint count);
+
+static int test_nsec3_params_from_wire()
+{
+ /* Create sample NSEC3PARAM rdata */
+ knot_rdata_item_t items[4];
+ knot_rdata_t *rdata = knot_rdata_new();
+ rdata->items = items;
+ rdata->count = 4;
+ knot_rdata_item_set_raw_data(rdata, 0, (uint16_t *)"\x1\x0\x1");
+ knot_rdata_item_set_raw_data(rdata, 1, (uint16_t *)"\x1\x0\x0");
+ knot_rdata_item_set_raw_data(rdata, 2, (uint16_t *)"\x2\x0\x0\x64");
+ knot_rdata_item_set_raw_data(rdata, 3,
+ (uint16_t *)"\xF\x0\xE\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF");
+
+ knot_rrset_t *rrset =
+ knot_rrset_new(knot_dname_new_from_str("cz.",
+ strlen("cz."), NULL),
+ KNOT_RRTYPE_NSEC3PARAM,
+ KNOT_CLASS_IN,
+ 3600);
+ assert(rrset);
+ int ret = knot_rrset_add_rdata(rrset, rdata);
+ assert(ret == KNOT_EOK);
+
+ knot_nsec3_params_t nsec3_test_params;
+
+ int errors = 0;
+ int lived = 0;
+ lives_ok({
+ /* Create special variable for this block. */
+ if (knot_nsec3_params_from_wire(NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_nsec3_params_from_wire(&nsec3_test_params, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_nsec3_params_from_wire(NULL, rrset) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+
+ }, "nsec3 params from wire NULL tests");
+ errors += lived != 1;
+
+ if (knot_nsec3_params_from_wire(&nsec3_test_params,
+ rrset) != KNOT_EOK) {
+ diag("Could not convert nsec3 params to wire!");
+ return 0;
+ }
+
+ if (nsec3_test_params.algorithm != 1) {
+ diag("Algorithm error");
+ errors++;
+ }
+
+ if (nsec3_test_params.flags != 0) {
+ diag("Flags error %d", nsec3_test_params.flags);
+ errors++;
+ }
+
+ if (nsec3_test_params.iterations != 100) {
+ diag("Iterations error %d", nsec3_test_params.iterations);
+ errors++;
+ }
+ printf("salt length: %d\n", nsec3_test_params.salt_length);
+
+ if (nsec3_test_params.salt_length != 14) {
+ diag("Salt length error %d", nsec3_test_params.salt_length);
+ return 0;
+ }
+
+ if (compare_wires_simple((uint8_t *)nsec3_test_params.salt,
+ (uint8_t *)"\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF\xF",
+ 14) != 0) {
+ diag("Salt wire error");
+ errors++;
+ }
+
+ knot_rrset_free(&rrset);
+ return (errors == 0);
+}
+
+static int test_nsec3_sha1()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_nsec3_params_t nsec3_test_params;
+
+ lives_ok({
+ if (knot_nsec3_sha1(NULL, NULL, 1, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_nsec3_sha1(&nsec3_test_params,
+ NULL, 1, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ uint8_t data[20];
+ lived = 1;
+ lived = 0;
+ if (knot_nsec3_sha1(&nsec3_test_params,
+ data, 20, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ uint8_t *digest = NULL;
+ lived = 1;
+ lived = 0;
+ if (knot_nsec3_sha1(&nsec3_test_params,
+ data, 20, &digest, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+// size_t size = 0;
+// lived = 1;
+// lived = 0;
+// if (knot_nsec3_sha1(&nsec3_test_params,
+// data, 20, &digest, &size) !=
+// KNOT_EBADARG) {
+// errors++;
+// }
+ lived = 1;
+ }, "NSEC3: nsec3 sha1 NULL tests");
+ if (errors) {
+ diag("Does not return KNOT_EBADARG after "
+ "execution with wrong arguments!");
+ }
+
+ errors += lived != 1;
+
+ uint8_t *digest = NULL;
+ size_t digest_size = 0;
+ if (knot_nsec3_sha1(&nsec3_test_params,
+ (uint8_t *)"\2ns\3nic\2cz",
+ strlen("\2ns\3nic\2cz"), &digest,
+ &digest_size) != KNOT_EOK) {
+ diag("Could not hash name!");
+ return 0;
+ }
+
+#ifdef TEST_WITH_LDNS
+ ldns_rdf *name = ldns_dname_new_frm_str("ns.nic.cz.");
+ assert(name);
+ ldns_rdf *hashed_name = ldns_nsec3_hash_name(name,
+ nsec3_test_params.algorithm,
+ nsec3_test_params.iterations,
+ nsec3_test_params.salt_length,
+ nsec3_test_params.salt);
+ assert(hashed_name);
+// knot_dname_t *dname_from_ldns =
+// knot_dname_new_from_wire(ldns_rdf_data(hashed_name),
+// ldns_rdf_size(hashed_name),
+// NULL);
+
+ char *name_b32 = NULL;
+ size_t size_b32 = base32hex_encode_alloc((char *)digest, digest_size,
+ &name_b32);
+
+// hex_print(name_b32, size_b32);
+// hex_print(ldns_rdf_data(hashed_name), ldns_rdf_size(hashed_name));
+ if (ldns_rdf_size(hashed_name) != size_b32) {
+ diag("Wrong hashed name length! Should be: %d is: %d",
+ ldns_rdf_size(hashed_name), size_b32);
+ return 0;
+ }
+
+ if (compare_wires_simple(ldns_rdf_data(hashed_name),
+ (uint8_t *)name_b32, size_b32) != 0) {
+ diag("Wrong hashed name wire!");
+ errors++;
+ }
+#endif
+
+#ifndef TEST_WITH_LDNS
+ diag("Warning: without ldns this test is only partial!");
+#endif
+ return (errors == 0);
+}
+
+static const int KNOT_NSEC3_TESTS_COUNT = 2;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_nsec3_tests_count(int argc, char *argv[])
+{
+ return KNOT_NSEC3_TESTS_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_nsec3_tests_run(int argc, char *argv[])
+{
+ ok(test_nsec3_params_from_wire(), "nsec3: params from wire");
+ ok(test_nsec3_sha1(), "nsec3: sha1");
+ return 1;
+}
diff --git a/src/tests/libknot/libknot/nsec3_tests.h b/src/tests/libknot/libknot/nsec3_tests.h
new file mode 100644
index 0000000..10e7ed9
--- /dev/null
+++ b/src/tests/libknot/libknot/nsec3_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_NSEC3_TESTS_H_
+#define _KNOTD_NSEC3_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api nsec3_tests_api;
+
+#endif /* _KNOTD_NSEC3_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/packet_tests.c b/src/tests/libknot/libknot/packet_tests.c
new file mode 100644
index 0000000..185504f
--- /dev/null
+++ b/src/tests/libknot/libknot/packet_tests.c
@@ -0,0 +1,430 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "packet_tests.h"
+#include "libknot/util/error.h"
+#include "libknot/packet/packet.h"
+#include "libknot/util/wire.h"
+/* *test_t structures */
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+
+static int packet_tests_count(int argc, char *argv[]);
+static int packet_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api packet_tests_api = {
+ "packet", //! Unit name
+ &packet_tests_count, //! Count scheduled tests
+ &packet_tests_run //! Run scheduled tests
+};
+
+static int test_packet_new()
+{
+ int errors = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (packet == NULL) {
+ diag("Could not create packet using prealloc_node constant!");
+ errors++;
+ }
+ knot_packet_free(&packet);
+
+ packet = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ if (packet == NULL) {
+ diag("Could not create packet using prealloc_query constant!");
+ errors++;
+ }
+ knot_packet_free(&packet);
+
+ packet = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ if (packet == NULL) {
+ diag("Could not create packet using prealloc_resp constant!");
+ errors++;
+ }
+ knot_packet_free(&packet);
+
+ /*!< \todo Should it create packet using any size? */
+
+ return (errors == 0);
+}
+
+static int test_packet_parse_from_wire()
+{
+ int errors = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+
+ int tmp = 0;
+ lives_ok({
+ if (knot_packet_parse_from_wire(NULL, NULL, 0, 0) !=
+ KNOT_EBADARG) {
+ diag("Trying to parse NULL packet with NULL wire "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ tmp = 1;
+ tmp = 0;
+ if (knot_packet_parse_from_wire(packet, NULL, 0, 0) !=
+ KNOT_EBADARG) {
+ diag("Trying to parse with NULL wire "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ tmp = 1;
+ tmp = 0;
+ if (knot_packet_parse_from_wire(packet, (uint8_t *)0xbeef,
+ 0, 0) !=
+ KNOT_EFEWDATA) {
+ diag("Trying to parse 0 lengt"
+ "did not return KNOT_EOK!");
+ errors++;
+ }
+ tmp = 1;
+ }, "packet: parse from wire NULL tests.");
+ errors += tmp != 1;
+
+ knot_packet_free(&packet);
+
+ return (errors == 0);
+}
+
+static int test_packet_parse_next_rr_answer()
+{
+ int errors = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ int tmp = 0;
+ lives_ok({
+ int ret = 0;
+ if (knot_packet_parse_next_rr_answer(NULL, NULL) !=
+ KNOT_EBADARG) {
+ diag("Trying to parse next RR answer with "
+ "NULL packet with and NULL RRSet "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ tmp = 1;
+ tmp = 0;
+ if ((ret = knot_packet_parse_next_rr_answer(packet,
+ NULL)) !=
+ KNOT_EBADARG) {
+ diag("Trying to parse next RR with NULL RRSet pointer "
+ "did not return KNOT_EBADARG! Got %d.",
+ ret);
+ errors++;
+ }
+ tmp = 1;
+// knot_rrset_t *rrset = (knot_rrset_t *)0xaaaa;
+// tmp = 0;
+// if (knot_packet_parse_next_rr_answer(packet,
+// &rrset) !=
+// KNOT_EBADARG) {
+// diag("Trying to parse next RR answer with rrset pointer"
+// " not pointing to NULL did not "
+// "return KNOT_EBADARG!");
+// errors++;
+// }
+// tmp = 1;
+ }, "packet: parse next rr answer NULL tests.");
+ errors += tmp != 1;
+
+ knot_packet_free(&packet);
+
+ return (errors == 0);
+}
+
+static int test_packet_parse_rest()
+{
+ int res = 0;
+ lives_ok({res = knot_packet_parse_rest(NULL);},
+ "packet: parse rest NULL test");
+
+ if (res != KNOT_EBADARG) {
+ diag("parse rest NULL did not return KNOT_EBADARG.\n");
+ return 1;
+ }
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ assert(packet);
+
+ todo();
+ lives_ok({res = knot_packet_parse_rest(packet);},
+ "packet: parser rest empty packet");
+ endtodo;
+
+ knot_packet_free(&packet);
+
+ return 1;
+}
+
+
+static int test_packet_set_max_size()
+{
+ int errors = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ assert(packet);
+
+ int lived = 0;
+
+ lives_ok({
+ lived = 0;
+ if (knot_packet_set_max_size(NULL, 1) != KNOT_EBADARG) {
+ diag("Calling packet_set_max() with NULL packet "
+ "did not return KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+ }, "packet: set max size NULL test");
+
+ errors += lived != 1;
+
+ if (knot_packet_set_max_size(packet, 0) != KNOT_EBADARG) {
+ diag("Calling packet_set_max() with size eqeal to 0 did not "
+ "return KNOT_EBADARG");
+ errors++;
+ }
+
+ if (knot_packet_set_max_size(packet, 10) != KNOT_EOK) {
+ diag("Calling packet_set_max() with valid arguments did not "
+ "return KNOT_EOK");
+ errors++;
+ }
+
+ knot_packet_free(&packet);
+
+ return (errors == 0);
+}
+
+static int test_packet_add_tmp_rrset()
+{
+ int errors = 0;
+ int lived = 0;
+
+ /* knot_packet_add_tmp_rrset only works with pointers. */
+ knot_rrset_t *rrset = (knot_rrset_t *)0xabcdef;
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ lives_ok({
+ if (knot_packet_add_tmp_rrset(NULL, rrset) !=
+ KNOT_EBADARG) {
+ diag("Trying to add to NULL packet did not return "
+ "KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_packet_add_tmp_rrset(packet, NULL) !=
+ KNOT_EBADARG) {
+ diag("Trying to add NULL rrset did not return "
+ "KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_packet_add_tmp_rrset(NULL, NULL) !=
+ KNOT_EBADARG) {
+ diag("Trying to add NULL rrset to NULL packet "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "packet: add tmp rrset NULL test");
+ errors += lived != 1;
+
+ if (knot_packet_add_tmp_rrset(packet, rrset) != KNOT_EOK) {
+ diag("Could not add valid RRSet to packet!");
+ errors++;
+ }
+
+ /* Not freeing because RRSet is fake. */
+// knot_packet_free(&packet);
+
+ free(packet->wireformat);
+ free(packet);
+
+ return (errors == 0);
+}
+
+//static int test_packet_contains()
+//{
+// int errors = 0;
+// int lives = 0;
+
+// knot_packet_t *packet =
+// knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+// assert(packet);
+
+// lives_ok({
+// if (knot_packet_contains(packet, NULL,
+// KNOT_RRSET_COMPARE_PTR) !=
+// KNOT_EBADARG{
+// diag();
+// }
+// }, "packet: contains NULL tests);
+
+// knot_packet_contains()
+
+//}
+
+static int test_packet_header_to_wire()
+{
+ int errors = 0;
+ int lived = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+ size_t size;
+
+ lives_ok({
+ knot_packet_header_to_wire(NULL, NULL, NULL);
+ lived = 1;
+ lived = 0;
+ knot_packet_header_to_wire(&packet->header, NULL, &size);
+ lived = 1;
+ }, "packet: header to wire NULL tests");
+ errors += lived != 1;
+
+ knot_packet_free(&packet);
+ return (errors == 0);
+}
+
+static int test_packet_question_to_wire()
+{
+ int errors = 0 ;
+ int lived = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ lives_ok({
+ if (knot_packet_question_to_wire(NULL) != KNOT_EBADARG) {
+ diag("Calling packet_question_to_wire with "
+ "NULL pointer did not result to KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "packet: question to wire NULL tests");
+ errors += lived != 1;
+
+ packet->size = KNOT_WIRE_HEADER_SIZE + 1;
+ if (knot_packet_question_to_wire(packet) != KNOT_ERROR) {
+ diag("Calling packet_question_to_wire with oversized packet "
+ "did not return KNOT_ERROR!");
+ errors++;
+ }
+
+ knot_packet_free(&packet);
+ return (errors == 0);
+}
+
+static int test_packet_edns_to_wire()
+{
+ int errors = 0 ;
+ int lived = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ lives_ok({
+ knot_packet_edns_to_wire(NULL);
+ lived = 1;
+ }, "packet: question to wire NULL tests");
+ errors += lived != 1;
+
+ knot_packet_free(&packet);
+ return (errors == 0);
+}
+
+static int test_packet_to_wire()
+{
+ int errors = 0 ;
+ int lived = 0;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ lives_ok({
+ if (knot_packet_to_wire(NULL, NULL, NULL) != KNOT_EBADARG) {
+ diag("Calling packet_to_wire with "
+ "NULL pointers did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ size_t size;
+ lived = 0;
+ if (knot_packet_to_wire(packet, NULL, &size) !=
+ KNOT_EBADARG) {
+ diag("Calling packet_to_wire with "
+ "NULL wire did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ uint8_t *wire = (uint8_t *)0xabcdef;
+ lived = 0;
+ if (knot_packet_to_wire(packet, &wire, &size) !=
+ KNOT_EBADARG) {
+ diag("Calling packet_to_wire with "
+ "wire not pointing to NULL did not return"
+ " KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "packet: to wire NULL tests");
+ errors += lived != 1;
+
+ knot_packet_free(&packet);
+ return (errors == 0);
+}
+
+static const uint KNOT_PACKET_TEST_COUNT = 21;
+
+static int packet_tests_count(int argc, char *argv[])
+{
+ return KNOT_PACKET_TEST_COUNT;
+}
+
+static int packet_tests_run(int argc, char *argv[])
+{
+ int res = 0;
+ ok(res = test_packet_new(), "packet: new");
+ skip(!res, 20);
+ ok(test_packet_parse_rest(), "packet: parse rest");
+ ok(test_packet_parse_from_wire(), "packet: parse from wire");
+ ok(test_packet_parse_next_rr_answer(), "packet: parse next rr answer");
+ ok(test_packet_set_max_size(), "packet: set max size");
+ ok(test_packet_add_tmp_rrset(), "packet: add tmp rrset");
+ ok(test_packet_header_to_wire(), "packet: header to wire");
+ ok(test_packet_question_to_wire(), "packet: header to wire");
+ ok(test_packet_edns_to_wire(), "packet: header to wire");
+ ok(test_packet_to_wire(), "packet: to wire");
+// ok(res = test_packet_contains(), "Packet: contains");
+ endskip;
+ return 1;
+}
diff --git a/src/tests/libknot/libknot/packet_tests.h b/src/tests/libknot/libknot/packet_tests.h
new file mode 100644
index 0000000..5a8ce03
--- /dev/null
+++ b/src/tests/libknot/libknot/packet_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_PACKET_TESTS_H_
+#define _KNOTD_PACKET_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api packet_tests_api;
+
+#endif /* _KNOTD_PACKET_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/query_tests.c b/src/tests/libknot/libknot/query_tests.c
new file mode 100644
index 0000000..1e4e081
--- /dev/null
+++ b/src/tests/libknot/libknot/query_tests.c
@@ -0,0 +1,160 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "packet_tests.h"
+#include "libknot/util/error.h"
+#include "libknot/packet/packet.h"
+#include "libknot/util/wire.h"
+#include "libknot/packet/query.h"
+/* *test_t structures */
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+
+static int query_tests_count(int argc, char *argv[]);
+static int query_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api query_tests_api = {
+ "query", //! Unit name
+ &query_tests_count, //! Count scheduled tests
+ &query_tests_run //! Run scheduled tests
+};
+
+static const uint KNOT_QUERY_TEST_COUNT = 1;
+
+static int query_tests_count(int argc, char *argv[])
+{
+ return KNOT_QUERY_TEST_COUNT;
+}
+
+static int test_query_init()
+{
+ int errors = 0;
+ int lived = 0;
+ knot_packet_t *query =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ assert(query);
+ lives_ok({
+ if (knot_query_init(NULL) != KNOT_EBADARG) {
+ diag("Calling query_init with NULL query did "
+ "not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "query: init NULL tests");
+ errors += lived != 1;
+
+ assert(knot_packet_set_max_size(query, 1024 * 10) == KNOT_EOK);
+ if (knot_query_init(query) != KNOT_EOK) {
+ diag("Calling query_init with valid query did not return "
+ "KNOT_EOK!");
+ errors++;
+ }
+
+ if (!knot_packet_is_query(query)) {
+ diag("QR flag was not set!");
+ errors++;
+ }
+
+ return (errors == 0);
+}
+
+static int test_query_set_question()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_packet_t *query =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ assert(query);
+ assert(knot_packet_set_max_size(query, 1024 * 10) == KNOT_EOK);
+ knot_query_init(query);
+
+ knot_rrset_t *rrset =
+ knot_rrset_new(knot_dname_new_from_str("a.ns.cz.",
+ strlen("a.ns.cz."),
+ NULL),
+ KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ assert(rrset);
+
+ knot_question_t *question = malloc(sizeof(knot_question_t));
+ assert(question);
+ question->qname = rrset->owner;
+ question->qtype = rrset->type;
+ question->qclass = rrset->rclass;
+
+ lives_ok({
+ if (knot_query_set_question(NULL, NULL) != KNOT_EBADARG) {
+ diag("Calling query_set_question with NULL");
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_query_set_question(query, NULL) != KNOT_EBADARG) {
+ diag("Calling query_set_question with NULL");
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_query_set_question(NULL, question) != KNOT_EBADARG) {
+ diag("Calling query_set_question with NULL");
+ errors++;
+ }
+ lived = 1;
+ }, "query: set question NULL tests");
+ errors += lived != 1;
+
+ if (knot_query_set_question(query, question) != KNOT_EOK) {
+ diag("Calling query_set_question with valid arguments ");
+ errors++;
+ }
+
+ if (query->question.qname != rrset->owner) {
+ diag("Qname was not set right!");
+ errors++;
+ }
+
+ if (query->question.qtype != rrset->type) {
+ diag("Qtype was not set right!");
+ errors++;
+ }
+
+ if (query->question.qclass != rrset->rclass) {
+ diag("Qclass was not set right!");
+ errors++;
+ }
+
+ if (query->header.qdcount != 1) {
+ diag("Qdcount was not set right!");
+ errors++;
+ }
+
+ knot_packet_free(&query);
+ knot_rrset_deep_free(&rrset, 1, 0, 0);
+
+ return (errors == 0);
+}
+
+static int query_tests_run(int argc, char *argv[])
+{
+ ok(test_query_init(), "query: init");
+ ok(test_query_set_question(), "query: set question");
+ return 1;
+}
diff --git a/src/tests/libknot/libknot/query_tests.h b/src/tests/libknot/libknot/query_tests.h
new file mode 100644
index 0000000..037ecab
--- /dev/null
+++ b/src/tests/libknot/libknot/query_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_QUERY_TESTS_H_
+#define _KNOTD_QUERY_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api query_tests_api;
+
+#endif /* _KNOTD_QUERY_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/rdata_tests.c b/src/tests/libknot/libknot/rdata_tests.c
new file mode 100644
index 0000000..561686a
--- /dev/null
+++ b/src/tests/libknot/libknot/rdata_tests.c
@@ -0,0 +1,954 @@
+/* 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 <stdlib.h>
+#include <assert.h>
+
+#include "tests/libknot/libknot/rdata_tests.h"
+#include "libknot/common.h"
+#include "libknot/rdata.h"
+#include "libknot/dname.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/util/utils.h"
+#include "libknot/util/error.h"
+
+enum { TEST_DOMAINS_OK = 8 };
+
+struct test_domain {
+ char *str;
+ char *wire;
+ uint size;
+ char *labels;
+ short label_count;
+};
+
+/*! \warning Do not change the order in those, if you want to test some other
+ * feature with new dname, add it at the end of these arrays.
+ */
+static const struct test_domain
+ test_domains_ok[TEST_DOMAINS_OK] = {
+ { "abc.test.domain.com.", "\3abc\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "xyz.test.domain.com.", "\3xyz\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "test.domain.com.", "\4test\6domain\3com", 17,
+ "\x0\x5\xC", 3 },
+ { ".", "\0", 1,
+ "", 0 },
+ { "foo.bar.net.", "\3foo\3bar\3net", 13,
+ "\x0\x4\x8", 3},
+ { "bar.net.", "\3bar\3net", 9,
+ "\x0\x4", 2}
+};
+
+
+static int knot_rdata_tests_count(int argc, char *argv[]);
+static int knot_rdata_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api rdata_tests_api = {
+ "DNS library - rdata", //! Unit name
+ &knot_rdata_tests_count, //! Count scheduled tests
+ &knot_rdata_tests_run //! Run scheduled tests
+};
+
+/*----------------------------------------------------------------------------*/
+/*
+ * Unit implementation.
+ */
+
+static uint16_t *RDATA_ITEM_PTR = (uint16_t *)0xDEADBEEF;
+
+enum { RDATA_ITEMS_COUNT = 7, TEST_RDATA_COUNT = 4 , RDATA_DNAMES_COUNT = 2 };
+
+static knot_dname_t RDATA_DNAMES[RDATA_DNAMES_COUNT] = {
+ {{}, (uint8_t *)"\6abcdef\7example\3com", 20,
+ (uint8_t *)"\x0\x7\xF", 3},
+ {{}, (uint8_t *)"\6abcdef\3foo\3com", 16,
+ (uint8_t *)"\x0\x7\xB", 3}
+};
+
+static knot_rdata_item_t TEST_RDATA_ITEMS[RDATA_ITEMS_COUNT] = {
+ {.dname = (knot_dname_t *)0xF00},
+ {.raw_data = (uint16_t *)"some data"},
+ {.raw_data = (uint16_t *)"other data"},
+ {.raw_data = (uint16_t *)"123456"},
+ {.raw_data = (uint16_t *)"654321"},
+ {.dname = &RDATA_DNAMES[0]},
+ {.dname = &RDATA_DNAMES[1]}
+};
+
+/* \note indices 0 to 3 should not be changed - used in (and only in)
+ * test_rdata_compare() - better than creating new struct just for this
+ */
+static knot_rdata_t test_rdata[TEST_RDATA_COUNT] = {
+ {&TEST_RDATA_ITEMS[3], 1, &test_rdata[1]},
+ {&TEST_RDATA_ITEMS[4], 1, &test_rdata[2]},
+ {&TEST_RDATA_ITEMS[5], 1, &test_rdata[3]},
+ {&TEST_RDATA_ITEMS[6], 1, &test_rdata[4]},
+};
+
+static knot_rdata_t TEST_RDATA = {
+ &TEST_RDATA_ITEMS[0],
+ 3,
+ &TEST_RDATA
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_new().
+ *
+ * Creates new RDATA structure with no items and tests if there really are no
+ * items in it.
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_create()
+{
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (rdata == NULL) {
+ diag("RDATA structure not created!");
+ return 0;
+ }
+
+ if (knot_rdata_item(rdata, 0) != NULL) {
+ diag("Get item returned something else than NULL!");
+ knot_rdata_free(&rdata);
+ return 0;
+ }
+
+ knot_rdata_free(&rdata);
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_free().
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_delete()
+{
+ // how to test this??
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void generate_rdata(uint8_t *data, int size)
+{
+ for (int i = 0; i < size; ++i) {
+ data[i] = rand() % 256;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int fill_rdata(uint8_t *data, int max_size, uint16_t rrtype,
+ knot_rdata_t *rdata)
+{
+ assert(rdata != NULL);
+ assert(data != NULL);
+ assert(max_size > 0);
+
+ uint8_t *pos = data;
+ int used = 0;
+ int wire_size = 0;
+
+ //note("Filling RRType %u", rrtype);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrtype);
+
+ uint item_count = desc->length;
+ knot_rdata_item_t *items =
+ (knot_rdata_item_t *)malloc(item_count
+ * sizeof(knot_rdata_item_t));
+
+ for (int i = 0; i < item_count; ++i) {
+ uint size = 0;
+ int domain = 0;
+ knot_dname_t *dname = NULL;
+ int binary = 0;
+ int stored_size = 0;
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[0].wire,
+ test_domains_ok[0].size, NULL);
+ assert(dname != NULL);
+ /* note("Created domain name: %s",
+ knot_dname_name(dname)); */
+ //note("Domain name ptr: %p", dname);
+ domain = 1;
+ size = knot_dname_size(dname);
+ //note("Size of created domain name: %u", size);
+ assert(size < KNOT_MAX_RDATA_ITEM_SIZE);
+ // store size of the domain name
+ *(pos++) = size;
+ // copy the domain name
+ memcpy(pos, knot_dname_name(dname), size);
+ pos += size;
+ break;
+ default:
+ binary = 1;
+ size = rand() % KNOT_MAX_RDATA_ITEM_SIZE;
+ }
+
+ if (binary) {
+ // Rewrite the actual 2 bytes in the data array
+ // with length.
+ // (this is a bit ugly, but does the work ;-)
+ knot_wire_write_u16(pos, size);
+ //*pos = size;
+ }
+
+ //note("Filling %u bytes", size);
+ used += size;
+ assert(used < max_size);
+
+ if (domain) {
+ items[i].dname = dname;
+ wire_size += knot_dname_size(dname);
+/* note("Saved domain name ptr on index %d: %p",
+ i, items[i].dname); */
+ } else {
+ free(dname);
+// note("Saved raw data ptr on index %d: %p",i, pos);
+ items[i].raw_data = (uint16_t *)pos;
+ pos += size;
+ wire_size += size;
+ if (binary && !stored_size) {
+ wire_size -= 2;
+ }
+ }
+ }
+
+ int res = knot_rdata_set_items(rdata, items, item_count);
+ if (res != 0) {
+ diag("knot_rdata_set_items() returned %d.", res);
+ free(items);
+ return -1;
+ } else {
+ free(items);
+ return wire_size;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if all RDATA items in the given RDATA structure are correct.
+ *
+ * \return Number of errors encountered. Error is either if some RDATA item
+ * is not set (i.e. NULL) or if it has other than the expected value.
+ */
+static int check_rdata(const uint8_t *data, int max_size, uint16_t rrtype,
+ const knot_rdata_t *rdata)
+{
+ assert(rdata != NULL);
+ assert(data != NULL);
+ assert(max_size > 0);
+
+ int errors = 0;
+
+ const uint8_t *pos = data;
+ int used = 0;
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrtype);
+ uint item_count = desc->length;
+ //note("check_rdata(), RRType: %u", rrtype);
+ //note(" item count: %u", item_count);
+
+ for (int i = 0; i < item_count; ++i) {
+ uint size = 0;
+ int domain = 0;
+ int binary = 0;
+
+ //note(" item: %d", i);
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ //note(" domain name");
+ domain = 1;
+ size = knot_dname_size(knot_rdata_item(
+ rdata, i)->dname);
+ break;
+ default:
+ size =
+ knot_wire_read_u16((uint8_t *)
+ (knot_rdata_item(
+ rdata, i)->raw_data));
+ }
+
+ assert(size > 0);
+ //note("Size: %u", size);
+ used += size;
+ assert(used < max_size);
+
+ //note(" item size: %u", size);
+
+ if (domain) {
+ /*note("Domain name ptr: %p",
+ knot_rdata_get_item(rdata, i)->dname);*/
+ // check dname size
+ if (*pos != size) {
+ diag("Domain name stored in %d-th"
+ "RDATA has wrong size: %d"
+ " (should be %d)", size, *pos);
+ ++errors;
+ } else if (strncmp((char *)knot_dname_name(
+ knot_rdata_item(rdata, i)->dname),
+ (char *)(pos + 1), *pos) != 0) {
+ diag("Domain name stored in %d-th"
+ "RDATA item is wrong: %s ("
+ "should be %.*s)", i,
+ knot_dname_name(knot_rdata_item(
+ rdata, i)->dname),
+ *pos, (char *)(pos + 1));
+ ++errors;
+ }
+
+ pos += *pos + 1;
+
+ continue;
+ }
+
+ if (binary &&
+ size !=
+ knot_wire_read_u16(
+ (uint8_t *)(knot_rdata_item(rdata, i)->raw_data))) {
+ diag("Size of stored binary data is wrong:"
+ " %u (should be %u)",
+ knot_rdata_item(rdata, i)->raw_data[0] + 1,
+ size);
+ ++errors;
+ }
+
+ if (strncmp((char *)
+ (&knot_rdata_item(rdata, i)->raw_data[0]),
+ (char *)pos, size) != 0) {
+/* knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrtype); */
+
+ diag("Data stored in %d-th RDATA item are wrong.", i);
+ ++errors;
+ }
+
+ pos += size;
+ }
+
+ return errors;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//static int convert_to_wire(const uint8_t *data, int max_size, uint16_t rrtype,
+// uint8_t *data_wire)
+//{
+// //note("Converting type %u", rrtype);
+
+// int wire_size = 0;
+// const uint8_t *pos = data;
+// uint8_t *pos_wire = data_wire;
+
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(rrtype);
+// uint item_count = desc->length;
+
+// for (int i = 0; i < item_count; ++i) {
+// const uint8_t *from = NULL;
+// uint to_copy = 0;
+
+// switch (desc->wireformat[i]) {
+// case KNOT_RDATA_WF_COMPRESSED_DNAME:
+// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+// case KNOT_RDATA_WF_LITERAL_DNAME:
+// // copy the domain name without its length
+// from = pos + 1;
+// to_copy = *pos;
+// pos += *pos + 1;
+///* note("Domain name in wire format (size %u): %s",
+// to_copy, (char *)from); */
+// break;
+// case KNOT_RDATA_WF_BYTE:
+// //note(" 1byte int");
+// from = pos;
+// to_copy = 1;
+// pos += 1;
+// break;
+// case KNOT_RDATA_WF_SHORT:
+// //note(" 2byte int");
+// from = pos;
+// to_copy = 2;
+// pos += 2;
+// break;
+// case KNOT_RDATA_WF_LONG:
+// //note(" 4byte int");
+// from = pos;
+// to_copy = 4;
+// pos += 4;
+// break;
+// case KNOT_RDATA_WF_A:
+// //note(" A");
+// from = pos;
+// to_copy = 4;
+// pos += 4;
+// break;
+// case KNOT_RDATA_WF_AAAA:
+// //note(" AAAA");
+// from = pos;
+// to_copy = 16;
+// pos += 16;
+// break;
+// case KNOT_RDATA_WF_BINARY:
+// case KNOT_RDATA_WF_APL: // saved as binary
+// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
+// //note(" binary");
+// from = pos + 1;
+// to_copy = *pos;
+// pos += *pos + 1;
+// break;
+// case KNOT_RDATA_WF_TEXT:
+// case KNOT_RDATA_WF_BINARYWITHLENGTH:
+// //note(" text or binary with length (%u)", *pos);
+// to_copy = *pos + 1;
+// from = pos;
+// pos += *pos + 1;
+// break;
+// default:
+// assert(0);
+// }
+
+// //note("Copying %u bytes from %p", to_copy, from);
+
+// assert(from != NULL);
+// assert(to_copy != 0);
+
+// memcpy(pos_wire, from, to_copy);
+// pos_wire += to_copy;
+// wire_size += to_copy;
+
+// assert(wire_size < max_size);
+// }
+
+// return wire_size;
+//}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_set_item().
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_set_item()
+{
+ knot_rdata_t *rdata = knot_rdata_new();
+ knot_rdata_item_t item;
+ item.raw_data = RDATA_ITEM_PTR;
+
+ int ret = knot_rdata_set_item(rdata, 0, item);
+ if (ret == 0) {
+ diag("knot_rdata_set_item() called on empty RDATA"
+ "returned %d instead of error (-1).", ret);
+ knot_rdata_free(&rdata);
+ return 0;
+ }
+
+// uint8_t *data = malloc(sizeof(uint8_t) * KNOT_MAX_RDATA_WIRE_SIZE);
+// assert(data);
+ uint8_t data[KNOT_MAX_RDATA_WIRE_SIZE];
+ generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+
+ // set items through set_items() and then call set_item()
+ uint16_t rrtype = rand() % KNOT_RRTYPE_LAST + 1;
+ if (fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, rrtype, rdata) < 0) {
+ knot_rdata_free(&rdata);
+ diag("Error filling RDATA");
+ return 0;
+ }
+
+ uint8_t pos = rand() % knot_rrtype_descriptor_by_type(rrtype)->length;
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrtype);
+
+ // if the rdata on this position is domain name, free it to avoid leaks
+ if (desc->wireformat[pos] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[pos] == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[pos] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ knot_dname_free(&(rdata->items[pos].dname));
+ }
+
+ ret = knot_rdata_set_item(rdata, pos, item);
+ if (ret != 0) {
+ diag("knot_rdata_set_item() called on filled"
+ " RDATA returned %d instead of 0.", ret);
+ knot_rdata_free(&rdata);
+ return 0;
+ }
+
+ if (knot_rdata_item(rdata, pos)->raw_data != RDATA_ITEM_PTR) {
+ diag("RDATA item on position %d is wrong: %p (should be %p).",
+ pos, knot_rdata_item(rdata, pos)->raw_data,
+ RDATA_ITEM_PTR);
+ knot_rdata_free(&rdata);
+ return 0;
+ }
+
+ for (int x = 0; x < desc->length; x++) {
+ if (x != pos && (
+ desc->wireformat[x] ==
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[x] ==
+ KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[x] ==
+ KNOT_RDATA_WF_LITERAL_DNAME)) {
+ knot_dname_free(&(rdata->items[x].dname));
+ }
+ }
+
+// knot_rdata_free(&rdata);
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_set_items().
+ *
+ * Iterates over the test_rdatas array and for each testing RDATA it creates
+ * the RDATA structure, sets its items (\see set_rdata_all()) and checks if the
+ * items are set properly (\see check_rdata()).
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_set_items()
+{
+ knot_rdata_t *rdata = NULL;
+ knot_rdata_item_t *item = (knot_rdata_item_t *)0xDEADBEEF;
+ int errors = 0;
+
+ // check error return values
+ if (knot_rdata_set_items(rdata, NULL, 0) != KNOT_EBADARG) {
+ diag("Return value of knot_rdata_set_items() "
+ "when rdata == NULL is wrong");
+ return 0;
+ } else {
+ rdata = knot_rdata_new();
+ assert(rdata != NULL);
+
+ if (knot_rdata_set_items(rdata, NULL, 0) != KNOT_EBADARG) {
+ diag("Return value of knot_rdata_set_items()"
+ " when items == NULL is wrong");
+// knot_rdata_free(&rdata);
+ return 0;
+ } else if (knot_rdata_set_items(rdata, item, 0) !=
+ KNOT_EBADARG) {
+ diag("Return value of knot_rdata_set_items()"
+ " when count == 0"
+ "is wrong");
+// knot_rdata_free(&rdata);
+ return 0;
+ }
+// knot_rdata_free(&rdata);
+ }
+
+ // generate some random data
+// uint8_t *data = malloc(sizeof(uint8_t) * KNOT_MAX_RDATA_WIRE_SIZE);
+ uint8_t data [KNOT_MAX_RDATA_WIRE_SIZE];
+ generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+
+ for (int i = 0; i <= KNOT_RRTYPE_LAST; ++i) {
+ rdata = knot_rdata_new();
+
+ if (fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i, rdata)
+ < 0) {
+ ++errors;
+ }
+ errors += check_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i,
+ rdata);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(i);
+
+ for (int x = 0; x < desc->length; x++) {
+ if (desc->wireformat[x] ==
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[x] ==
+ KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[x] ==
+ KNOT_RDATA_WF_LITERAL_DNAME) {
+// printf("freeing %p\n", rdata->items[x].dname);
+ knot_dname_free(&(rdata->items[x].dname));
+ }
+ }
+
+// knot_rdata_free(&rdata);
+ }
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_get_item().
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_get_item()
+{
+ const knot_rdata_t *rdata = &TEST_RDATA;
+
+ if (knot_rdata_item(rdata, TEST_RDATA.count) != NULL) {
+ diag("knot_rdata_get_item() called with"
+ "invalid position did not return NULL");
+ return 0;
+ }
+
+ int errors = 0;
+ if ((knot_rdata_item(rdata, 0)->dname)
+ != TEST_RDATA.items[0].dname) {
+ diag("RDATA item on position 0 is wrong: %p (should be %p)",
+ knot_rdata_item(rdata, 0), TEST_RDATA.items[0]);
+ ++errors;
+ }
+ if ((knot_rdata_item(rdata, 1)->raw_data)
+ != TEST_RDATA.items[1].raw_data) {
+ diag("RDATA item on position 0 is wrong: %p (should be %p)",
+ knot_rdata_item(rdata, 1), TEST_RDATA.items[1]);
+ ++errors;
+ }
+ if ((knot_rdata_item(rdata, 2)->raw_data)
+ != TEST_RDATA.items[2].raw_data) {
+ diag("RDATA item on position 0 is wrong: %p (should be %p)",
+ knot_rdata_item(rdata, 2), TEST_RDATA.items[2]);
+ ++errors;
+ }
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int test_rdata_compare()
+{
+ int errors = 0;
+
+ uint8_t format_rawdata = KNOT_RDATA_WF_BINARY;
+
+ uint8_t format_dname = KNOT_RDATA_WF_LITERAL_DNAME;
+
+ /* 123456 \w 654321 -> result -1 */
+ if (knot_rdata_compare(&test_rdata[0],
+ &test_rdata[1],
+ &format_rawdata) != -1) {
+ diag("RDATA raw data comparison failed 0");
+ errors++;
+ }
+
+ /* 123456 \w 123456 -> result 0 */
+ if (knot_rdata_compare(&test_rdata[0],
+ &test_rdata[0],
+ &format_rawdata) != 0) {
+ diag("RDATA raw data comparison failed 1 ");
+ errors++;
+ }
+
+ /* 123456 \w 654321 -> result 1 */
+ if (knot_rdata_compare(&test_rdata[1],
+ &test_rdata[0],
+ &format_rawdata) != 1) {
+ diag("RDATA raw data comparison failed 2");
+ errors++;
+ }
+
+ /* abcdef.example.com. \w abcdef.foo.com. -> result -1 */
+ int ret = 0;
+ if ((ret = knot_rdata_compare(&test_rdata[2],
+ &test_rdata[3],
+ &format_dname)) >= 0) {
+ diag("RDATA dname comparison failed 3");
+ errors++;
+ }
+
+ /* abcdef.example.com. \w abcdef.example.com. -> result 0 */
+ if (knot_rdata_compare(&test_rdata[2],
+ &test_rdata[2],
+ &format_dname) != 0) {
+ diag("RDATA dname comparison failed 4");
+ errors++;
+ }
+
+ /* abcdef.example.com. \w abcdef.foo.com -> result 1 */
+ if (knot_rdata_compare(&test_rdata[3],
+ &test_rdata[2],
+ &format_dname) != 1) {
+ diag("RDATA dname comparison failed 5");
+ errors++;
+ }
+
+
+
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+//static int test_rdata_wire_size()
+//{
+// knot_rdata_t *rdata;
+// int errors = 0;
+
+// // generate some random data
+// uint8_t data[KNOT_MAX_RDATA_WIRE_SIZE];
+// generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+
+// for (int i = 0; i <= KNOT_RRTYPE_LAST; ++i) {
+// rdata = knot_rdata_new();
+
+// int size =
+// fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i, rdata);
+
+// if (size < 0) {
+// ++errors;
+// } else {
+// int counted_size = knot_rdata_wire_size(rdata,
+// knot_rrtype_descriptor_by_type(i)->wireformat);
+// if (size != counted_size) {
+// diag("Wrong wire size computed (type %d):"
+// " %d (should be %d)",
+// i, counted_size, size);
+// ++errors;
+// }
+// }
+
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(i);
+
+// for (int x = 0; x < desc->length; x++) {
+// if (desc->wireformat[x] ==
+// KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_COMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_LITERAL_DNAME) {
+// knot_dname_free(&(rdata->items[x].dname));
+// }
+// }
+// knot_rdata_free(&rdata);
+// }
+
+// return (errors == 0);
+//}
+
+/*----------------------------------------------------------------------------*/
+
+//static int test_rdata_to_wire()
+//{
+// knot_rdata_t *rdata;
+// int errors = 0;
+
+// // generate some random data
+// uint8_t data[KNOT_MAX_RDATA_WIRE_SIZE];
+// uint8_t data_wire[KNOT_MAX_RDATA_WIRE_SIZE];
+// uint8_t rdata_wire[KNOT_MAX_RDATA_WIRE_SIZE];
+// generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+
+// for (int i = 0; i <= KNOT_RRTYPE_LAST; ++i) {
+// rdata = knot_rdata_new();
+
+// int size =
+// fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i, rdata);
+
+// int size_expected =
+// convert_to_wire(data, KNOT_MAX_RDATA_WIRE_SIZE, i,
+// data_wire);
+
+// if (size < 0) {
+// ++errors;
+// } else {
+// if (size != size_expected) {
+// diag("Wire format size (%u) not"
+// " as expected (%u)",
+// size, size_expected);
+// ++errors;
+// } else {
+// if (knot_rdata_to_wire(rdata,
+// knot_rrtype_descriptor_by_type(i)->
+// wireformat, rdata_wire,
+// KNOT_MAX_RDATA_WIRE_SIZE) != 0) {
+// diag("Error while converting RDATA"
+// " to wire format.");
+// ++errors;
+// } else {
+// if (strncmp((char *)data_wire,
+// (char *)rdata_wire, size)
+// != 0) {
+// diag("RDATA converted to wire"
+// "format does not match"
+// " the expected value");
+// ++errors;
+// }
+// }
+// }
+// }
+
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(i);
+
+// for (int x = 0; x < desc->length; x++) {
+// if (desc->wireformat[x] ==
+// KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_COMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_LITERAL_DNAME) {
+// knot_dname_free(&(rdata->items[x].dname));
+// }
+// }
+// knot_rdata_free(&rdata);
+// }
+
+// return (errors == 0);
+//}
+
+static int test_rdata_free()
+{
+ return 0;
+// knot_rdata_t *tmp_rdata;
+
+// tmp_rdata = knot_rdata_new();
+
+// knot_rdata_free(&tmp_rdata);
+
+// return (tmp_rdata == NULL);
+}
+/* Can't test this with current implementation
+ * would be trying to free pointers on stack */
+static int test_rdata_deep_free()
+{
+ return 0;
+
+/* int errors = 0;
+
+ knot_rdata_t *tmp_rdata;
+
+ uint8_t data[KNOT_MAX_RDATA_WIRE_SIZE];
+
+ for (int i = 0; i <= KNOT_RRTYPE_LAST; i++) {
+ tmp_rdata = knot_rdata_new();
+
+ fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i, tmp_rdata);
+
+ knot_rdata_deep_free(&tmp_rdata, i, 0);
+ errors += (tmp_rdata != NULL);
+ }
+
+ return (errors == 0); */
+}
+
+/*----------------------------------------------------------------------------*/
+
+static const int KNOT_RDATA_TEST_COUNT = 8;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_rdata_tests_count(int argc, char *argv[])
+{
+ return KNOT_RDATA_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_rdata_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_final = 1;
+
+ res = test_rdata_create();
+ ok(res, "rdata: create empty");
+ res_final *= res;
+
+ skip(!res, 6);
+
+ todo();
+
+ ok(res = test_rdata_delete(), "rdata: delete");
+ //res_final *= res;
+
+ endtodo;
+
+ ok(res = test_rdata_get_item(), "rdata: get item");
+ res_final *= res;
+
+ skip(!res, 4)
+
+ ok(res = test_rdata_set_items(), "rdata: set items all at once");
+ res_final *= res;
+
+ skip(!res, 3);
+
+ ok(res = test_rdata_set_item(), "rdata: set items one-by-one");
+ res_final *= res;
+
+ ok(res = test_rdata_compare(), "rdata: compare");
+ res_final *= res;
+
+// ok(res = test_rdata_wire_size(), "rdata: wire size");
+// res_final *= res;
+
+// skip(!res, 1);
+
+// ok(res = test_rdata_to_wire(), "rdata: to wire");
+// res_final *= res;
+
+// endskip; /* test_rdata_wire_size() failed */
+
+ endskip; /* test_rdata_set_items() failed */
+
+ endskip; /* test_rdata_get_item() failed */
+
+ endskip; /* test_rdata_create() failed */
+
+ todo();
+
+ ok(res = test_rdata_deep_free(), "rdata: deep free");
+ res_final *= res;
+
+ ok(res = test_rdata_free(), "rdata: free");
+ res_final *= res;
+
+ endtodo;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/rdata_tests.h b/src/tests/libknot/libknot/rdata_tests.h
new file mode 100644
index 0000000..1f43c91
--- /dev/null
+++ b/src/tests/libknot/libknot/rdata_tests.h
@@ -0,0 +1,52 @@
+/* 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 rdata_tests.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * Contains unit tests for RDATA (knot_rdata_t) and RDATA item
+ * (knot_rdata_item_t) structures.
+ *
+ * Contains tests for:
+ * - creating empty RDATA structure with or without reserved space.
+ * - setting RDATA items one-by-one
+ * - setting RDATA items all at once
+ *
+ * As for now, the tests use several (TEST_RDATAS) RDATA structures, each
+ * with different number of RDATA items (given by test_rdatas). These are all
+ * initialized to pointers derived from RDATA_ITEM_PTR (first is RDATA_ITEM_PTR,
+ * second RDATA_ITEM_PTR + 1, etc.). The functions only test if the pointer
+ * is set properly.
+ *
+ * \todo It may be better to test also some RDATAs with predefined contents,
+ * such as some numbers, some domain name, etc. For this purpose, we'd
+ * need RDATA descriptors (telling the types of each RDATA item within an
+ * RDATA).
+ *
+ * \todo It will be fine to test all possible output values of all functions,
+ * e.g. test whether knot_rdata_get_item() returns NULL when passed an
+ * illegal position, etc.
+ */
+#ifndef _KNOTD_RDATA_TESTS_H_
+#define _KNOTD_RDATA_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api rdata_tests_api;
+
+#endif /* _KNOTD_RDATA_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/response_tests.c b/src/tests/libknot/libknot/response_tests.c
new file mode 100644
index 0000000..93cf2df
--- /dev/null
+++ b/src/tests/libknot/libknot/response_tests.c
@@ -0,0 +1,450 @@
+/* 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 <inttypes.h>
+
+//#define RESP_TEST_DEBUG
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "tests/libknot/libknot/response_tests.h"
+#include "common/lists.h"
+#include "libknot/common.h"
+#include "libknot/util/error.h"
+#include "libknot/packet/response.h"
+#include "libknot/rdata.h"
+#include "libknot/rrset.h"
+#include "libknot/dname.h"
+#include "libknot/util/wire.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/edns.h"
+
+#ifdef TEST_WITH_LDNS
+#include "ldns/ldns.h"
+#endif
+
+static int knot_response_tests_count(int argc, char *argv[]);
+static int knot_response_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api response_tests_api = {
+ "DNS library - response", //! Unit name
+ &knot_response_tests_count, //! Count scheduled tests
+ &knot_response_tests_run //! Run scheduled tests
+};
+
+static int test_response_init()
+{
+ int errors = 0;
+ int lived = 0;
+ lives_ok({
+ if (knot_response_init(NULL) != KNOT_EBADARG) {
+ diag("Calling response_init with NULL packet did "
+ "not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "response: init NULL tests");
+ errors += lived != 1;
+
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ assert(response);
+ response->max_size = KNOT_WIRE_HEADER_SIZE - 1;
+ if (knot_response_init(response) != KNOT_ESPACE) {
+ diag("Calling response_init too small packet did "
+ "not return KNOT_ESPACE!");
+ errors++;
+ }
+
+ return (errors == 0);
+}
+
+static int test_response_init_query()
+{
+ int errors = 0;
+ int lived = 0;
+ lives_ok({
+ if (knot_response_init_from_query(NULL, NULL) !=
+ KNOT_EBADARG) {
+ diag("Calling response_init_query with NULL packet and "
+ "NULL query did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ knot_packet_set_max_size(response,
+ KNOT_PACKET_PREALLOC_RESPONSE);
+ knot_response_init(response);
+ lived = 0;
+ if (knot_response_init_from_query(response, NULL) !=
+ KNOT_EBADARG) {
+ diag("Calling response_init_query with NULL query "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_packet_t *query =
+ knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ if (knot_response_init_from_query(NULL, query) !=
+ KNOT_EBADARG) {
+ diag("Calling response_init_query with NULL response "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ }, "response: init from query NULL tests");
+ errors += lived != 1;
+
+ /* Cannot test the rest of return values, since there is now constant
+ * controlling value that could return KNOT_EDNAMEPTR */
+
+ return (errors == 0);
+}
+
+int compare_wires_simple(uint8_t *wire1, uint8_t *wire2, uint count)
+{
+ int i = 0;
+ while (i < count &&
+ wire1[i] == wire2[i]) {
+ i++;
+ }
+ return (!(count == i));
+}
+
+
+//static int test_response_clear()
+//{
+// int errors = 0;
+// int lived = 0;
+// lives_ok({
+// knot_response_clear(NULL, 1);
+// lived = 1;
+// }, "response: clear NULL tests");
+// errors += lived != 1;
+
+// /*
+// * Create new response, convert to wire, then add something, clear
+// * the response, convert to wire again and compare wires.
+// */
+
+// knot_packet_t *response =
+// knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+// knot_packet_set_max_size(response, KNOT_WIRE_HEADER_SIZE * 100);
+// assert(knot_response_init(response) == KNOT_EOK);
+
+// uint8_t *original_wire = NULL;
+// size_t original_size = 0;
+// assert(knot_packet_to_wire(response, &original_wire,
+// &original_size) ==
+// KNOT_EOK);
+// /* Do something in question section. */
+//// test_dname_t test_dname;
+//// test_dname.str = "ns8.nic.cz.";
+//// knot_dname_t *dname = dname_from_test_dname_str(&test_dname);
+//// assert(dname);
+
+// response->question.qtype = KNOT_RRTYPE_HINFO;
+// response->question.qclass = KNOT_CLASS_CH;
+
+// uint8_t *question_changed_wire = NULL;
+// size_t question_changed_size = 0;
+// assert(knot_packet_to_wire(response,
+// &question_changed_wire,
+// &question_changed_size) ==
+// KNOT_EOK);
+
+// knot_response_set_aa(response);
+// knot_response_set_tc(response);
+// knot_response_set_rcode(response, knot_quick_rand());
+
+// knot_response_clear(response, 0);
+// uint8_t *new_wire = NULL;
+// size_t new_size = 0;
+// assert(knot_packet_to_wire(response, &new_wire, &new_size) ==
+// KNOT_EOK);
+// if (question_changed_size != new_size) {
+// diag("Wrong wire size after calling response_clear! "
+// "got %d should be %d", new_size, question_changed_size);
+// errors++;
+// } else {
+// if (compare_wires_simple(question_changed_wire,
+// new_wire, new_size)) {
+// diag("Wrong wire after calling response_clear! ");
+// errors++;
+// }
+// }
+// free(new_wire);
+
+// new_wire = NULL;
+// new_size = 0;
+
+// /*!< \todo figure out this segfault! */
+
+//// knot_response_clear(response, 1);
+//// assert(knot_packet_to_wire(response, &new_wire, &new_size) ==
+//// KNOT_EOK);
+
+//// if (original_size != new_size) {
+//// diag("Wrong wire size after calling response_clear!");
+//// errors++;
+//// } else {
+//// if (compare_wires_simple(original_wire,
+//// new_wire, new_size)) {
+//// diag("Wrong wire after calling response_clear!");
+//// errors++;
+//// }
+//// }
+
+//// free(new_wire);
+//// free(original_wire);
+//// free(question_changed_wire);
+//// knot_packet_free(&response);
+
+// return (errors == 0);
+//}
+
+static int test_response_add_opt()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_opt_rr_t opt;
+ opt.payload = 512;
+ opt.ext_rcode = 0;
+ opt.version = EDNS_VERSION_0;
+ opt.flags = 0;
+ opt.options = NULL;
+ opt.option_count = 0;
+ opt.options_max = 0;
+ opt.size = 25; // does it matter?
+
+ lives_ok({
+ if (knot_response_add_opt(NULL, NULL, 0) != KNOT_EBADARG) {
+ diag("Calling response add opt with NULL arguments "
+ "did not result to KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ lived = 0;
+ if (knot_response_add_opt(response,
+ NULL, 0) != KNOT_EBADARG) {
+ diag("Calling response add opt with NULL OPT RR "
+ "did not result to KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_response_add_opt(NULL,
+ &opt, 0) != KNOT_EBADARG) {
+ diag("Calling response add opt with NULL response "
+ "did not result to KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+ knot_packet_free(&response);
+ }, "response: add opt NULL tests");
+ errors += lived != 1;
+
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ knot_packet_set_max_size(response, KNOT_PACKET_PREALLOC_RESPONSE * 100);
+ assert(knot_response_init(response) == KNOT_EOK);;
+
+ if (knot_response_add_opt(response, &opt, 0) != KNOT_EOK) {
+ diag("Adding valid OPT RR to response "
+ "did not return KNOT_EOK");
+ errors++;
+ }
+
+ opt.payload = response->max_size + 1;
+ if (knot_response_add_opt(response, &opt, 1) != KNOT_EPAYLOAD) {
+ diag("If OPT RR payload is bigger than response max size "
+ "response_add_opt does not return KNOT_EPAYLOAD!");
+ errors++;
+ }
+
+ opt.payload = 0;
+ if (knot_response_add_opt(response, &opt, 1) != KNOT_EBADARG) {
+ diag("Calling response_add_opt with OPT RR payload set to 0 "
+ "did not return KNOT_EBADARG");
+ }
+
+ knot_packet_free(&response);
+ return (errors == 0);
+}
+
+static int test_response_add_generic(int (*func)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int))
+{
+ int errors = 0;
+ int lived = 0;
+
+ lives_ok({
+ if (func(NULL, NULL, 0, 0, 0) != KNOT_EBADARG) {
+ diag("Calling response add rrset with NULL "
+ "arguments did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ lived = 0;
+ if (func(response, NULL, 0, 0, 0) != KNOT_EBADARG) {
+ diag("Calling response add rrset with NULL rrset "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_dname_t *owner =
+ knot_dname_new_from_str("ns.nic.cz.",
+ strlen("ns.nic.cz."),
+ NULL);
+ assert(owner);
+ knot_rrset_t *rrset =
+ knot_rrset_new(owner, KNOT_RRTYPE_A,
+ KNOT_CLASS_IN, 3600);
+ assert(rrset);
+ lived = 0;
+ if (func(NULL, rrset, 0, 0, 0) != KNOT_EBADARG) {
+ diag("Calling response add rrset with NULL response "
+ "did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_rrset_deep_free(&rrset, 1, 0, 0);
+ knot_packet_free(&response);
+ }, "response: rrset adding NULL tests");
+ errors += lived != 1;
+
+ /*!< \todo Test case when KNOT_ESPACE should be returned. */
+ /*!< \todo Compression and so on - should it be tested here? */
+
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+
+ knot_dname_t *owner =
+ knot_dname_new_from_str("ns12.nic.cz.",
+ strlen("ns12.nic.cz."),
+ NULL);
+ assert(owner);
+ knot_rrset_t *rrset =
+ knot_rrset_new(owner, KNOT_RRTYPE_NS,
+ KNOT_CLASS_IN, 3600);
+ assert(rrset);
+ if (func(response, rrset, 0, 0, 0) != KNOT_EOK) {
+ diag("Adding valid RRSet to response did not result to "
+ "KNOT_EOK");
+ errors++;
+ }
+
+ knot_rrset_deep_free(&rrset, 1, 0, 0);
+ knot_packet_free(&response);
+
+ return (errors == 0);
+}
+
+static void test_response_add_rrset()
+{
+ ok(test_response_add_generic(knot_response_add_rrset_answer),
+ "response: add answer rrset");
+ ok(test_response_add_generic(knot_response_add_rrset_authority),
+ "response: add answer authority");
+ ok(test_response_add_generic(knot_response_add_rrset_additional),
+ "response: add answer additional");
+}
+
+static int test_response_add_nsid()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+
+ uint8_t *nsid = (uint8_t *)"knotDNS";
+ uint16_t nsid_size = strlen((char *)nsid);
+ lives_ok({
+ if (knot_response_add_nsid(NULL,
+ NULL, 1) != KNOT_EBADARG) {
+ diag("Calling response add nsid with NULL arguments "
+ "did not return KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ if (knot_response_add_nsid(NULL, nsid,
+ nsid_size) != KNOT_EBADARG) {
+ diag("Calling response add nsid with NULL response "
+ "did not return KNOT_EBADARG");
+ errors++;
+ }
+ lived = 1;
+// lived = 0;
+// if (knot_response_add_nsid(response, nsid,
+// 0) != KNOT_EBADARG) {
+// diag("Calling response add nsid with zero size "
+// "did not return KNOT_EBADARG");
+// errors++;
+// }
+// lived = 1;
+ }, "response: add nsid NULL tests");
+ errors += lived != 1;
+
+ if (knot_response_add_nsid(response, nsid,
+ nsid_size) != KNOT_EOK) {
+ diag("Adding valid nsid to response did not return KNOT_EOK");
+ errors++;
+ }
+
+ knot_packet_free(&response);
+ return (errors == 0);
+}
+
+static const int KNOT_response_TEST_COUNT = 14;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_response_tests_count(int argc, char *argv[])
+{
+ return KNOT_response_TEST_COUNT;
+}
+
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_response_tests_run(int argc, char *argv[])
+{
+ ok(test_response_init(), "response: init");
+ ok(test_response_init_query(), "response: init from query");
+// ok(test_response_clear(), "response: clear");
+ ok(test_response_add_opt(), "response: add opt");
+ test_response_add_rrset();
+ ok(test_response_add_nsid(), "response: add nsid");
+ return 1;
+}
diff --git a/src/tests/libknot/libknot/response_tests.h b/src/tests/libknot/libknot/response_tests.h
new file mode 100644
index 0000000..c9a117b
--- /dev/null
+++ b/src/tests/libknot/libknot/response_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_response_TESTS_H_
+#define _KNOTD_response_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api response_tests_api;
+
+#endif /* _KNOTD_response_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/rrset_tests.c b/src/tests/libknot/libknot/rrset_tests.c
new file mode 100644
index 0000000..fa75195
--- /dev/null
+++ b/src/tests/libknot/libknot/rrset_tests.c
@@ -0,0 +1,888 @@
+/* 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 "tests/libknot/libknot/rrset_tests.h"
+#include "libknot/common.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/rrset.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/util/utils.h"
+#include "libknot/zone/node.h"
+#include "libknot/util/debug.h"
+
+static int knot_rrset_tests_count(int argc, char *argv[]);
+static int knot_rrset_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api rrset_tests_api = {
+ "DNS library - rrset", //! Unit name
+ &knot_rrset_tests_count, //! Count scheduled tests
+ &knot_rrset_tests_run //! Run scheduled tests
+};
+
+/*----------------------------------------------------------------------------*/
+/*
+ * Unit implementation.
+ */
+
+static knot_node_t *NODE_ADDRESS = (knot_node_t *)0xDEADBEEF;
+
+enum { TEST_RRSETS = 6 , TEST_RRSIGS = 6};
+
+//void *RRSIG_ADDRESS = (void *)0xDEADBEEF;
+//void *RRSIG_FIRST = RRSIG_ADDRESS + 10;
+
+struct test_domain {
+ char *str;
+ char *wire;
+ uint size;
+ char *labels;
+ short label_count;
+};
+
+struct test_rrset {
+ char *owner;
+ uint16_t type;
+ uint16_t rclass;
+ uint32_t ttl;
+ knot_rdata_t *rdata;
+ const knot_rrset_t *rrsigs;
+};
+
+/* this has to changed */
+static const char *signature_strings[TEST_RRSIGS] =
+{"signature 1", "signature 2", "signature 3",
+ "signature 4", "signature 5", "signature 6"};
+
+enum {
+ RR_DNAMES_COUNT = 3,
+ RR_ITEMS_COUNT = 3,
+ RR_RDATA_COUNT = 4,
+};
+
+enum { TEST_DOMAINS_OK = 8 };
+
+static knot_dname_t RR_DNAMES[RR_DNAMES_COUNT] =
+ { {{}, (uint8_t *)"\7example\3com", 13, NULL}, //0's at the end are added
+ {{}, (uint8_t *)"\3ns1\7example\3com", 17, NULL},
+ {{}, (uint8_t *)"\3ns2\7example\3com", 17, NULL} };
+
+/* 192.168.1.1 */
+static uint8_t address[4] = {0xc0, 0xa8, 0x01, 0x01};
+
+static knot_rdata_item_t RR_ITEMS[RR_ITEMS_COUNT] =
+ { {.dname = &RR_DNAMES[1]},
+ {.dname = &RR_DNAMES[2]},
+ {.raw_data = (uint16_t *)address} };
+
+/*! \warning Do not change the order. */
+/* TODO this does not work as expected */
+static knot_rdata_t RR_RDATA[RR_RDATA_COUNT] =
+ { {&RR_ITEMS[0], 1, &RR_RDATA[0]}, /* first ns */
+ {&RR_ITEMS[1], 1, &RR_RDATA[1]}, /* second ns */
+ {&RR_ITEMS[0], 1, &RR_RDATA[3]}, /* both in cyclic list */
+ {&RR_ITEMS[1], 1, &RR_RDATA[2]} };
+
+/*! \warning Do not change the order in those, if you want to test some other
+ * feature with new dname, add it at the end of these arrays.
+ */
+static const struct test_domain
+ test_domains_ok[TEST_DOMAINS_OK] = {
+ { "abc.test.domain.com.", "\3abc\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "xyz.test.domain.com.", "\3xyz\4test\6domain\3com", 21,
+ "\x0\x4\x9\x10", 4 },
+ { "some.test.domain.com.", "\4some\4test\6domain\3com", 22,
+ "\x0\x5\xA\x11", 4 },
+ { "test.domain.com.", "\4test\6domain\3com", 17,
+ "\x0\x5\xC", 3 },
+ { ".", "\0", 1,
+ "", 0 },
+ { "foo.bar.net.", "\3foo\3bar\3net", 13,
+ "\x0\x4\x8", 3},
+ { "bar.net.", "\3bar\3net", 9,
+ "\x0\x4", 2}
+};
+
+static struct test_rrset test_rrsets[TEST_RRSETS] = {
+ { "example.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL },
+ { "example2.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL },
+ { "example3.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL },
+ { "example.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL },
+ { "example.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL },
+ { "example.com.", KNOT_RRTYPE_NS, KNOT_CLASS_IN,
+ 3600, NULL, NULL }
+};
+
+static const struct test_rrset test_rrsigs[TEST_RRSIGS] = {
+ { "example.com.", 46, 1, 3600, NULL },
+ { "example2.com.", 46, 1, 3600, NULL },
+ { "example3.com.", 46, 1, 3600, NULL },
+ { "example4.com.", 46, 1, 3600, NULL },
+ { "example5.com.", 46, 1, 3600, NULL },
+ { "example6.com.", 46, 1, 3600, NULL }
+};
+
+static void generate_rdata(uint8_t *data, int size)
+{
+ for (int i = 0; i < size; ++i) {
+ data[i] = rand() % 256;
+ }
+}
+
+static int fill_rdata_r(uint8_t *data, int max_size, uint16_t rrtype,
+ knot_rdata_t *rdata)
+{
+ assert(rdata != NULL);
+ assert(data != NULL);
+ assert(max_size > 0);
+
+ uint8_t *pos = data;
+ int used = 0;
+ int wire_size = 0;
+
+// note("Filling RRType %u", rrtype);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrtype);
+
+ uint item_count = desc->length;
+ knot_rdata_item_t *items =
+ (knot_rdata_item_t *)malloc(item_count
+ * sizeof(knot_rdata_item_t));
+
+ for (int i = 0; i < item_count; ++i) {
+ uint size = 0;
+ int domain = 0;
+ knot_dname_t *dname = NULL;
+ int binary = 0;
+ int stored_size = 0;
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ dname = knot_dname_new_from_wire(
+ (uint8_t *)test_domains_ok[0].wire,
+ test_domains_ok[0].size, NULL);
+ assert(dname != NULL);
+// note("Created domain name: %s",
+// knot_dname_name(dname));
+// note("Domain name ptr: %p", dname);
+ domain = 1;
+ size = knot_dname_size(dname);
+// note("Size of created domain name: %u", size);
+ assert(size < KNOT_MAX_RDATA_ITEM_SIZE);
+ // store size of the domain name
+ *(pos++) = size;
+ // copy the domain name
+ memcpy(pos, knot_dname_name(dname), size);
+ pos += size;
+ break;
+ default:
+ binary = 1;
+ size = rand() % KNOT_MAX_RDATA_ITEM_SIZE;
+ }
+
+ if (binary) {
+ // Rewrite the actual 2 bytes in the data array
+ // with length.
+ // (this is a bit ugly, but does the work ;-)
+ knot_wire_write_u16(pos, size);
+ //*pos = size;
+ }
+
+ //note("Filling %u bytes", size);
+ used += size;
+ assert(used < max_size);
+
+ if (domain) {
+ items[i].dname = dname;
+ wire_size += knot_dname_size(dname);
+/* note("Saved domain name ptr on index %d: %p",
+ i, items[i].dname); */
+ } else {
+ free(dname);
+// note("Saved raw data ptr on index %d: %p",i, pos);
+ items[i].raw_data = (uint16_t *)pos;
+ pos += size;
+ wire_size += size;
+ if (binary && !stored_size) {
+ wire_size -= 2;
+ }
+ }
+ }
+
+ int res = knot_rdata_set_items(rdata, items, item_count);
+ if (res != 0) {
+ diag("knot_rdata_set_items() returned %d.", res);
+ free(items);
+ return -1;
+ } else {
+ free(items);
+ return wire_size;
+ }
+}
+
+/* fills test_rrsets with random rdata when empty */
+static void create_rdata()
+{
+ knot_rdata_t *r;
+
+ uint8_t *data =
+ malloc(sizeof(uint8_t) * KNOT_MAX_RDATA_WIRE_SIZE);
+
+ assert(data);
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ if (test_rrsets[i].rdata == NULL) {
+ r = knot_rdata_new();
+
+ /* from rdata tests */
+ generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+ if (fill_rdata_r(data, KNOT_MAX_RDATA_WIRE_SIZE,
+ test_rrsets[i].type, r) <= 0) {
+ diag("Error creating rdata!");
+
+ }
+
+ test_rrsets[i].rdata = r;
+ }
+ }
+
+ free(data);
+}
+
+static int check_rrset(const knot_rrset_t *rrset, int i,
+ int check_rdata, int check_items,
+ int check_rrsigs)
+{
+ /* following implementation should be self-explanatory */
+ int errors = 0;
+
+ if (rrset == NULL) {
+ diag("RRSet not created!");
+ return 1;
+ }
+
+ char *owner = knot_dname_to_str(rrset->owner);
+ if (strcmp(owner, test_rrsets[i].owner) != 0) {
+ diag("OWNER domain name wrong: '%s' (should be '%s')",
+ owner, test_rrsets[i].owner);
+ ++errors;
+ }
+ free(owner);
+
+ if (rrset->type != test_rrsets[i].type) {
+ diag("TYPE wrong: %u (should be: %u)", rrset->type,
+ test_rrsets[i].type);
+ ++errors;
+ }
+
+ if (rrset->rclass != test_rrsets[i].rclass) {
+ diag("CLASS wrong: %u (should be: %u)", rrset->rclass,
+ test_rrsets[i].rclass);
+ ++errors;
+ }
+
+ if (rrset->ttl != test_rrsets[i].ttl) {
+ diag("TTL wrong: %u (should be: %u)", rrset->ttl,
+ test_rrsets[i].ttl);
+ ++errors;
+ }
+
+ if (check_rdata) {
+ /* TODO use rdata_compare */
+ knot_rdata_t *rdata = rrset->rdata;
+
+ if (rdata == NULL) {
+ diag("There are no RDATAs in the RRSet");
+ ++errors;
+ }
+
+ if (rdata != NULL) {
+ while (rdata->next != NULL &&
+ rdata->next != rrset->rdata) {
+ rdata = rdata->next;
+ }
+ if (rdata->next == NULL) {
+ diag("The list of RDATAs is not cyclic!");
+ ++errors;
+ } else {
+ assert(rdata->next == rrset->rdata);
+ }
+ }
+ }
+
+ if (check_items) {
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+ if (knot_rdata_compare(rrset->rdata,
+ test_rrsets[i].rdata,
+ desc->wireformat) != 0) {
+ diag("Rdata items do not match.");
+ errors++;
+ }
+ }
+
+ /* TODO this deserves a major improvement!!! */
+
+ /*
+ * Will work only with null terminated strings,
+ * consider changing to more versatile implementation
+ */
+
+ /* How about, once it's tested, using rdata_compare */
+
+ if (check_rrsigs) {
+
+ const knot_rrset_t *rrsigs;
+
+ rrsigs = knot_rrset_rrsigs(rrset);
+ if (strcmp((const char *)rrsigs->rdata->items[0].raw_data,
+ signature_strings[i])) {
+ diag("Signatures are not equal"
+ "to those set when creating."
+ "Comparing %s with %s",
+ rrsigs->rdata->items[0].raw_data,
+ signature_strings[i]);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_create()
+{
+ int errors = 0;
+
+ for (int i = 0; i < TEST_RRSETS; ++i) {
+ knot_dname_t *owner = knot_dname_new_from_str(
+ test_rrsets[i].owner,
+ strlen(test_rrsets[i].owner),
+ NODE_ADDRESS);
+ if (owner == NULL) {
+ diag("Error creating owner domain name!");
+ return 0;
+ }
+ knot_rrset_t *rrset = knot_rrset_new(owner,
+ test_rrsets[i].type,
+ test_rrsets[i].rclass,
+ test_rrsets[i].ttl);
+
+ errors += check_rrset(rrset, i, 0, 0, 0);
+
+ knot_rrset_free(&rrset);
+ knot_dname_free(&owner);
+ }
+
+ //diag("Total errors: %d", errors);
+
+ return (errors == 0);
+}
+
+/* Not implemented - no way how to test unfreed memory from here (yet) */
+static int test_rrset_delete()
+{
+ return 0;
+}
+
+static int test_rrset_add_rdata()
+{
+ /* rdata add */
+ int errors = 0;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ knot_dname_t *owner = knot_dname_new_from_str(
+ test_rrsets[i].owner,
+ strlen(test_rrsets[i].owner),
+ NODE_ADDRESS);
+ if (owner == NULL) {
+ diag("Error creating owner domain name!");
+ return 0;
+ }
+
+ knot_rrset_t *rrset = knot_rrset_new(owner,
+ test_rrsets[i].type,
+ test_rrsets[i].rclass,
+ test_rrsets[i].ttl);
+
+ knot_rrset_add_rdata(rrset, test_rrsets[i].rdata);
+
+ errors += check_rrset(rrset, i, 1, 0, 0);
+
+ knot_rrset_free(&rrset);
+ knot_dname_free(&owner);
+ }
+
+ /* test whether adding works properly = keeps order of added elements */
+
+ /*
+ * Beware, this is dependent on the internal structure of rrset and
+ * may change.
+ */
+
+ knot_rrset_t *rrset = knot_rrset_new(NULL, 0, 0, 0);
+
+ knot_rdata_t *r;
+
+ knot_rdata_item_t *item;
+
+ static const char *test_strings[10] =
+ { "-2", "9", "2", "10", "1", "5", "8", "4", "6", "7" };
+
+ /* add items */
+
+ for (int i = 0; i < 10; i++) {
+ r = knot_rdata_new();
+ item = malloc(sizeof(knot_rdata_item_t));
+ item->raw_data = (uint16_t *)test_strings[i];
+ //following statement creates a copy
+ knot_rdata_set_items(r, item, 1);
+ knot_rrset_add_rdata(rrset, r);
+ free(item);
+ }
+
+ knot_rdata_t *tmp = rrset->rdata;
+
+ /* check if order has been kept */
+
+ int i = 0;
+ while (tmp->next != rrset->rdata && !errors) {
+ if (strcmp(test_strings[i], (char *)tmp->items[0].raw_data)) {
+ diag("Adding RDATA error!, is %s should be %s",
+ tmp->items[0].raw_data, test_strings[i]);
+ errors++;
+ }
+ i++;
+ tmp = tmp->next;
+
+ }
+
+ tmp = rrset->rdata;
+
+ knot_rdata_t *next;
+
+ while (tmp->next != rrset->rdata) {
+ next = tmp->next;
+ knot_rdata_free(&tmp);
+ tmp = next;
+ }
+
+ knot_rdata_free(&tmp);
+
+ knot_rrset_free(&rrset);
+
+ return (errors == 0);
+}
+
+static int test_rrset_rrsigs()
+{
+ int errors = 0;
+
+ knot_rdata_item_t *item;
+
+ knot_rdata_t *tmp;
+
+ knot_dname_t *owner;
+
+ knot_rrset_t *rrset;
+
+ /* Gets rrsigs and checks, if signatures are the same */
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ owner = knot_dname_new_from_str(test_rrsets[i].owner,
+ strlen(test_rrsets[i].owner), NODE_ADDRESS);
+ if (owner == NULL) {
+ diag("Error creating owner domain name!");
+ return 0;
+ }
+
+ rrset = knot_rrset_new(owner, test_rrsets[i].type,
+ test_rrsets[i].rclass, test_rrsets[i].ttl);
+
+ knot_rrset_add_rdata(rrset, test_rrsets[i].rdata);
+
+ //owners are the same
+
+ assert(TEST_RRSETS == TEST_RRSIGS);
+
+ knot_rrset_t *rrsig = knot_rrset_new(owner,
+ test_rrsigs[i].type,
+ test_rrsigs[i].rclass,
+ test_rrsigs[i].ttl);
+
+ tmp = knot_rdata_new();
+ item = malloc(sizeof(knot_rdata_item_t));
+ /* signature is just a string,
+ * should be sufficient for testing */
+ item->raw_data = (uint16_t *)signature_strings[i];
+ knot_rdata_set_items(tmp, item, 1);
+ knot_rrset_add_rdata(rrsig, tmp);
+
+ if (knot_rrset_set_rrsigs(rrset, rrsig)
+ != 0) {
+ diag("Could not set rrsig");
+ errors++;
+ }
+ errors += check_rrset(rrset, i, 0, 0, 1);
+ knot_rrset_free(&rrset);
+ free(item);
+ knot_rdata_free(&tmp);
+ knot_rrset_free(&rrsig);
+ }
+ return (errors == 0);
+}
+
+static int test_rrset_merge()
+{
+ knot_rrset_t *merger1;
+ knot_rrset_t *merger2;
+ knot_dname_t *owner1;
+ knot_dname_t *owner2;
+
+ int r;
+
+ owner1 = knot_dname_new_from_str(test_rrsets[3].owner,
+ strlen(test_rrsets[3].owner), NULL);
+ merger1 = knot_rrset_new(owner1, test_rrsets[3].type,
+ test_rrsets[3].rclass,
+ test_rrsets[3].ttl);
+
+ knot_rrset_add_rdata(merger1, test_rrsets[3].rdata);
+
+ owner2 = knot_dname_new_from_str(test_rrsets[4].owner,
+ strlen(test_rrsets[4].owner), NULL);
+ merger2 = knot_rrset_new(owner2, test_rrsets[4].type,
+ test_rrsets[4].rclass,
+ test_rrsets[4].ttl);
+
+ knot_rrset_add_rdata(merger2, test_rrsets[4].rdata);
+
+// knot_rrset_dump(merger1, 1);
+
+ int ret = 0;
+ if ((ret = knot_rrset_merge((void **)&merger1,
+ (void **)&merger2)) != 0) {
+ diag("Could not merge rrsets. (reason %d)", ret);
+ return 0;
+ }
+
+// knot_rrset_dump(merger1, 1);
+
+ r = check_rrset(merger1, 5, 1, 1, 0);
+
+ knot_rrset_free(&merger1);
+ knot_rrset_free(&merger2);
+
+ if (r) {
+ diag("Merged rdata are wrongly set.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_rrset_owner(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ char *dname_str =
+ knot_dname_to_str(knot_rrset_owner(rrsets[i]));
+ if (strcmp(dname_str, test_rrsets[i].owner)) {
+ diag("Got wrong value for owner from rrset.");
+ errors++;
+ }
+ free(dname_str);
+ }
+ return errors;
+}
+
+static int test_rrset_type(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ if (knot_rrset_type(rrsets[i]) != test_rrsets[i].type) {
+ errors++;
+ diag("Got wrong value for type from rrset.");
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_class(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ if (knot_rrset_class(rrsets[i]) != test_rrsets[i].rclass) {
+ errors++;
+ diag("Got wrong value for class from rrset.");
+ }
+ }
+
+ return errors;
+}
+
+static int test_rrset_ttl(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ if (knot_rrset_ttl(rrsets[i]) != test_rrsets[i].ttl) {
+ errors++;
+ diag("Got wrong value for ttl from rrset.");
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_ret_rdata(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+
+ knot_rrtype_descriptor_t *desc;
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+
+ desc = knot_rrtype_descriptor_by_type(rrsets[i]->type);
+ assert(desc);
+
+// knot_rdata_dump(test_rrsets[i].rdata, 1);
+ // knot_rdata_dump(rrsets[i]->rdata, 1);
+
+ if (knot_rdata_compare(knot_rrset_rdata(rrsets[i]),
+ test_rrsets[i].rdata,
+ desc->wireformat)) {
+ errors++;
+ diag("Got wrong value for rdata from rrset.");
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_get_rdata(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+
+ knot_rrtype_descriptor_t *desc;
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ desc = knot_rrtype_descriptor_by_type(rrsets[i]->type);
+ assert(desc);
+ if (knot_rdata_compare(knot_rrset_get_rdata(rrsets[i]),
+ test_rrsets[i].rdata,
+ desc->wireformat)) {
+ errors++;
+ diag("Got wrong value for rdata from rrset. (Get)");
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_ret_rrsigs(knot_rrset_t **rrsets)
+{
+ int errors = 0;
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ /* TODO should I test the insides of structure as well? */
+ if (knot_rrset_rrsigs(rrsets[i]) != test_rrsets[i].rrsigs) {
+ errors++;
+ diag("Got wrong value for rrsigs from rrset.");
+ }
+ }
+ return errors;
+}
+
+static int test_rrset_getters(uint type)
+{
+ int errors = 0;
+
+ knot_rrset_t *rrsets[TEST_RRSETS];
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ knot_dname_t *owner = knot_dname_new_from_str(
+ test_rrsets[i].owner,
+ strlen(test_rrsets[i].owner),
+ NODE_ADDRESS);
+ if (owner == NULL) {
+ diag("Error creating owner domain name!");
+ return 0;
+ }
+ rrsets[i] = knot_rrset_new(owner,
+ test_rrsets[i].type,
+ test_rrsets[i].rclass,
+ test_rrsets[i].ttl);
+
+ knot_rrset_add_rdata(rrsets[i], test_rrsets[i].rdata);
+ }
+
+ switch (type) {
+ case 0: {
+ errors += test_rrset_owner(rrsets);
+ break;
+ }
+ case 1: {
+ errors += test_rrset_type(rrsets);
+ break;
+ }
+ case 2: {
+ errors += test_rrset_class(rrsets);
+ break;
+ }
+ case 3: {
+ errors += test_rrset_ttl(rrsets);
+ break;
+ }
+ case 4: {
+ errors += test_rrset_ret_rdata(rrsets);
+ break;
+ }
+ case 5: {
+ errors += test_rrset_get_rdata(rrsets);
+ break;
+ }
+ case 6: {
+ errors += test_rrset_ret_rrsigs(rrsets);
+ break;
+ }
+ } /* switch */
+
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ knot_dname_free(&rrsets[i]->owner);
+ knot_rrset_free(&rrsets[i]);
+ }
+
+
+ return (errors == 0);
+}
+
+static int test_rrset_deep_free()
+{
+ /*!< \warning Cannot be run when some rdata are on stack! */
+ int errors = 0;
+
+ knot_rrset_t *tmp_rrset;
+ knot_dname_t *owner;
+ for (int i = 0; i < TEST_RRSETS; i++) {
+ owner = knot_dname_new_from_str(
+ test_rrsets[i].owner,
+ strlen(test_rrsets[i].owner),
+ NODE_ADDRESS);
+ if (owner == NULL) {
+ diag("Error creating owner domain name!");
+ return 0;
+ }
+
+ tmp_rrset = knot_rrset_new(owner,
+ test_rrsets[i].type,
+ test_rrsets[i].rclass,
+ test_rrsets[i].ttl);
+
+ knot_rrset_add_rdata(tmp_rrset, test_rrsets[i].rdata);
+
+ knot_rrset_deep_free(&tmp_rrset, 1, 1, 0);
+
+ errors += (tmp_rrset != NULL);
+ }
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static const int KNOT_RRSET_TEST_COUNT = 13;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_rrset_tests_count(int argc, char *argv[])
+{
+ return KNOT_RRSET_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_rrset_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_final = 1;
+
+/* for (int i = 0; i < 4; i++) {
+ knot_rdata_dump(&RR_RDATA[i], 2, 1);
+ printf("%p %p\n", &RR_RDATA[i], (&RR_RDATA)[i]->next);
+ } */
+
+ create_rdata();
+
+ res = test_rrset_create();
+ ok(res, "rrset: create");
+ res_final *= res;
+
+ skip(!res, 11);
+
+ todo();
+
+ ok(res = test_rrset_delete(), "rrset: delete");
+ //res_final *= res;
+
+ endtodo;
+
+ ok(res = test_rrset_getters(0), "rrset: owner");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(1), "rrset: type");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(2), "rrset: class");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(3), "rrset: ttl");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(4), "rrset: rdata");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(5), "rrset: get rdata");
+ res_final *= res;
+
+ ok(res = test_rrset_getters(6), "rrset: rrsigs");
+ res_final *= res;
+
+ ok(res = test_rrset_add_rdata(), "rrset: add_rdata");
+ res_final *= res;
+
+ ok(res = test_rrset_rrsigs(), "rrset: rrsigs manipulation");
+ res_final *= res;
+
+ ok(res = test_rrset_merge(), "rrset: rdata merging");
+ res_final *= res;
+
+ ok(res = test_rrset_deep_free(), "rrset: deep free");
+ res_final *= res;
+
+ endskip; /* !res_create */
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/rrset_tests.h b/src/tests/libknot/libknot/rrset_tests.h
new file mode 100644
index 0000000..b0787d6
--- /dev/null
+++ b/src/tests/libknot/libknot/rrset_tests.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/>.
+ */
+/*!
+ * \file rrset_tests.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * Contains unit tests for RRSet (knot_rrset_t) and its API.
+ *
+ * Contains tests for:
+ * -
+ */
+#ifndef _KNOTD_RRSET_TESTS_H_
+#define _KNOTD_RRSET_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api rrset_tests_api;
+
+#endif /* _KNOTD_RRSET_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/zone_tests.c b/src/tests/libknot/libknot/zone_tests.c
new file mode 100644
index 0000000..2fdd61a
--- /dev/null
+++ b/src/tests/libknot/libknot/zone_tests.c
@@ -0,0 +1,853 @@
+/* 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 "tests/libknot/libknot/zone_tests.h"
+#include "libknot/common.h"
+#include "libknot/zone/dname-table.h"
+#include "libknot/zone/zone.h"
+#include "libknot/util/error.h"
+#include "libknot/zone/node.h"
+
+static int knot_zone_tests_count(int argc, char *argv[]);
+static int knot_zone_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api zone_tests_api = {
+ "DNS library - zone", //! Unit name
+ &knot_zone_tests_count, //! Count scheduled tests
+ &knot_zone_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+enum { TEST_NODES_GOOD = 7, TEST_NODES_BAD = 1, TRAVERSAL_TYPES = 3};
+
+struct zone_test_node {
+ knot_dname_t owner;
+ knot_node_t *parent;
+};
+
+static struct zone_test_node test_apex =
+{{{}, (uint8_t *)"\3com\0", 5, (uint8_t *)"\x0", 1}, (knot_node_t *)NULL};
+
+static struct zone_test_node test_nodes_bad[TEST_NODES_BAD] = {
+ {{{},(uint8_t *)"\5other\6domain\0", 14, (uint8_t *)"\x0\x6", 2},
+ (knot_node_t *)NULL}
+};
+
+static struct zone_test_node test_nodes_good[TEST_NODES_GOOD] = {
+ {{{}, (uint8_t *)"\7example\3com\0", 13, (uint8_t *)"\x0\x8", 2},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\3www\7example\3com\0", 17, (uint8_t *)"\x0\x4\xC", 3},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\7another\6domain\3com\0", 20, (uint8_t *)"\x0\x8\xF", 3},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\5mail1\7example\3com\0", 19, (uint8_t *)"\x0\x6\xE", 3},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\5mail2\7example\3com\0", 19, (uint8_t *)"\x0\x6\xE", 3},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\3smb\7example\3com\0", 17, (uint8_t *)"\x0\x4\xC", 3},
+ (knot_node_t *)NULL},
+ {{{}, (uint8_t *)"\4smtp\7example\3com\0", 18, (uint8_t *)"\x0\x5\xD", 3},
+ (knot_node_t *)NULL},
+};
+
+static int test_zone_check_node(const knot_node_t *node,
+ const struct zone_test_node *test_node,
+ int test_parent)
+{
+ return (node->owner == &test_node->owner) &&
+ ((test_parent) ? node->parent == test_node->parent : 1);
+}
+
+static int test_zone_create(knot_zone_contents_t **zone)
+{
+// knot_dname_t *dname = knot_dname_new_from_wire(
+// test_apex.owner.name, test_apex.owner.size, NULL);
+// assert(dname);
+
+ knot_node_t *node = knot_node_new(&test_apex.owner,
+ test_apex.parent, 0);
+ if (node == NULL) {
+ diag("zone: Could not create zone apex.");
+ return 0;
+ }
+
+ *zone = knot_zone_contents_new(node, 0, 0, NULL);
+
+ if ((*zone) == NULL) {
+ diag("zone: Failed to create zone.");
+ knot_node_free(&node, 1, 0);
+ return 0;
+ }
+
+ if ((*zone)->apex != node) {
+ diag("zone: Zone apex not set right.");
+ knot_node_free(&node, 1, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3)
+{
+ /*
+ * NSEC3 nodes are de facto identical to normal nodes, so there is no
+ * need for separate tests. The only difference is where are they stored
+ * in the zone structure.
+ */
+
+ int errors = 0;
+ int res = 0;
+
+ //note("Good nodes");
+
+ for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+ knot_node_t *node = knot_node_new(&test_nodes_good[i].owner,
+ test_nodes_good[i].parent, 0);
+ if (node == NULL) {
+ diag("zone: Could not create node.");
+ return 0;
+ }
+
+ if ((res = ((nsec3) ? knot_zone_contents_add_nsec3_node(zone, node, 0, 0, 0)
+ : knot_zone_contents_add_node(zone, node, 0, 0, 0))) != 0) {
+ diag("zone: Failed to insert node into zone (returned"
+ " %d).", res);
+ knot_node_free(&node, 0, 0);
+ ++errors;
+ }
+ /* TODO check values in the node as well */
+ }
+
+ //note("Bad nodes");
+
+ for (int i = 0; i < TEST_NODES_BAD; ++i) {
+ knot_node_t *node = knot_node_new(&test_nodes_bad[i].owner,
+ test_nodes_bad[i].parent, 0);
+ if (node == NULL) {
+ diag("zone: Could not create node.");
+ return 0;
+ }
+
+ if ((res = ((nsec3) ? knot_zone_contents_add_nsec3_node(zone, node, 0, 0, 0)
+ : knot_zone_contents_add_node(zone, node, 0, 0, 0))) !=
+ KNOT_EBADZONE) {
+ diag("zone: Inserting wrong node did not result in"
+ "proper return value (%d instead of %d).", res,
+ KNOT_EBADZONE);
+ ++errors;
+ }
+ knot_node_free(&node, 0, 0);
+ }
+
+ //note("NULL zone");
+
+ note("Inserting into NULL zone...\n");
+
+ knot_node_t *node = knot_node_new(&test_nodes_good[0].owner,
+ test_nodes_good[0].parent, 0);
+ if (node == NULL) {
+ diag("zone: Could not create node.");
+ return 0;
+ }
+
+ if ((res = ((nsec3) ? knot_zone_contents_add_nsec3_node(NULL, node, 0, 0, 0)
+ : knot_zone_contents_add_node(NULL, node, 0, 0, 0))) != KNOT_EBADARG) {
+ diag("zone: Inserting node to NULL zone did not result in"
+ "proper return value (%d instead of %d)", res,
+ KNOT_EBADARG);
+ ++errors;
+ }
+
+ knot_node_free(&node, 0, 0);
+
+ //note("NULL node");
+ note("Inserting NULL node...\n");
+
+ if ((res = ((nsec3) ? knot_zone_contents_add_nsec3_node(zone, NULL, 0, 0, 0)
+ : knot_zone_contents_add_node(zone, NULL, 0, 0, 0))) != KNOT_EBADARG) {
+ diag("zone: Inserting NULL node to zone did not result in"
+ "proper return value (%d instead of %d)", res,
+ KNOT_EBADARG);
+ ++errors;
+ }
+
+ if (!nsec3) {
+ //note("Inserting Apex again...\n");
+
+ node = knot_node_new(&test_apex.owner, test_apex.parent, 0);
+ if (node == NULL) {
+ diag("zone: Could not create node.");
+ return 0;
+ }
+
+ //note("Apex again");
+
+ if ((res = knot_zone_contents_add_node(zone, node, 0, 0, 0)) !=
+ KNOT_EBADZONE) {
+ diag("zone: Inserting zone apex again did not result in"
+ "proper return value (%d instead of -2)",
+ KNOT_EBADZONE);
+ ++errors;
+ }
+
+ knot_node_free(&node, 0, 0);
+ }
+
+ // check if all nodes are inserted
+ //int nodes = 0;
+ if (!nsec3
+ && !test_zone_check_node(knot_zone_contents_apex(zone), &test_apex, !nsec3)) {
+ diag("zone: Apex of zone not right.");
+// diag("Apex owner: %s (%p), apex parent: %p\n",
+// knot_dname_to_str(knot_zone_apex(zone)->owner),
+// knot_zone_apex(zone)->owner,
+// knot_zone_apex(zone)->parent);
+// diag("Should be: owner: %s (%p), parent: %p\n",
+// knot_dname_to_str(&test_apex.owner),
+// &test_apex.owner,
+// test_apex.parent);
+ ++errors;
+ }
+ //++nodes;
+ for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+ const knot_node_t *n = ((nsec3) ? knot_zone_contents_find_nsec3_node(
+ zone, &test_nodes_good[i].owner) :
+ knot_zone_contents_find_node(zone, &test_nodes_good[i].owner));
+ if (n == NULL) {
+ diag("zone: Missing node with owner %s",
+ test_nodes_good[i].owner.name);
+ ++errors;
+ continue;
+ }
+
+ if (!test_zone_check_node(n, &test_nodes_good[i], !nsec3)) {
+ diag("zone: Node does not match: owner: %s (should be "
+ "%s), parent: %p (should be %p)",
+ n->owner->name, test_nodes_good[i].owner.name,
+ n->parent, test_nodes_good[i].parent);
+ ++errors;
+ }
+ //++nodes;
+ }
+
+ //note("zone: %d nodes in the zone (including apex)", nodes);
+
+ return (errors == 0);
+}
+
+static int test_zone_get_node(knot_zone_contents_t *zone, int nsec3)
+{
+ int errors = 0;
+
+ for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+ if (((nsec3) ? knot_zone_contents_get_nsec3_node(
+ zone, &test_nodes_good[i].owner)
+ : knot_zone_contents_get_node(zone, &test_nodes_good[i].owner))
+ == NULL) {
+ diag("zone: Node (%s) not found in zone.",
+ (char *)test_nodes_good[i].owner.name);
+ ++errors;
+ }
+ }
+
+ for (int i = 0; i < TEST_NODES_BAD; ++i) {
+ if (((nsec3) ? knot_zone_contents_get_nsec3_node(
+ zone, &test_nodes_bad[i].owner)
+ : knot_zone_contents_get_node(zone, &test_nodes_bad[i].owner))
+ != NULL) {
+ diag("zone: Node (%s) found in zone even if it should"
+ "not be there.",
+ (char *)test_nodes_bad[i].owner.name);
+ ++errors;
+ }
+ }
+
+ if (((nsec3)
+ ? knot_zone_contents_get_nsec3_node(NULL, &test_nodes_good[0].owner)
+ : knot_zone_contents_get_node(NULL, &test_nodes_good[0].owner)) != NULL) {
+ diag("zone: Getting node from NULL zone did not result in"
+ "proper return value (NULL)");
+ ++errors;
+ }
+
+ if (((nsec3) ? knot_zone_contents_get_nsec3_node(zone, NULL)
+ : knot_zone_contents_get_node(zone, NULL)) != NULL) {
+ diag("zone: Getting node with NULL owner from zone did not "
+ "result in proper return value (NULL)");
+ ++errors;
+ }
+
+ if (!nsec3 && knot_zone_contents_get_node(zone, &test_apex.owner) == NULL) {
+ diag("zone: Getting zone apex from the zone failed");
+ ++errors;
+ }
+
+ return (errors == 0);
+}
+
+static int test_zone_find_node(knot_zone_contents_t *zone, int nsec3)
+{
+ int errors = 0;
+
+ for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+ if (((nsec3) ? knot_zone_contents_find_nsec3_node(
+ zone, &test_nodes_good[i].owner)
+ : knot_zone_contents_find_node(zone, &test_nodes_good[i].owner))
+ == NULL) {
+ diag("zone: Node (%s) not found in zone.",
+ (char *)test_nodes_good[i].owner.name);
+ ++errors;
+ }
+ }
+
+ for (int i = 0; i < TEST_NODES_BAD; ++i) {
+ if (((nsec3) ? knot_zone_contents_find_nsec3_node(
+ zone, &test_nodes_bad[i].owner)
+ : knot_zone_contents_find_node(zone, &test_nodes_bad[i].owner))
+ != NULL) {
+ diag("zone: Node (%s) found in zone even if it should"
+ "not be there.",
+ (char *)test_nodes_bad[i].owner.name);
+ ++errors;
+ }
+ }
+
+ if (((nsec3)
+ ? knot_zone_contents_find_nsec3_node(NULL, &test_nodes_good[0].owner)
+ : knot_zone_contents_find_node(NULL, &test_nodes_good[0].owner)) != NULL) {
+ diag("zone: Finding node from NULL zone did not result in"
+ "proper return value (NULL)");
+ ++errors;
+ }
+
+ if (((nsec3) ? knot_zone_contents_find_nsec3_node(zone, NULL)
+ : knot_zone_contents_find_node(zone, NULL)) != NULL) {
+ diag("zone: Finding node with NULL owner from zone did not "
+ "result in proper return value (NULL)");
+ ++errors;
+ }
+
+ if (!nsec3 && knot_zone_contents_find_node(zone, &test_apex.owner) == NULL) {
+ diag("zone: Finding zone apex from the zone failed");
+ ++errors;
+ }
+
+ return (errors == 0);
+}
+
+//static void test_zone_destroy_node_from_tree(knot_node_t *node,
+// void *data)
+//{
+// UNUSED(data);
+// knot_node_free(&node, 0);
+//}
+
+/* explained below */
+static size_t node_index = 0;
+
+/*! \brief
+ * This function will overwrite parent field in node structure -
+ * we don't (and can't, with current structures) use it in these tests anyway.
+ * Since zone structure itself has no count field, only option known to me
+ * is (sadly) to use a global variable.
+ */
+static void tmp_apply_function(knot_node_t *node, void *data)
+{
+ node->parent = (knot_node_t *)node_index;
+ node_index++;
+}
+
+/* \note Since I am unaware of a way how to get a return value from traversal
+ * functions, I will use (hopefully for the last time here) global variable
+ */
+
+static int compare_ok = 1;
+
+static void tmp_compare_function(knot_node_t *node, void *data)
+{
+ /* node_index will start set to zero */
+ if (node->parent != (knot_node_t *)node_index) {
+ compare_ok = 0;
+ return;
+ } else if (!compare_ok) {
+ diag("Traversal function has partially set values right");
+ }
+ node->parent = NULL;
+ node_index++;
+}
+
+static int test_zone_tree_apply(knot_zone_contents_t *zone,
+ int type, int nsec3)
+{
+
+ assert(node_index == 0);
+ assert(compare_ok == 1);
+
+ int (*traversal_func)(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node,
+ void *data),
+ void *data);
+
+ switch (type) {
+ case 0: {
+ if (nsec3) {
+ traversal_func =
+ &knot_zone_contents_nsec3_apply_postorder;
+ diag("Testing postorder traversal");
+ } else {
+ traversal_func =
+ &knot_zone_contents_tree_apply_postorder;
+ diag("Testing postorder traversal - NSEC3");
+ }
+ break;
+ }
+ case 1: {
+ if (nsec3) {
+ traversal_func =
+ &knot_zone_contents_nsec3_apply_inorder;
+ diag("Testing inorder traversal");
+ } else {
+ traversal_func =
+ &knot_zone_contents_tree_apply_inorder;
+ diag("Testing inorder traversal - NSEC3");
+ }
+ break;
+ }
+ case 2: {
+ if (nsec3) {
+ traversal_func =
+ &knot_zone_contents_nsec3_apply_inorder_reverse;
+ diag("Testing inorder reverse traversal");
+ } else {
+ traversal_func =
+ &knot_zone_contents_tree_apply_inorder_reverse;
+ diag("Testing inorder reverse "
+ "traversal - NSEC3");
+ }
+ break;
+ }
+ default: {
+ diag("Unknown traversal function type");
+ return 0;
+ }
+ }
+
+ /*
+ * This will iterate through tree and set node->parent field values
+ * from 0 to number of nodes.
+ */
+
+ traversal_func(zone, &tmp_apply_function, NULL);
+
+ node_index = 0;
+
+ /*
+ * This will check whether the values were set accordingly.
+ */
+
+ traversal_func(zone, &tmp_compare_function, NULL);
+
+ int ret = compare_ok;
+
+ compare_ok = 1;
+ node_index = 0;
+
+ return (ret);
+}
+
+/* Tests all kinds of zone traversals, explainded above */
+static int test_zone_traversals(knot_zone_contents_t *zone)
+{
+ for (int i = 0; i < TRAVERSAL_TYPES; i++) {
+ for (int j = 0; j < 2; j++) {
+ if (!test_zone_tree_apply(zone, i, j)) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+struct zone_test_param {
+ /* Times 2 so that we don't have to mess with mallocs. */
+ knot_node_t *knot_node_array[TEST_NODES_GOOD * 5];
+ knot_dname_t *table_node_array[TEST_NODES_GOOD * 5];
+ size_t count;
+};
+
+static void tree_node_to_array(knot_node_t *node, void *data)
+{
+ struct zone_test_param *param = (struct zone_test_param *)data;
+ param->knot_node_array[param->count++] = node;
+}
+
+static void tree_dname_node_to_array(knot_dname_t *node,
+ void *data)
+{
+ struct zone_test_param *param = (struct zone_test_param *)data;
+ param->table_node_array[param->count++] = node;
+}
+
+extern int compare_wires_simple(uint8_t *w1, uint8_t *w2, uint count);
+static int test_zone_shallow_copy()
+{
+ int errors = 0;
+ int lived = 0;
+ knot_dname_t *apex_dname =
+ knot_dname_new_from_str("a.ns.nic.cz.",
+ strlen("a.ns.nic.cz"), NULL);
+ assert(apex_dname);
+ knot_node_t *apex_node =
+ knot_node_new(apex_dname, NULL, 0);
+ assert(apex_node);
+ lives_ok({
+ if (knot_zone_contents_shallow_copy(NULL, NULL) != KNOT_EBADARG) {
+ diag("Calling zone_shallow_copy with NULL "
+ "arguments did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ knot_zone_contents_t *zone = knot_zone_contents_new(apex_node,
+ 0, 1, 0);
+ if (knot_zone_contents_shallow_copy(zone, NULL) != KNOT_EBADARG) {
+ diag("Calling zone_shallow_copy with NULL destination "
+ "zone argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_contents_shallow_copy(NULL, &zone) != KNOT_EBADARG) {
+ diag("Calling zone_shallow_copy with NULL source "
+ "zone argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_contents_shallow_copy(zone, &zone) != KNOT_EBADARG) {
+ diag("Calling zone_shallow_copy with identical source "
+ "and destination zone did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ knot_zone_contents_free(&zone);
+ }, "zone: shallow copy NULL tests");
+ errors += lived != 1;
+
+ knot_dname_t *d = knot_dname_deep_copy(&test_nodes_good[0].owner);
+ if (d == NULL) {
+ return 0;
+ }
+ knot_node_t *n = knot_node_new(d, NULL, 0);
+
+ /* example.com. */
+// knot_zone_t *from_zone =
+// knot_zone_new(knot_node_new(&test_nodes_good[0].owner,
+// test_nodes_good[0].parent, 0), 10, 1);
+ knot_zone_t *from_zone = knot_zone_new(n, 10, 1);
+ knot_zone_contents_t *from = knot_zone_get_contents(from_zone);
+
+ /* Add nodes to zone. */
+ for (int i = 1; i < TEST_NODES_GOOD; ++i) {
+ knot_dname_t *d = knot_dname_deep_copy(&test_nodes_good[i].owner);
+ if (d == NULL) {
+ return 0;
+ }
+ knot_node_t *node = knot_node_new(d, test_nodes_good[i].parent,
+ 0);
+ if (node == NULL) {
+ diag("zone: Could not create node.");
+ return 0;
+ }
+
+ if (knot_zone_contents_add_node(from, node, 1, 1, 1) != KNOT_EOK) {
+ diag("zone: Could not add node. %s",
+ knot_dname_to_str(node->owner));
+// return 0;
+ }
+ }
+
+ /* Make a copy of zone */
+ knot_zone_contents_t *to = NULL;
+ int ret = 0;
+ if ((ret = knot_zone_contents_shallow_copy(from, &to) != KNOT_EOK)) {
+ diag("Could not copy zone! %s", knot_strerror(ret));
+ return 0;
+ }
+
+ assert(to);
+
+ /* Compare non-tree parts of the zone. */
+// if (from->data != to->data) {
+// diag("Zone data field wrong after shallow copy!");
+// errors++;
+// }
+
+// if (from->dtor != to->dtor) {
+// diag("Zone data destructor field wrong after shallow copy!");
+// errors++;
+// }
+
+ if (from->node_count != to->node_count) {
+ diag("Zone node count data field wrong after shallow copy!");
+ errors++;
+ }
+
+// if (from->version != to->version) {
+// diag("Zone version data field wrong after shallow copy!");
+// errors++;
+// }
+
+ if (from->apex != to->apex) {
+ diag("Zone apex differ after shallow copy!");
+ }
+
+ if (compare_wires_simple((uint8_t *)(&from->nsec3_params),
+ (uint8_t *)(&to->nsec3_params),
+ sizeof(from->nsec3_params)) != 0) {
+ diag("Nsec3_params data field wrong after shallow copy!");
+ errors++;
+ }
+
+ if (from->nodes == to->nodes) {
+ diag("Copied zones have identical trees!");
+ errors++;
+ }
+
+ if (from->nsec3_nodes == to->nsec3_nodes) {
+ diag("Copied zones have identical trees!");
+ errors++;
+ }
+
+ /* Compare nodes, convert tree to array then compare those arrays. */
+ struct zone_test_param param1;
+ memset(&param1, 0, sizeof(struct zone_test_param));
+
+ knot_zone_contents_tree_apply_inorder(from, tree_node_to_array,
+ (void *)&param1);
+
+ struct zone_test_param param2;
+ memset(&param2, 0, sizeof(struct zone_test_param));
+
+ knot_zone_contents_tree_apply_inorder(to, tree_node_to_array,
+ (void *)&param2);
+
+ if (param1.count != param2.count) {
+ diag("wrong tree");
+ return 0;
+ }
+
+ for (int i = 0; i < param1.count; i++) {
+ if (param1.knot_node_array[i] !=
+ param2.knot_node_array[i]) {
+ diag("wrong tree");
+ return 0;
+ }
+ }
+
+ param1.count = 0;
+ knot_dname_table_tree_inorder_apply(from->dname_table,
+ tree_dname_node_to_array,
+ (void *)&param1);
+
+ param2.count = 0;
+ knot_dname_table_tree_inorder_apply(to->dname_table,
+ tree_dname_node_to_array,
+ (void *)&param2);
+
+ if (param1.count != param2.count) {
+ diag("wrong table count");
+ return 0;
+ }
+
+ for (int i = 0; i < param1.count; i++) {
+ if (param1.table_node_array[i] != param2.table_node_array[i]) {
+ diag("wrong table nodes");
+ errors++;
+ }
+ }
+
+#ifdef USE_HASH_TABLE
+ if (from->table) {
+ if (from->table == to->table) {
+ diag("hash tables after shallow copy are identical!");
+ return 0;
+ }
+ uint i;
+ if (hashsize(from->table->table_size_exp) !=
+ hashsize(to->table->table_size_exp)) {
+ diag("hash tables after shallow copy error!");
+ return 0;
+ }
+
+ if (from->table->table_count != to->table->table_count) {
+ diag("hash tables after shallow copy error!");
+ return 0;
+ }
+
+ for (uint t = 0; t < from->table->table_count; ++t) {
+ for (i = 0; i <
+ hashsize(from->table->table_size_exp); i++) {
+ if (from->table->tables[t][i] == NULL) {
+ if (to->table->tables[t][i] != NULL) {
+ diag("hash table item error");
+ }
+ continue;
+ }
+ if ((from->table->tables[t])[i]->key_length !=
+ (to->table->tables[t])[i]->key_length) {
+ diag("hash table key lengths error!");
+ return 0;
+ }
+ if ((from->table->tables[t])[i]->key !=
+ (to->table->tables[t])[i]->key) {
+ diag("hash table key error!");
+ return 0;
+ }
+ if ((from->table->tables[t])[i]->value !=
+ (to->table->tables[t])[i]->value) {
+ diag("hash table value error!");
+ return 0;
+ }
+ }
+ }
+
+ ck_stash_item_t *item1 = from->table->stash;
+ ck_stash_item_t *item2 = to->table->stash;
+ while (item1 != NULL && item2 != NULL) {
+ if (item1->item->key_length !=
+ item2->item->key_length) {
+ diag("hash stash key length error!");
+ return 0;
+ }
+ if (item1->item->key != item2->item->key) {
+ diag("hash stash key error!");
+ return 0;
+ }
+ if (item1->item->value != item2->item->value) {
+ diag("hash stash value error!");
+ return 0;
+ }
+
+ item1 = item1->next;
+ item2 = item2->next;
+ }
+ } else {
+ if (to->table) {
+ diag("Hash table is not set to NULL "
+ "after shallow copy!");
+ errors++;
+ }
+ }
+#endif
+
+// knot_zone_deep_free(&from_zone, 0);
+// knot_zone_contents_free(&to);
+ return (errors == 0);
+
+}
+
+//static int test_zone_free(knot_zone_t **zone)
+//{
+// knot_zone_tree_apply_postorder(*zone,
+// test_zone_destroy_node_from_tree,
+// NULL);
+// knot_zone_nsec3_apply_postorder(*zone,
+// test_zone_destroy_node_from_tree,
+// NULL);
+// knot_zone_free(zone);
+// return (*zone == NULL);
+//}
+
+static const int KNOT_ZONE_TEST_COUNT = 10;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_zone_tests_count(int argc, char *argv[])
+{
+ return KNOT_ZONE_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_zone_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_final = 0;
+
+ knot_zone_contents_t *zone = NULL;
+
+ ok((res = test_zone_create(&zone)), "zone: create");
+ res_final *= res;
+
+ skip(!res, 6);
+
+ ok((res = test_zone_add_node(zone, 0)), "zone: add node");
+ res_final *= res;
+
+ skip(!res, 2);
+
+ ok((res = test_zone_get_node(zone, 0)), "zone: get node");
+ res_final *= res;
+
+ skip(!res, 1);
+
+ ok((res = test_zone_find_node(zone, 0)), "zone: find node");
+ res_final *= res;
+
+ endskip; // get node failed
+
+ endskip; // add node failed
+
+ ok((res = test_zone_add_node(zone, 1)), "zone: add nsec3 node");
+ res_final *= res;
+
+ skip(!res, 2);
+
+ ok((res = test_zone_get_node(zone, 1)), "zone: get nsec3 node");
+ res_final *= res;
+
+ skip(!res, 1);
+
+ ok((res = test_zone_find_node(zone, 1)), "zone: find nsec3 node");
+ res_final *= res;
+
+ endskip; // get nsec3 node failed
+
+ endskip; // add nsec3 node failed
+
+ ok(res = test_zone_traversals(zone), "zone: traversals");
+ res_final *= res;
+
+ ok((res = test_zone_shallow_copy()), "zone: shallow copy");
+ res_final *= res;
+
+// ok((res = test_zone_free(&zone)), "zone: free");
+// res_final *= res;
+
+ endskip; // create failed
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/zone_tests.h b/src/tests/libknot/libknot/zone_tests.h
new file mode 100644
index 0000000..5539709
--- /dev/null
+++ b/src/tests/libknot/libknot/zone_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ZONE_TESTS_H_
+#define _KNOTD_ZONE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api zone_tests_api;
+
+#endif /* _KNOTD_ZONE_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/zone_tree_tests.c b/src/tests/libknot/libknot/zone_tree_tests.c
new file mode 100644
index 0000000..c26746e
--- /dev/null
+++ b/src/tests/libknot/libknot/zone_tree_tests.c
@@ -0,0 +1,758 @@
+/* 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 <string.h>
+#include <assert.h>
+
+#include "tests/libknot/libknot/zone_tree_tests.h"
+#include "libknot/zone/zone-tree.h"
+#include "libknot/util/error.h"
+
+static int knot_zone_tree_tests_count(int argc, char *argv[]);
+static int knot_zone_tree_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api zone_tree_tests_api = {
+ "DNS library - zone tree", //! Unit name
+ &knot_zone_tree_tests_count, //! Count scheduled tests
+ &knot_zone_tree_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+static int test_tree_init()
+{
+ int errors = 0;
+ int lived = 0;
+
+ lives_ok({
+ if (knot_zone_tree_init(NULL) != KNOT_EBADARG) {
+ diag("Calling knot_zone_tree_init with NULL "
+ "tree did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: init NULL tests");
+ errors += lived != 1;
+
+ return (errors == 0);
+}
+
+static int test_tree_insert()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+ knot_node_t *node =
+ knot_node_new(knot_dname_new_from_str("a.ns.nic.cz.",
+ strlen("a.ns.nic.cz."),
+ NULL),
+ NULL, 0);
+ assert(node);
+
+ lives_ok({
+ if (knot_zone_tree_insert(NULL, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_insert(tree, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_insert(NULL, node) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: insert NULL tests");
+ if (errors) {
+ diag("Zone tree insert did not return KNOT_EBADARG "
+ "when given wrong arguments");
+ }
+ errors += lived != 1;
+
+ if (knot_zone_tree_insert(tree, node) != KNOT_EOK) {
+ diag("Calling zone tree insert with valid arguments "
+ "did not return KNOT_EOK");
+ errors++;
+ }
+
+ /* Sorting will be tested in traversal functions. */
+ return (errors == 0);
+}
+
+static int test_tree_finding()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+ const knot_node_t *node =
+ knot_node_new(knot_dname_new_from_str("a.ns.nic.cz.",
+ strlen("a.ns.nic.cz."),
+ NULL),
+ NULL, 0);
+ assert(node);
+
+ lives_ok({
+ if (knot_zone_tree_find(NULL, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_find(tree, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_find(tree, node->owner,
+ NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ const knot_node_t *found_node = NULL;
+ lived = 0;
+ if (knot_zone_tree_find(NULL, node->owner,
+ &found_node) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_find(tree, NULL,
+ &found_node) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: find NULL tests");
+ if (errors) {
+ diag("Zone tree find did not return KNOT_EBADARG "
+ "when given wrong arguments");
+ }
+
+ errors += lived != 1;
+
+ /* Insert node */
+ assert(knot_zone_tree_insert(tree, (knot_node_t *)node) == KNOT_EOK);
+
+ knot_node_t *found_node = NULL;
+ if (knot_zone_tree_find(tree, node->owner,
+ (const knot_node_t **)&found_node) !=
+ KNOT_EOK) {
+ diag("Calling zone tree find with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree find did not return right node");
+ errors++;
+ }
+
+ if (knot_zone_tree_get(tree, node->owner, &found_node) !=
+ KNOT_EOK) {
+ diag("Calling zone tree get with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree get did not return right node");
+ errors++;
+ }
+
+ /* Try to search for node not in tree. */
+ knot_dname_t *alien_dname =
+ knot_dname_new_from_str("this.name.is.not.in.the.tree.",
+ strlen("this.name.is.not.in.the.tree."),
+ NULL);
+
+ if (knot_zone_tree_find(tree, alien_dname,
+ (const knot_node_t **)&found_node) !=
+ KNOT_EOK) {
+ diag("Calling zone tree find with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != NULL) {
+ diag("Zone tree find returned node that was not in the tree!");
+ errors++;
+ }
+
+ if (knot_zone_tree_get(tree, alien_dname, &found_node) !=
+ KNOT_EOK) {
+ diag("Calling zone tree get with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != NULL) {
+ diag("Zone tree get returned node that was not in the tree!");
+ errors++;
+ }
+
+ return (errors == 0);
+}
+
+static int test_tree_finding_less_or_equal()
+{
+ diag("Issue nr.: 1145");
+ int errors = 0;
+ int lived = 0;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+ const knot_node_t *node =
+ knot_node_new(knot_dname_new_from_str("a.ns.nic.cz.",
+ strlen("a.ns.nic.cz"),
+ NULL),
+ NULL, 0);
+ assert(node);
+
+ lives_ok({
+ if (knot_zone_tree_find_less_or_equal(NULL,
+ NULL,
+ NULL,
+ NULL, 0) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_find_less_or_equal(tree, NULL,
+ NULL, NULL, 0) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_find_less_or_equal(tree,
+ node->owner,
+ NULL,
+ NULL, 0) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ const knot_node_t *found_node = NULL;
+ lived = 0;
+ if (knot_zone_tree_find_less_or_equal(NULL, node->owner,
+ &found_node, NULL, 0) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ const knot_node_t *previous_node = NULL;
+ lived = 0;
+ if (knot_zone_tree_find_less_or_equal(tree, NULL,
+ &found_node,
+ &previous_node, 0) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: tree find less or equal NULL tests");
+ if (errors) {
+ diag("Zone tree find did not return KNOT_EBADARG "
+ "when given wrong arguments");
+ }
+
+ if (!lived) {
+ errors++;
+ }
+
+ const knot_node_t *previous_node = NULL;
+
+ /* Insert node - exact match. */
+ assert(knot_zone_tree_insert(tree, (knot_node_t *)node) == KNOT_EOK);
+
+
+ const knot_node_t *found_node = NULL;
+ if (knot_zone_tree_find_less_or_equal(tree,
+ node->owner,
+ &found_node,
+ &previous_node, 0) <= 0) {
+ diag("Calling zone tree find less with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree find did not return right node");
+ errors++;
+ }
+
+ if (knot_zone_tree_get_less_or_equal(tree, node->owner,
+ (knot_node_t **)&found_node,
+ (knot_node_t **)&previous_node, 0) <=
+ 0) {
+ diag("Calling zone tree get less with valid arguments did "
+ "not return KNOT_EOK");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree get less did not return right node");
+ errors++;
+ }
+
+ knot_dname_t *less_dname =
+ knot_dname_new_from_str("ns.nic.cz.",
+ strlen("ns.nic.cz."),
+ NULL);
+
+ assert(knot_dname_compare(less_dname, node->owner) < 0);
+
+ if (knot_zone_tree_find_less_or_equal(tree,
+ less_dname,
+ &found_node,
+ &previous_node, 0) <= 0) {
+ diag("Calling zone tree find less or equal "
+ "with valid arguments did "
+ "not return > 0");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree find less or equal did not return right node");
+ errors++;
+ }
+
+ if (knot_zone_tree_get_less_or_equal(tree, less_dname,
+ (knot_node_t **)&found_node,
+ (knot_node_t **)&previous_node, 0) <=
+ 0) {
+ diag("Calling zone tree less or equal with valid arguments did "
+ "not return > 0");
+ errors++;
+ }
+
+ if (found_node != node) {
+ diag("Zone tree get less or equal did not return right node");
+ errors++;
+ }
+
+ /* Try to search for node not in tree. */
+ knot_dname_t *alien_dname =
+ knot_dname_new_from_str("this.name.is.not.in.the.tree.",
+ strlen("this.name.is.not.in.the.tree."),
+ NULL);
+
+ if (knot_zone_tree_find_less_or_equal(tree, alien_dname,
+ &found_node,
+ &previous_node, 0) !=
+ 0) {
+ diag("Calling zone tree find less with valid arguments did "
+ "not return 0");
+ errors++;
+ }
+
+ if (knot_zone_tree_get_less_or_equal(tree,
+ alien_dname,
+ (knot_node_t **)&found_node,
+ (knot_node_t **)&previous_node, 0) !=
+ 0) {
+ diag("Calling zone tree get with valid arguments did "
+ "not return 0");
+ errors++;
+ }
+
+ /* Set node previous label. */
+ knot_node_t *tmp_node =
+ knot_node_new(knot_dname_new_from_str("ns.nic.cz.",
+ strlen("ns.nic.cz"),
+ NULL), NULL, 0);
+ assert(tmp_node);
+ knot_node_set_parent((knot_node_t *)node, tmp_node);
+
+ if (knot_zone_tree_find_less_or_equal(tree, node->owner,
+ &found_node,
+ &previous_node, 0) <=
+ 0) {
+ diag("Calling zone tree find with valid arguments did "
+ "not return > 0");
+ errors++;
+ }
+
+ if (found_node != node || previous_node != tmp_node) {
+ diag("Zone tree find did not return valid nodes!");
+ errors++;
+ }
+
+
+ if (knot_zone_tree_get_less_or_equal(tree, node->owner,
+ (knot_node_t **)&found_node,
+ (knot_node_t **)&previous_node, 0) <=
+ 0) {
+ diag("Calling zone tree get with valid arguments did "
+ "not return > 0");
+ errors++;
+ }
+
+ if (found_node != node || previous_node != tmp_node) {
+ diag("Zone get find did not return valid nodes!");
+ errors++;
+ }
+
+ return (errors == 0);
+}
+
+static int test_tree_remove()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+ knot_node_t *node =
+ knot_node_new(knot_dname_new_from_str("a.ns.nic.cz.",
+ strlen("a.ns.nic.cz"),
+ NULL),
+ NULL, 0);
+ assert(node);
+
+ /* Add node. */
+ int ret = knot_zone_tree_insert(tree, node);
+ assert(ret == 0);
+ assert(ret == 0);
+
+ lives_ok({
+ if (knot_zone_tree_remove(NULL, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_remove(tree, NULL, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_remove(tree, node->owner, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_remove(NULL, node->owner, NULL) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ knot_zone_tree_node_t *deleted_node = NULL;
+ lived = 0;
+ if (knot_zone_tree_remove(NULL, node->owner, &deleted_node) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_remove(tree, NULL, &deleted_node) !=
+ KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: remove NULL tests");
+ if (errors) {
+ diag("Zone tree remove did not return KNOT_EBADARG "
+ "when given wrong arguments");
+ }
+
+ errors += lived != 1;
+
+ knot_zone_tree_node_t *removed_node = NULL;
+
+ /* Remove previously inserted node. */
+ if (knot_zone_tree_remove(tree, node->owner, &removed_node) !=
+ KNOT_EOK) {
+ diag("Could not remove previously inserted node!");
+ errors++;
+ }
+
+ if (removed_node == NULL || removed_node->node != node) {
+ diag("Wrong node was removed!");
+ errors++;
+ }
+
+ /*
+ * Try remove the node again - it should not be there and
+ * removed_node should be NULL.
+ */
+
+ if (knot_zone_tree_remove(tree, node->owner, &removed_node) !=
+ KNOT_EOK) {
+ diag("Could not remove previously inserted node!");
+ errors++;
+ }
+
+ if (removed_node != NULL) {
+ diag("Zone tree remove returned previously removed node!");
+ errors++;
+ }
+
+ return (errors == 0);
+
+}
+
+struct test_zone_tree_args {
+ knot_node_t *array[10 * 1024];
+ size_t count;
+};
+
+static void add_to_array(knot_zone_tree_node_t *node, void *data)
+{
+ struct test_zone_tree_args *args =
+ (struct test_zone_tree_args *)data;
+ args->array[args->count++] = node->node;
+}
+
+static int test_traversal(knot_node_t **nodes,
+ size_t node_count,
+ uint code)
+{
+ int errors = 0;
+ int lived = 0;
+
+ int (*trav_func)(knot_zone_tree_t *,
+ void (*)(knot_zone_tree_node_t *, void *),
+ void *);
+
+ trav_func = (code) ? knot_zone_tree_reverse_apply_inorder :
+ knot_zone_tree_forward_apply_inorder;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+
+ lives_ok({
+ if (trav_func(NULL, NULL, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (trav_func(tree, NULL, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (trav_func(NULL, add_to_array, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: traversal NULL tests");
+
+ if (errors) {
+ diag("Traversal function did not return KNOT_EBADARG "
+ "when given NULL parameters");
+ }
+
+ errors += lived != 1;
+
+ /* Add nodes to tree. */
+ for (int i = 0; i < node_count; i++) {
+ assert(knot_zone_tree_insert(tree, nodes[i]) == KNOT_EOK);
+ }
+
+ struct test_zone_tree_args args;
+ args.count = 0;
+
+ trav_func(tree, add_to_array, &args);
+
+ if (args.count != node_count) {
+ diag("Traversal function traversed more nodes than it "
+ "should have!");
+ return ++errors;
+ }
+
+ for (int i = 0; i < node_count; i++) {
+ int match = nodes[i] == args.array[i];
+ if (!match) {
+ diag("Traversal function returned nodes in wrong "
+ "order!");
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+static int test_tree_traversals()
+{
+ /*!< \todo I can test inorder and reverse inorder, but I don't know
+ * how to test others. It is somehow tested in zone tests. */
+ int errors = 0;
+
+ /* Create few nodes. (5 should be enough) */
+ knot_node_t *nodes[5];
+ for (int i = 0; i < 5; i++) {
+ char owner_string[20];
+ owner_string[0] = i + '0';
+ memcpy(owner_string + 1, ".ns.test.cz.",
+ strlen(".ns.test.cz.") + 1);
+ nodes[i] =
+ knot_node_new(knot_dname_new_from_str(owner_string,
+ strlen(owner_string),
+ NULL), NULL, 0);
+ }
+
+ if (test_traversal(nodes, 5, 0)) {
+ diag("Inorder traversal failed");
+ errors++;
+ }
+
+ for (int i = 0; i < 5; i++) {
+ char owner_string[20];
+ owner_string[0] = (5 - i) + '0';
+ memcpy(owner_string + 1, ".ns.test.cz.",
+ strlen(".ns.test.cz.") + 1);
+ nodes[i] =
+ knot_node_new(knot_dname_new_from_str(owner_string,
+ strlen(owner_string),
+ NULL), NULL, 0);
+ }
+
+ if (test_traversal(nodes, 5, 1)) {
+ diag("Reverse inorder traversal failed");
+ errors++;
+ }
+
+ return (errors == 0);
+}
+
+static int test_tree_shallow_copy()
+{
+ int errors = 0;
+ int lived = 0;
+
+ knot_zone_tree_t *tree = malloc(sizeof(knot_zone_tree_t));
+ assert(tree);
+ knot_zone_tree_init(tree);
+
+ lives_ok({
+ if (knot_zone_tree_shallow_copy(NULL, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_shallow_copy(tree, NULL) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ lived = 0;
+ if (knot_zone_tree_shallow_copy(NULL, tree) != KNOT_EBADARG) {
+ errors++;
+ }
+ lived = 1;
+ }, "zone tree: shallow copy NULL tests");
+ if (errors) {
+ diag("Zone tree shallow copy did not return KNOT_EBADARG when "
+ "given NULL arguments");
+ }
+ errors += lived != 1;
+
+ /* Create few nodes. (5 should be enough) */
+ knot_node_t *nodes[5];
+ for (int i = 0; i < 5; i++) {
+ char owner_string[20];
+ owner_string[0] = i + '0';
+ memcpy(owner_string + 1, ".ns.test.cz.",
+ strlen(".ns.test.cz.") + 1);
+ nodes[i] =
+ knot_node_new(knot_dname_new_from_str(owner_string,
+ strlen(owner_string),
+ NULL), NULL, 0);
+ /* Insert node to tree. */
+ assert(knot_zone_tree_insert(tree, nodes[i]) == KNOT_EOK);
+ }
+
+ /* Create shallow copy. */
+ knot_zone_tree_t *new_tree = malloc(sizeof(knot_zone_tree_t));
+ assert(new_tree);
+ knot_zone_tree_init(new_tree);
+
+ if (knot_zone_tree_shallow_copy(tree, new_tree) != KNOT_EOK) {
+ diag("Zone tree shallow copy did not return KNOT_EOK "
+ "when executed with valid parameters");
+ return 0;
+ }
+
+ /* Traverse the tree twice and check that arrays are the same. */
+ struct test_zone_tree_args args1;
+ args1.count = 0;
+
+ knot_zone_tree_forward_apply_inorder(tree, add_to_array,
+ &args1);
+
+
+ struct test_zone_tree_args args2;
+ args2.count = 0;
+ knot_zone_tree_forward_apply_inorder(new_tree, add_to_array,
+ &args2);
+
+ if (args1.count != args2.count) {
+ diag("Zone tree created by shallow copy has wrong count"
+ "of nodes");
+ return 0;
+ }
+
+ for (int i = 0; i < args1.count; i++) {
+ if (args1.array[i] != args2.array[i]) {
+ diag("Zone tree created by shallow copy has wrong "
+ "nodes");
+ errors++;
+ }
+ }
+
+ return (errors == 0);
+
+}
+
+
+static const int KNOT_ZONE_TREE_TEST_COUNT = 14;
+
+static int knot_zone_tree_tests_count(int argc, char *argv[])
+{
+ return KNOT_ZONE_TREE_TEST_COUNT;
+}
+
+static int knot_zone_tree_tests_run(int argc, char *argv[])
+{
+ ok(test_tree_init(), "zone tree: init");
+ ok(test_tree_insert(), "zone tree: insertion");
+ ok(test_tree_finding(), "zone tree: finding");
+ todo();
+ ok(test_tree_finding_less_or_equal(), "zone tree: find less or equal");
+ endtodo;
+ ok(test_tree_remove(), "zone tree: removal");
+ ok(test_tree_traversals(), "zone tree: traversals");
+ ok(test_tree_shallow_copy(), "zone tree: shallow copy");
+
+ return 1;
+}
diff --git a/src/tests/libknot/libknot/zone_tree_tests.h b/src/tests/libknot/libknot/zone_tree_tests.h
new file mode 100644
index 0000000..4cea88c
--- /dev/null
+++ b/src/tests/libknot/libknot/zone_tree_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTDZONE_TREE_TESTS_H_
+#define _KNOTDZONE_TREE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api zone_tree_tests_api;
+
+#endif /* _KNOTDZONE_TREE_TESTS_H_ */
diff --git a/src/tests/libknot/libknot/zonedb_tests.c b/src/tests/libknot/libknot/zonedb_tests.c
new file mode 100644
index 0000000..7b45587
--- /dev/null
+++ b/src/tests/libknot/libknot/zonedb_tests.c
@@ -0,0 +1,44 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/libknot/libknot/zonedb_tests.h"
+
+
+static int zonedb_tests_count(int argc, char *argv[]);
+static int zonedb_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api zonedb_tests_api = {
+ "Zone database", //! Unit name
+ &zonedb_tests_count, //! Count scheduled tests
+ &zonedb_tests_run //! Run scheduled tests
+};
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int zonedb_tests_count(int argc, char *argv[])
+{
+ return 0;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int zonedb_tests_run(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/src/tests/libknot/libknot/zonedb_tests.h b/src/tests/libknot/libknot/zonedb_tests.h
new file mode 100644
index 0000000..0c4f8ef
--- /dev/null
+++ b/src/tests/libknot/libknot/zonedb_tests.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ZONEDB_TESTS_H_
+#define _KNOTD_ZONEDB_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api zonedb_tests_api;
+
+#endif /* _KNOTD_ZONEDB_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/files/parsed_data b/src/tests/libknot/realdata/files/parsed_data
new file mode 100644
index 0000000..fe22b90
--- /dev/null
+++ b/src/tests/libknot/realdata/files/parsed_data
Binary files differ
diff --git a/src/tests/libknot/realdata/files/parsed_data_queries b/src/tests/libknot/realdata/files/parsed_data_queries
new file mode 100644
index 0000000..5857c87
--- /dev/null
+++ b/src/tests/libknot/realdata/files/parsed_data_queries
Binary files differ
diff --git a/src/tests/libknot/realdata/files/raw_data b/src/tests/libknot/realdata/files/raw_data
new file mode 100644
index 0000000..502005e
--- /dev/null
+++ b/src/tests/libknot/realdata/files/raw_data
Binary files differ
diff --git a/src/tests/libknot/realdata/files/raw_data_queries b/src/tests/libknot/realdata/files/raw_data_queries
new file mode 100644
index 0000000..9062d5a
--- /dev/null
+++ b/src/tests/libknot/realdata/files/raw_data_queries
Binary files differ
diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.c b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c
new file mode 100644
index 0000000..d0216c7
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c
@@ -0,0 +1,411 @@
+/* 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 <string.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "tests/libknot/realdata/libknot/dname_tests_realdata.h"
+#include "libknot/dname.h"
+#include "libknot/common.h"
+
+#include "common/print.h"
+#include "common/lists.h"
+
+static int knot_dname_tests_count(int argc, char *argv[]);
+static int knot_dname_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api dname_tests_api = {
+ "DNS library - dname", //! Unit name
+ &knot_dname_tests_count, //! Count scheduled tests
+ &knot_dname_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+int check_domain_name(const knot_dname_t *dname,
+ const test_dname_t *test_dname)
+{
+ int errors = 0;
+
+ if (dname == NULL) {
+ diag("Domain name not created!");
+ return 1;
+ }
+
+// diag("test_dname: %p, dname: %p", test_dname, dname);
+ // check size
+ if (knot_dname_size(dname) != test_dname->size) {
+ diag("Bad size of the created domain name: %u (should be %u).",
+ knot_dname_size(dname), test_dname->size);
+ ++errors;
+ } else {
+ // check wire format
+ uint size = knot_dname_size(dname);
+ if (strncmp((char *)knot_dname_name(dname),
+ (char *)test_dname->wire, size) != 0) {
+ diag("The wire format of the created "
+ "domain name is wrong:"
+ " '%.*s' (should be '%.*s').",
+ size, knot_dname_name(dname),
+ size, test_dname->wire);
+ ++errors;
+ }
+ }
+ // check labels
+ if (test_dname->label_count != dname->label_count) {
+ diag("Label count of the created domain name is wrong:"
+ " %d (should be %d)\n", dname->label_count,
+ test_dname->label_count);
+ ++errors;
+ }
+ if (strncmp((char *)dname->labels, (char *)test_dname->labels,
+ test_dname->label_count) != 0) {
+ diag("Label offsets of the created domain name are wrong.\n");
+ hex_print((char *)dname->labels, test_dname->label_count);
+ hex_print((char *)test_dname->labels, test_dname->label_count);
+ ++errors;
+ }
+
+ return errors;
+}
+
+static int test_dname_create_from_str(const list *dname_list)
+{
+ int errors = 0;
+ knot_dname_t *dname = NULL;
+
+ /* Test with real data. */
+ node *n = NULL;
+ WALK_LIST(n, *dname_list) {
+ //note("testing domain: %s", test_domains_ok[i].str);
+ test_dname_t *test_dname = (test_dname_t *)n;
+ dname = knot_dname_new_from_str(test_dname->str,
+ strlen(test_dname->str), NULL);
+ errors += check_domain_name(dname, test_dname);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_create_from_wire(const list *dname_list)
+{
+ int errors = 0;
+ knot_dname_t *dname = NULL;
+
+ node *n = NULL;
+ WALK_LIST(n, *dname_list) {
+ test_dname_t *test_dname = (test_dname_t *)n;
+ dname = knot_dname_new_from_wire(test_dname->wire,
+ test_dname->size, NULL);
+ errors += check_domain_name(dname, test_dname);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_to_str(const list *dname_list)
+{
+ int errors = 0;
+
+ /*
+ * Converts dname wireformat to string represenation, which is compared
+ * with entries in test_domains structure.
+ */
+
+ knot_dname_t *dname = NULL;
+
+ /* Test with real data. */
+ node *n = NULL;
+ WALK_LIST(n, *dname_list) {
+ //note("testing domain: %s", test_domains_ok[i].str);
+ test_dname_t *test_dname = (test_dname_t *)n;
+ dname = knot_dname_new_from_wire(
+ test_dname->wire,
+ test_dname->size,
+ NULL);
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return 0;
+ }
+
+ char *name_str = knot_dname_to_str(dname);
+ if (strcmp(name_str, test_dname->str) != 0) {
+ diag("Presentation format of domain name wrong:"
+ " %s (should be %s)",
+ name_str, test_dname->str);
+ ++errors;
+ }
+ free(name_str);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+static int test_dname_is_fqdn(const list *dname_list)
+{
+ int errors = 0;
+
+ knot_dname_t *dname;
+
+ /* All dnames from real data are fqdn */
+
+ node *n = NULL;
+ WALK_LIST(n, *dname_list) {
+ test_dname_t *test_dname = (test_dname_t *)n;
+ dname = knot_dname_new_from_wire(test_dname->wire,
+ test_dname->size, NULL);
+ errors += !knot_dname_is_fqdn(dname);
+ knot_dname_free(&dname);
+ }
+
+ return (errors == 0);
+}
+
+//static int check_wires(const uint8_t *wire1, uint size1,
+// uint8_t *wire2, uint size2)
+//{
+// if (size1 != size2) {
+// return 0;
+// }
+
+// int i;
+
+// for (i = 0; (i < size1); i++) {
+// if (wire1[i] != wire2[i]) {
+// return 0;
+// }
+// }
+
+// return 1;
+//}
+
+///* \note not to be run separately */
+//static int test_dname_name(knot_dname_t **dnames_fqdn,
+// knot_dname_t **dnames_non_fqdn)
+//{
+// assert(dnames_fqdn);
+// assert(dnames_non_fqdn);
+
+// int errors = 0;
+
+// for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+// const uint8_t *tmp_name;
+// tmp_name = knot_dname_name(dnames_fqdn[i]);
+// if (!check_wires(tmp_name, dnames_fqdn[i]->size,
+// (uint8_t *)test_domains_ok[i].wire,
+// test_domains_ok[i].size)) {
+// diag("Got bad name value from structure: "
+// "%s, should be: %s",
+// tmp_name, test_domains_ok[i].wire);
+// errors++;
+// }
+// }
+
+// for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+// const uint8_t *tmp_name;
+// tmp_name = knot_dname_name(dnames_non_fqdn[i]);
+// if (!check_wires(tmp_name, dnames_non_fqdn[i]->size,
+// (uint8_t *)test_domains_non_fqdn[i].wire,
+// test_domains_non_fqdn[i].size)) {
+// diag("Got bad name value from structure: "
+// "%s, should be: %s",
+// tmp_name, test_domains_non_fqdn[i].wire);
+// errors++;
+// }
+// }
+
+// return errors;
+//}
+
+///* \note not to be run separately */
+//static int test_dname_size(knot_dname_t **dnames_fqdn,
+// knot_dname_t **dnames_non_fqdn)
+//{
+// assert(dnames_fqdn);
+// assert(dnames_non_fqdn);
+
+// int errors = 0;
+
+// for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+// uint8_t tmp_size;
+// if ((tmp_size = knot_dname_size(dnames_fqdn[i])) !=
+// test_domains_ok[i].size) {
+// diag("Got bad size value from structure: "
+// "%u, should be: %u",
+// tmp_size, test_domains_ok[i].size);
+// errors++;
+// }
+// }
+
+// for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+// uint8_t tmp_size;
+// if ((tmp_size = knot_dname_size(dnames_non_fqdn[i])) !=
+// test_domains_non_fqdn[i].size) {
+// diag("Got bad size value from structure: "
+// "%u, should be: %u",
+// tmp_size, test_domains_non_fqdn[i].size);
+// errors++;
+// }
+// }
+
+// return errors;
+//}
+
+///* \note not to be run separately */
+//static int test_dname_node(knot_dname_t **dnames_fqdn,
+// knot_dname_t **dnames_non_fqdn)
+//{
+// assert(dnames_fqdn);
+// assert(dnames_non_fqdn);
+
+// int errors = 0;
+
+// for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+// const knot_node_t *tmp_node;
+// if ((tmp_node = knot_dname_node(dnames_fqdn[i])) !=
+// NODE_ADDRESS) {
+// diag("Got bad node value from structure: "
+// "%p, should be: %p",
+// tmp_node, NODE_ADDRESS);
+// errors++;
+// }
+// }
+
+// for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+// const knot_node_t *tmp_node;
+// if ((tmp_node = knot_dname_node(dnames_non_fqdn[i])) !=
+// NODE_ADDRESS) {
+// diag("Got bad node value from structure: "
+// "%s, should be: %s",
+// tmp_node, NODE_ADDRESS);
+// errors++;
+// }
+// }
+
+// return errors;
+//}
+
+//static int test_dname_getters(uint type)
+//{
+// int errors = 0;
+
+// knot_dname_t *dnames_fqdn[TEST_DOMAINS_OK];
+// knot_dname_t *dnames_non_fqdn[TEST_DOMAINS_NON_FQDN];
+
+// for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+// dnames_fqdn[i] = knot_dname_new_from_wire(
+// (uint8_t *)test_domains_ok[i].wire,
+// test_domains_ok[i].size, NODE_ADDRESS);
+// assert(dnames_fqdn[i] != NULL);
+// }
+
+// for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+// dnames_non_fqdn[i] = knot_dname_new_from_wire(
+// (uint8_t *)test_domains_non_fqdn[i].wire,
+// test_domains_non_fqdn[i].size, NODE_ADDRESS);
+// assert(dnames_non_fqdn[i] != NULL);
+// }
+
+// switch (type) {
+// case 0: {
+// errors += test_dname_name(dnames_fqdn, dnames_non_fqdn);
+// break;
+// }
+
+// case 1: {
+// errors += test_dname_size(dnames_fqdn, dnames_non_fqdn);
+// break;
+// }
+
+// case 2: {
+// errors += test_dname_node(dnames_fqdn, dnames_non_fqdn);
+// break;
+// }
+// } /* switch */
+
+// for (int i = 0; i < TEST_DOMAINS_OK; i++) {
+// knot_dname_free(&dnames_fqdn[i]);
+// }
+
+// for (int i = 0; i < TEST_DOMAINS_NON_FQDN; i++) {
+// knot_dname_free(&dnames_non_fqdn[i]);
+// }
+
+// return (errors == 0);
+//}
+
+static const int KNOT_DNAME_TEST_COUNT = 4;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_dname_tests_count(int argc, char *argv[])
+{
+ return KNOT_DNAME_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_dname_tests_run(int argc, char *argv[])
+{
+ const test_data_t *data = data_for_knot_tests;
+
+ int res = 0,
+ res_str = 0,
+ res_wire = 0,
+ res_str_non_fqdn = 0,
+ res_final = 1;
+
+ ok((res_str = test_dname_create_from_str(&data->dname_list)),
+ "dname: create from string");
+ ok((res_wire = test_dname_create_from_wire(&data->dname_list)),
+ "dname: create from wire");
+
+ res_final *= res_str;
+ res_final *= res_wire;
+ res_final *= res_str_non_fqdn;
+
+// res = test_dname_getters(0);
+// ok(res, "dname: name");
+
+// res = test_dname_getters(1);
+// ok(res, "dname: size");
+
+// res = test_dname_getters(2);
+// ok(res, "dname: node");
+
+// skip(!res_str || !res_wire || !res_str_non_fqdn, 2);
+
+ ok((res = test_dname_to_str(&data->dname_list)),
+ "dname: convert to str");
+ res_final *= res;
+
+// endskip; /* !res_str || !res_wire */
+
+ ok((res = test_dname_is_fqdn(&data->dname_list)), "dname: fqdn");
+ res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.h b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h
new file mode 100644
index 0000000..a7d75aa
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_DNAME_TESTS_H_
+#define _KNOTD_DNAME_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api dname_tests_api;
+
+#endif /* _KNOTD_DNAME_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.c b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c
new file mode 100644
index 0000000..257d480
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c
@@ -0,0 +1,563 @@
+/* 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 "tests/libknot/realdata/libknot/edns_tests_realdata.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/common.h"
+#include "libknot/edns.h"
+
+static int knot_edns_tests_count(int argc, char *argv[]);
+static int knot_edns_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api edns_tests_api = {
+ "DNS library - EDNS", //! Unit name
+ &knot_edns_tests_count, //! Count scheduled tests
+ &knot_edns_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+///* Creates actual knot_opt_rr_t variable from test_edns_t variable */
+//static knot_opt_rr_t *opt_rr_from_test_edns(test_edns_t *test_edns)
+//{
+// knot_opt_rr_t *ret = knot_edns_new();
+
+// CHECK_ALLOC_LOG(ret, NULL);
+
+// ret->flags = test_edns->flags;
+// ret->ext_rcode = test_edns->ext_rcode;
+// ret->payload = test_edns->payload;
+// ret->version = test_edns->version;
+
+// for (int i = 0; i < test_edns->option_count; i++) {
+// if (knot_edns_add_option(ret, test_edns->options[i].code,
+// test_edns->options[i].length,
+// test_edns->options[i].data) != 0) {
+// knot_edns_free(&ret);
+// return NULL;
+// }
+// }
+
+// return ret;
+//}
+
+///* simple wire compare - 0 if same, 1 otherwise */
+//static int edns_compare_wires(uint8_t *wire1,
+// uint8_t *wire2,
+// uint16_t length)
+//{
+// for (uint i = 0; i < length; i++) {
+// if (wire1[i] != wire2[i]) {
+// return 1;
+// }
+// }
+
+// return 0;
+//}
+
+//static int check_edns(const knot_opt_rr_t *edns,
+// const test_edns_t *test_edns)
+//{
+// if (edns->option_count != test_edns->option_count) {
+// diag("Option count is wrong");
+// return -1;
+// }
+
+// for (int i = 0; i < edns->option_count; i++) {
+// /* check options */
+// if (edns->options[i].code != test_edns->options[i].code) {
+// diag("Code in options is wrong");
+// return -1;
+// }
+
+// if (edns->options[i].length != test_edns->options[i].length) {
+// diag("Length in options is wrong");
+// return -1;
+// }
+
+// if (edns_compare_wires(edns->options[i].data,
+// test_edns->options[i].data,
+// edns->options[i].length) != 0) {
+// diag("Data in options are wrong");
+// return -1;
+// }
+// }
+
+// if (edns->version != test_edns->version) {
+// diag("Version is wrong");
+// return -1;
+// }
+
+// if (edns->flags != test_edns->flags) {
+// diag("Flags are wrong");
+// return -1;
+// }
+
+// if (edns->size != test_edns->size) {
+// diag("Size is wrong");
+// return -1;
+// }
+
+// return 0;
+//}
+
+//static int test_edns_get_payload(const knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// if (knot_edns_get_payload(edns) !=
+// test_edns->payload) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_get_ext_rcode(const knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// if (knot_edns_get_ext_rcode(edns) !=
+// test_edns->ext_rcode) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_get_flags(const knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// if (knot_edns_get_flags(edns) !=
+// test_edns->flags) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_get_version(const knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// if (knot_edns_get_version(edns) !=
+// test_edns->version) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_do(const knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// if (knot_edns_do(edns) !=
+// (test_edns->flags & KNOT_EDNS_DO_MASK)) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_size(knot_opt_rr_t *edns, test_edns_t *test_edns)
+//{
+// diag("%d %d\n", edns->size, test_edns->size);
+// if (knot_edns_size(edns) !=
+// test_edns->size) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_set_payload(knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// knot_edns_set_payload(edns, test_edns->payload);
+
+// if (edns->payload !=
+// test_edns->payload) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_set_ext_rcode(knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// knot_edns_set_ext_rcode(edns, test_edns->ext_rcode);
+// if (edns->ext_rcode !=
+// test_edns->ext_rcode) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_set_version(knot_opt_rr_t *edns,
+// test_edns_t *test_edns)
+//{
+// knot_edns_set_version(edns,
+// test_edns->version);
+
+// if (edns->version !=
+// test_edns->version) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_set_do(knot_opt_rr_t *edns)
+//{
+// knot_edns_set_do(edns);
+
+// if (!knot_edns_do(edns)) {
+// return 0;
+// } else {
+// return 1;
+// }
+//}
+
+//static int test_edns_getters(uint type)
+//{
+// int errors = 0;
+// for (int i = 0; i < TEST_EDNS; i++) {
+// knot_opt_rr_t *edns =
+// opt_rr_from_test_edns(&(test_edns_data[i]));
+// if (edns == NULL) {
+// ERR_ALLOC_FAILED;
+// return -1;
+// }
+
+// switch(type) {
+// case 0:
+// if (test_edns_get_payload(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong payload!");
+// errors++;
+// }
+// break;
+// case 1:
+// if (test_edns_get_ext_rcode(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong extended RCODE!");
+// errors++;
+// }
+// break;
+// case 2:
+// if (test_edns_get_flags(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong flags!");
+
+// errors++;
+// }
+// break;
+// case 3:
+// if (test_edns_get_version(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong version!");
+// errors++;
+// }
+// break;
+// case 4:
+// if (test_edns_do(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong DO bit!");
+// errors++;
+// }
+// break;
+// case 5:
+// if (test_edns_size(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Got wrong size!");
+// errors++;
+// }
+// break;
+// default:
+// diag("Unknown option");
+// errors++;
+// } /* switch */
+
+// knot_edns_free(&edns);
+// }
+
+// return (errors == 0);
+//}
+
+//static int test_edns_setters(uint type)
+//{
+// int errors = 0;
+// for (int i = 0; i < TEST_EDNS; i++) {
+// knot_opt_rr_t *edns =
+// opt_rr_from_test_edns(&(test_edns_data[i]));
+// if (edns == NULL) {
+// ERR_ALLOC_FAILED;
+// return -1;
+// }
+
+// switch(type) {
+// case 0:
+// if (test_edns_set_payload(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Set wrong payload!");
+// errors++;
+// }
+// break;
+// case 1:
+// if (test_edns_set_ext_rcode(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Set wrong ext_rcode");
+// errors++;
+// }
+// break;
+// case 2:
+// if (test_edns_set_version(edns,
+// &test_edns_data[i]) != 1) {
+// diag("Set wrong version!");
+// errors++;
+// }
+// break;
+// case 3:
+// if (test_edns_set_do(edns) != 1) {
+// diag("Set wrong DO bit!");
+// errors++;
+// }
+// break;
+// default:
+// diag("Unknown option");
+// errors++;
+// } /* switch */
+
+// knot_edns_free(&edns);
+// }
+
+// return (errors == 0);
+//}
+
+//static int test_edns_wire()
+//{
+// /*
+// * Tests to_wire and from_wire in one test.
+// */
+// for (int i = 0; i < TEST_EDNS; i++) {
+// /* Creates instance from test_edns_t. */
+// knot_opt_rr_t *edns =
+// opt_rr_from_test_edns(&(test_edns_data[i]));
+// if (edns == NULL) {
+// ERR_ALLOC_FAILED;
+// return -1;
+// }
+
+// uint8_t *wire = NULL;
+// wire = malloc(sizeof(uint8_t) * edns->size);
+// CHECK_ALLOC_LOG(wire, 0);
+
+// /* Converts EDNS to wire. */
+// short wire_size = knot_edns_to_wire(edns, wire, 100);
+
+// if (wire_size == -1) {
+// diag("Could not create EDNS wire");
+// return 0;
+// }
+
+// knot_opt_rr_t *edns_from_wire = knot_edns_new();
+// if (edns == NULL) {
+// return 0;
+// }
+
+// /* TODO use some constant */
+// /* Creates new EDNS from wire */
+// if (knot_edns_new_from_wire(edns_from_wire,
+// wire,
+// 100) <= 0) {
+// diag("Could not create from wire");
+// return 0;
+// }
+
+// /* Checks whether EDNS created from wire is the same */
+// if (check_edns(edns_from_wire,
+// &(test_edns_data[i])) != 0) {
+// diag("EDNS created from wire is different from the "
+// "original one");
+// }
+
+// free(wire);
+// knot_edns_free(&edns_from_wire);
+// knot_edns_free(&edns);
+// }
+// return 1;
+//}
+
+//static int test_edns_add_option()
+//{
+// /*
+// * Create empty EDNS and add options one by one, testing their presence.
+// */
+// for (int i = 0; i < TEST_EDNS; i++) {
+// knot_opt_rr_t *edns = knot_edns_new();
+// assert(edns->option_count == 0);
+
+// if (edns == NULL) {
+// ERR_ALLOC_FAILED;
+// return 0;
+// }
+
+// for (int j = 0; j < test_edns_data[i].option_count; j++) {
+// if (knot_edns_add_option(edns,
+// test_edns_data[i].options[j].code,
+// test_edns_data[i].options[j].length,
+// test_edns_data[i].options[j].
+// data) != 0) {
+// diag("Could not add option");
+// return 0;
+// }
+
+// if (edns->options[j].code !=
+// test_edns_data[i].options[j].code) {
+// diag("Option code wrongly added!");
+// return 0;
+// }
+
+// if (edns->options[j].length !=
+// test_edns_data[i].options[j].length) {
+// diag("Option length wrongly added!");
+// return 0;
+// }
+
+// if (edns_compare_wires(edns->options[j].data,
+// test_edns_data[i].
+// options[j].data,
+// edns->options[j].length) != 0) {
+// diag("Option wire wrongly added!");
+// return 0;
+// }
+// }
+// knot_edns_free(&edns);
+// }
+// return 1;
+//}
+
+//static int test_edns_has_option()
+//{
+// /*
+// * Create empty EDNS and add options one by one, testing their presence
+// */
+// for (int i = 0; i < TEST_EDNS; i++) {
+// knot_opt_rr_t *edns = knot_edns_new();
+// assert(edns->option_count == 0);
+
+// if (edns == NULL) {
+// ERR_ALLOC_FAILED;
+// return 0;
+// }
+
+// for (int j = 0; j < test_edns_data[i].option_count; j++) {
+// if (knot_edns_add_option(edns,
+// test_edns_data[i].options[j].code,
+// test_edns_data[i].options[j].length,
+// test_edns_data[i].options[j].
+// data) != 0) {
+// diag("Could not add option");
+// return 0;
+// }
+
+// if (knot_edns_has_option(edns,
+// test_edns_data[i].options[j].code) != 1) {
+// diag("Option not found!");
+// return 0;
+// }
+// }
+// knot_edns_free(&edns);
+// }
+// return 1;
+//}
+
+static const int KNOT_EDNS_TESTS_COUNT = 0;
+
+///*! This helper routine should report number of
+// * scheduled tests for given parameters.
+// */
+static int knot_edns_tests_count(int argc, char *argv[])
+{
+ return KNOT_EDNS_TESTS_COUNT;
+}
+
+///*! Run all scheduled tests for given parameters.
+// */
+static int knot_edns_tests_run(int argc, char *argv[])
+{
+// int res = 0;
+ int res_final = 1;
+
+// res = test_edns_getters(0);
+// ok(res, "EDNS: get payload");
+// res_final *= res;
+
+// res = test_edns_getters(1);
+// ok(res, "EDNS: get extenden RCODE");
+// res_final *= res;
+
+// res = test_edns_getters(2);
+// ok(res, "EDNS: get flags");
+// res_final *= res;
+
+// res = test_edns_getters(3);
+// ok(res, "EDNS: get version");
+// res_final *= res;
+
+// res = test_edns_getters(4);
+// ok(res, "EDNS: do");
+// res_final *= res;
+
+// res = test_edns_getters(5);
+// ok(res, "EDNS: size");
+// res_final *= res;
+
+// res = test_edns_setters(0);
+// ok(res, "EDNS: set payload");
+// res_final *= res;
+
+// res = test_edns_setters(1);
+// ok(res, "EDNS: set extended RCODE");
+// res_final *= res;
+
+// res = test_edns_setters(2);
+// ok(res, "EDNS: set version");
+// res_final *= res;
+
+// res = test_edns_setters(3);
+// ok(res, "EDNS: set DO");
+// res_final *= res;
+
+// res = test_edns_add_option();
+// ok(res, "EDNS: add option");
+// res_final *= res;
+
+// res = test_edns_has_option();
+// ok(res, "EDNS: has option");
+// res_final *= res;
+
+// res = test_edns_wire();
+// ok(res, "EDNS: to_wire and from_wire");
+// res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.h b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h
new file mode 100644
index 0000000..cfa64b0
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h
@@ -0,0 +1,35 @@
+/*!
+ * \file edns_tests.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * Contains unit tests for ENDS API
+ *
+ * Contains tests for:
+ * - ENDS API
+ */
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD__EDNS_TESTS_H_
+#define _KNOTD__EDNS_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api edns_tests_api;
+
+#endif /* _KNOTD__EDNS_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.c b/src/tests/libknot/realdata/libknot/node_tests_realdata.c
new file mode 100644
index 0000000..3218c8e
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.c
@@ -0,0 +1,385 @@
+/* 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 "tests/libknot/realdata/libknot/node_tests_realdata.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/dname.h"
+#include "libknot/zone/node.h"
+#include "libknot/util/descriptor.h"
+
+static int knot_node_tests_count(int argc, char *argv[]);
+static int knot_node_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api node_tests_api = {
+ "DNS library - node", //! Unit name
+ &knot_node_tests_count, //! Count scheduled tests
+ &knot_node_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+/* TODO It would be wise not to name variables totally same as structures ... */
+knot_dname_t *dname_from_test_dname(const test_dname_t *test_dname)
+{
+ assert(test_dname != NULL);
+ knot_dname_t *ret = knot_dname_new_from_wire(test_dname->wire,
+ test_dname->size,
+ NULL);
+ CHECK_ALLOC(ret, NULL);
+
+ return ret;
+}
+
+static knot_rrset_t *rrset_from_test_rrset(const test_rrset_t *test_rrset)
+{
+ assert(test_rrset != NULL);
+ knot_dname_t *owner = dname_from_test_dname(test_rrset->owner);
+ CHECK_ALLOC(owner, NULL);
+
+ knot_rrset_t *ret = knot_rrset_new(owner, test_rrset->type,
+ test_rrset->rclass,
+ test_rrset->ttl);
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&owner);
+ return NULL;
+ }
+
+ return ret;
+}
+
+static int test_node_create(const list *node_list)
+{
+ /* Tests creation of node by comparing with test_node struct */
+ knot_node_t *tmp;
+ int errors = 0;
+
+ node *n = NULL;
+ WALK_LIST(n, *node_list) {
+ const test_node_t *tmp_node = (test_node_t *)n;
+ assert(tmp_node);
+
+ knot_dname_t *owner =
+ dname_from_test_dname(tmp_node->owner);
+ if (owner == NULL) {
+ return 0;
+ }
+ tmp = knot_node_new(owner,
+ (knot_node_t *)tmp_node->parent, 0);
+ if (tmp == NULL ||
+ (strncmp((char *)tmp->owner->name,
+ (char *)tmp_node->owner->wire,
+ tmp->owner->size) != 0) ||
+ tmp->parent != (knot_node_t *)tmp_node->parent ||
+ tmp->rrset_tree == NULL) {
+ errors++;
+ diag("Failed to create node structure");
+ }
+ knot_node_free(&tmp, 0, 0);
+ }
+
+ return (errors == 0);
+}
+
+static int test_node_add_rrset(list *rrset_list)
+{
+ knot_node_t *tmp;
+ knot_rrset_t *rrset;
+ int errors = 0;
+
+ node *n = NULL;
+ WALK_LIST(n, *rrset_list) {
+ test_rrset_t *test_rrset = (test_rrset_t *)n;
+ rrset = rrset_from_test_rrset(test_rrset);
+ if (rrset == NULL) {
+ diag("Could not create rrset from test data");
+ return 0;
+ }
+
+ /* create node from test_node structure. Always the first one.*/
+ knot_dname_t *owner =
+ dname_from_test_dname(test_rrset->owner);
+ if (owner == NULL) {
+ diag("Could not create owner from test data");
+ return 0;
+ }
+
+ tmp = knot_node_new(owner, NULL, 0);
+
+ if (knot_node_add_rrset(tmp, rrset, 0) != 0) {
+ errors++;
+ diag("Failed to insert rrset into node");
+ }
+
+ /* check if rrset is really there */
+
+ const knot_rrset_t *rrset_from_node = NULL;
+ if ((rrset_from_node =
+ knot_node_rrset(tmp, rrset->type)) == NULL) {
+ errors++;
+ diag("Inserted rrset could not be found");
+ continue;
+ }
+
+ /* compare rrset from node with original rrset */
+
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ int cmp = 0;
+
+ if ((rrset_from_node->rdata == NULL) &&
+ (rrset->rdata == NULL)) {
+ cmp = 0;
+ } else if ((rrset_from_node->rdata != NULL) &&
+ (rrset->rdata != NULL)) {
+ cmp = knot_rdata_compare(rrset_from_node->rdata,
+ rrset->rdata,
+ desc->wireformat);
+ } else { /* one is not NULL and other is -> error */
+ cmp = 1;
+ }
+
+ if (!((rrset_from_node->type == rrset->type) &&
+ (rrset_from_node->rclass == rrset->rclass) &&
+ (rrset_from_node->ttl == rrset->ttl) &&
+ (rrset_from_node->rrsigs == rrset->rrsigs) &&
+ (cmp == 0))) {
+ errors++;
+ diag("Values in found rrset are wrong");
+ }
+
+ knot_node_free(&tmp, 1, 0);
+ }
+
+ return (errors == 0);
+}
+
+//static int test_node_get_rrset()
+//{
+// knot_node_t *tmp;
+// knot_rrset_t *rrset;
+// int errors = 0;
+
+// knot_node_t *nodes[TEST_NODES];
+
+// for (int i = 0; i < TEST_NODES && !errors; i++) {
+// tmp = knot_node_new(&test_nodes[i].owner,
+// test_nodes[i].parent);
+// nodes[i] = tmp;
+// for (int j = 0; j < RRSETS; j++) {
+// knot_node_add_rrset(tmp, &rrsets[j]);
+// }
+// }
+
+// for (int i = 0; i < TEST_NODES && !errors; i++) {
+// for (int j = 0; j < RRSETS; j++) {
+// rrset = &rrsets[j];
+// if (knot_node_rrset(nodes[i], rrset->type)
+// != rrset) {
+// errors++;
+// diag("Failed to get proper rrset from node");
+// }
+// }
+// knot_node_free(&nodes[i], 0);
+// }
+
+// return (errors == 0);
+//}
+
+//static int test_node_get_parent()
+//{
+// knot_node_t *tmp;
+// knot_rrset_t *rrset;
+// int errors = 0;
+
+// knot_node_t *nodes[TEST_NODES];
+
+// for (int i = 0; i < TEST_NODES && !errors; i++) {
+// tmp = knot_node_new(&test_nodes[i].owner,
+// test_nodes[i].parent);
+// nodes[i] = tmp;
+// rrset = &rrsets[i];
+// knot_node_add_rrset(tmp, rrset);
+// }
+
+// for (int i = 0; i < TEST_NODES && !errors; i++) {
+// rrset = &rrsets[i];
+// if (knot_node_parent(nodes[i]) != test_nodes[i].parent) {
+// errors++;
+// diag("Failed to get proper parent from node");
+// }
+// knot_node_free(&nodes[i], 0);
+// }
+// return (errors == 0);
+//}
+
+//static int test_node_sorting()
+//{
+// knot_node_t *tmp = NULL;
+// knot_rrset_t *rrset = NULL;
+// int errors = 0;
+
+// knot_dname_t *owner = dname_from_test_dname(test_nodes[0].owner);
+
+// tmp = knot_node_new(owner,
+// (knot_node_t *)test_nodes[0].parent);
+
+// /* Will add rrsets to node. */
+// knot_node_add_rrset(tmp, rrset);
+// }
+
+// const skip_node_t *node = skip_first(tmp->rrsets);
+
+// int last = *((uint16_t *)node->key);
+
+// /* TODO there is now an API function knot_node_rrsets ... */
+
+// /* Iterates through skip list and checks, whether it is sorted. */
+
+// while ((node = skip_next(node)) != NULL) {
+// if (last > *((uint16_t *)node->key)) {
+// errors++;
+// diag("RRset sorting error");
+// }
+// last = *((uint16_t *)node->key);
+// }
+
+// knot_node_free(&tmp, 1);
+// return (errors == 0);
+//}
+
+//static int test_node_delete()
+//{
+// int errors = 0;
+
+// knot_node_t *tmp_node;
+
+// for (int i = 0; i < TEST_NODES; i++) {
+// knot_dname_t *owner =
+// dname_from_test_dname(test_nodes[i].owner);
+// tmp_node = knot_node_new(owner,
+// (knot_node_t *)test_nodes[i].parent);
+
+// knot_node_free(&tmp_node, 1);
+
+// errors += (tmp_node != NULL);
+// }
+
+// return (errors == 0);
+//}
+
+//static int test_node_set_parent()
+//{
+// knot_node_t *tmp_parent = (knot_node_t *)0xABCDEF;
+// int errors = 0;
+
+// knot_node_t *tmp_node;
+
+// for (int i = 0; i < TEST_NODES; i++) {
+// tmp_node = knot_node_new(&test_nodes[i].owner,
+// test_nodes[i].parent);
+
+// knot_node_set_parent(tmp_node, tmp_parent);
+
+// if (tmp_node->parent != tmp_node->parent) {
+// diag("Parent node is wrongly set.");
+// errors++;
+// }
+// knot_node_free(&tmp_node, 0);
+// }
+// return (errors == 0);
+//}
+
+//static int test_node_free_rrsets()
+//{
+// int errors = 0;
+
+// knot_node_t *tmp_node;
+
+// for (int i = 0; i < TEST_NODES; i++) {
+// knot_dname_t *owner =
+// dname_from_test_dname(test_nodes[i].owner);
+// if (owner == NULL) {
+// return 0;
+// }
+
+// tmp_node = knot_node_new(owner,
+// (knot_node_t *)test_nodes[i].parent);
+
+// knot_node_free_rrsets(tmp_node, 0);
+
+// errors += (tmp_node->rrsets != NULL);
+
+// knot_node_free(&tmp_node, 1);
+// }
+// return (errors == 0);
+//}
+
+static const int KNOT_NODE_TEST_COUNT = 2;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_node_tests_count(int argc, char *argv[])
+{
+ return KNOT_NODE_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_node_tests_run(int argc, char *argv[])
+{
+ test_data_t *data = data_for_knot_tests;
+ int res = 0,
+ res_final = 1;
+
+ res = test_node_create(&data->node_list);
+ ok(res, "node: create");
+ res_final *= res;
+
+
+ ok((res = test_node_add_rrset(&data->rrset_list)), "node: add");
+ res_final *= res;
+
+// ok((res = test_node_get_rrset()), "node: get");
+// res_final *= res;
+
+// ok((res = test_node_get_parent()), "node: get parent");
+// res_final *= res;
+
+// ok((res = test_node_set_parent()), "node: set parent");
+// res_final *= res;
+
+// ok((res = test_node_sorting()), "node: sort");
+// res_final *= res;
+
+// ok((res = test_node_free_rrsets()), "node: free rrsets");
+// res_final *= res;
+
+// endskip;
+
+// ok((res = test_node_delete()), "node: delete");
+// //res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.h b/src/tests/libknot/realdata/libknot/node_tests_realdata.h
new file mode 100644
index 0000000..a90179f
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_NODE_TESTS_H_
+#define _KNOTD_NODE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api node_tests_api;
+
+#endif /* _KNOTD_NODE_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.c b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c
new file mode 100644
index 0000000..08c0882
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c
@@ -0,0 +1,679 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+
+#include <config.h>
+#include "knot/common.h"
+#include "packet_tests_realdata.h"
+#include "libknot/util/error.h"
+#include "libknot/packet/packet.h"
+#include "libknot/packet/response.h"
+/* *test_t structures */
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#ifdef TEST_WITH_LDNS
+#include "ldns/ldns.h"
+#endif
+
+static int packet_tests_count(int argc, char *argv[]);
+static int packet_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api packet_tests_api = {
+ "DNS library - packet", //! Unit name
+ &packet_tests_count, //! Count scheduled tests
+ &packet_tests_run //! Run scheduled tests
+};
+
+#ifdef TEST_WITH_LDNS
+/* Compares one rdata knot with rdata from ldns.
+ * Comparison is done through comparing wireformats.
+ * Returns 0 if rdata are the same, 1 otherwise
+ */
+int compare_rr_rdata(knot_rdata_t *rdata, ldns_rr *rr,
+ uint16_t type)
+{
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ for (int i = 0; i < rdata->count; i++) {
+ /* check for ldns "descriptors" as well */
+
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME) {
+ if (rdata->items[i].dname->size !=
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) {
+ diag("%s", rdata->items[i].dname->name);
+ diag("%s", ldns_rdf_data(ldns_rr_rdf(rr, i)));
+ diag("%d", ldns_rdf_size(ldns_rr_rdf(rr, i)));
+ diag("%d", rdata->items[i].dname->size);
+ diag("Dname sizes in rdata differ");
+ return 1;
+ }
+ if (compare_wires_simple(rdata->items[i].dname->name,
+ ldns_rdf_data(ldns_rr_rdf(rr, i)),
+ rdata->items[i].dname->size) != 0) {
+ diag("%s", rdata->items[i].dname->name);
+ diag("%s", ldns_rdf_data(ldns_rr_rdf(rr, i)));
+ diag("Dname wires in rdata differ");
+ return 1;
+ }
+ } else {
+ /* Compare sizes first, then actual data */
+ if (rdata->items[i].raw_data[0] !=
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) {
+ /* \note ldns stores the size including the
+ * length, knot does not */
+ diag("Raw data sizes in rdata differ");
+ diag("knot: %d ldns: %d",
+ rdata->items[i].raw_data[0],
+ ldns_rdf_size(ldns_rr_rdf(rr, i)));
+// hex_print((char *)
+// (rdata->items[i].raw_data + 1),
+// rdata->items[i].raw_data[0]);
+// hex_print((char *)ldns_rdf_data(ldns_rr_rdf(rr,
+// i)),
+// ldns_rdf_size(ldns_rr_rdf(rr, i)));
+ if (abs(rdata->items[i].raw_data[0] -
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) != 1) {
+ return 1;
+ }
+ }
+ if (compare_wires_simple((uint8_t *)
+ (rdata->items[i].raw_data + 1),
+ ldns_rdf_data(ldns_rr_rdf(rr, i)),
+ rdata->items[i].raw_data[0]) != 0) {
+// hex_print((char *)
+// (rdata->items[i].raw_data + 1),
+// rdata->items[i].raw_data[0]);
+// hex_print((char *)
+// ldns_rdf_data(ldns_rr_rdf(rr, i)),
+// rdata->items[i].raw_data[0]);
+ diag("Raw data wires in rdata differ in item "
+ "%d", i);
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int compare_rrset_w_ldns_rr(const knot_rrset_t *rrset,
+ ldns_rr_list *rr_set, char check_rdata)
+{
+ /* We should have only one rrset from ldns, although it is
+ * represented as rr_list ... */
+
+ int errors = 0;
+
+ ldns_rr *rr = ldns_rr_list_rr(rr_set, 0);
+ assert(rr);
+ assert(rrset);
+
+ /* compare headers */
+
+ if (rrset->owner->size != ldns_rdf_size(ldns_rr_owner(rr))) {
+ char *tmp_dname = knot_dname_to_str(rrset->owner);
+ diag("RRSet owner names differ in length");
+ diag("ldns: %d, knot: %d", ldns_rdf_size(ldns_rr_owner(rr)),
+ rrset->owner->size);
+ diag("%s", tmp_dname);
+ diag("%s", ldns_rdf_data(ldns_rr_owner(rr)));
+ free(tmp_dname);
+ errors++;
+ }
+
+ if (compare_wires_simple(rrset->owner->name,
+ ldns_rdf_data(ldns_rr_owner(rr)),
+ rrset->owner->size) != 0) {
+ diag("RRSet owner wireformats differ");
+ diag("%s \\w %s\n", rrset->owner->name,
+ ldns_rdf_data(ldns_rr_owner(rr)));
+ errors++;
+ }
+
+ if (rrset->type != ldns_rr_get_type(rr)) {
+ diag("RRset types differ");
+ diag("knot type: %d Ldns type: %d", rrset->type,
+ ldns_rr_get_type(rr));
+ errors++;
+ }
+
+ if (rrset->rclass != ldns_rr_get_class(rr)) {
+ diag("RRset classes differ");
+ errors++;
+ }
+
+ if (rrset->ttl != ldns_rr_ttl(rr)) {
+ diag("RRset TTLs differ");
+ diag("knot: %d ldns: %d", rrset->ttl, ldns_rr_ttl(rr));
+ errors++;
+ }
+
+ /* compare rdatas */
+
+ if (rrset->rdata == NULL) {
+ diag("RRSet has no RDATA!");
+ return errors;
+ }
+ knot_rdata_t *tmp_rdata = rrset->rdata;
+
+ int i = 0;
+
+ while ((rr = ldns_rr_list_pop_rr(rr_set))) {
+ assert(rr);
+
+ if (compare_rr_rdata(tmp_rdata, rr, rrset->type) != 0) {
+ diag("Rdata differ");
+ return 1;
+ }
+
+ tmp_rdata = tmp_rdata->next;
+ i++;
+ }
+
+//// if (check_rdata) {
+//// if (compare_rr_rdata(rrset->rdata, rr, rrset->type) != 0) {
+//// diag("Rdata differ");
+//// errors++;
+//// }
+//// }
+
+ return errors;
+}
+
+int compare_rrsets_w_ldns_rrlist(const knot_rrset_t **rrsets,
+ ldns_rr_list *rrlist, int count)
+{
+ int errors = 0;
+
+ /* There are no rrsets currenty. Everything is just rr */
+
+ ldns_rr_list *rr_set = NULL;
+
+ ldns_rr_list_sort(rrlist);
+
+ if (count < 0) {
+ return 0;
+ }
+
+ for (int i = 0; i < count ; i++) {
+ /* normally ldns_pop_rrset or such should be here */
+
+ rr_set = ldns_rr_list_pop_rrset(rrlist);
+ /* Get one rr from list. */
+ ldns_rr *rr = ldns_rr_list_rr(rr_set, 0);
+ assert(rr);
+
+ if (rr_set == NULL) {
+ diag("Ldns and knot structures have different "
+ "counts of rrsets.");
+ diag("knot: %d ldns: %d",
+ count, (count - 1) - i);
+ return -1;
+ }
+
+// diag("RRset from ldns is %d long", ldns_rr_list_rr_count(rr_set));
+
+// diag("Got type from ldns: %d (%d)\n", ldns_rr_get_type(rr), i);
+
+ int j = 0;
+ for (j = 0; j < count; j++) {
+// diag("Got type from knot: %d\n", rrsets[j]->type);
+ if (rrsets[j]->type == ldns_rr_get_type(rr) &&
+ rrsets[j]->owner->size ==
+ ldns_rdf_size(ldns_rr_owner(rr)) &&
+ (compare_wires_simple(ldns_rdf_data(ldns_rr_owner(rr)), rrsets[j]->owner->name,
+ rrsets[j]->owner->size) == 0)) {
+ errors += compare_rrset_w_ldns_rr(rrsets[j],
+ rr_set, 1);
+ break;
+ }
+ }
+ if (j == count) {
+ diag("There was no RRSet of the same type!");
+// errors++;
+ }
+ }
+
+ return errors;
+}
+
+int check_packet_w_ldns_packet(knot_packet_t *packet,
+ ldns_pkt *ldns_packet,
+ int check_header,
+ int check_question,
+ int check_body,
+ int check_edns)
+{
+ int errors = 0;
+ if (check_header) {
+// if (packet->header.id != ldns_pkt_id(ldns_packet)) {
+// diag("response ID does not match - %d %d",
+// packet->header.id,
+// ldns_pkt_id(ldns_packet));
+// errors++;
+// }
+
+ /* qdcount is always 1 in knot's case */
+
+ /* TODO check flags1 and flags2 - no API for that,
+ * write my own */
+
+ if (packet->header.ancount !=
+ ldns_pkt_ancount(ldns_packet)) {
+ diag("Answer RRSet count wrongly converted");
+ errors++;
+ }
+
+ if (packet->header.nscount !=
+ ldns_pkt_nscount(ldns_packet)) {
+ diag("Authority RRSet count wrongly converted.\n"
+ "got %d should be %d",
+ packet->header.nscount,
+ ldns_pkt_nscount(ldns_packet));
+ errors++;
+ }
+
+ /* - 1 because ldns does not include OPT_RR to additional "
+ "section */
+ int minus = (!ldns_pkt_edns_version(ldns_packet)) ? 1 : 0;
+// int minus = 0;
+
+ if ((packet->header.arcount - minus) !=
+ ldns_pkt_arcount(ldns_packet)) {
+ diag("Additional RRSet count wrongly converted.\n"
+ "got %d should be %d",
+ packet->header.arcount,
+ ldns_pkt_arcount(ldns_packet));
+ errors++;
+ }
+
+ /*!< \todo Check OPT RR! */
+
+ if (errors) {
+ return errors;
+ }
+ }
+ /* Header checked */
+
+ /* Question section */
+
+ int ret = 0;
+ if (check_question) {
+ knot_rrset_t *question_rrset =
+ knot_rrset_new(packet->
+ question.qname,
+ packet->
+ question.qtype,
+ packet->
+ question.qclass,
+ 3600);
+
+ if ((ret = compare_rrset_w_ldns_rr(question_rrset,
+ ldns_pkt_question(ldns_packet), 0)) != 0) {
+ diag("Question rrsets wrongly converted");
+ errors++;
+ }
+ knot_rrset_free(&question_rrset);
+ }
+
+ if (check_body) {
+
+ /* other RRSets */
+
+ if ((ret =
+ compare_rrsets_w_ldns_rrlist(packet->answer,
+ ldns_pkt_answer(ldns_packet),
+ knot_packet_answer_rrset_count(packet))) != 0) {
+ diag("Answer rrsets wrongly converted");
+ errors++;
+ }
+
+
+
+ if ((ret = compare_rrsets_w_ldns_rrlist(packet->authority,
+ ldns_pkt_authority(ldns_packet),
+ knot_packet_authority_rrset_count(packet))) != 0) {
+ diag("Authority rrsets wrongly converted - %d", ret);
+ errors++;
+ }
+
+ /* We don't want to test OPT RR, which is the last rrset
+ * in the additional section */
+
+ if ((ret = compare_rrsets_w_ldns_rrlist(packet->additional,
+ ldns_pkt_additional(ldns_packet),
+ knot_packet_additional_rrset_count(packet) - 1)) != 0) {
+ diag("Additional rrsets wrongly converted");
+ errors++;
+ }
+
+ }
+
+ if (check_edns) {
+
+ /* OPT RR */
+
+ if (ldns_pkt_edns(ldns_packet)) {
+ /* if (packet->edns_packet == NULL) {
+ diag("ldns has edns section, knot has not");
+ return 1;
+ } */
+
+ knot_opt_rr_t *opt = &(packet->opt_rr);
+
+ if (ldns_pkt_edns_udp_size(ldns_packet) !=
+ knot_edns_get_payload(opt)) {
+ diag("Payloads in EDNS are different");
+ errors++;
+ }
+
+ if (ldns_pkt_edns_version(ldns_packet) !=
+ knot_edns_get_version(opt)) {
+ diag("Versions in EDNS are different");
+ errors++;
+ }
+
+ if (ldns_pkt_edns_extended_rcode(ldns_packet) !=
+ knot_edns_get_ext_rcode(opt)) {
+ diag("Extended rcodes in EDNS are different");
+ errors++;
+ }
+
+ /* TODO parse flags do bit, z value ... */
+ }
+ }
+
+ return errors;
+}
+#endif
+
+extern knot_rrset_t *rrset_from_test_rrset(const test_rrset_t *test_rrset);
+extern knot_dname_t *dname_from_test_dname(const test_dname_t *test_dname);
+
+/* Converts knot_rrset_t to knot_opt_rr */
+static knot_opt_rr_t *opt_rrset_to_opt_rr(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return NULL;
+ }
+
+ knot_opt_rr_t *opt_rr = knot_edns_new();
+ assert(opt_rr);
+
+ knot_edns_set_payload(opt_rr, rrset->rclass);
+
+ knot_edns_set_ext_rcode(opt_rr, rrset->ttl);
+
+ /* TODO rdata? mostly empty, I guess, but should be done */
+
+ return opt_rr;
+}
+
+knot_packet_t *packet_from_test_response(test_response_t *test_packet)
+{
+ knot_rrset_t *parsed_opt = NULL;
+
+ for (int j = 0; j < test_packet->arcount; j++) {
+ if (test_packet->additional[j]->type ==
+ KNOT_RRTYPE_OPT) {
+ parsed_opt =
+ rrset_from_test_rrset(
+ test_packet->additional[j]);
+ assert(parsed_opt);
+ break;
+ }
+ }
+
+ knot_opt_rr_t *opt_rr = NULL;
+ if (parsed_opt != NULL) {
+ opt_rr =
+ opt_rrset_to_opt_rr(parsed_opt);
+ assert(opt_rr);
+ } else {
+ opt_rr = NULL;
+ }
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+ knot_packet_set_max_size(packet, 1024 * 10);
+
+ if (opt_rr != NULL) {
+ packet->opt_rr = *opt_rr;
+ }
+
+ packet->header.id = test_packet->id;
+ packet->header.qdcount = test_packet->qdcount;
+
+ packet->question.qname = dname_from_test_dname(test_packet->qname);
+ packet->size += test_packet->qname->size;
+ packet->question.qtype = test_packet->qtype;
+ packet->question.qclass = test_packet->qclass;
+
+ packet->size += 4;
+
+ packet->answer =
+ malloc(sizeof(knot_rrset_t *) * test_packet->ancount);
+ assert(packet->answer);
+
+ for (int j = 0; j < test_packet->ancount; j++) {
+ if (&(test_packet->answer[j])) {
+ packet->answer[packet->an_rrsets++] =
+ rrset_from_test_rrset(test_packet->answer[j]);
+ }
+ }
+
+ packet->authority =
+ malloc(sizeof(knot_rrset_t *) * test_packet->nscount);
+ assert(packet->answer);
+
+ for (int j = 0; j < test_packet->nscount; j++) {
+ if (&(test_packet->authority[j])) {
+ packet->authority[packet->ns_rrsets++] =
+ rrset_from_test_rrset(test_packet->authority[j]);
+ }
+ }
+
+ packet->authority =
+ malloc(sizeof(knot_rrset_t *) * test_packet->arcount);
+ assert(packet->answer);
+
+ for (int j = 0; j < test_packet->arcount; j++) {
+ if (&(test_packet->additional[j])) {
+ if (test_packet->additional[j]->type ==
+ KNOT_RRTYPE_OPT) {
+ continue;
+ }
+ packet->additional[packet->ar_rrsets++] =
+ rrset_from_test_rrset(test_packet->additional[j]);
+ }
+ }
+
+ return packet;
+}
+
+static int test_packet_parse_from_wire(list raw_response_list)
+{
+#ifdef TEST_WITH_LDNS
+ int errors = 0;
+
+ node *n = NULL;
+ WALK_LIST(n ,raw_response_list) {
+ test_raw_packet_t *raw_packet = (test_raw_packet_t *)n;
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ int ret = 0;
+ if ((ret =
+ knot_packet_parse_from_wire(packet, raw_packet->data,
+ raw_packet->size, 0)) !=
+ KNOT_EOK) {
+ diag("Warning: could not parse wire! "
+ "(might be caused by malformed dump) - "
+ "knot error: %s", knot_strerror(ret));
+// hex_print(raw_packet->data,
+// raw_packet->size);
+ continue;
+ }
+
+ ldns_pkt *ldns_packet = NULL;
+
+ if (ldns_wire2pkt(&ldns_packet, raw_packet->data,
+ raw_packet->size) != LDNS_STATUS_OK) {
+ diag("Could not parse wire using ldns");
+ diag("%s",
+ ldns_get_errorstr_by_id(ldns_wire2pkt(&ldns_packet,
+ raw_packet->data,
+ raw_packet->size)));
+ return 0;
+ }
+
+ if (check_packet_w_ldns_packet(packet, ldns_packet, 1,
+ 1, 1, 1) != 0) {
+ diag("Wrongly created packet");
+ errors++;
+ }
+
+ ldns_pkt_free(ldns_packet);
+ knot_packet_free(&packet);
+ }
+
+ return (errors == 0);
+#endif
+#ifndef TEST_WITH_LDNS
+ diag("Enable ldns to test this feature");
+ return 0;
+#endif
+}
+
+static int test_packet_to_wire(list raw_response_list)
+{
+#ifdef TEST_WITH_LDNS
+ int errors = 0;
+ /*!< \todo test queries too! */
+// /* We'll need data from both lists. */
+// test_packet_t **test_packets = NULL;
+// uint test_packet_count = 0;
+// node *n = NULL;
+// WALK_LIST(n, response_list) {
+// test_packet_count++;
+// }
+
+// test_packets =
+// malloc(sizeof(test_packet_t *) * test_packet_count);
+// assert(test_packets);
+// int i = 0;
+// WALK_LIST(n, response_list) {
+// test_packets[i++] = (test_response_t *)n;
+// }
+
+// test_raw_packet_t **test_packets = NULL;
+// uint test_packet_count = 0;
+// n = NULL;
+// WALK_LIST(n, raw_response_list) {
+// test_packet_count++;
+// }
+
+// test_packets =
+// malloc(sizeof(test_raw_packet_t *) * test_packet_count);
+// assert(test_packets);
+// i = 0;
+// WALK_LIST(n, raw_response_list) {
+// test_packets[i++] = (test_raw_packet_t *)n;
+// }
+
+// assert(test_response_count == test_packet_count);
+ node *n = NULL;
+ WALK_LIST(n, raw_response_list) {
+ /* Create packet from raw response. */
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+ test_raw_packet_t *raw_packet = (test_raw_packet_t *)n;
+ if (knot_packet_parse_from_wire(packet, raw_packet->data,
+ raw_packet->size, 0) !=
+ KNOT_EOK) {
+ diag("Warning: could not parse wire! "
+ "(might be caused be malformed dump)");
+ continue;
+ }
+ knot_packet_set_max_size(packet, 1024 * 10);
+ /* Use this packet to create wire */
+ uint8_t *wire = NULL;
+ size_t size = 0;
+ if (knot_packet_to_wire(packet, &wire ,&size) != KNOT_EOK) {
+ diag("Could not convert packet to wire");
+ }
+ /* Create ldns packet from created wire */
+ ldns_pkt *ldns_packet = NULL;
+
+ if (ldns_wire2pkt(&ldns_packet, wire,
+ size) != LDNS_STATUS_OK) {
+ diag("Could not parse wire using ldns");
+ /*!< \todo get rid of this */
+ diag("%s",
+ ldns_get_errorstr_by_id(ldns_wire2pkt(&ldns_packet,
+ wire,
+ size)));
+ return 0;
+ }
+
+ if (check_packet_w_ldns_packet(packet, ldns_packet, 1, 1, 1,
+ 1) != 0) {
+ diag("Packet wrongly converted to wire!");
+ errors++;
+ }
+ knot_packet_free(&packet);
+ ldns_pkt_free(ldns_packet);
+ }
+
+ return (errors == 0);
+#endif
+#ifndef TEST_WITH_LDNS
+ diag("Enable ldns to test this feature!");
+ return 0;
+#endif
+}
+
+static const uint KNOT_PACKET_TEST_COUNT = 2;
+
+static int packet_tests_count(int argc, char *argv[])
+{
+ return KNOT_PACKET_TEST_COUNT;
+}
+
+static int packet_tests_run(int argc, char *argv[])
+{
+ const test_data_t *data = data_for_knot_tests;
+
+ int res = 0;
+ todo();
+ ok(res = test_packet_parse_from_wire(data->raw_packet_list),
+ "packet: from wire");
+ diag("Resolve issue with arcount.");
+ endtodo;
+// skip(!res, 1);
+ ok(test_packet_to_wire(data->raw_packet_list), "packet: to wire");
+// endskip;
+
+ return 1;
+}
+
diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.h b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h
new file mode 100644
index 0000000..c0e0479
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_PACKET_REALDATA_TESTS_H_
+#define _KNOTD_PACKET_REALDATA_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api packet_tests_api;
+
+#endif /* _KNOTD_PACKET_REALDATA_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c
new file mode 100644
index 0000000..f4ba64c
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c
@@ -0,0 +1,329 @@
+/* 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 <stdlib.h>
+#include <assert.h>
+
+#include "tests/libknot/realdata/libknot/rdata_tests_realdata.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/common.h"
+#include "libknot/rdata.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/util/utils.h"
+#include "libknot/util/error.h"
+
+static int knot_rdata_tests_count(int argc, char *argv[]);
+static int knot_rdata_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api rdata_tests_api = {
+ "DNS library - rdata", //! Unit name
+ &knot_rdata_tests_count, //! Count scheduled tests
+ &knot_rdata_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+extern int check_domain_name(const knot_dname_t *dname,
+ const test_dname_t *test_dname);
+
+extern int compare_wires_simple(uint8_t *wire1, uint8_t *wire2, uint count);
+
+/*!
+ * \brief Checks if all RDATA items in the given RDATA structure are correct.
+ *
+ * \return Number of errors encountered. Error is either if some RDATA item
+ * is not set (i.e. NULL) or if it has other than the expected value.
+ */
+static int check_rdata(const knot_rdata_t *rdata,
+ const test_rdata_t *test_rdata)
+{
+ assert(rdata != NULL);
+ assert(test_rdata != NULL);
+
+ int errors = 0;
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(test_rdata->type);
+ //note("check_rdata(), RRType: %u", rrtype);
+
+ for (int i = 0; i < desc->length; ++i) {
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ if (check_domain_name(rdata->items[i].dname,
+ test_rdata->items[i].dname) != 0) {
+ errors++;
+ diag("Rdata contains wrong dname item");
+ }
+ break;
+ default:
+ if (test_rdata->items[i].raw_data[0] !=
+ rdata->items[i].raw_data[0]) {
+ diag("Raw rdata in items have different "
+ "sizes!");
+ return 0;
+ }
+
+ errors +=
+ compare_wires_simple(
+ (uint8_t *)test_rdata->items[i].raw_data,
+ (uint8_t *)rdata->items[i].raw_data,
+ (uint)rdata->items[i].raw_data[0]);
+ }
+ }
+ return errors;
+}
+
+extern knot_dname_t *dname_from_test_dname(test_dname_t *test_dname);
+
+///*!
+// * \brief Tests knot_rdata_set_item().
+// *
+// * \retval > 0 on success.
+// * \retval 0 otherwise.
+// */
+//static int test_rdata_set_item(list rdata_list)
+//{
+// node *n = NULL;
+// WALK_LIST(n, rdata_list) {
+// knot_rdata_t *rdata = knot_rdata_new();
+// assert(rdata);
+// test_rdata_t *test_rdata = (test_rdata_t *)n;
+
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(test_rdata->type);
+// for (int i = 0; i < test_rdata->count; i++) {
+// knot_rdata_item_t item;
+// if (test_rdata->items[i].type == TEST_ITEM_DNAME) {
+// item.dname =
+// dname_from_test_dname(
+// test_rdata->items[i].dname);
+// } else {
+// item.raw_data = test_rdata->items[i].raw_data;
+// }
+// if (knot_rdata_set_item(rdata, i, item) != 0) {
+// diag("Could not set item, rdata count: %d",
+// rdata->count);
+// return 0;
+// }
+// }
+
+// /* Check that all items are OK */
+// if (check_rdata(rdata, test_rdata) != 0) {
+// return 0;
+// }
+// }
+// return 1;
+//}
+
+static knot_rdata_item_t *items_from_test_items(test_item_t *test_items,
+ size_t count)
+{
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * count);
+ assert(items);
+ for (int i = 0; i < count; i++) {
+ if (test_items[i].type == TEST_ITEM_DNAME) {
+ items[i].dname =
+ dname_from_test_dname(test_items[i].dname);
+ } else {
+ items[i].raw_data = test_items[i].raw_data;
+ }
+ }
+
+ return items;
+}
+
+static int test_rdata_set_items(list rdata_list)
+{
+ int errors = 0;
+
+ // check error return values
+ knot_rdata_t *rdata = knot_rdata_new();
+ assert(rdata);
+
+ node *n = NULL;
+ WALK_LIST(n, rdata_list) {
+ test_rdata_t *test_rdata = (test_rdata_t *)n;
+ knot_rdata_t *rdata = knot_rdata_new();
+
+ /* create dnslib items from tests items. */
+ knot_rdata_item_t *items =
+ items_from_test_items(test_rdata->items,
+ test_rdata->count);
+
+ assert(items);
+ assert(test_rdata->count > 0);
+ assert(rdata->items == NULL);
+
+ if (knot_rdata_set_items(rdata, items,
+ test_rdata->count) != 0) {
+ diag("Could not set items!");
+ errors++;
+ }
+
+ if (check_rdata(rdata, test_rdata) != 0) {
+ diag("Wrong rdata after knot_rdata_set_items!");
+ errors++;
+ }
+
+ knot_rdata_free(&rdata);
+ }
+
+ return (errors == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tests knot_rdata_get_item().
+ *
+ * \retval > 0 on success.
+ * \retval 0 otherwise.
+ */
+static int test_rdata_get_item(list rdata_list)
+{
+ node *n = NULL;
+ WALK_LIST(n, rdata_list) {
+ test_rdata_t *test_rdata = (test_rdata_t *)n;
+ knot_rdata_t *rdata = knot_rdata_new();
+ assert(rdata);
+ knot_rdata_item_t *items =
+ items_from_test_items(test_rdata->items,
+ test_rdata->count);
+ assert(knot_rdata_set_items(rdata, items,
+ test_rdata->count) == 0);
+ knot_rdata_item_t *new_items =
+ malloc(sizeof(knot_rdata_item_t) * test_rdata->count);
+ for (int i = 0; i < test_rdata->count; i++) {
+ knot_rdata_item_t *item =
+ knot_rdata_get_item(rdata, i);
+ if (item == NULL) {
+ diag("Could not get item");
+ return 0;
+ }
+ new_items[i] = *item;
+ }
+
+ knot_rdata_free(&rdata);
+ free(items);
+
+ knot_rdata_t *new_rdata = knot_rdata_new();
+ assert(new_rdata);
+ assert(knot_rdata_set_items(new_rdata,
+ new_items,
+ test_rdata->count) == 0);
+
+ if (check_rdata(new_rdata, test_rdata) != 0) {
+ diag("Wrong rdata created using rdata_get_item");
+ return 0;
+ }
+
+ knot_rdata_free(&new_rdata);
+ free(new_items);
+ }
+
+ return 1;
+}
+
+//static int test_rdata_wire_size()
+//{
+// knot_rdata_t *rdata;
+// int errors = 0;
+
+// // generate some random data
+// uint8_t data[KNOT_MAX_RDATA_WIRE_SIZE];
+// generate_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE);
+
+// for (int i = 0; i <= KNOT_RRTYPE_LAST; ++i) {
+// rdata = knot_rdata_new();
+
+// int size =
+// fill_rdata(data, KNOT_MAX_RDATA_WIRE_SIZE, i, rdata);
+
+// if (size < 0) {
+// ++errors;
+// } else {
+// int counted_size = knot_rdata_wire_size(rdata,
+// knot_rrtype_descriptor_by_type(i)->wireformat);
+// if (size != counted_size) {
+// diag("Wrong wire size computed (type %d):"
+// " %d (should be %d)",
+// i, counted_size, size);
+// ++errors;
+// }
+// }
+
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(i);
+
+// for (int x = 0; x < desc->length; x++) {
+// if (desc->wireformat[x] ==
+// KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_COMPRESSED_DNAME ||
+// desc->wireformat[x] ==
+// KNOT_RDATA_WF_LITERAL_DNAME) {
+// knot_dname_free(&(rdata->items[x].dname));
+// }
+// }
+// knot_rdata_free(&rdata);
+// }
+
+// return (errors == 0);
+//}
+
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+
+static const int KNOT_RDATA_TEST_COUNT = 2;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_rdata_tests_count(int argc, char *argv[])
+{
+ return KNOT_RDATA_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_rdata_tests_run(int argc, char *argv[])
+{
+ test_data_t *data = data_for_knot_tests;
+ int res = 0,
+ res_final = 1;
+
+ ok(res = test_rdata_set_items(data->rdata_list),
+ "rdata: set items all at once");
+ res_final *= res;
+
+ ok(res = test_rdata_get_item(data->rdata_list),
+ "rdata: get item");
+ res_final *= res;
+
+// ok(res = test_rdata_set_item(data->rdata_list),
+// "rdata: set items one-by-one");
+// res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h
new file mode 100644
index 0000000..570b2b1
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h
@@ -0,0 +1,53 @@
+/*!
+ * \file rdata_tests.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * Contains unit tests for RDATA (knot_rdata_t) and RDATA item
+ * (knot_rdata_item_t) structures.
+ *
+ * Contains tests for:
+ * - creating empty RDATA structure with or without reserved space.
+ * - setting RDATA items one-by-one
+ * - setting RDATA items all at once
+ *
+ * As for now, the tests use several (TEST_RDATAS) RDATA structures, each
+ * with different number of RDATA items (given by test_rdatas). These are all
+ * initialized to pointers derived from RDATA_ITEM_PTR (first is RDATA_ITEM_PTR,
+ * second RDATA_ITEM_PTR + 1, etc.). The functions only test if the pointer
+ * is set properly.
+ *
+ * \todo It may be better to test also some RDATAs with predefined contents,
+ * such as some numbers, some domain name, etc. For this purpose, we'd
+ * need RDATA descriptors (telling the types of each RDATA item within an
+ * RDATA).
+ *
+ * \todo It will be fine to test all possible output values of all functions,
+ * e.g. test whether knot_rdata_get_item() returns NULL when passed an
+ * illegal position, etc.
+ */
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_RDATA_TESTS_H_
+#define _KNOTD_RDATA_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api rdata_tests_api;
+
+#endif /* _KNOTD_RDATA_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.c b/src/tests/libknot/realdata/libknot/response_tests_realdata.c
new file mode 100644
index 0000000..5bbda36
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.c
@@ -0,0 +1,173 @@
+/* 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/>.
+ */
+/* blame: jan.kadlec@nic.cz */
+
+#include <assert.h>
+
+#include "packet_tests_realdata.h"
+#include "knot/common.h"
+#include "libknot/util/error.h"
+#include "libknot/packet/packet.h"
+#include "libknot/packet/response.h"
+/* *test_t structures */
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#ifdef TEST_WITH_LDNS
+#include "ldns/packet.h"
+#endif
+
+static int response_tests_count(int argc, char *argv[]);
+static int response_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api response_tests_api = {
+ "DNS library - response", //! Unit name
+ &response_tests_count, //! Count scheduled tests
+ &response_tests_run //! Run scheduled tests
+};
+
+#ifdef TEST_WITH_LDNS
+extern int compare_wires_simple(uint8_t *wire1, uint8_t *wire2, uint count);
+extern int compare_rr_rdata(knot_rdata_t *rdata, ldns_rr *rr, uint16_t type);
+extern int compare_rrset_w_ldns_rr(const knot_rrset_t *rrset,
+ ldns_rr *rr, char check_rdata);
+extern int compare_rrsets_w_ldns_rrlist(const knot_rrset_t **rrsets,
+ ldns_rr_list *rrlist, int count);
+
+extern int check_packet_w_ldns_packet(knot_packet_t *packet,
+ ldns_pkt *ldns_packet,
+ int check_header,
+ int check_question,
+ int check_body,
+ int check_edns);
+#endif
+
+extern knot_packet_t *packet_from_test_response(test_response_t *response);
+
+static int test_response_init_from_query(list query_list)
+{
+ int errors = 0;
+ node *n = NULL;
+ WALK_LIST(n, query_list) {
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ knot_packet_t *query =
+ packet_from_test_response((test_response_t *)n);
+ assert(query);
+ knot_packet_set_max_size(response, 1024 * 10);
+ if (knot_response_init_from_query(response,
+ query) != KNOT_EOK) {
+ diag("Could not init response from query!");
+ errors++;
+ }
+ knot_packet_free(&response);
+ knot_packet_free(&query);
+ }
+ return (errors == 0);
+}
+
+//static int test_response_add_opt(list opt_list)
+//{
+// int errors = 0;
+// node *n = NULL;
+// WALK_LIST(n, query_list) {
+// knot_packet_t *response =
+// knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+// assert(response);
+// knot_opt_rr_t *opt =
+// opt_from_test_opt((test_opt_t *)n);
+// assert(query);
+// if (knot_response_add_opt(response,
+// opt, 1)!= KNOT_EOK) {
+// diag("Could not add OPT RR to response!");
+// errors++;
+// }
+// knot_packet_free(&response);
+// knot_opt_rr_free(&opt);
+// }
+// return (errors == 0);
+//}
+
+extern knot_rrset_t *rrset_from_test_rrset(test_rrset_t *test_rrset);
+
+static int test_response_add_generic(int (*func)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int),
+ list rrset_list)
+{
+ /*!< \todo Now adding only one RRSet at the time, try more, use nodes */
+ int errors = 0;
+ node *n = NULL;
+ WALK_LIST(n, rrset_list) {
+ knot_packet_t *response =
+ knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(response);
+ knot_packet_set_max_size(response,
+ KNOT_PACKET_PREALLOC_RESPONSE * 100);
+ assert(knot_response_init(response) == KNOT_EOK);
+
+ knot_rrset_t *rrset =
+ rrset_from_test_rrset((test_rrset_t *)n);
+ assert(rrset);
+
+ int ret = 0;
+ if ((ret = func(response, rrset, 0, 1, 0)) != KNOT_EOK) {
+ diag("Could not add RRSet to response! Returned: %d",
+ ret);
+ diag("(owner: %s type %s)",
+ ((test_rrset_t *)n)->owner->str,
+ knot_rrtype_to_string((
+ (test_rrset_t *)n)->type));
+ errors++;
+ }
+ knot_packet_free(&response);
+ knot_rrset_deep_free(&rrset, 1, 1, 1);
+ }
+
+ return (errors == 0);
+}
+
+static void test_response_add_rrset(list rrset_list)
+{
+ ok(test_response_add_generic(knot_response_add_rrset_answer,
+ rrset_list),
+ "response: add answer rrset");
+ ok(test_response_add_generic(knot_response_add_rrset_authority,
+ rrset_list),
+ "response: add authority rrset");
+ ok(test_response_add_generic(knot_response_add_rrset_additional,
+ rrset_list),
+ "response: add additional rrset");
+}
+
+static const uint KNOT_response_TEST_COUNT = 4;
+
+static int response_tests_count(int argc, char *argv[])
+{
+ return KNOT_response_TEST_COUNT;
+}
+
+static int response_tests_run(int argc, char *argv[])
+{
+ const test_data_t *data = data_for_knot_tests;
+
+// int res = 0;
+ ok(test_response_init_from_query(data->query_list),
+ "response: init from query");
+ test_response_add_rrset(data->rrset_list);
+ return 1;
+}
diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.h b/src/tests/libknot/realdata/libknot/response_tests_realdata.h
new file mode 100644
index 0000000..731604b
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_PACKET_response_TESTS_H_
+#define _KNOTD_PACKET_response_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api response_tests_api;
+
+#endif /* _KNOTD_PACKET_response_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c
new file mode 100644
index 0000000..cb59f4c
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c
@@ -0,0 +1,289 @@
+/* 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 "tests/libknot/realdata/libknot/rrset_tests_realdata.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/common.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/rrset.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/util/utils.h"
+#include "libknot/zone/node.h"
+#include "libknot/util/debug.h"
+
+static int knot_rrset_tests_count(int argc, char *argv[]);
+static int knot_rrset_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api rrset_tests_api = {
+ "DNS library - rrset", //! Unit name
+ &knot_rrset_tests_count, //! Count scheduled tests
+ &knot_rrset_tests_run //! Run scheduled tests
+};
+
+/*----------------------------------------------------------------------------*/
+/*
+ * Unit implementation.
+ */
+
+/* count1 == count2 */
+int compare_wires_simple(uint8_t *wire1, uint8_t *wire2, uint count)
+{
+ int i = 0;
+ while (i < count &&
+ wire1[i] == wire2[i]) {
+ i++;
+ }
+ return (!(count == i));
+}
+
+
+knot_rrset_t *rrset_from_test_rrset(const test_rrset_t *test_rrset)
+{
+// diag("owner: %s\n", test_rrset->owner->str);
+ knot_dname_t *owner =
+ knot_dname_new_from_wire(test_rrset->owner->wire,
+ test_rrset->owner->size, NULL);
+
+// diag("Created owner: %s (%p) from %p\n", knot_dname_to_str(owner),
+// owner, test_rrset->owner);
+
+ if (!owner) {
+ return NULL;
+ }
+
+ knot_rrset_t *ret = knot_rrset_new(owner, test_rrset->type,
+ test_rrset->rclass,
+ test_rrset->ttl);
+
+ /* Add rdata to rrset. */
+ knot_rdata_t *rdata = knot_rdata_new();
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(test_rrset->type);
+
+ node *n = NULL;
+ WALK_LIST(n, test_rrset->rdata_list) {
+ test_rdata_t *test_rdata = (test_rdata_t *)n;
+ if (test_rdata->count != desc->length) {
+ diag("Malformed RRSet data!");
+ knot_rdata_free(&rdata);
+ return ret;
+ }
+ assert(test_rdata->type == test_rrset->type);
+ /* Add items to the actual rdata. */
+ rdata->items = malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (rdata->items == NULL) {
+ return NULL;
+ }
+// diag("Rdata type: %s\n", knot_rrtype_to_string(test_rrset->type));
+ for (int i = 0; i < desc->length; i++) {
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME) {
+// diag("%p\n", test_rdata->items[i].raw_data);
+ assert(test_rdata->items[i].type == TEST_ITEM_DNAME);
+ rdata->items[i].dname =
+ knot_dname_new_from_wire(test_rdata->items[i].dname->wire,
+ test_rdata->items[i].dname->size,
+ NULL);
+ } else {
+// diag("%p\n", test_rdata->items[i].dname);
+ assert(test_rdata->items[i].type == TEST_ITEM_RAW_DATA);
+ assert(test_rdata->items[i].raw_data != NULL);
+ rdata->items[i].raw_data = test_rdata->items[i].raw_data;
+ }
+ }
+ }
+
+ rdata->next = rdata;
+
+ ret->rdata = rdata;
+
+ return ret;
+}
+
+extern int check_domain_name(knot_dname_t *dname, test_dname_t *test_dname);
+
+int check_rrset(const knot_rrset_t *rrset,
+ const test_rrset_t *test_rrset,
+ int check_rdata, int check_items,
+ int check_rrsigs)
+{
+ /* following implementation should be self-explanatory */
+ int errors = 0;
+
+ if (rrset == NULL) {
+ diag("RRSet not created!");
+ return 1;
+ }
+
+ errors += check_domain_name(rrset->owner, test_rrset->owner);
+
+ if (rrset->type != test_rrset->type) {
+ diag("TYPE wrong: %u (should be: %u)", rrset->type,
+ test_rrset->type);
+ ++errors;
+ }
+
+ if (rrset->rclass != test_rrset->rclass) {
+ diag("CLASS wrong: %u (should be: %u)", rrset->rclass,
+ test_rrset->rclass);
+ ++errors;
+ }
+
+ if (rrset->ttl != test_rrset->ttl) {
+ diag("TTL wrong: %u (should be: %u)", rrset->ttl,
+ test_rrset->ttl);
+ ++errors;
+ }
+
+ if (check_rdata) {
+ /* TODO use rdata_compare */
+ knot_rdata_t *rdata = rrset->rdata;
+
+ if (rdata == NULL) {
+ diag("There are no RDATAs in the RRSet");
+ ++errors;
+ }
+
+ if (rdata != NULL) {
+ while (rdata->next != NULL &&
+ rdata->next != rrset->rdata) {
+ rdata = rdata->next;
+ }
+ if (rdata->next == NULL) {
+ diag("The list of RDATAs is not cyclic!");
+ ++errors;
+ } else {
+ assert(rdata->next == rrset->rdata);
+ }
+ }
+ }
+
+ /* Iterate rrset rdata list and compare items. */
+ if (check_items && rrset->rdata != NULL) {
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+ node *n = NULL;
+ knot_rdata_t *tmp_rdata = rrset->rdata;
+ WALK_LIST(n, test_rrset->rdata_list) {
+ test_rdata_t *test_rdata = (test_rdata_t *)n;
+ for (int i = 0; i < desc->length; i++) {
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ errors += check_domain_name(tmp_rdata->items[i].dname,
+ test_rdata->items[i].dname);
+ } else {
+ assert(tmp_rdata != NULL);
+ errors += compare_wires_simple((uint8_t *)tmp_rdata->items[i].raw_data,
+ (uint8_t *)test_rdata->items[i].raw_data,
+ test_rdata->items[i].raw_data[0]);
+ }
+ }
+ }
+ } else if (check_items && rrset->rdata == NULL) {
+ diag("Could not test items, since rdata is empty!");
+ }
+
+ if (check_rrsigs) {
+ /* there are currently no rrsigs */
+ }
+ return errors;
+}
+
+extern knot_dname_t *dname_from_test_dname(test_dname_t *test_dname);
+
+static int test_rrset_create(const list rrset_list)
+{
+ int errors = 0;
+
+ /* Test with real data. */
+ node *n = NULL;
+ WALK_LIST(n, rrset_list) {
+ test_rrset_t *test_rrset = (test_rrset_t *)n;
+ knot_rrset_t *rrset =
+ knot_rrset_new(dname_from_test_dname
+ (test_rrset->owner),
+ test_rrset->type,
+ test_rrset->rclass,
+ test_rrset->ttl);
+ assert(rrset);
+ errors += check_rrset(rrset, test_rrset, 0, 0, 0);
+ knot_rrset_deep_free(&rrset, 1, 0, 0);
+ }
+
+ return (errors == 0);
+}
+
+static int test_rrset_add_rdata(list rrset_list)
+{
+ int errors = 0;
+ node *n = NULL;
+ WALK_LIST(n, rrset_list) {
+ test_rrset_t *test_rrset = (test_rrset_t *)n;
+ knot_rrset_t *tmp_rrset = rrset_from_test_rrset(test_rrset);
+ /* TODO use all the rdata */
+ assert(tmp_rrset->rdata->next = tmp_rrset->rdata);
+ knot_rrset_t *rrset =
+ knot_rrset_new(dname_from_test_dname
+ (test_rrset->owner),
+ test_rrset->type,
+ test_rrset->rclass,
+ test_rrset->ttl);
+ assert(rrset);
+ knot_rrset_add_rdata(rrset, tmp_rrset->rdata);
+ errors += check_rrset(rrset, test_rrset, 1, 1, 1);
+ knot_rrset_free(&tmp_rrset);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+
+ }
+ return (errors == 0);
+}
+
+static const int KNOT_RRSET_TEST_COUNT = 2;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_rrset_tests_count(int argc, char *argv[])
+{
+ return KNOT_RRSET_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_rrset_tests_run(int argc, char *argv[])
+{
+ test_data_t *data = data_for_knot_tests;
+
+ int res = 0,
+ res_final = 1;
+
+ res = test_rrset_create(data->rrset_list);
+ ok(res, "rrset: create");
+ res_final *= res;
+
+ ok(res = test_rrset_add_rdata(data->rrset_list), "rrset: add_rdata");
+ res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h
new file mode 100644
index 0000000..cc3b705
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h
@@ -0,0 +1,35 @@
+/*!
+ * \file rrset_tests.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * Contains unit tests for RRSet (knot_rrset_t) and its API.
+ *
+ * Contains tests for:
+ * -
+ */
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_RRSET_TESTS_H_
+#define _KNOTD_RRSET_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api rrset_tests_api;
+
+#endif /* _KNOTD_RRSET_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.c b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c
new file mode 100644
index 0000000..445997f
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c
@@ -0,0 +1,335 @@
+/* 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 "tests/libknot/realdata/libknot/zone_tests_realdata.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/common.h"
+#include "libknot/zone/zone.h"
+#include "libknot/util/error.h"
+#include "libknot/zone/node.h"
+
+static int knot_zone_tests_count(int argc, char *argv[]);
+static int knot_zone_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api zone_tests_api = {
+ "DNS library - zone", //! Unit name
+ &knot_zone_tests_count, //! Count scheduled tests
+ &knot_zone_tests_run //! Run scheduled tests
+};
+
+/*
+ * Unit implementation.
+ */
+
+extern knot_dname_t *dname_from_test_dname(test_dname_t *test_dname);
+extern knot_rrset_t *rrset_from_test_rrset(test_rrset_t *test_rrset);
+
+static knot_node_t *node_from_test_node(const test_node_t *test_node)
+{
+ knot_dname_t *owner = dname_from_test_dname(test_node->owner);
+ /* TODO parent? */
+ knot_node_t *new_node = knot_node_new(owner, NULL, 0);
+ node *n = NULL;
+ WALK_LIST(n, test_node->rrset_list) {
+ test_rrset_t *test_rrset = (test_rrset_t *)n;
+ knot_rrset_t *rrset = rrset_from_test_rrset(test_rrset);
+ assert(rrset);
+ assert(knot_node_add_rrset(new_node, rrset, 0) == 0);
+ }
+
+ return new_node;
+}
+
+static int test_zone_create(list node_list)
+{
+// knot_dname_t *dname = knot_dname_new_from_wire(
+// test_apex.owner.name, test_apex.owner.size, NULL);
+// assert(dname);
+ int errors = 0;
+
+ node *n = NULL;
+ WALK_LIST(n, node_list) {
+ test_node_t *test_node = (test_node_t *)n;
+ knot_node_t *node = node_from_test_node(test_node);
+ assert(node);
+
+ knot_zone_t *zone = knot_zone_new(node, 0, 0);
+ if (zone == NULL) {
+ diag("Could not create zone with owner: %s\n",
+ test_node->owner->str);
+ errors++;
+ }
+ knot_node_free_rrsets(node, 1);
+ knot_node_free(&node, 0, 0);
+ }
+
+ return (errors == 0);
+}
+
+//static int test_zone_add_node(knot_zone_t *zone, int nsec3)
+//{
+// /*
+// * NSEC3 nodes are de facto identical to normal nodes, so there is no
+// * need for separate tests. The only difference is where are they stored
+// * in the zone structure.
+// */
+
+// int errors = 0;
+// int res = 0;
+
+// //note("Good nodes");
+
+// for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+// knot_node_t *node = knot_node_new(&test_nodes_good[i].owner,
+// test_nodes_good[i].parent);
+// if (node == NULL) {
+// diag("zone: Could not create node.");
+// return 0;
+// }
+
+// if ((res = ((nsec3) ? knot_zone_add_nsec3_node(zone, node)
+// : knot_zone_add_node(zone, node))) != 0) {
+// diag("zone: Failed to insert node into zone (returned"
+// " %d).", res);
+// knot_node_free(&node, 0);
+// ++errors;
+// }
+// /* TODO check values in the node as well */
+// }
+
+// //note("Bad nodes");
+
+// for (int i = 0; i < TEST_NODES_BAD; ++i) {
+// knot_node_t *node = knot_node_new(&test_nodes_bad[i].owner,
+// test_nodes_bad[i].parent);
+// if (node == NULL) {
+// diag("zone: Could not create node.");
+// return 0;
+// }
+
+// if ((res = ((nsec3) ? knot_zone_add_nsec3_node(zone, node)
+// : knot_zone_add_node(zone, node))) !=
+// KNOT_EBADZONE) {
+// diag("zone: Inserting wrong node did not result in"
+// "proper return value (%d instead of %d).", res,
+// KNOT_EBADZONE);
+// ++errors;
+// }
+// knot_node_free(&node, 0);
+// }
+
+// // check if all nodes are inserted
+// //int nodes = 0;
+// if (!nsec3
+// && !test_zone_check_node(knot_zone_apex(zone), &test_apex)) {
+// diag("zone: Apex of zone not right.");
+//// diag("Apex owner: %s (%p), apex parent: %p\n",
+//// knot_dname_to_str(knot_zone_apex(zone)->owner),
+//// knot_zone_apex(zone)->owner,
+//// knot_zone_apex(zone)->parent);
+//// diag("Should be: owner: %s (%p), parent: %p\n",
+//// knot_dname_to_str(&test_apex.owner),
+//// &test_apex.owner,
+//// test_apex.parent);
+// ++errors;
+// }
+// //++nodes;
+// for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+// const knot_node_t *n = ((nsec3) ? knot_zone_find_nsec3_node(
+// zone, &test_nodes_good[i].owner) :
+// knot_zone_find_node(zone, &test_nodes_good[i].owner));
+// if (n == NULL) {
+// diag("zone: Missing node with owner %s",
+// test_nodes_good[i].owner.name);
+// ++errors;
+// continue;
+// }
+
+// if (!test_zone_check_node(n, &test_nodes_good[i])) {
+// diag("zone: Node does not match: owner: %s (should be "
+// "%s), parent: %p (should be %p)",
+// node->owner->name, test_nodes_good[i].owner.name,
+// node->parent, test_nodes_good[i].parent);
+// ++errors;
+// }
+// //++nodes;
+// }
+
+// //note("zone: %d nodes in the zone (including apex)", nodes);
+
+// return (errors == 0);
+//}
+
+//static int test_zone_get_node(knot_zone_t *zone, int nsec3)
+//{
+// int errors = 0;
+
+// for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+// if (((nsec3) ? knot_zone_get_nsec3_node(
+// zone, &test_nodes_good[i].owner)
+// : knot_zone_get_node(zone, &test_nodes_good[i].owner))
+// == NULL) {
+// diag("zone: Node (%s) not found in zone.",
+// (char *)test_nodes_good[i].owner.name);
+// ++errors;
+// }
+// }
+
+// for (int i = 0; i < TEST_NODES_BAD; ++i) {
+// if (((nsec3) ? knot_zone_get_nsec3_node(
+// zone, &test_nodes_bad[i].owner)
+// : knot_zone_get_node(zone, &test_nodes_bad[i].owner))
+// != NULL) {
+// diag("zone: Node (%s) found in zone even if it should"
+// "not be there.",
+// (char *)test_nodes_bad[i].owner.name);
+// ++errors;
+// }
+// }
+
+// if (((nsec3)
+// ? knot_zone_get_nsec3_node(NULL, &test_nodes_good[0].owner)
+// : knot_zone_get_node(NULL, &test_nodes_good[0].owner)) != NULL) {
+// diag("zone: Getting node from NULL zone did not result in"
+// "proper return value (NULL)");
+// ++errors;
+// }
+
+// if (((nsec3) ? knot_zone_get_nsec3_node(zone, NULL)
+// : knot_zone_get_node(zone, NULL)) != NULL) {
+// diag("zone: Getting node with NULL owner from zone did not "
+// "result in proper return value (NULL)");
+// ++errors;
+// }
+
+// if (!nsec3 && knot_zone_get_node(zone, &test_apex.owner) == NULL) {
+// diag("zone: Getting zone apex from the zone failed");
+// ++errors;
+// }
+
+// return (errors == 0);
+//}
+
+//static int test_zone_find_node(knot_zone_t *zone, int nsec3)
+//{
+// int errors = 0;
+
+// for (int i = 0; i < TEST_NODES_GOOD; ++i) {
+// if (((nsec3) ? knot_zone_find_nsec3_node(
+// zone, &test_nodes_good[i].owner)
+// : knot_zone_find_node(zone, &test_nodes_good[i].owner))
+// == NULL) {
+// diag("zone: Node (%s) not found in zone.",
+// (char *)test_nodes_good[i].owner.name);
+// ++errors;
+// }
+// }
+
+// for (int i = 0; i < TEST_NODES_BAD; ++i) {
+// if (((nsec3) ? knot_zone_find_nsec3_node(
+// zone, &test_nodes_bad[i].owner)
+// : knot_zone_find_node(zone, &test_nodes_bad[i].owner))
+// != NULL) {
+// diag("zone: Node (%s) found in zone even if it should"
+// "not be there.",
+// (char *)test_nodes_bad[i].owner.name);
+// ++errors;
+// }
+// }
+
+// if (((nsec3)
+// ? knot_zone_find_nsec3_node(NULL, &test_nodes_good[0].owner)
+// : knot_zone_find_node(NULL, &test_nodes_good[0].owner)) != NULL) {
+// diag("zone: Finding node from NULL zone did not result in"
+// "proper return value (NULL)");
+// ++errors;
+// }
+
+// if (((nsec3) ? knot_zone_find_nsec3_node(zone, NULL)
+// : knot_zone_find_node(zone, NULL)) != NULL) {
+// diag("zone: Finding node with NULL owner from zone did not "
+// "result in proper return value (NULL)");
+// ++errors;
+// }
+
+// if (!nsec3 && knot_zone_find_node(zone, &test_apex.owner) == NULL) {
+// diag("zone: Finding zone apex from the zone failed");
+// ++errors;
+// }
+
+// return (errors == 0);
+//}
+
+static const int KNOT_ZONE_TEST_COUNT = 1;
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_zone_tests_count(int argc, char *argv[])
+{
+ return KNOT_ZONE_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_zone_tests_run(int argc, char *argv[])
+{
+ int res = 0,
+ res_final = 0;
+
+ test_data_t *data = data_for_knot_tests;
+
+ ok((res = test_zone_create(data->node_list)), "zone: create");
+ res_final *= res;
+
+// skip(!res, 6);
+
+// ok((res = test_zone_add_node(zone, 0)), "zone: add node");
+// res_final *= res;
+
+// skip(!res, 2);
+
+// skip(!res, 1);
+
+// ok((res = test_zone_find_node(zone, 0)), "zone: find node");
+// res_final *= res;
+
+// endskip; // get node failed
+
+// endskip; // add node failed
+
+// ok((res = test_zone_add_node(zone, 1)), "zone: add nsec3 node");
+// res_final *= res;
+
+// skip(!res, 2);
+
+// skip(!res, 1);
+
+// ok((res = test_zone_find_node(zone, 1)), "zone: find nsec3 node");
+// res_final *= res;
+
+// endskip; // get nsec3 node failed
+
+// endskip; // add nsec3 node failed
+
+// endskip; // create failed
+
+ return res_final;
+}
diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.h b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h
new file mode 100644
index 0000000..5539709
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ZONE_TESTS_H_
+#define _KNOTD_ZONE_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api zone_tests_api;
+
+#endif /* _KNOTD_ZONE_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c
new file mode 100644
index 0000000..96d1517
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c
@@ -0,0 +1,44 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/libknot/realdata/libknot/zonedb_tests_realdata.h"
+
+
+static int zonedb_tests_count(int argc, char *argv[]);
+static int zonedb_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api zonedb_tests_api = {
+ "Zone database", //! Unit name
+ &zonedb_tests_count, //! Count scheduled tests
+ &zonedb_tests_run //! Run scheduled tests
+};
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int zonedb_tests_count(int argc, char *argv[])
+{
+ return 0;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int zonedb_tests_run(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h
new file mode 100644
index 0000000..0c4f8ef
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ZONEDB_TESTS_H_
+#define _KNOTD_ZONEDB_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api zonedb_tests_api;
+
+#endif /* _KNOTD_ZONEDB_TESTS_H_ */
diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
new file mode 100644
index 0000000..71a9c3d
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
@@ -0,0 +1,1300 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "common/libtap/tap.h"
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+#include "libknot/util/descriptor.h"
+
+#include "tests/libknot/realdata/parsed_data.rc"
+#include "tests/libknot/realdata/raw_data.rc"
+TREE_DEFINE(test_node, avl);
+
+/* Virtual I/O over memory. */
+static int mem_read(void *dst, size_t n, const char **src,
+ unsigned *remaining)
+{
+// printf("reading %u\n", n);
+ if (n > *remaining) {
+ return 0;
+ }
+
+
+ memcpy(dst, *src, n);
+ *src += n;
+ *remaining -= n;
+// printf("remaining %u\n", *remaining);
+ return 1;
+}
+
+static int load_raw_packets(test_data_t *data, uint32_t *count,
+ const char *src, unsigned src_size)
+{
+
+ uint16_t tmp_size = 0;
+
+ /* Packets are stored like this: [size][packet_data]+ */
+
+ if(!mem_read(count, sizeof(uint32_t), &src, &src_size)) {
+ return -1;
+ }
+
+ for (int i = 0; i < *count; i++) {
+ uint16_t query = 0;
+ if (!mem_read(&query, sizeof(query), &src, &src_size)) {
+ return -1;
+ }
+
+ if(!mem_read(&tmp_size, sizeof(uint16_t), &src, &src_size)) {
+ return -1;
+ }
+
+ test_raw_packet_t *packet = malloc(sizeof(test_raw_packet_t));
+
+
+ packet->size = tmp_size;
+ packet->data = malloc(sizeof(uint8_t) * (tmp_size));
+ if(!mem_read(packet->data,
+ sizeof(uint8_t) * tmp_size, &src, &src_size)) {
+ return -1;
+ }
+
+ if (query) {
+ add_tail(&data->raw_query_list, (void *)packet);
+ } else {
+ add_tail(&data->raw_response_list, (void *)packet);
+ }
+
+ test_raw_packet_t *new_packet =
+ malloc(sizeof(test_raw_packet_t));
+ assert(new_packet);
+ new_packet->data = packet->data;
+ new_packet->size = packet->size;
+
+ add_tail(&data->raw_packet_list, (void *)new_packet);
+ }
+
+ return 0;
+}
+
+/* Returns size of type where avalailable */
+size_t wireformat_size_load(uint wire_type)
+{
+ switch(wire_type) {
+ case KNOT_RDATA_WF_BYTE:
+ return 1;
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ return 2;
+ break;
+ case KNOT_RDATA_WF_LONG:
+ return 4;
+ break;
+ case KNOT_RDATA_WF_A:
+ return 4;
+ break;
+ case KNOT_RDATA_WF_AAAA:
+ return 16;
+ break;
+ default: /* unknown size */
+ return 0;
+ break;
+ } /* switch */
+}
+
+static int add_label(uint8_t **labels, const uint8_t label,
+ uint *label_count)
+{
+ void *ret = realloc(*labels, sizeof(uint8_t) * (*label_count + 1));
+ if (ret == NULL) {
+ return -1;
+ }
+
+ *labels = ret;
+ (*labels)[(*label_count)++] = label;
+
+ return 0;
+}
+/* Dnames are stored label by label in the dump */
+/* TODO STRING AS WELL */
+static test_dname_t *load_test_dname(const char **src,
+ unsigned *src_size)
+{
+ test_dname_t *ret = malloc(sizeof(test_dname_t));
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ ret->size = 0;
+ ret->str = NULL;
+ ret->labels = NULL;
+ ret->wire = NULL;
+ ret->label_count = 0;
+ ret->next = NULL;
+ ret->prev = NULL;
+
+ uint8_t label_size = 0;
+ uint8_t *label_wire = NULL;
+ uint8_t *labels = NULL;
+ char *dname_str = NULL;
+ uint label_count = 0;
+ uint dname_length = 0;
+ do {
+ /* Read label size */
+ if (!mem_read(&label_size,
+ sizeof(uint8_t),
+ src,
+ src_size)) {
+ fprintf(stderr, "Faulty read\n");
+ return NULL;
+ }
+
+// diag("%d", label_size);
+
+ add_label(&labels, ret->size, &label_count);
+
+ dname_length += label_size + 1;
+
+ label_wire = malloc(sizeof(uint8_t) * (label_size + 2));
+
+ if (label_wire == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret);
+ return NULL;
+ }
+
+ label_wire[0] = label_size;
+
+ /* Read label wire */
+ if (!mem_read(label_wire + 1,
+ sizeof(uint8_t) *
+ label_size,
+ src,
+ src_size)) {
+ free(label_wire);
+ fprintf(stderr, "Faulty read\n");
+ return NULL;
+ }
+
+ label_wire[label_size + 1] = '\0';
+
+ dname_str = malloc(sizeof(char) * (label_size + 2));
+
+ if (label_size != 0) {
+ /* n - 1 : . */
+ dname_str[label_size] = '.';
+ dname_str[label_size + 1] = '\0';
+
+ memcpy(dname_str, label_wire + 1, label_size);
+ }
+
+ if (ret->size == 0) {
+ ret->wire = malloc(sizeof(uint8_t) * (label_size + 2));
+ if (ret->wire == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret);
+ return NULL;
+ }
+
+ memcpy(ret->wire, label_wire, label_size + 2);
+
+ if (label_size != 0) {
+
+ ret->str =
+ malloc(sizeof(char) * (label_size + 2));
+ if (ret->str == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret->wire);
+ free(ret);
+ return NULL;
+ }
+
+ memcpy(ret->str, dname_str, label_size + 2);
+ }
+
+ ret->size = label_size + 2;
+ } else {
+ /* Concatenate */
+ void *p = realloc(ret->wire,
+ ret->size + (label_size + 2));
+ if (p == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret->wire);
+ free(ret->labels);
+ free(ret);
+ return NULL;
+ }
+ ret->wire = p;
+
+ /* TODO Safe concat? But I set the values myself, right? */
+ /* or maybe memcpy... */
+ strcat((char *)ret->wire, (char *)label_wire);
+ assert(ret->wire);
+
+
+ if (label_size != 0) {
+
+ p = realloc(ret->str,
+ ret->size + (label_size + 2));
+ if (p == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret->wire);
+ free(ret->str);
+ free(ret->labels);
+ free(ret);
+ return NULL;
+ }
+ ret->str = p;
+
+ strcat(ret->str, dname_str);
+ assert(ret->str);
+ }
+
+ ret->size += label_size + 2;
+ }
+
+ free(label_wire);
+ free(dname_str);
+
+ } while (label_size != 0);
+
+ /*!< \warning even wireformat is ended with 0 every time !!! */
+
+ /* Root domain */
+// if (ret->size == 0) {
+// assert(ret->wire == NULL);
+
+// ret->wire = malloc(sizeof(uint8_t) * 1);
+// if (ret->wire == NULL) {
+// ERR_ALLOC_FAILED;
+// free(ret);
+// return NULL;
+// }
+
+// ret->wire[0] = '\0';
+
+// ret->labels = malloc(sizeof(uint8_t) * 1);
+// if (ret->labels == NULL) {
+// ERR_ALLOC_FAILED;
+// free(ret->wire);
+// free(ret);
+// return NULL;
+// }
+
+// ret->labels[0] = '\0';
+// ret->label_count = 1;
+// }
+
+// printf("OK: %s (%d)\n",ret->str, ret->size);
+
+ ret->labels = labels;
+ ret->size = ret->size - (label_count);
+ ret->label_count = --label_count;
+ ret->next = NULL;
+ ret->prev = NULL;
+
+ assert(ret != NULL);
+
+ return ret;
+}
+
+/*!
+ * \brief Reads dname label by label
+ */
+static test_rdata_t *load_response_rdata(uint16_t type,
+ const char **src,
+ unsigned *src_size)
+{
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "reading rdata for type: %s\n",
+ knot_rrtype_to_string(type));
+#endif
+ /*
+ * Binary format of rdata is as following:
+ * [total_length(except for some types) - see below][rdata_item]+
+ * Dname items are read label by label
+ */
+
+ test_rdata_t *rdata = malloc(sizeof(test_rdata_t));
+
+ CHECK_ALLOC_LOG(rdata, NULL);
+
+ rdata->count = 0;
+ rdata->items = NULL;
+ rdata->type = 0;
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+
+ rdata->type = type;
+
+ test_item_t *items =
+ malloc(sizeof(test_item_t) * desc->length);
+
+ if (items == NULL) {
+ ERR_ALLOC_FAILED;
+ free(rdata);
+ return NULL;
+ }
+
+ /* TODO consider realloc */
+
+ uint16_t total_raw_data_length = 0;
+ uint8_t raw_data_length;
+
+ /*
+ * These types have no length, unfortunatelly (python library
+ * does not provide this)
+ */
+ /* TODO the are more types with no length for sure ... */
+
+ if (type != KNOT_RRTYPE_A &&
+ type != KNOT_RRTYPE_NS &&
+ type != KNOT_RRTYPE_AAAA) {
+ if (!mem_read(&total_raw_data_length,
+ sizeof(total_raw_data_length), src, src_size)) {
+ free(rdata);
+ free(items);
+ fprintf(stderr, "Faulty read\n");
+ return NULL;
+ }
+ }
+
+ size_t total_read = 0;
+
+ int i;
+
+ /*
+ * TODO save number of items
+ * in the dump - of minor importance, however
+ */
+ for (i = 0; i < desc->length; i++) {
+ if ((desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME)) {
+ unsigned tmp_remaining = *src_size;
+ items[i].dname = load_test_dname(src, src_size);
+
+ if (items[i].dname == NULL) {
+ fprintf(stderr, "Could not load DNAME!\n");
+ free(rdata);
+ free(items);
+
+ /* TODO something like Marek's purge */
+
+ return NULL;
+ }
+
+// diag("Created DNAME %p item: %d %s %s\n",
+// items[i].dname, i, knot_rrtype_to_string(type),
+// items[i].dname->str);
+
+ rdata->count++;
+ items[i].type = TEST_ITEM_DNAME;
+ items[i].raw_data = NULL;
+ total_read += tmp_remaining - *src_size;
+ } else {
+ if (desc->wireformat[i] ==
+ KNOT_RDATA_WF_BINARYWITHLENGTH) {
+ if (!mem_read(&raw_data_length,
+ sizeof(raw_data_length), src, src_size)) {
+ return NULL;
+ }
+
+ total_read++;
+
+ items[i].raw_data =
+ malloc(sizeof(uint8_t) *
+ (raw_data_length + 3));
+
+ items[i].raw_data[0] =
+ (uint16_t) raw_data_length + 1;
+
+ /* let's store the length again */
+
+ ((uint8_t *)items[i].raw_data)[2] =
+ raw_data_length;
+
+ if (!mem_read(((uint8_t *)
+ items[i].raw_data) + 3,
+ sizeof(uint8_t) * (raw_data_length),
+ src, src_size)) {
+ fprintf(stderr, "Wrong read!\n");
+ return NULL;
+ }
+
+ rdata->count++;
+ items[i].type = TEST_ITEM_RAW_DATA;
+ items[i].dname = NULL;
+ total_read += sizeof(uint8_t) * raw_data_length;
+/* printf("read len (from wire): %d\n",
+ items[i].raw_data[0]);
+ hex_print((char *)items[i].raw_data + 1,
+ items[i].raw_data[0]);
+ */
+ } else {
+ /* Other type than dname or BINARYWITHLENGTH */
+ /* Set dname to NULL */
+ items[i].dname = NULL;
+
+ uint16_t size_fr_desc =
+ (uint16_t)
+ wireformat_size_load(desc->wireformat[i]);
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "reading %d\n", size_fr_desc);
+#endif
+
+ if (size_fr_desc == 0) { /* unknown length */
+/* size_fr_desc = wireformat_size_n(type,
+ items,
+ i);
+ */
+ if ((i != desc->length - 1) &&
+ desc->wireformat[i] !=
+ KNOT_RDATA_WF_TEXT ) {
+ fprintf(stderr,
+ "I dont know how "
+ "to parse this type: %d\n",
+ type);
+ return NULL;
+ } else {
+ size_fr_desc =
+ total_raw_data_length -
+ total_read;
+ if (desc->wireformat[i] ==
+ KNOT_RDATA_WF_TEXT) {
+ break;
+ }
+
+// fprintf(stderr,
+// "Guessed size: %d"
+// " for type: %s"
+// " and index: %d\n",
+// size_fr_desc,
+// knot_rrtype_to_string(type),
+// i);
+ }
+ }
+
+ items[i].raw_data =
+ malloc(sizeof(uint8_t) * size_fr_desc + 2);
+
+// diag("creating raw_data for item %d type %s %p\n",
+// i, knot_rrtype_to_string(type),
+// items[i].raw_data);
+
+ if (items[i].raw_data == NULL) {
+ ERR_ALLOC_FAILED;
+ free(rdata);
+ free(items);
+ return NULL;
+ }
+
+ items[i].raw_data[0] = size_fr_desc;
+
+ if (!mem_read(items[i].raw_data + 1,
+ size_fr_desc,
+ src, src_size)) {
+ fprintf(stderr, "Wrong read\n!");
+ return NULL;
+ }
+
+ rdata->count++;
+ items[i].type = TEST_ITEM_RAW_DATA;
+ items[i].dname = NULL;
+ total_read += size_fr_desc;
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr,
+ "read len (from descriptor): %d\n",
+ items[i].raw_data[0]);
+/* hex_print((char *)items[i].raw_data + 1,
+ items[i].raw_data[0]); */
+
+ if (desc->zoneformat[i] ==
+ KNOT_RDATA_ZF_ALGORITHM) {
+ hex_print((char *)items[i].raw_data,
+ items[i].raw_data[0] + 2);
+ } else {
+ hex_print((char *)items[i].raw_data,
+ items[i].raw_data[0] + 2);
+ }
+#endif
+ }
+ }
+ }
+
+/* if (knot_rdata_set_items(rdata, items, i) != 0) {
+ diag("Error: could not set items\n");
+ return NULL;
+ } */
+
+ rdata->items = items;
+
+ return rdata;
+}
+
+static test_rrset_t *load_response_rrset(const char **src, unsigned *src_size,
+ char is_question)
+{
+ test_rrset_t *rrset = NULL;
+ uint16_t rrset_type = 0;
+ uint16_t rrset_class = 0;
+ uint32_t rrset_ttl = 0;
+
+ /* Each rrset will only have one rdata entry */
+
+ /*
+ * RRSIGs will be read as separate RRSets because that's the way they
+ * are stored in responses
+ */
+
+ /* Read owner first */
+
+ uint8_t dname_size;
+// uint8_t *dname_wire = NULL;
+
+ rrset = malloc(sizeof(test_rrset_t));
+
+ rrset->rrsigs = NULL;
+
+ CHECK_ALLOC_LOG(rrset, NULL);
+
+ init_list(&rrset->rdata_list);
+
+ /* TODO change in dump, size is useless now! */
+ if (!mem_read(&dname_size, sizeof(dname_size), src, src_size)) {
+ free(rrset);
+ return NULL;
+ }
+
+/* dname_wire = malloc(sizeof(uint8_t) * dname_size);
+
+ CHECK_ALLOC_LOG(dname_wire, NULL);
+
+ if (!mem_read(dname_wire, sizeof(uint8_t) * dname_size, src,
+ src_size)) {
+ free(dname_wire);
+ return NULL;
+ } */
+
+ test_dname_t *owner = load_test_dname(src, src_size);
+
+ if (owner == NULL) {
+ free(rrset);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ {
+ fprintf(stderr, "Got owner: %s", owner->str);
+ }
+#endif
+ /* Read other data */
+
+ if (!mem_read(&rrset_type, sizeof(rrset_type), src, src_size)) {
+ return NULL;
+ }
+
+ if (!mem_read(&rrset_class, sizeof(rrset_class), src, src_size)) {
+ return NULL;
+ }
+
+ if (!is_question) {
+ if (!mem_read(&rrset_ttl, sizeof(rrset_ttl), src, src_size)) {
+ return NULL;
+ }
+ } else {
+ rrset_ttl = 0;
+ }
+
+// rrset = knot_rrset_new(owner, rrset_type, rrset_class, rrset_ttl);
+
+ rrset->owner = owner;
+ rrset->type = rrset_type;
+ rrset->rclass = rrset_class;
+ rrset->ttl = rrset_ttl;
+
+ /* Question rrsets have no rdata */
+
+ if (!is_question) {
+ test_rdata_t *tmp_rdata;
+
+ tmp_rdata = load_response_rdata(rrset->type, src, src_size);
+
+ if (tmp_rdata == NULL) {
+ fprintf(stderr,
+ "Could not load rrset rdata - type: %d",
+ rrset->type);
+ free(rrset);
+ return NULL;
+ }
+
+ assert(tmp_rdata->type == rrset->type);
+
+ add_tail(&rrset->rdata_list, (node *)tmp_rdata);
+ }
+
+ return rrset;
+}
+
+static test_response_t *load_parsed_response(const char **src,
+ unsigned *src_size)
+{
+ /* Loads parsed response/query from binary format,
+ * which is as following:
+ * [id][qdcount][ancount][nscount][arcount]
+ * [question_rrset+][answer_rrset+][authority_rrset+]
+ * [additional_rrset]+
+ */
+
+ test_response_t *resp = malloc(sizeof(test_response_t));
+
+ CHECK_ALLOC_LOG(resp, NULL);
+
+ if (!mem_read(&resp->id, sizeof(resp->id), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "id %d\n", resp->id);
+#endif
+
+ if (!mem_read(&resp->qdcount, sizeof(resp->qdcount), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "qdcount: %d\n", resp->qdcount);
+#endif
+
+ if (!mem_read(&resp->ancount, sizeof(resp->ancount), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "ancount: %d\n", resp->ancount);
+#endif
+
+ if (!mem_read(&resp->nscount, sizeof(resp->nscount), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "nscount: %d\n", resp->nscount);
+#endif
+
+ if (!mem_read(&resp->arcount, sizeof(resp->arcount), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+#ifdef RESP_TEST_DEBUG
+ fprintf(stderr, "arcount: %d\n", resp->arcount);
+#endif
+
+ if (!mem_read(&resp->query, sizeof(resp->query), src, src_size)) {
+ free(resp);
+ return NULL;
+ }
+
+ test_rrset_t **question_rrsets;
+
+ question_rrsets = malloc(sizeof(test_rrset_t *) * resp->qdcount);
+
+ for (int i = 0; i < resp->qdcount; i++) {
+ question_rrsets[i] = load_response_rrset(src, src_size, 1);
+ if (question_rrsets[i] == NULL) {
+ fprintf(stderr, "Could not load question rrsets\n");
+
+ for (int j = 0; j < i; j++) {
+ free(question_rrsets[i]);
+ }
+ free(question_rrsets);
+ free(resp);
+ return NULL;
+ }
+ }
+
+ /* only one question in our case */
+
+ resp->qname = question_rrsets[0]->owner;
+ resp->qtype = question_rrsets[0]->type;
+ resp->qclass = question_rrsets[0]->rclass;
+
+ resp->question = NULL;
+
+/* for (int i = 0; i < resp->qdcount; i++) {
+ knot_rrset_free(&(question_rrsets[i]));
+ } */
+
+ free(question_rrsets);
+
+ test_rrset_t *tmp_rrset = NULL;
+
+ if (resp->ancount > 0) {
+ resp->answer =
+ malloc(sizeof(test_rrset_t *) * resp->ancount);
+ } else {
+ resp->answer = NULL;
+ }
+
+ for (int i = 0; i < resp->ancount; i++) {
+ tmp_rrset = load_response_rrset(src, src_size, 0);
+ resp->answer[i] = tmp_rrset;
+ if (resp->answer[i] == NULL) {
+ fprintf(stderr, "Could not load answer rrsets\n");
+ free(resp->answer);
+ free(resp);
+ return NULL;
+ }
+ }
+
+ if (resp->nscount > 0) {
+ resp->authority =
+ malloc(sizeof(test_rrset_t *) * resp->nscount);
+ } else {
+ resp->authority = NULL;
+ }
+
+ for (int i = 0; i < resp->nscount; i++) {
+ tmp_rrset = load_response_rrset(src, src_size, 0);
+ resp->authority[i] = tmp_rrset;
+ if (resp->authority[i] == NULL) {
+ fprintf(stderr, "Could not load authority rrsets\n");
+ free(resp->authority);
+ free(resp->answer);
+ free(resp);
+ return NULL;
+ }
+ }
+
+ if (resp->arcount > 0) {
+ resp->additional =
+ malloc(sizeof(test_rrset_t *) * resp->arcount);
+ } else {
+ resp->additional = NULL;
+ }
+
+ for (int i = 0; i < resp->arcount; i++) {
+ tmp_rrset = load_response_rrset(src, src_size, 0);
+ if (tmp_rrset == NULL) {
+ fprintf(stderr, "Could not load rrset (additional)\n");
+ free(resp->additional);
+ free(resp->authority);
+ free(resp->answer);
+ free(resp);
+ return NULL;
+ }
+
+ resp->additional[i] = tmp_rrset;
+ }
+
+ /* this will never be used */
+
+ resp->flags1 = 0;
+ resp->flags2 = 0;
+
+ return resp;
+}
+
+static void test_dname_free(test_dname_t **dname)
+{
+ assert(dname != NULL && *dname != NULL);
+ free((*dname)->labels);
+// free((*dname)->str);
+ free((*dname)->wire);
+
+ free(*dname);
+ *dname = NULL;
+}
+
+static int wire_is_dname(uint type)
+{
+ return (type == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ type == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ type == KNOT_RDATA_WF_LITERAL_DNAME);
+}
+
+static void test_rdata_free(test_rdata_t **rdata)
+{
+ assert(rdata != NULL && *rdata != NULL);
+
+ /* Free all the items */
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type((*rdata)->type);
+
+ for (int i = 0; i < (*rdata)->count; i++) {
+ if ((wire_is_dname(desc->wireformat[i])) &&
+ ((*rdata)->items[i].dname != NULL)) {
+ test_dname_free(&(*rdata)->items[i].dname);
+ } else if ((*rdata)->items[i].raw_data != NULL) {
+ free((*rdata)->items[i].raw_data);
+ (*rdata)->items[i].raw_data = NULL;
+ }
+ }
+// free((*rdata)->items);
+// free(*rdata);
+ *rdata = NULL;
+}
+
+static void test_rrset_free(test_rrset_t **rrset)
+{
+ assert(rrset && *rrset);
+
+ test_dname_free(&(*rrset)->owner);
+ /* Free all the rdatas */
+ node *n = NULL, *nxt = NULL;
+ WALK_LIST_DELSAFE(n, nxt, (*rrset)->rdata_list) {
+ test_rdata_t *tmp_rdata = (test_rdata_t *)n;
+ assert(tmp_rdata);
+ if (tmp_rdata != NULL) {
+ test_rdata_free(&tmp_rdata);
+ }
+ }
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+static void test_response_free(test_response_t **response)
+{
+ assert(response && *response);
+ if ((*response)->qname != NULL) {
+ test_dname_free(&(*response)->qname);
+ }
+
+ if ((*response)->additional != NULL) {
+ for (int j = 0; j < (*response)->arcount; j++) {
+ test_rrset_free(&((*response)->additional[j]));
+ }
+
+ free((*response)->additional);
+ }
+
+ if ((*response)->answer != NULL) {
+ for (int j = 0; j < (*response)->ancount; j++) {
+ test_rrset_free(&((*response)->answer[j]));
+ }
+
+ free((*response)->answer);
+ }
+
+ if ((*response)->authority != NULL) {
+ for (int j = 0; j < (*response)->nscount; j++) {
+ test_rrset_free(&((*response)->authority[j]));
+ }
+
+ free((*response)->authority);
+ }
+
+ free((*response));
+ *response = NULL;
+}
+
+static void get_and_save_data_from_rdata(test_rdata_t *rdata,
+ test_data_t *data, uint16_t type)
+{
+ /* We only want to extract dnames */
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+
+ if (rdata->count == 0) {
+// diag("Rdata count not set!\n");
+ rdata->count = desc->length;
+ }
+
+ for(int i = 0; i < rdata->count; i++) {
+ if ((desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME)) {
+ add_tail(&data->dname_list,
+ (node *)rdata->items[i].dname);
+ test_item_t *temp_item = malloc(sizeof(test_item_t));
+ temp_item->dname = rdata->items[i].dname;
+ temp_item->type = TEST_ITEM_DNAME;
+ temp_item->raw_data = NULL;
+ add_tail(&data->item_list, (node *)temp_item);
+ } else {
+ test_item_t *temp_item = malloc(sizeof(test_item_t));
+ temp_item->dname = NULL;
+ temp_item->type = TEST_ITEM_RAW_DATA;
+ temp_item->raw_data = rdata->items[i].raw_data;
+ add_tail(&data->item_list, (node *)temp_item);
+ }
+ }
+}
+
+static void get_and_save_data_from_rrset(const test_rrset_t *rrset,
+ test_data_t *data)
+{
+// knot_rrtype_descriptor_t *desc =
+// knot_rrtype_descriptor_by_type(rrset->type);
+ /* RDATA are in a list. */
+ node *n = NULL;
+ int i = 0;
+ WALK_LIST(n, rrset->rdata_list) {
+ test_rdata_t *tmp_rdata = (test_rdata_t *)n;
+ assert(tmp_rdata);
+ assert(&data->rdata_list);
+ assert(&data->rdata_list != &rrset->rdata_list);
+ assert(tmp_rdata->type == rrset->type);
+
+ test_rdata_t *new_rdata = malloc(sizeof(test_rdata_t));
+ new_rdata->count = tmp_rdata->count;
+ new_rdata->items = tmp_rdata->items;
+ new_rdata->type = tmp_rdata->type;
+
+ add_tail(&data->rdata_list, (node *)new_rdata);
+ get_and_save_data_from_rdata(tmp_rdata, data, rrset->type);
+ i++;
+ }
+ assert(i == 1);
+}
+
+static int add_rrset_to_node(const test_rrset_t *rrset, test_data_t *data)
+{
+ /* First, create node from rrset */
+ test_node_t *tmp_node = malloc(sizeof(test_node_t));
+ memset(tmp_node, 0, sizeof(test_node_t));
+ CHECK_ALLOC_LOG(tmp_node, -1);
+
+ tmp_node->owner = rrset->owner;
+ tmp_node->parent = NULL;
+ tmp_node->rrset_count = 0;
+
+ /* Will not be used in list now */
+ tmp_node->prev = NULL;
+ tmp_node->next = NULL;
+
+
+// printf("%s\n", rrset->owner->wire);
+// getchar();
+
+/* tmp_node->avl_left = NULL;
+ tmp_node->avl_right = NULL;
+ tmp_node->avl_height = 0; */
+
+ test_node_t *found_node =
+ TREE_FIND(data->node_tree, test_node, avl, tmp_node);
+
+ if (found_node == NULL) {
+ /* Insert new node with current rrset */
+ init_list(&tmp_node->rrset_list);
+ add_tail(&tmp_node->rrset_list, (node *)rrset);
+ tmp_node->rrset_count++;
+
+ TREE_INSERT(data->node_tree, test_node, avl, tmp_node);
+ } else {
+ free(tmp_node);
+ /* append rrset */
+
+ add_tail(&found_node->rrset_list, (node *)rrset);
+ found_node->rrset_count++;
+ }
+
+ return 0;
+}
+
+static void get_and_save_data_from_response(const test_response_t *response,
+ test_data_t *data)
+{
+ /* Go through all the rrsets in the response */
+
+ for (int i = 0; i < response->ancount; i++) {
+ assert(response->answer[i]);
+ /* Add rrset to the list of rrsets - there will be duplicates
+ * But not the same pointers */
+ add_tail(&data->rrset_list, (node *)response->answer[i]);
+ get_and_save_data_from_rrset(response->answer[i], data);
+ if (add_rrset_to_node(response->answer[i], data) != 0) {
+ return;
+ }
+ }
+
+ for (int i = 0; i < response->arcount; i++) {
+ /* Add rrset to the list of rrsets - there will be duplicates */
+ assert(response->additional[i]);
+ add_tail(&data->rrset_list, (node *)response->additional[i]);
+ get_and_save_data_from_rrset(response->additional[i], data);
+ if (add_rrset_to_node(response->additional[i], data) != 0) {
+ return;
+ }
+ }
+
+ for (int i = 0; i < response->nscount; i++) {
+ assert(response->authority[i]);
+ /* Add rrset to the list of rrsets - there will be duplicates */
+ add_tail(&data->rrset_list, (node *)response->authority[i]);
+ get_and_save_data_from_rrset(response->authority[i], data);
+ if (add_rrset_to_node(response->authority[i], data) != 0) {
+ return;
+ }
+ }
+
+// for (int i = 0; i < response->qdcount; i++) {
+// /* Add rrset to the list of rrsets - there will be duplicates */
+// add_tail(&data->rrset_list, (node *)response->question[i]);
+// get_and_save_data_from_rrset(response->question[i], data);
+// }
+}
+
+static int load_parsed_responses(test_data_t *data, uint32_t *count,
+ const char* src, unsigned src_size)
+{
+ if (!mem_read(count, sizeof(*count), &src, &src_size)) {
+ fprintf(stderr, "Wrong read\n");
+ return -1;
+ }
+
+// *responses = malloc(sizeof(test_response_t *) * *count);
+
+ for (int i = 0; i < *count; i++) {
+ test_response_t *tmp_response =
+ load_parsed_response(&src, &src_size);
+
+ if (tmp_response == NULL) {
+ fprintf(stderr, "Could not load response - %d"
+ "- returned NULL\n",
+ i);
+ return -1;
+ }
+
+ if (tmp_response->query) {
+ add_tail(&data->query_list, (node *)tmp_response);
+ } else {
+ add_tail(&data->response_list, (node *)tmp_response);
+ }
+
+ /* Create new node */
+ test_response_t *resp = malloc(sizeof(test_response_t));
+ assert(resp);
+ memcpy(resp, tmp_response, sizeof(test_response_t));
+ add_tail(&data->packet_list,
+ (node *)resp);
+ }
+
+ return 0;
+}
+
+//void free_parsed_responses(test_response_t ***responses, uint32_t *count)
+//{
+// if (*responses != NULL) {
+// for (int i = 0; i < *count; i++) {
+// free_parsed_response((*responses)[i]);
+// }
+// free(*responses);
+// }
+//}
+
+static int compare_nodes(test_node_t *node1, test_node_t *node2)
+{
+ assert(node1->owner && node2->owner);
+ /*!< \warning Wires have to be \0 terminated. */
+ return (strcmp((char *)node1->owner->wire, (char *)node2->owner->wire));
+}
+
+static int init_data(test_data_t *data)
+{
+ if (data == NULL) {
+ return 0;
+ }
+
+ /* Initialize all the lists */
+ init_list(&data->dname_list);
+ init_list(&data->edns_list);
+ init_list(&data->node_list);
+ init_list(&data->response_list);
+ init_list(&data->rdata_list);
+ init_list(&data->rrset_list);
+ init_list(&data->item_list);
+ init_list(&data->raw_response_list);
+ init_list(&data->raw_query_list);
+ init_list(&data->raw_packet_list);
+ init_list(&data->query_list);
+ init_list(&data->packet_list);
+
+ data->node_tree = malloc(sizeof(avl_tree_test_t));
+ CHECK_ALLOC_LOG(data->node_tree, 0);
+
+ TREE_INIT(data->node_tree, compare_nodes);
+
+ return 1;
+}
+
+static void print_stats(test_data_t *data)
+{
+ uint resp_count = 0, dname_count = 0, node_count = 0,
+ rdata_count = 0, rrset_count = 0, item_count = 0, query_count = 0,
+ raw_query_count = 0, response_count = 0, packet_count = 0,
+ raw_packet_count = 0, raw_response_count = 0;
+
+ node *n = NULL; /* Will not be used */
+
+ WALK_LIST(n, data->response_list) {
+ resp_count++;
+ }
+
+ WALK_LIST(n, data->rrset_list) {
+// node *tmp = NULL;
+// assert(((test_rrset_t *)n)->owner);
+// WALK_LIST(tmp, ((test_rrset_t *)n)->rdata_list) {
+// test_rdata_t *rdata = (test_rdata_t *)tmp;
+// assert(rdata->type == ((test_rrset_t *)n)->type);
+// }
+ rrset_count++;
+ }
+
+ WALK_LIST(n, data->rdata_list) {
+ rdata_count++;
+ }
+
+ WALK_LIST(n, data->dname_list) {
+ dname_count++;
+ }
+
+ WALK_LIST(n, data->node_list) {
+ node_count++;
+ }
+
+ WALK_LIST(n, data->item_list) {
+ item_count++;
+ }
+
+ WALK_LIST(n, data->raw_response_list) {
+ raw_response_count++;
+ }
+
+ WALK_LIST(n, data->query_list) {
+ query_count++;
+ }
+
+ WALK_LIST(n, data->response_list) {
+ response_count++;
+ }
+
+ WALK_LIST(n, data->raw_query_list) {
+ raw_query_count++;
+ }
+
+ WALK_LIST(n, data->packet_list) {
+ packet_count++;
+ }
+
+ WALK_LIST(n, data->raw_packet_list) {
+ raw_packet_count++;
+ }
+
+ printf("Loaded: Responses: %d RRSets: %d RDATAs: %d Dnames: %d "
+ "Nodes: %d Items: %d Raw_responses: %d Queries: %d \n"
+ "Raw_queries; %d Total packets: %d Total_raw_packets: %d\n", resp_count, rrset_count,
+ rdata_count, dname_count, node_count, item_count,
+ raw_response_count, query_count, raw_query_count, packet_count,
+ raw_packet_count);
+}
+
+static void save_node_to_list(test_node_t *n, void *p)
+{
+ test_data_t *data = (test_data_t *)p;
+
+ add_tail(&data->node_list, (node *)n);
+}
+
+static void del_node(test_node_t *n, void *p)
+{
+// test_data_t *data = (test_data_t *)p;
+ free(n);
+}
+
+
+void free_data(test_data_t **data)
+{
+ assert(data && *data);
+ /* We will free all the data using responses
+ * (others are just references )*/
+ node *n = NULL, *nxt = NULL;
+ WALK_LIST_DELSAFE(n, nxt, (*data)->response_list) {
+ test_response_t *tmp_response = (test_response_t *)n;
+ if (tmp_response != NULL) {
+ test_response_free(&tmp_response);
+ }
+ }
+
+ TREE_POST_ORDER_APPLY((*data)->node_tree, test_node, avl, del_node,
+ NULL);
+
+ free((*data)->node_tree);
+
+ free(*data);
+ *data = NULL;
+}
+
+test_data_t *create_test_data_from_dump()
+{
+ test_data_t *ret = malloc(sizeof(test_data_t));
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ if (!init_data(ret)) {
+ free(ret);
+ return NULL;
+ }
+
+ uint32_t raw_packet_count = 0;
+
+ if (load_raw_packets(ret, &raw_packet_count, raw_data_rc,
+ raw_data_rc_size) != 0) {
+ fprintf(stderr, "Could not load raw_data, quitting");
+ /* TODO walk the lists*/
+ free(ret);
+ return NULL;
+ }
+
+ uint32_t response_count = 0;
+
+ if (load_parsed_responses(ret, &response_count, parsed_data_rc,
+ parsed_data_rc_size) != 0) {
+ fprintf(stderr, "Could not load responses, quitting");
+ /* TODO walk the lists*/
+ free(ret);
+ return NULL;
+ }
+
+ /* For each parsed response - create more data from it. */
+ /* Probably not the most effective way, but it is better than to
+ * rewrite most of the code .*/
+
+ node *n = NULL;
+
+ WALK_LIST(n , ret->response_list) {
+ get_and_save_data_from_response((test_response_t *)n, ret);
+ }
+
+ /* Create list from AVL tree */
+
+ TREE_FORWARD_APPLY(ret->node_tree, test_node, avl,
+ save_node_to_list, ret);
+
+ print_stats(ret);
+
+ return ret;
+}
diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.h b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h
new file mode 100644
index 0000000..8f57944
--- /dev/null
+++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h
@@ -0,0 +1,179 @@
+/* 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_TESTS_LOADER_H
+#define KNOT_TESTS_LOADER_H
+
+#include <stdint.h>
+
+#include "libknot/common.h"
+#include "common/lists.h"
+#include "common/tree.h"
+
+
+/* Parsed raw packet*/
+struct test_raw_packet {
+ struct node *next, *prev;
+ uint size;
+ uint8_t *data;
+};
+
+typedef struct test_raw_packet test_raw_packet_t;
+
+/* Test type definitions */
+
+struct test_dname {
+ struct node *next, *prev;
+ char *str;
+ uint8_t *wire;
+ uint size;
+ uint8_t *labels;
+ short label_count;
+};
+
+typedef struct test_dname test_dname_t;
+
+struct test_edns_options {
+ struct node *next, *prev;
+ uint16_t code;
+ uint16_t length;
+ uint8_t *data;
+};
+
+struct test_edns {
+ struct node *next, *prev;
+ struct test_edns_options *options;
+ uint16_t payload;
+ uint8_t ext_rcode;
+ uint8_t version;
+ uint16_t flags;
+ uint16_t *wire;
+ short option_count;
+ short options_max;
+ short size;
+};
+
+typedef struct test_edns test_edns_t;
+
+typedef TREE_HEAD(avl_tree_test, test_node) avl_tree_test_t;
+
+struct test_node {
+ struct node *next, *prev;
+ test_dname_t *owner;
+ short rrset_count;
+ struct test_node *parent;
+ list rrset_list;
+
+ TREE_ENTRY(test_node) avl;
+};
+
+typedef struct test_node test_node_t;
+
+enum item_type {
+ TEST_ITEM_DNAME,
+ TEST_ITEM_RAW_DATA
+};
+
+typedef enum item_type item_type_t;
+
+struct test_item {
+ uint16_t *raw_data;
+ test_dname_t *dname;
+ item_type_t type;
+};
+
+typedef struct test_item test_item_t;
+
+struct test_rdata {
+ struct node *next, *prev;
+ uint count;
+ uint type; /*!< Might be handy */
+ test_item_t *items;
+};
+
+typedef struct test_rdata test_rdata_t;
+
+struct test_rrset {
+ struct node *next, *prev;
+ test_dname_t *owner;
+ uint16_t type;
+ uint16_t rclass;
+ uint32_t ttl;
+ struct test_rrset *rrsigs;
+ uint16_t *wire;
+ list rdata_list;
+};
+
+typedef struct test_rrset test_rrset_t;
+
+struct test_response {
+ struct node *next, *prev;
+ /* This is basically same thing as actual response structure */
+ uint16_t query;
+ test_dname_t *qname;
+ uint16_t qclass;
+ uint16_t qtype;
+ uint16_t id;
+ uint8_t flags1;
+ uint8_t flags2;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+
+ /* Arrays of rrsets */
+
+ test_rrset_t **question;
+ test_rrset_t **answer;
+ test_rrset_t **authority;
+ test_rrset_t **additional;
+
+ short size;
+
+ /* what about the rest of the values?
+ * they cannot be modified from API, but this is probably the best
+ * place to test them as well */
+};
+
+typedef struct test_response test_response_t;
+
+/*!< \brief contains lists of all the structures */
+struct test_data {
+ list dname_list;
+ list edns_list;
+ list rdata_list;
+ list node_list;
+ list rrset_list;
+ list response_list;
+ list raw_response_list;
+ list query_list;
+ list raw_query_list;
+ list item_list;
+ /* responses and queries together */
+ list packet_list;
+ list raw_packet_list;
+
+ avl_tree_test_t *node_tree;
+};
+
+typedef struct test_data test_data_t;
+
+/*!< \brief Parses resource with data and creates all possible structures. */
+test_data_t *create_test_data_from_dump();
+
+test_data_t *data_for_knot_tests;
+
+#endif // KNOT_TESTS_LOADER_H
diff --git a/src/tests/libknot/realdata/unittests_libknot_realdata.c b/src/tests/libknot/realdata/unittests_libknot_realdata.c
new file mode 100644
index 0000000..e557c43
--- /dev/null
+++ b/src/tests/libknot/realdata/unittests_libknot_realdata.c
@@ -0,0 +1,93 @@
+/* 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 "knot/common.h"
+#include "common/libtap/tap_unit.h"
+
+#include "tests/libknot/realdata/libknot_tests_loader_realdata.h"
+
+// Units to test
+#include "tests/libknot/realdata/libknot/dname_tests_realdata.h"
+#include "tests/libknot/realdata/libknot/response_tests_realdata.h"
+//#include "libknot/edns_tests.h"
+#include "tests/libknot/realdata/libknot/node_tests_realdata.h"
+#include "tests/libknot/realdata/libknot/rdata_tests_realdata.h"
+//#include "libknot/response_tests_realdata.h"
+#include "tests/libknot/realdata/libknot/rrset_tests_realdata.h"
+//#include "libknot/zone_tests_realdata.h"
+#include "tests/libknot/realdata/libknot/zonedb_tests_realdata.h"
+#include "tests/libknot/realdata/libknot/packet_tests_realdata.h"
+
+#include "common/lists.h"
+// Run all loaded units
+int main(int argc, char *argv[])
+{
+ data_for_knot_tests = create_test_data_from_dump();
+
+ if (data_for_knot_tests == NULL) {
+ diag("Data could not be loaded!");
+ return 0;
+ }
+
+ // Open log
+// log_init(LOG_UPTO(LOG_ERR), LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING));
+
+ // Build test set
+ unit_api *tests[] = {
+
+ /* DNS units */
+// &cuckoo_tests_api, //! Cuckoo hashing unit
+ &dname_tests_api, //! DNS library (dname) unit
+// &edns_tests_api, //! DNS library (EDNS0) unit
+ &node_tests_api, //! DNS library (node) unit
+ &rdata_tests_api, //! DNS library (rdata) unit
+ &packet_tests_api,
+// &response_tests_api, //! DNS library (response) unit
+ &response_tests_api, //! DNS library (response) unit
+ &rrset_tests_api, //! DNS library (rrset) unit
+// &zone_tests_api, //! DNS library (zone) unit
+// &zonedb_tests_api, //! DNS library (zonedb) unit
+ NULL
+ };
+
+ // Plan number of tests
+ int id = 0;
+ int test_count = 0;
+ note("Units:");
+ while (tests[id] != NULL) {
+ note("- %s : %d tests", tests[id]->name,
+ tests[id]->count(argc, argv));
+ test_count += tests[id]->count(argc, argv);
+ ++id;
+ }
+
+ plan(test_count);
+
+ // Run tests
+ id = 0;
+ while (tests[id] != NULL) {
+ diag("Testing unit: %s", tests[id]->name);
+ tests[id]->run(argc, argv);
+ ++id;
+ }
+
+// log_close();
+
+ // Evaluate
+ return exit_status();
+}
+
diff --git a/src/tests/libknot/unittests_libknot.c b/src/tests/libknot/unittests_libknot.c
new file mode 100644
index 0000000..62d4b90
--- /dev/null
+++ b/src/tests/libknot/unittests_libknot.c
@@ -0,0 +1,90 @@
+/* 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 "knot/common.h"
+#include "common/libtap/tap_unit.h"
+
+// Units to test
+#include "tests/libknot/libknot/cuckoo_tests.h"
+#include "tests/libknot/libknot/dname_tests.h"
+#include "tests/libknot/libknot/edns_tests.h"
+#include "tests/libknot/libknot/node_tests.h"
+#include "tests/libknot/libknot/rdata_tests.h"
+#include "tests/libknot/libknot/response_tests.h"
+#include "tests/libknot/libknot/rrset_tests.h"
+#include "tests/libknot/libknot/zone_tests.h"
+#include "tests/libknot/libknot/dname_table_tests.h"
+#include "tests/libknot/libknot/nsec3_tests.h"
+#include "tests/libknot/libknot/packet_tests.h"
+#include "tests/libknot/libknot/query_tests.h"
+#include "tests/libknot/libknot/zonedb_tests.h"
+#include "tests/libknot/libknot/zone_tree_tests.h"
+
+// Run all loaded units
+int main(int argc, char *argv[])
+{
+ // Open log
+// log_init(LOG_UPTO(LOG_ERR), LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING));
+
+ // Build test set
+ unit_api *tests[] = {
+
+ /* DNS units */
+ &cuckoo_tests_api, //! Cuckoo hashing unit
+ &dname_tests_api, //! DNS library (dname) unit
+ &edns_tests_api, //! DNS library (EDNS0) unit
+ &zone_tests_api, //! DNS library (zone) unit
+ &node_tests_api, //! DNS library (node) unit
+ &rdata_tests_api, //! DNS library (rdata) unit
+ &response_tests_api, //! DNS library (response) unit
+ &rrset_tests_api, //! DNS library (rrset) unit
+ &dname_table_tests_api,
+ &nsec3_tests_api,
+ &packet_tests_api,
+ &query_tests_api,
+ &zonedb_tests_api, //! DNS library (zonedb) unit
+ &zone_tree_tests_api,
+ NULL
+ };
+
+ // Plan number of tests
+ int id = 0;
+ int test_count = 0;
+ note("Units:");
+ while (tests[id] != NULL) {
+ note("- %s : %d tests", tests[id]->name,
+ tests[id]->count(argc, argv));
+ test_count += tests[id]->count(argc, argv);
+ ++id;
+ }
+
+ plan(test_count);
+
+ // Run tests
+ id = 0;
+ while (tests[id] != NULL) {
+ diag("Testing unit: %s", tests[id]->name);
+ tests[id]->run(argc, argv);
+ ++id;
+ }
+
+// log_close();
+
+ // Evaluate
+ return exit_status();
+}
+
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
new file mode 100644
index 0000000..2d57ed2
--- /dev/null
+++ b/src/tests/unittests_main.c
@@ -0,0 +1,84 @@
+/* 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 "knot/common.h"
+#include "common/libtap/tap_unit.h"
+
+// Units to test
+#include "tests/common/slab_tests.h"
+#include "tests/common/skiplist_tests.h"
+#include "tests/common/events_tests.h"
+#include "tests/common/da_tests.h"
+#include "tests/common/acl_tests.h"
+#include "tests/common/fdset_tests.h"
+#include "tests/knot/dthreads_tests.h"
+#include "tests/knot/journal_tests.h"
+#include "tests/knot/server_tests.h"
+#include "tests/knot/conf_tests.h"
+
+// Run all loaded units
+int main(int argc, char *argv[])
+{
+ // Open log
+ log_init();
+ log_levels_set(LOGT_SYSLOG, LOG_ANY, 0);
+
+ // Build test set
+ unit_api *tests[] = {
+ /* Core data structures. */
+ &journal_tests_api, //! Journal unit
+ &slab_tests_api, //! SLAB allocator unit
+ &skiplist_tests_api, //! Skip list unit
+ &dthreads_tests_api, //! DThreads testing unit
+ &events_tests_api, //! Events testing unit
+ &da_tests_api, //! Dynamic array unit
+ &acl_tests_api, //! ACLs
+ &fdset_tests_api, //! FDSET polling wrapper
+
+ /* Server parts. */
+ &conf_tests_api, //! Configuration parser tests
+ &server_tests_api, //! Server unit
+ NULL
+ };
+
+ // Plan number of tests
+ int id = 0;
+ int test_count = 0;
+ note("Units:");
+ while (tests[id] != NULL) {
+ note("- %s : %d tests", tests[id]->name,
+ tests[id]->count(argc, argv));
+ test_count += tests[id]->count(argc, argv);
+ ++id;
+ }
+
+ plan(test_count);
+
+ // Run tests
+ id = 0;
+ while (tests[id] != NULL) {
+ diag("Testing unit: %s", tests[id]->name);
+ tests[id]->run(argc, argv);
+ ++id;
+ }
+
+ log_close();
+
+ // Evaluate
+ return exit_status();
+}
+
diff --git a/src/zcompile/LICENSE b/src/zcompile/LICENSE
new file mode 100644
index 0000000..55faacf
--- /dev/null
+++ b/src/zcompile/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+
+This software is open source.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+Neither the name of the NLNET LABS nor the names of its contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/zcompile/parser-descriptor.c b/src/zcompile/parser-descriptor.c
new file mode 100644
index 0000000..41e7f2d
--- /dev/null
+++ b/src/zcompile/parser-descriptor.c
@@ -0,0 +1,535 @@
+/*!
+ * \file parser-descriptor.c
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet Labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief Contains resource record descriptor and its API
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+//#include "common.h"
+#include "zcompile/parser-descriptor.h"
+/* TODO this has to be removed - move tokens to separate file
+ but can it be done?) */
+#include "zcompile/zcompile.h"
+/* FIXME: Generate .y and .l to zoneparser/ */
+#include "zparser.h"
+
+enum desclen { PARSER_RRTYPE_DESCRIPTORS_LENGTH = 32770 }; // used to be 101
+
+/* Taken from RFC 1035, section 3.2.4. */
+static knot_lookup_table_t dns_rrclasses[] = {
+ { PARSER_CLASS_IN, "IN" }, /* the Internet */
+ { PARSER_CLASS_CS, "CS" }, /* the CSNET class (Obsolete) */
+ { PARSER_CLASS_CH, "CH" }, /* the CHAOS class */
+ { PARSER_CLASS_HS, "HS" }, /* Hesiod */
+ { 0, NULL }
+};
+static parser_rrtype_descriptor_t
+ knot_rrtype_descriptors[PARSER_RRTYPE_DESCRIPTORS_LENGTH] = {
+ /* 0 */
+ { 0, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 1 */
+ { PARSER_RRTYPE_A, T_A, "A", 1, { PARSER_RDATA_WF_A }, true },
+ /* 2 */
+ { PARSER_RRTYPE_NS, T_NS, "NS", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 3 */
+ { PARSER_RRTYPE_MD, T_MD, "MD", 1,
+ { PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 4 */
+ { PARSER_RRTYPE_MF, T_MF, "MF", 1,
+ { PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 5 */
+ { PARSER_RRTYPE_CNAME, T_CNAME, "CNAME", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 6 */
+ { PARSER_RRTYPE_SOA, T_SOA, "SOA", 7,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME, PARSER_RDATA_WF_COMPRESSED_DNAME,
+ PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG,
+ PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG }, true },
+ /* 7 */
+ { PARSER_RRTYPE_MB, T_MB, "MB", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 8 */
+ { PARSER_RRTYPE_MG, T_MG, "MG", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 9 */
+ { PARSER_RRTYPE_MR, T_MR, "MR", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 10 */
+ { PARSER_RRTYPE_NULL, T_NULL, NULL, 1,
+ { PARSER_RDATA_WF_BINARY }, true },
+ /* 11 */
+ { PARSER_RRTYPE_WKS, T_WKS, "WKS", 2,
+ { PARSER_RDATA_WF_A, PARSER_RDATA_WF_BINARY }, true },
+ /* 12 */
+ { PARSER_RRTYPE_PTR, T_PTR, "PTR", 1,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 13 */
+ { PARSER_RRTYPE_HINFO, T_HINFO, "HINFO", 2,
+ { PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT }, true },
+ /* 14 */
+ { PARSER_RRTYPE_MINFO, T_MINFO, "MINFO", 2,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME,
+ PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 15 */
+ { PARSER_RRTYPE_MX, T_MX, "MX", 2,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 16 */ /* This is obscure, but I guess there's no other way */
+ { PARSER_RRTYPE_TXT, T_TXT, "TXT", PARSER_MAX_RDATA_ITEMS,
+ { PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT }, false },
+ /* 17 */
+ { PARSER_RRTYPE_RP, T_RP, "RP", 2,
+ { PARSER_RDATA_WF_COMPRESSED_DNAME,
+ PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 18 */
+ { PARSER_RRTYPE_AFSDB, T_AFSDB, "AFSDB", 2,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 19 */
+ { PARSER_RRTYPE_X25, T_X25, "X25", 1,
+ { PARSER_RDATA_WF_TEXT }, true },
+ /* 20 */
+ { PARSER_RRTYPE_ISDN, T_ISDN, "ISDN", 2,
+ { PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT }, false },
+ /* 21 */
+ { PARSER_RRTYPE_RT, T_RT, "RT", 2,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_COMPRESSED_DNAME }, true },
+ /* 22 */
+ { PARSER_RRTYPE_NSAP, T_NSAP, "NSAP", 1,
+ { PARSER_RDATA_WF_BINARY }, true },
+ /* 23 */
+ { 23, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 24 */
+ { PARSER_RRTYPE_SIG, T_SIG, "SIG", 9,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG,
+ PARSER_RDATA_WF_SHORT,PARSER_RDATA_WF_UNCOMPRESSED_DNAME,
+ PARSER_RDATA_WF_BINARY }, true },
+ /* 25 */
+ { PARSER_RRTYPE_KEY, T_KEY, "KEY", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BINARY }, true },
+ /* 26 */
+ { PARSER_RRTYPE_PX, T_PX, "PX", 3,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_UNCOMPRESSED_DNAME,
+ PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 27 */
+ { 27, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 28 */
+ { PARSER_RRTYPE_AAAA, T_AAAA, "AAAA", 1,
+ { PARSER_RDATA_WF_AAAA }, true },
+ /* 29 */
+ { PARSER_RRTYPE_LOC, T_LOC, "LOC", 1,
+ { PARSER_RDATA_WF_BINARY }, true },
+ /* 30 */
+ { PARSER_RRTYPE_NXT, T_NXT, "NXT", 2,
+ { PARSER_RDATA_WF_UNCOMPRESSED_DNAME,
+ PARSER_RDATA_WF_BINARY }, true },
+ /* 31 */
+ { 31, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 32 */
+ { 32, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 33 */
+ { PARSER_RRTYPE_SRV, T_SRV, "SRV", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_SHORT,
+ PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_UNCOMPRESSED_DNAME },
+ true },
+ /* 34 */
+ { 34, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 35 */
+ { PARSER_RRTYPE_NAPTR, T_NAPTR, "NAPTR", 6,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 36 */
+ { PARSER_RRTYPE_KX, T_KX, "KX", 2,
+ { PARSER_RDATA_WF_SHORT,
+ PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 37 */
+ { PARSER_RRTYPE_CERT, T_CERT, "CERT", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_SHORT,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BINARY }, true },
+ /* 38 */
+ { PARSER_RRTYPE_A6, T_A6, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 39 */
+ { PARSER_RRTYPE_DNAME, T_DNAME, "DNAME", 1,
+ { PARSER_RDATA_WF_UNCOMPRESSED_DNAME }, true },
+ /* 40 */
+ { 40, 0, NULL, 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 41 */
+ /* OPT has its parser token, but should never be in zone file... */
+ { PARSER_RRTYPE_OPT, T_OPT, "OPT", 1,
+ { PARSER_RDATA_WF_BINARY }, true },
+ /* 42 */
+ { PARSER_RRTYPE_APL, T_APL, "APL", PARSER_MAX_RDATA_ITEMS,
+ { PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL,
+ PARSER_RDATA_WF_APL, PARSER_RDATA_WF_APL }, false },
+ /* 43 */
+ { PARSER_RRTYPE_DS, T_DS, "DS", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BINARY }, true },
+ /* 44 */
+ { PARSER_RRTYPE_SSHFP, T_SSHFP, "SSHFP", 3,
+ { PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BINARY }, true },
+ /* 45 */
+ { PARSER_RRTYPE_IPSECKEY, T_IPSECKEY, "IPSECKEY", 5,
+ { PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_IPSECGATEWAY,
+ PARSER_RDATA_WF_BINARY }, false },
+ /* 46 */
+ { PARSER_RRTYPE_RRSIG, T_RRSIG, "RRSIG", 9,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_LONG,
+ PARSER_RDATA_WF_LONG, PARSER_RDATA_WF_LONG,
+ PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BINARY,
+ PARSER_RDATA_WF_BINARY }, true },
+ /* 47 */
+ { PARSER_RRTYPE_NSEC, T_NSEC, "NSEC", 2,
+ { PARSER_RDATA_WF_BINARY, PARSER_RDATA_WF_BINARY }, true },
+ /* 48 */
+ { PARSER_RRTYPE_DNSKEY, T_DNSKEY, "DNSKEY", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BINARY }, true },
+ /* 49 */
+ { PARSER_RRTYPE_DHCID, T_DHCID, "DHCID", 1, { PARSER_RDATA_WF_BINARY }, true },
+ /* 50 */
+ { PARSER_RRTYPE_NSEC3, T_NSEC3, "NSEC3", 6,
+ { PARSER_RDATA_WF_BYTE, /* hash type */
+ PARSER_RDATA_WF_BYTE, /* flags */
+ PARSER_RDATA_WF_SHORT, /* iterations */
+ PARSER_RDATA_WF_BINARYWITHLENGTH, /* salt */
+ PARSER_RDATA_WF_BINARYWITHLENGTH, /* next hashed name */
+ PARSER_RDATA_WF_BINARY /* type bitmap */ }, true },
+ /* 51 */
+ { PARSER_RRTYPE_NSEC3PARAM, T_NSEC3PARAM, "NSEC3PARAM", 4,
+ { PARSER_RDATA_WF_BYTE, /* hash type */
+ PARSER_RDATA_WF_BYTE, /* flags */
+ PARSER_RDATA_WF_SHORT, /* iterations */
+ PARSER_RDATA_WF_BINARYWITHLENGTH /* salt */ }, true },
+ /* 52 */
+
+
+ /* In NSD they have indices between 52 and 99 filled with
+ unknown types. TODO add here if it's really needed? */
+ /* it is indeed needed, in rrtype_from_string */
+
+ /* There's a GNU extension that works like this: [first ... last] = value */
+
+ [53 ... 98] = { PARSER_RRTYPE_TYPEXXX, T_UTYPE, NULL, 1, { PARSER_RDATA_WF_BINARY }},
+
+ /* 99 */
+ [99] = { PARSER_RRTYPE_SPF, T_SPF, "SPF", PARSER_MAX_RDATA_ITEMS,
+ { PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT,
+ PARSER_RDATA_WF_TEXT, PARSER_RDATA_WF_TEXT }, false },
+ [100 ... 32768] = { PARSER_RRTYPE_TYPEXXX, T_UTYPE, NULL, 1, { PARSER_RDATA_WF_BINARY }},
+ /* 32769 */
+ [32769] = { PARSER_RRTYPE_DLV, T_DLV, "DLV", 4,
+ { PARSER_RDATA_WF_SHORT, PARSER_RDATA_WF_BYTE,
+ PARSER_RDATA_WF_BYTE, PARSER_RDATA_WF_BINARY } },
+};
+
+parser_rrtype_descriptor_t *parser_rrtype_descriptor_by_type(uint16_t type)
+{
+ if (type <= PARSER_RRTYPE_DLV) {
+ return &knot_rrtype_descriptors[type];
+ }
+ return &knot_rrtype_descriptors[0];
+}
+
+/* I see a lot of potential here to speed up zone parsing - this is O(n) *
+ * could be better */
+parser_rrtype_descriptor_t *parser_rrtype_descriptor_by_name(const char *name)
+{
+ if (!name) {
+ return NULL;
+ }
+
+ if (strcasecmp(name, "IN") == 0) {
+ return NULL;
+ }
+
+ if (isdigit((int)name[0])) {
+ return NULL;
+ }
+
+// /* The most common - A and NS. */
+// if (strcasecmp(name, "NS") == 0) {
+// return &knot_rrtype_descriptors[2];
+// }
+
+// if (strcasecmp(name, "A") == 0) {
+// return &knot_rrtype_descriptors[1];
+// }
+
+// /* Then RRSIG */
+// if (strcasecmp(name, "RRSIG") == 0) {
+// return &knot_rrtype_descriptors[46];
+// }
+
+// /* Then DS */
+// if (strcasecmp(name, "DS") == 0) {
+// return &knot_rrtype_descriptors[43];
+// }
+// /* Then NSEC3 */
+// if (strcasecmp(name, "NSEC3") == 0) {
+// return &knot_rrtype_descriptors[50];
+// }
+// /* Then NSEC */
+// if (strcasecmp(name, "NSEC") == 0) {
+// return &knot_rrtype_descriptors[47];
+// }
+
+ int i;
+
+ for (i = 0; i < PARSER_RRTYPE_LAST + 1; ++i) {
+ if (knot_rrtype_descriptors[i].name &&
+ strcasecmp(knot_rrtype_descriptors[i].name, name) == 0) {
+ return &knot_rrtype_descriptors[i];
+ }
+ }
+
+ if (knot_rrtype_descriptors[PARSER_RRTYPE_DLV].name &&
+ strcasecmp(knot_rrtype_descriptors[PARSER_RRTYPE_DLV].name,
+ name) == 0) {
+ return &knot_rrtype_descriptors[PARSER_RRTYPE_DLV];
+ }
+
+ return NULL;
+}
+
+const char *parser_rrtype_to_string(uint16_t rrtype)
+{
+ static char buf[20];
+ parser_rrtype_descriptor_t *descriptor =
+ parser_rrtype_descriptor_by_type(rrtype);
+ if (descriptor->name) {
+ return descriptor->name;
+ } else {
+ snprintf(buf, sizeof(buf), "TYPE%d", (int) rrtype);
+ return buf;
+ }
+}
+
+uint16_t parser_rrtype_from_string(const char *name)
+{
+ char *end;
+ long rrtype;
+ parser_rrtype_descriptor_t *entry;
+
+ entry = parser_rrtype_descriptor_by_name(name);
+ if (entry) {
+ return entry->type;
+ }
+
+ if (strlen(name) < 5) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "TYPE", 4) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[4])) {
+ return 0;
+ }
+
+ /* The rest from the string must be a number. */
+ rrtype = strtol(name + 4, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrtype < 0 || rrtype > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrtype;
+}
+
+const char *parser_rrclass_to_string(uint16_t rrclass)
+{
+ static char buf[20];
+ knot_lookup_table_t *entry = knot_lookup_by_id(dns_rrclasses,
+ rrclass);
+ if (entry) {
+ assert(strlen(entry->name) < sizeof(buf));
+ knot_strlcpy(buf, entry->name, sizeof(buf));
+ } else {
+ snprintf(buf, sizeof(buf), "CLASS%d", (int) rrclass);
+ }
+ return buf;
+}
+
+uint16_t parser_rrclass_from_string(const char *name)
+{
+ char *end;
+ long rrclass;
+ knot_lookup_table_t *entry;
+
+ entry = knot_lookup_by_name(dns_rrclasses, name);
+ if (entry) {
+ return (uint16_t) entry->id;
+ }
+
+ if (strlen(name) < 6) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "CLASS", 5) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[5])) {
+ return 0;
+ }
+
+ // The rest from the string must be a number.
+ rrclass = strtol(name + 5, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrclass < 0 || rrclass > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrclass;
+}
+
diff --git a/src/zcompile/parser-descriptor.h b/src/zcompile/parser-descriptor.h
new file mode 100644
index 0000000..c7865dd
--- /dev/null
+++ b/src/zcompile/parser-descriptor.h
@@ -0,0 +1,278 @@
+/*!
+ * \file parser-descriptor.h
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet Labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief Contains resource record descriptor and its API
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOTD_PARSER_DESCRIPTOR_H_
+#define _KNOTD_PARSER_DESCRIPTOR_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/util/utils.h"
+
+enum parser_mxrdtln {
+ PARSER_MAX_RDATA_ITEMS = 64,
+ PARSER_MAX_RDATA_ITEM_SIZE = 255,
+ PARSER_MAX_RDATA_WIRE_SIZE =
+ PARSER_MAX_RDATA_ITEMS * PARSER_MAX_RDATA_ITEM_SIZE
+};
+//#define MAXRDATALEN 64
+
+/* 64 is in NSD. Seems a little too much, but I'd say it's not a real issue. */
+
+/*!
+ * \brief Enum containing RR class codes.
+ */
+enum parser_rr_class {
+ PARSER_CLASS_IN = 1,
+ PARSER_CLASS_CS,
+ PARSER_CLASS_CH,
+ PARSER_CLASS_HS,
+ PARSER_CLASS_NONE = 254,
+ PARSER_CLASS_ANY = 255
+};
+
+typedef enum parser_rr_class parser_rr_class_t;
+
+enum parser_rr_type {
+ PARSER_RRTYPE_UNKNOWN, /*!< 0 - an unknown type */
+ PARSER_RRTYPE_A, /*!< 1 - a host address */
+ PARSER_RRTYPE_NS, /*!< 2 - an authoritative name server */
+ PARSER_RRTYPE_MD, /*!< 3 - a mail destination (Obsolete - use MX) */
+ PARSER_RRTYPE_MF, /*!< 4 - a mail forwarder (Obsolete - use MX) */
+ PARSER_RRTYPE_CNAME, /*!< 5 - the canonical name for an alias */
+ PARSER_RRTYPE_SOA, /*!< 6 - marks the start of a zone of authority */
+ PARSER_RRTYPE_MB, /*!< 7 - a mailbox domain name (EXPERIMENTAL) */
+ PARSER_RRTYPE_MG, /*!< 8 - a mail group member (EXPERIMENTAL) */
+ PARSER_RRTYPE_MR, /*!< 9 - a mail rename domain name (EXPERIMENTAL) */
+ PARSER_RRTYPE_NULL, /*!< 10 - a null RR (EXPERIMENTAL) */
+ PARSER_RRTYPE_WKS, /*!< 11 - a well known service description */
+ PARSER_RRTYPE_PTR, /*!< 12 - a domain name pointer */
+ PARSER_RRTYPE_HINFO, /*!< 13 - host information */
+ PARSER_RRTYPE_MINFO, /*!< 14 - mailbox or mail list information */
+ PARSER_RRTYPE_MX, /*!< 15 - mail exchange */
+ PARSER_RRTYPE_TXT, /*!< 16 - text strings */
+ PARSER_RRTYPE_RP, /*!< 17 - RFC1183 */
+ PARSER_RRTYPE_AFSDB, /*!< 18 - RFC1183 */
+ PARSER_RRTYPE_X25, /*!< 19 - RFC1183 */
+ PARSER_RRTYPE_ISDN, /*!< 20 - RFC1183 */
+ PARSER_RRTYPE_RT, /*!< 21 - RFC1183 */
+ PARSER_RRTYPE_NSAP, /*!< 22 - RFC1706 */
+
+ PARSER_RRTYPE_SIG = 24, /*!< 24 - 2535typecode */
+ PARSER_RRTYPE_KEY, /*!< 25 - 2535typecode */
+ PARSER_RRTYPE_PX, /*!< 26 - RFC2163 */
+
+ PARSER_RRTYPE_AAAA = 28, /*!< 28 - ipv6 address */
+ PARSER_RRTYPE_LOC, /*!< 29 - LOC record RFC1876 */
+ PARSER_RRTYPE_NXT, /*!< 30 - 2535typecode */
+
+ PARSER_RRTYPE_SRV = 33, /*!< 33 - SRV record RFC2782 */
+
+ PARSER_RRTYPE_NAPTR = 35, /*!< 35 - RFC2915 */
+ PARSER_RRTYPE_KX, /*!< 36 - RFC2230 Key Exchange Delegation Record */
+ PARSER_RRTYPE_CERT, /*!< 37 - RFC2538 */
+ PARSER_RRTYPE_A6, /*!< 38 - RFC2874 */
+ PARSER_RRTYPE_DNAME, /*!< 39 - RFC2672 */
+
+ PARSER_RRTYPE_OPT = 41, /*!< 41 - Pseudo OPT record... */
+ PARSER_RRTYPE_APL, /*!< 42 - RFC3123 */
+ PARSER_RRTYPE_DS, /*!< 43 - RFC 4033, 4034, and 4035 */
+ PARSER_RRTYPE_SSHFP, /*!< 44 - SSH Key Fingerprint */
+ PARSER_RRTYPE_IPSECKEY, /*!< 45 - public key for ipsec use. RFC 4025 */
+ PARSER_RRTYPE_RRSIG, /*!< 46 - RFC 4033, 4034, and 4035 */
+ PARSER_RRTYPE_NSEC, /*!< 47 - RFC 4033, 4034, and 4035 */
+ PARSER_RRTYPE_DNSKEY, /*!< 48 - RFC 4033, 4034, and 4035 */
+ PARSER_RRTYPE_DHCID, /*!< 49 - RFC4701 DHCP information */
+ /*!
+ * \brief 50 - NSEC3, secure denial, prevents zonewalking
+ */
+ PARSER_RRTYPE_NSEC3,
+ /*!
+ * \brief 51 - NSEC3PARAM at zone apex nsec3 parameters
+ */
+ PARSER_RRTYPE_NSEC3PARAM,
+
+ /* TODO consider some better way of doing this, indices too high */
+
+ PARSER_RRTYPE_SPF = 99, /*!< RFC 4408 */
+
+ // not designating any RRs
+ PARSER_RRTYPE_TSIG = 250,
+ PARSER_RRTYPE_IXFR = 251,
+ PARSER_RRTYPE_AXFR = 252,
+ /*!
+ * \brief A request for mailbox-related records (MB, MG or MR)
+ */
+ PARSER_RRTYPE_MAILB = 253,
+ /*!
+ * \brief A request for mail agent RRs (Obsolete - see MX)
+ */
+ PARSER_RRTYPE_MAILA = 254,
+ PARSER_RRTYPE_ANY = 255, /*!< any type (wildcard) */
+
+ // totally weird numbers (cannot use for indexing)
+ PARSER_RRTYPE_TA = 32768, /*!< DNSSEC Trust Authorities */
+ PARSER_RRTYPE_DLV = 32769, /*!< RFC 4431 */
+ PARSER_RRTYPE_TYPEXXX = 32770
+};
+
+/*!
+ * \brief Enum containing RR type codes.
+ *
+ * \todo Not all indices can be used for indexing.
+ */
+typedef enum parser_rr_type parser_rr_type_t;
+
+static uint const PARSER_RRTYPE_LAST = PARSER_RRTYPE_NSEC3PARAM;
+
+enum parser_rdata_wireformat {
+ /*!
+ * \brief Possibly compressed domain name.
+ */
+ PARSER_RDATA_WF_COMPRESSED_DNAME = 50,
+ PARSER_RDATA_WF_UNCOMPRESSED_DNAME = 51, /*!< Uncompressed domain name. */
+ PARSER_RDATA_WF_LITERAL_DNAME = 52, /*!< Literal (not downcased) dname. */
+ PARSER_RDATA_WF_BYTE = 1, /*!< 8-bit integer. */
+ PARSER_RDATA_WF_SHORT = 2, /*!< 16-bit integer. */
+ PARSER_RDATA_WF_LONG = 4, /*!< 32-bit integer. */
+ PARSER_RDATA_WF_TEXT = 53, /*!< Text string. */
+ PARSER_RDATA_WF_A = 58, /*!< 32-bit IPv4 address. */
+ PARSER_RDATA_WF_AAAA = 16, /*!< 128-bit IPv6 address. */
+ PARSER_RDATA_WF_BINARY = 54, /*!< Binary data (unknown length). */
+ /*!
+ * \brief Binary data preceded by 1 byte length
+ */
+ PARSER_RDATA_WF_BINARYWITHLENGTH = 55,
+ PARSER_RDATA_WF_APL = 56, /*!< APL data. */
+ PARSER_RDATA_WF_IPSECGATEWAY = 57 /*!< IPSECKEY gateway ip4, ip6 or dname. */
+};
+
+/*!
+ * \brief Enum containing wireformat codes. Taken from NSD's "dns.h"
+ */
+typedef enum parser_rdatawireformat parser_rdata_wireformat_t;
+
+struct parser_rrtype_descriptor {
+ uint16_t type; /*!< RR type */
+ int token; /*< Token used in zoneparser */
+ const char *name; /*!< Textual name. */
+ uint8_t length; /*!< Maximum number of RDATA items. */
+ /*!
+ * \brief rdata_wireformat_type
+ */
+ uint8_t wireformat[PARSER_MAX_RDATA_ITEMS];
+ bool fixed_items; /*!< Has fixed number of RDATA items? */
+};
+
+/*!
+ * \brief Structure holding RR descriptor
+ */
+typedef struct parser_rrtype_descriptor parser_rrtype_descriptor_t;
+
+/*!
+ * \brief Gets RR descriptor for given RR type.
+ *
+ * \param type Code of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given type code, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+parser_rrtype_descriptor_t *parser_rrtype_descriptor_by_type(uint16_t type);
+
+/*!
+ * \brief Gets RR descriptor for given RR name.
+ *
+ * \param name Mnemonic of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given name, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+parser_rrtype_descriptor_t *parser_rrtype_descriptor_by_name(const char *name);
+
+/*!
+ * \brief Converts numeric type representation to mnemonic string.
+ *
+ * \param rrtype Type RR type code to be converted.
+ *
+ * \return Mnemonic string if found, str(TYPE[rrtype]) otherwise.
+ */
+const char *parser_rrtype_to_string(uint16_t rrtype);
+
+/*!
+ * \brief Converts mnemonic string representation of a type to numeric one.
+ *
+ * \param name Mnemonic string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t parser_rrtype_from_string(const char *name);
+
+/*!
+ * \brief Converts numeric class representation to string one.
+ *
+ * \param rrclass Class code to be converted.
+ *
+ * \return String represenation of class if found,
+ * str(CLASS[rrclass]) otherwise.
+ */
+const char *parser_rrclass_to_string(uint16_t rrclass);
+
+/*!
+ * \brief Converts string representation of a class to numeric one.
+ *
+ * \param name Class string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t parser_rrclass_from_string(const char *name);
+
+#endif /* _KNOTD_PARSER_DESCRIPTOR_H_ */
+
+/*! @} */
diff --git a/src/zcompile/parser-util.c b/src/zcompile/parser-util.c
new file mode 100644
index 0000000..e7733ef
--- /dev/null
+++ b/src/zcompile/parser-util.c
@@ -0,0 +1,2435 @@
+/*!
+ * \file parser-util.c
+ *
+ * \author NLnet Labs
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief utility functions for zone parser.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+//#include "common.h"
+#include "common/base32hex.h"
+#include "zcompile/parser-util.h"
+#include "zcompile/zcompile.h"
+#include "libknot/util/descriptor.h"
+#include "libknot/util/utils.h"
+#include "zcompile/zcompile-error.h"
+
+#define IP6ADDRLEN (128/8)
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+#define NS_IN6ADDRSZ 16
+#define APL_NEGATION_MASK 0x80U
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+//int my_b32_pton(const char *src, uint8_t *target, size_t tsize)
+//{
+// char ch;
+// size_t p = 0;
+
+// memset(target, '\0', tsize);
+// while ((ch = *src++)) {
+// uint8_t d;
+// size_t b;
+// size_t n;
+
+// if (p + 5 >= tsize * 8) {
+// return -1;
+// }
+
+// if (isspace(ch)) {
+// continue;
+// }
+
+// if (ch >= '0' && ch <= '9') {
+// d = ch - '0';
+// } else if (ch >= 'A' && ch <= 'V') {
+// d = ch - 'A' + 10;
+// } else if (ch >= 'a' && ch <= 'v') {
+// d = ch - 'a' + 10;
+// } else {
+// return -1;
+// }
+
+// b = 7 - p % 8;
+// n = p / 8;
+
+// if (b >= 4) {
+// target[n] |= d << (b - 4);
+// } else {
+// target[n] |= d >> (4 - b);
+// target[n+1] |= d << (b + 4);
+// }
+// p += 5;
+// }
+// return (p + 7) / 8;
+//}
+
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+int inet_pton4(const char *src, uint8_t *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ uint8_t tmp[NS_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ uint32_t new = *tp * 10 + (pch - digits);
+
+ if (new > 255) {
+ return (0);
+ }
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4) {
+ return (0);
+ }
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4) {
+ return (0);
+ }
+ *++tp = 0;
+ saw_digit = 0;
+ } else {
+ return (0);
+ }
+ }
+ if (octets < 4) {
+ return (0);
+ }
+
+ memcpy(dst, tmp, NS_INADDRSZ);
+ return (1);
+}
+
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+int inet_pton6(const char *src, uint8_t *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ uint8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ uint32_t val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':') {
+ return (0);
+ }
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) {
+ pch = strchr((xdigits = xdigits_u), ch);
+ }
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff) {
+ return (0);
+ }
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp) {
+ return (0);
+ }
+ colonp = tp;
+ continue;
+ }
+ if (tp + NS_INT16SZ > endp) {
+ return (0);
+ }
+ *tp++ = (uint8_t)(val >> 8) & 0xff;
+ *tp++ = (uint8_t) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp) {
+ return (0);
+ }
+ *tp++ = (uint8_t)(val >> 8) & 0xff;
+ *tp++ = (uint8_t) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp) {
+ return (0);
+ }
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
+
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ 2 /* for systems without 16-bit ints */
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+//const char *inet_ntop(int af, const void *src, char *dst, size_t size)
+//{
+// switch (af) {
+// case AF_INET:
+// return (inet_ntop4(src, dst, size));
+// case AF_INET6:
+// return (inet_ntop6(src, dst, size));
+// default:
+// errno = EAFNOSUPPORT;
+// return (NULL);
+// }
+// /* NOTREACHED */
+//}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address, more or less like inet_ntoa()
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a u_char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *inet_ntop4(const u_char *src, char *dst, size_t size)
+{
+ static const char fmt[] = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ int l;
+
+ l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]);
+ if (l <= 0 || l >= (int)size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ knot_strlcpy(dst, tmp, size);
+ return (dst);
+}
+
+/* const char *
+ * inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *inet_ntop6(const u_char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
+ char *tp, *ep;
+ struct {
+ int base, len;
+ } best, cur;
+ best.base = cur.base =-1;
+ best.len = cur.len = 0;
+ u_int words[IN6ADDRSZ / INT16SZ];
+ int i;
+ int advance;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < IN6ADDRSZ; i++) {
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ }
+
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1) {
+ cur.base = i, cur.len = 1;
+ } else {
+ cur.len++;
+ }
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len) {
+ best = cur;
+ }
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len) {
+ best = cur;
+ }
+ }
+ if (best.base != -1 && best.len < 2) {
+ best.base = -1;
+ }
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ ep = tmp + sizeof(tmp);
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base) {
+ if (tp + 1 >= ep) {
+ return (NULL);
+ }
+ *tp++ = ':';
+ }
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0) {
+ if (tp + 1 >= ep) {
+ return (NULL);
+ }
+ *tp++ = ':';
+ }
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src + 12, tp, (size_t)(ep - tp))) {
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ advance = snprintf(tp, ep - tp, "%x", words[i]);
+ if (advance <= 0 || advance >= ep - tp) {
+ return (NULL);
+ }
+ tp += advance;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (IN6ADDRSZ / INT16SZ)) {
+ if (tp + 1 >= ep) {
+ return (NULL);
+ }
+ *tp++ = ':';
+ }
+ if (tp + 1 >= ep) {
+ return (NULL);
+ }
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ knot_strlcpy(dst, tmp, size);
+ return (dst);
+}
+
+
+static int b64rmap_initialized = 0;
+static uint8_t b64rmap[256];
+
+static const uint8_t b64rmap_special = 0xf0;
+static const uint8_t b64rmap_end = 0xfd;
+static const uint8_t b64rmap_space = 0xfe;
+static const uint8_t b64rmap_invalid = 0xff;
+
+/**
+ * Initializing the reverse map is not thread safe.
+ * Which is fine for NSD. For now...
+ **/
+void b64_initialize_rmap()
+{
+ int i;
+ char ch;
+
+ /* Null: end of string, stop parsing */
+ b64rmap[0] = b64rmap_end;
+
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ /* Whitespaces */
+ if (isspace(ch)) {
+ b64rmap[i] = b64rmap_space;
+ }
+ /* Padding: stop parsing */
+ else if (ch == Pad64) {
+ b64rmap[i] = b64rmap_end;
+ }
+ /* Non-base64 char */
+ else {
+ b64rmap[i] = b64rmap_invalid;
+ }
+ }
+
+ /* Fill reverse mapping for base64 chars */
+ for (i = 0; Base64[i] != '\0'; ++i) {
+ b64rmap[(uint8_t)Base64[i]] = i;
+ }
+
+ b64rmap_initialized = 1;
+}
+
+int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f)
+ << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03)
+ << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target[tarindex] != 0) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+
+int b64_pton_len(char const *src)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ state = 1;
+ break;
+ case 1:
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ if (!b64rmap_initialized) {
+ b64_initialize_rmap();
+ }
+
+ if (target) {
+ return b64_pton_do(src, target, targsize);
+ } else {
+ return b64_pton_len(src);
+ }
+}
+
+void set_bit(uint8_t bits[], size_t index)
+{
+ /*
+ * The bits are counted from left to right, so bit #0 is the
+ * left most bit.
+ */
+ bits[index / 8] |= (1 << (7 - index % 8));
+}
+
+uint32_t strtoserial(const char *nptr, const char **endptr)
+{
+ uint32_t i = 0;
+ uint32_t serial = 0;
+
+ for (*endptr = nptr; **endptr; (*endptr)++) {
+ switch (**endptr) {
+ case ' ':
+ case '\t':
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i *= 10;
+ i += (**endptr - '0');
+ break;
+ default:
+ break;
+ }
+ }
+ serial += i;
+ return serial;
+}
+
+inline void write_uint32(void *dst, uint32_t data)
+{
+#ifdef ALLOW_UNALIGNED_ACCESSES
+ *(uint32_t *) dst = htonl(data);
+#else
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t)((data >> 24) & 0xff);
+ p[1] = (uint8_t)((data >> 16) & 0xff);
+ p[2] = (uint8_t)((data >> 8) & 0xff);
+ p[3] = (uint8_t)(data & 0xff);
+#endif
+}
+
+uint32_t strtottl(const char *nptr, const char **endptr)
+{
+ uint32_t i = 0;
+ uint32_t seconds = 0;
+
+ for (*endptr = nptr; **endptr; (*endptr)++) {
+ switch (**endptr) {
+ case ' ':
+ case '\t':
+ break;
+ case 's':
+ case 'S':
+ seconds += i;
+ i = 0;
+ break;
+ case 'm':
+ case 'M':
+ seconds += i * 60;
+ i = 0;
+ break;
+ case 'h':
+ case 'H':
+ seconds += i * 60 * 60;
+ i = 0;
+ break;
+ case 'd':
+ case 'D':
+ seconds += i * 60 * 60 * 24;
+ i = 0;
+ break;
+ case 'w':
+ case 'W':
+ seconds += i * 60 * 60 * 24 * 7;
+ i = 0;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i *= 10;
+ i += (**endptr - '0');
+ break;
+ default:
+ seconds += i;
+ return seconds;
+ }
+ }
+ seconds += i;
+ return seconds;
+}
+
+/* Number of days per month (except for February in leap years). */
+static const int mdays[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static int is_leap_year(int year)
+{
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+}
+
+static int leap_days(int y1, int y2)
+{
+ --y1;
+ --y2;
+ return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
+}
+
+/*
+ * Code adapted from Python 2.4.1 sources (Lib/calendar.py).
+ */
+time_t mktime_from_utc(const struct tm *tm)
+{
+ int year = 1900 + tm->tm_year;
+ time_t days = 365 * (year - 1970) + leap_days(1970, year);
+ time_t hours;
+ time_t minutes;
+ time_t seconds;
+ int i;
+
+ for (i = 0; i < tm->tm_mon; ++i) {
+ days += mdays[i];
+ }
+ if (tm->tm_mon > 1 && is_leap_year(year)) {
+ ++days;
+ }
+ days += tm->tm_mday - 1;
+
+ hours = days * 24 + tm->tm_hour;
+ minutes = hours * 60 + tm->tm_min;
+ seconds = minutes * 60 + tm->tm_sec;
+
+ return seconds;
+}
+
+/*!< Following functions are conversions from text to wire. */
+
+//#define DEBUG_UNKNOWN_RDATA
+
+#ifdef DEBUG_UNKNOWN_RDATA
+#define dbg_rdata(msg...) fprintf(stderr, msg)
+#define DBG_RDATA(cmds) do { cmds } while (0)
+#else
+#define dbg_rdata(msg...)
+#define DBG_RDATA(cmds)
+#endif
+
+
+
+#define IP6ADDRLEN (128/8)
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+#define NS_IN6ADDRSZ 16
+#define APL_NEGATION_MASK 0x80U
+#define APL_LENGTH_MASK (~APL_NEGATION_MASK)
+
+//#define ZP_DEBUG
+
+#ifdef ZP_DEBUG
+#define dbg_zp(msg...) fprintf(stderr, msg)
+#else
+#define dbg_zp(msg...)
+#endif
+
+
+/*!
+ * \brief Return data of raw data item.
+ *
+ * \param item Item.
+ * \return uint16_t * Raw data.
+ */
+static inline uint16_t * rdata_atom_data(knot_rdata_item_t item)
+{
+ return (uint16_t *)(item.raw_data + 1);
+}
+
+/*!
+ * \brief Return type of RRSet covered by given RRSIG.
+ *
+ * \param rrset RRSIG.
+ * \return uint16_t Type covered.
+ */
+uint16_t rrsig_type_covered(knot_rrset_t *rrset)
+{
+ assert(rrset->rdata->items[0].raw_data[0] == sizeof(uint16_t));
+
+ return ntohs(*(uint16_t *) rdata_atom_data(rrset->rdata->items[0]));
+}
+
+/*!
+ * \brief Checks if item contains domain.
+ *
+ * \param type Type of RRSet.
+ * \param index Index to check.
+ *
+ * \return > 1 if item is domain, 0 otherwise.
+ */
+static inline int rdata_atom_is_domain(uint16_t type, size_t index)
+{
+ const knot_rrtype_descriptor_t *descriptor
+ = knot_rrtype_descriptor_by_type(type);
+ return (index < descriptor->length
+ && (descriptor->wireformat[index] ==
+ KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ descriptor->wireformat[index] ==
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME));
+}
+
+/*!
+ * \brief Returns which wireformat type is on given index.
+ *
+ * \param type Type of RRSet.
+ * \param index Index.
+ *
+ * \return uint8_t Wireformat type.
+ */
+static inline uint8_t rdata_atom_wireformat_type(uint16_t type, size_t index)
+{
+ const knot_rrtype_descriptor_t *descriptor =
+ knot_rrtype_descriptor_by_type(type);
+ assert(index < descriptor->length);
+ return descriptor->wireformat[index];
+}
+
+/*!
+ * \brief Converts rdata wireformat to rdata items.
+ *
+ * \param wireformat Wireformat/.
+ * \param rrtype RR type.
+ * \param data_size Size of wireformat.
+ * \param items created rdata items.
+ *
+ * \return Number of items converted.
+ */
+static ssize_t rdata_wireformat_to_rdata_atoms(const uint16_t *wireformat,
+ uint16_t rrtype,
+ const uint16_t data_size,
+ knot_rdata_item_t **items)
+{
+ dbg_rdata("read length: %d\n", data_size);
+ uint16_t const *end = (uint16_t *)((uint8_t *)wireformat + (data_size));
+ dbg_rdata("set end pointer: %p which means length: %d\n", end,
+ (uint8_t *)end - (uint8_t *)wireformat);
+ size_t i;
+ knot_rdata_item_t *temp_rdatas =
+ malloc(sizeof(*temp_rdatas) * MAXRDATALEN);
+ if (temp_rdatas == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+ memset(temp_rdatas, 0, sizeof(*temp_rdatas) * MAXRDATALEN);
+
+ knot_rrtype_descriptor_t *descriptor =
+ knot_rrtype_descriptor_by_type(rrtype);
+
+ assert(descriptor->length <= MAXRDATALEN);
+
+ dbg_rdata("will be parsing %d items, total size: %d\n",
+ descriptor->length, data_size);
+
+ for (i = 0; i < descriptor->length; ++i) {
+ int is_domain = 0;
+ int is_wirestore = 0;
+ size_t length = 0;
+ length = 0;
+ int required = descriptor->length;
+
+ switch (rdata_atom_wireformat_type(rrtype, i)) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ is_domain = 1;
+ break;
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ is_domain = 1;
+ is_wirestore = 1;
+ break;
+ case KNOT_RDATA_WF_BYTE:
+ length = sizeof(uint8_t);
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ length = sizeof(uint16_t);
+ break;
+ case KNOT_RDATA_WF_LONG:
+ length = sizeof(uint32_t);
+ break;
+ case KNOT_RDATA_WF_TEXT:
+ case KNOT_RDATA_WF_BINARYWITHLENGTH:
+ /* Length is stored in the first byte. */
+ length = 1;
+ if ((uint8_t *)wireformat + length <= (uint8_t *)end) {
+ // length += wireformat[length - 1];
+ length += *((uint8_t *)wireformat);
+ dbg_rdata("%d: set new length: %d\n", i,
+ length);
+ }
+ /*if (buffer_position(packet) + length <= end) {
+ length += buffer_current(packet)[length - 1];
+ }*/
+ break;
+ case KNOT_RDATA_WF_A:
+ length = sizeof(in_addr_t);
+ break;
+ case KNOT_RDATA_WF_AAAA:
+ length = IP6ADDRLEN;
+ break;
+ case KNOT_RDATA_WF_BINARY:
+ /* Remaining RDATA is binary. */
+ dbg_rdata("%d: guessing length from pointers: %p %p\n",
+ i,
+ wireformat, end);
+ length = (uint8_t *)end - (uint8_t *)wireformat;
+// length = end - buffer_position(packet);
+ break;
+ case KNOT_RDATA_WF_APL:
+ length = (sizeof(uint16_t) /* address family */
+ + sizeof(uint8_t) /* prefix */
+ + sizeof(uint8_t)); /* length */
+ if ((uint8_t *)wireformat + length <= (uint8_t *)end) {
+ /* Mask out negation bit. */
+ length += (wireformat[length - 1]
+ & APL_LENGTH_MASK);
+ }
+ break;
+ case KNOT_RDATA_WF_IPSECGATEWAY:
+ switch (rdata_atom_data(temp_rdatas[1])[0]) {
+ /* gateway type */
+ default:
+ case IPSECKEY_NOGATEWAY:
+ length = 0;
+ break;
+ case IPSECKEY_IP4:
+ length = 4;
+ break;
+ case IPSECKEY_IP6:
+ length = IP6ADDRLEN;
+ break;
+ case IPSECKEY_DNAME:
+ is_domain = 1;
+ is_wirestore = 1;
+ break;
+ }
+ break;
+ }
+
+ if (is_domain) {
+ knot_dname_t *dname;
+
+ if (!required && (wireformat == end)) {
+ break;
+ }
+
+ dname = knot_dname_new_from_str((char *)wireformat,
+ length,
+ NULL);
+
+ if (dname == NULL) {
+ dbg_rdata("malformed dname!\n");
+ /*! \todo rdata purge */
+ free(temp_rdatas);
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+ dbg_rdata("%d: created dname: %s\n", i,
+ knot_dname_to_str(dname));
+
+ if (is_wirestore) {
+ /*temp_rdatas[i].raw_data =
+ (uint16_t *) region_alloc(
+ region, sizeof(uint16_t) + dname->name_size);
+ temp_rdatas[i].data[0] = dname->name_size;
+ memcpy(temp_rdatas[i].data+1, dname_name(dname),
+ dname->name_size); */
+ temp_rdatas[i].raw_data =
+ malloc(sizeof(uint16_t) +
+ sizeof(uint8_t) * dname->size);
+ if (temp_rdatas[i].raw_data == NULL) {
+ ERR_ALLOC_FAILED;
+ /*! \todo rdata purge */
+ free(temp_rdatas);
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ temp_rdatas[i].raw_data[0] = dname->size;
+ memcpy(temp_rdatas[i].raw_data + 1,
+ dname->name, dname->size);
+
+ knot_dname_release(dname);
+ } else {
+ temp_rdatas[i].dname = dname;
+ }
+
+ } else {
+ dbg_rdata("%d :length: %d %d %p %p\n", i, length,
+ end - wireformat,
+ wireformat, end);
+ if ((uint8_t *)wireformat + length > (uint8_t *)end) {
+ if (required) {
+ /* Truncated RDATA. */
+ /*! \todo rdata purge */
+ free(temp_rdatas);
+ dbg_rdata("truncated rdata\n");
+ return KNOTDZCOMPILE_EBRDATA;
+ } else {
+ break;
+ }
+ }
+
+ assert(wireformat <= end); /*!< \todo remove! */
+ dbg_rdata("calling init with: %p and length : %d\n",
+ wireformat, length);
+ temp_rdatas[i].raw_data = alloc_rdata_init(wireformat,
+ length);
+ if (temp_rdatas[i].raw_data == NULL) {
+ ERR_ALLOC_FAILED;
+ /*! \todo rdata purge */
+ free(temp_rdatas);
+ return -1;
+ }
+
+// temp_rdatas[i].raw_data[0] = length;
+// memcpy(temp_rdatas[i].raw_data + 1, wireformat, length);
+
+/* temp_rdatas[i].data = (uint16_t *) region_alloc(
+ region, sizeof(uint16_t) + length);
+ temp_rdatas[i].data[0] = length;
+ buffer_read(packet,
+ temp_rdatas[i].data + 1, length); */
+ }
+ dbg_rdata("%d: adding length: %d (remaining: %d)\n", i, length,
+ (uint8_t *)end - ((uint8_t *)wireformat + length));
+// hex_print(temp_rdatas[i].raw_data + 1, length);
+ wireformat = (uint16_t *)((uint8_t *)wireformat + length);
+// wireformat = wireformat + length;
+ dbg_rdata("wire: %p\n", wireformat);
+ dbg_rdata("remaining now: %d\n",
+ end - wireformat);
+
+ }
+
+ dbg_rdata("%p %p\n", wireformat, (uint8_t *)wireformat);
+
+ if (wireformat < end) {
+ /* Trailing garbage. */
+ dbg_rdata("w: %p e: %p %d\n", wireformat, end, end - wireformat);
+// region_destroy(temp_region);
+ free(temp_rdatas);
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+
+ *items = temp_rdatas;
+ /* *rdatas = (rdata_atom_type *) region_alloc_init(
+ region, temp_rdatas, i * sizeof(rdata_atom_type)); */
+ return (ssize_t)i;
+}
+
+/* Taken from RFC 2535, section 7. */
+knot_lookup_table_t dns_algorithms[] = {
+ { 1, "RSAMD5" }, /* RFC 2537 */
+ { 2, "DH" }, /* RFC 2539 */
+ { 3, "DSA" }, /* RFC 2536 */
+ { 4, "ECC" },
+ { 5, "RSASHA1" }, /* RFC 3110 */
+ { 252, "INDIRECT" },
+ { 253, "PRIVATEDNS" },
+ { 254, "PRIVATEOID" },
+ { 0, NULL }
+};
+
+/* Taken from RFC 4398, section 2.1. */
+knot_lookup_table_t dns_certificate_types[] = {
+ /* 0 Reserved */
+ { 1, "PKIX" }, /* X.509 as per PKIX */
+ { 2, "SPKI" }, /* SPKI cert */
+ { 3, "PGP" }, /* OpenPGP packet */
+ { 4, "IPKIX" }, /* The URL of an X.509 data object */
+ { 5, "ISPKI" }, /* The URL of an SPKI certificate */
+ { 6, "IPGP" }, /* The fingerprint and URL of an OpenPGP packet */
+ { 7, "ACPKIX" }, /* Attribute Certificate */
+ { 8, "IACPKIX" }, /* The URL of an Attribute Certificate */
+ { 253, "URI" }, /* URI private */
+ { 254, "OID" }, /* OID private */
+ /* 255 Reserved */
+ /* 256-65279 Available for IANA assignment */
+ /* 65280-65534 Experimental */
+ /* 65535 Reserved */
+ { 0, NULL }
+};
+
+/* Imported from lexer. */
+extern int hexdigit_to_int(char ch);
+
+extern uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE];
+extern uint16_t nsec_highest_rcode;
+
+/*!
+ * \brief Allocate SIZE+sizeof(uint16_t) bytes and store SIZE in the first
+ * element. Return a pointer to the allocation.
+ *
+ * \param size How many bytes to allocate.
+ */
+static uint16_t * alloc_rdata(size_t size)
+{
+ uint16_t *result = malloc(sizeof(uint16_t) + size);
+ *result = size;
+ return result;
+}
+
+uint16_t *alloc_rdata_init(const void *data, size_t size)
+{
+ uint16_t *result = malloc(sizeof(uint16_t) + size);
+ if (result == NULL) {
+ return NULL;
+ }
+ *result = size;
+ memcpy(result + 1, data, size);
+ return result;
+}
+
+/*
+ * These are parser function for generic zone file stuff.
+ */
+uint16_t * zparser_conv_hex(const char *hex, size_t len)
+{
+ /* convert a hex value to wireformat */
+ uint16_t *r = NULL;
+ uint8_t *t;
+ int i;
+
+ if (len % 2 != 0) {
+ zc_error_prev_line("number of hex digits "
+ "must be a multiple of 2");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else if (len > MAX_RDLENGTH * 2) {
+ zc_error_prev_line("hex data exceeds maximum rdata length (%d)",
+ MAX_RDLENGTH);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ /* the length part */
+
+ r = alloc_rdata(len / 2);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ t = (uint8_t *)(r + 1);
+
+ /* Now process octet by octet... */
+ while (*hex) {
+ *t = 0;
+ for (i = 16; i >= 1; i -= 15) {
+ if (isxdigit((int)*hex)) {
+ *t += hexdigit_to_int(*hex) * i;
+ } else {
+ zc_error_prev_line(
+ "illegal hex character '%c'",
+ (int) *hex);
+ parser->error_occurred =
+ KNOTDZCOMPILE_EBRDATA;
+ free(r);
+ return NULL;
+ }
+ ++hex;
+ }
+ ++t;
+ }
+ }
+
+ return r;
+}
+
+/* convert hex, precede by a 1-byte length */
+uint16_t * zparser_conv_hex_length(const char *hex, size_t len)
+{
+ uint16_t *r = NULL;
+ uint8_t *t;
+ int i;
+ if (len % 2 != 0) {
+ zc_error_prev_line("number of hex digits must be a "
+ "multiple of 2");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else if (len > 255 * 2) {
+ zc_error_prev_line("hex data exceeds 255 bytes");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ uint8_t *l;
+
+ /* the length part */
+ r = alloc_rdata(len / 2 + 1);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+
+ t = (uint8_t *)(r + 1);
+
+ l = t++;
+ *l = '\0';
+
+ /* Now process octet by octet... */
+ while (*hex) {
+ *t = 0;
+ for (i = 16; i >= 1; i -= 15) {
+ if (isxdigit((int)*hex)) {
+ *t += hexdigit_to_int(*hex) * i;
+ } else {
+ zc_error_prev_line(
+ "illegal hex character '%c'",
+ (int) *hex);
+ parser->error_occurred =
+ KNOTDZCOMPILE_EBRDATA;
+ free(r);
+ return NULL;
+ }
+ ++hex;
+ }
+ ++t;
+ ++*l;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_time(const char *time)
+{
+ /* convert a time YYHM to wireformat */
+ uint16_t *r = NULL;
+ struct tm tm;
+
+ /* Try to scan the time... */
+ if (!strptime(time, "%Y%m%d%H%M%S", &tm)) {
+ zc_error_prev_line("date and time is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ uint32_t l = htonl(mktime_from_utc(&tm));
+ r = alloc_rdata_init(&l, sizeof(l));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_services(const char *protostr, char *servicestr)
+{
+ /*
+ * Convert a protocol and a list of service port numbers
+ * (separated by spaces) in the rdata to wireformat
+ */
+ uint16_t *r = NULL;
+ uint8_t *p;
+ uint8_t bitmap[65536/8];
+ char sep[] = " ";
+ char *word;
+ int max_port = -8;
+ /* convert a protocol in the rdata to wireformat */
+ struct protoent *proto;
+
+ memset(bitmap, 0, sizeof(bitmap));
+
+ proto = getprotobyname(protostr);
+ if (!proto) {
+ proto = getprotobynumber(atoi(protostr));
+ }
+ if (!proto) {
+ zc_error_prev_line("unknown protocol '%s'", protostr);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ return NULL;
+ }
+
+ char *sp = 0;
+ while ((word = strtok_r(servicestr, sep, &sp))) {
+ struct servent *service;
+ int port;
+
+ service = getservbyname(word, proto->p_name);
+ if (service) {
+ /* Note: ntohs not ntohl! Strange but true. */
+ port = ntohs((uint16_t) service->s_port);
+ } else {
+ char *end;
+ port = strtol(word, &end, 10);
+ if (*end != '\0') {
+ zc_error_prev_line(
+ "unknown service '%s' for"
+ " protocol '%s'",
+ word, protostr);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ continue;
+ }
+ }
+
+ if (port < 0 || port > 65535) {
+ zc_error_prev_line("bad port number %d", port);
+ } else {
+ set_bit(bitmap, port);
+ if (port > max_port) {
+ max_port = port;
+ }
+ }
+ }
+
+ r = alloc_rdata(sizeof(uint8_t) + max_port / 8 + 1);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+
+ p = (uint8_t *)(r + 1);
+ *p = proto->p_proto;
+ memcpy(p + 1, bitmap, *r);
+
+ return r;
+}
+
+uint16_t * zparser_conv_serial(const char *serialstr)
+{
+ uint16_t *r = NULL;
+ uint32_t serial;
+ const char *t;
+
+ serial = strtoserial(serialstr, &t);
+ if (*t != '\0') {
+ zc_error_prev_line("serial is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ serial = htonl(serial);
+ r = alloc_rdata_init(&serial, sizeof(serial));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_period(const char *periodstr)
+{
+ /* convert a time period (think TTL's) to wireformat) */
+ uint16_t *r = NULL;
+ uint32_t period;
+ const char *end;
+
+ /* Allocate required space... */
+ period = strtottl(periodstr, &end);
+ if (*end != '\0') {
+ zc_error_prev_line("time period is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ period = htonl(period);
+ r = alloc_rdata_init(&period, sizeof(period));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_short(const char *text)
+{
+ uint16_t *r = NULL;
+ uint16_t value;
+ char *end;
+
+ value = htons((uint16_t) strtol(text, &end, 10));
+ if (*end != '\0') {
+ zc_error_prev_line("integer value is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ r = alloc_rdata_init(&value, sizeof(value));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_byte(const char *text)
+{
+ uint16_t *r = NULL;
+ uint8_t value;
+ char *end;
+
+ value = (uint8_t) strtol(text, &end, 10);
+ if (*end != '\0') {
+ zc_error_prev_line("integer value is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ r = alloc_rdata_init(&value, sizeof(value));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_algorithm(const char *text)
+{
+ const knot_lookup_table_t *alg;
+ uint8_t id;
+
+ alg = knot_lookup_by_name(dns_algorithms, text);
+ if (alg) {
+ id = (uint8_t) alg->id;
+ } else {
+ char *end;
+ id = (uint8_t) strtol(text, &end, 10);
+ if (*end != '\0') {
+ zc_error_prev_line("algorithm is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ return NULL;
+ }
+ }
+
+ uint16_t *r = alloc_rdata_init(&id, sizeof(id));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+
+ return r;
+}
+
+uint16_t * zparser_conv_certificate_type(const char *text)
+{
+ /* convert a algoritm string to integer */
+ const knot_lookup_table_t *type;
+ uint16_t id;
+
+ type = knot_lookup_by_name(dns_certificate_types, text);
+ if (type) {
+ id = htons((uint16_t) type->id);
+ } else {
+ char *end;
+ id = htons((uint16_t) strtol(text, &end, 10));
+ if (*end != '\0') {
+ zc_error_prev_line("certificate type is expected");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ return NULL;
+ }
+ }
+
+ uint16_t *r = alloc_rdata_init(&id, sizeof(id));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+
+ return r;
+}
+
+uint16_t * zparser_conv_a(const char *text)
+{
+ in_addr_t address;
+ uint16_t *r = NULL;
+
+ if (inet_pton(AF_INET, text, &address) != 1) {
+ zc_error_prev_line("invalid IPv4 address '%s'", text);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ r = alloc_rdata_init(&address, sizeof(address));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+
+ return r;
+}
+
+uint16_t * zparser_conv_aaaa(const char *text)
+{
+ uint8_t address[IP6ADDRLEN];
+ uint16_t *r = NULL;
+
+ if (inet_pton(AF_INET6, text, address) != 1) {
+ zc_error_prev_line("invalid IPv6 address '%s'", text);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ r = alloc_rdata_init(address, sizeof(address));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_text(const char *text, size_t len)
+{
+ uint16_t *r = NULL;
+
+ dbg_zp("Converting text: %s\n", text);
+
+ if (len > 255) {
+ zc_error_prev_line("text string is longer than 255 characters,"
+ " try splitting it into multiple parts");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ uint8_t *p;
+ r = alloc_rdata(len + 1);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ p = (uint8_t *)(r + 1);
+ *p = len;
+ memcpy(p + 1, text, len);
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_dns_name(const uint8_t *name, size_t len)
+{
+ uint16_t *r = NULL;
+ uint8_t *p = NULL;
+ r = alloc_rdata(len);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ p = (uint8_t *)(r + 1);
+ memcpy(p, name, len);
+
+ return r;
+}
+
+uint16_t * zparser_conv_b32(const char *b32)
+{
+ uint8_t buffer[B64BUFSIZE];
+ uint16_t *r = NULL;
+ size_t i = B64BUFSIZE - 1;
+
+ if (strcmp(b32, "-") == 0) {
+ r = alloc_rdata_init("", 1);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ return r;
+ }
+
+ /*!< \todo BLEEDING EYES! */
+
+ char b32_copy[strlen(b32) + 1];
+
+ for (int i = 0; i < strlen(b32); i++) {
+ b32_copy[i] = toupper(b32[i]);
+ }
+
+ /*!< \todo BLEEDING EYES! */
+ b32_copy[strlen(b32)] = '\0';
+
+ if (!base32hex_decode(b32_copy,
+ strlen(b32_copy), (char *)buffer + 1, &i)) {
+ zc_error_prev_line("invalid base32 data");
+ parser->error_occurred = 1;
+ } else {
+ buffer[0] = i; /* store length byte */
+ r = alloc_rdata_init(buffer, i + 1);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_b64(const char *b64)
+{
+ uint8_t buffer[B64BUFSIZE];
+ uint16_t *r = NULL;
+ int i;
+
+ i = b64_pton(b64, buffer, B64BUFSIZE);
+ if (i == -1) {
+ zc_error_prev_line("invalid base64 data\n");
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ r = alloc_rdata_init(buffer, i);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_rrtype(const char *text)
+{
+ uint16_t *r = NULL;
+ uint16_t type = knot_rrtype_from_string(text);
+
+ if (type == 0) {
+ zc_error_prev_line("unrecognized RR type '%s'", text);
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ } else {
+ type = htons(type);
+ r = alloc_rdata_init(&type, sizeof(type));
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+ }
+ return r;
+}
+
+uint16_t * zparser_conv_nxt(uint8_t nxtbits[])
+{
+ /* nxtbits[] consists of 16 bytes with some zero's in it
+ * copy every byte with zero to r and write the length in
+ * the first byte
+ */
+ uint16_t i;
+ uint16_t last = 0;
+
+ for (i = 0; i < 16; i++) {
+ if (nxtbits[i] != 0) {
+ last = i + 1;
+ }
+ }
+
+ uint16_t *r = alloc_rdata_init(nxtbits, last);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ return NULL;
+ }
+
+ return r;
+}
+
+
+/* we potentially have 256 windows, each one is numbered. empty ones
+ * should be discarded
+ */
+uint16_t * zparser_conv_nsec(uint8_t nsecbits[NSEC_WINDOW_COUNT]
+ [NSEC_WINDOW_BITS_SIZE])
+{
+ /* nsecbits contains up to 64K of bits which represent the
+ * types available for a name. Walk the bits according to
+ * nsec++ draft from jakob
+ */
+ uint16_t *r;
+ uint8_t *ptr;
+ size_t i, j;
+ uint16_t window_count = 0;
+ uint16_t total_size = 0;
+ uint16_t window_max = 0;
+
+ /* The used windows. */
+ int used[NSEC_WINDOW_COUNT];
+ /* The last byte used in each the window. */
+ int size[NSEC_WINDOW_COUNT];
+
+ window_max = 1 + (nsec_highest_rcode / 256);
+
+ /* used[i] is the i-th window included in the nsec
+ * size[used[0]] is the size of window 0
+ */
+
+ /* walk through the 256 windows */
+ for (i = 0; i < window_max; ++i) {
+ int empty_window = 1;
+ /* check each of the 32 bytes */
+ for (j = 0; j < NSEC_WINDOW_BITS_SIZE; ++j) {
+ if (nsecbits[i][j] != 0) {
+ size[i] = j + 1;
+ empty_window = 0;
+ }
+ }
+ if (!empty_window) {
+ used[window_count] = i;
+ window_count++;
+ }
+ }
+
+ for (i = 0; i < window_count; ++i) {
+ total_size += sizeof(uint16_t) + size[used[i]];
+ }
+
+ r = alloc_rdata(total_size);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ parser->error_occurred = KNOTDZCOMPILE_EBRDATA;
+ return NULL;
+ }
+ ptr = (uint8_t *)(r + 1);
+
+ /* now walk used and copy it */
+ for (i = 0; i < window_count; ++i) {
+ ptr[0] = used[i];
+ ptr[1] = size[used[i]];
+ memcpy(ptr + 2, &nsecbits[used[i]], size[used[i]]);
+ ptr += size[used[i]] + 2;
+ }
+
+ return r;
+}
+
+/* Parse an int terminated in the specified range. */
+static int parse_int(const char *str,
+ char **end,
+ int *result,
+ const char *name,
+ int min,
+ int max)
+{
+ long value;
+ value = strtol(str, end, 10);
+ if (value < min || value > max) {
+ zc_error_prev_line("%s must be within the range [%d .. %d]",
+ name,
+ min,
+ max);
+ return 0;
+ } else {
+ *result = (int) value;
+ return 1;
+ }
+}
+
+/* RFC1876 conversion routines */
+static uint32_t poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+ 1000000, 10000000, 100000000, 1000000000
+ };
+
+/*
+ * Converts ascii size/precision X * 10**Y(cm) to 0xXY.
+ * Sets the given pointer to the last used character.
+ *
+ */
+static uint8_t precsize_aton(char *cp, char **endptr)
+{
+ unsigned int mval = 0, cmval = 0;
+ uint8_t retval = 0;
+ int exponent;
+ int mantissa;
+
+ while (isdigit((int)*cp)) {
+ mval = mval * 10 + hexdigit_to_int(*cp++);
+ }
+
+ if (*cp == '.') { /* centimeters */
+ cp++;
+ if (isdigit((int)*cp)) {
+ cmval = hexdigit_to_int(*cp++) * 10;
+ if (isdigit((int)*cp)) {
+ cmval += hexdigit_to_int(*cp++);
+ }
+ }
+ }
+
+ if (mval >= poweroften[7]) {
+ /* integer overflow possible for *100 */
+ mantissa = mval / poweroften[7];
+ exponent = 9; /* max */
+ } else {
+ cmval = (mval * 100) + cmval;
+
+ for (exponent = 0; exponent < 9; exponent++)
+ if (cmval < poweroften[exponent+1]) {
+ break;
+ }
+
+ mantissa = cmval / poweroften[exponent];
+ }
+ if (mantissa > 9) {
+ mantissa = 9;
+ }
+
+ retval = (mantissa << 4) | exponent;
+
+ if (*cp == 'm') {
+ cp++;
+ }
+
+ *endptr = cp;
+
+ return (retval);
+}
+
+/*
+ * Parses a specific part of rdata.
+ *
+ * Returns:
+ *
+ * number of elements parsed
+ * zero on error
+ *
+ */
+uint16_t * zparser_conv_loc(char *str)
+{
+ uint16_t *r;
+ uint32_t *p;
+ int i;
+ int deg, min, secs; /* Secs is stored times 1000. */
+ uint32_t lat = 0, lon = 0, alt = 0;
+ /* encoded defaults: version=0 sz=1m hp=10000m vp=10m */
+ uint8_t vszhpvp[4] = {0, 0x12, 0x16, 0x13};
+ char *start;
+ double d;
+
+ for (;;) {
+ deg = min = secs = 0;
+
+ /* Degrees */
+ if (*str == '\0') {
+ zc_error_prev_line("unexpected end of LOC data");
+ return NULL;
+ }
+
+ if (!parse_int(str, &str, &deg, "degrees", 0, 180)) {
+ return NULL;
+ }
+ if (!isspace((int)*str)) {
+ zc_error_prev_line("space expected after degrees");
+ return NULL;
+ }
+ ++str;
+
+ /* Minutes? */
+ if (isdigit((int)*str)) {
+ if (!parse_int(str, &str, &min, "minutes", 0, 60)) {
+ return NULL;
+ }
+ if (!isspace((int)*str)) {
+ zc_error_prev_line("space expected after minutes");
+ return NULL;
+ }
+ ++str;
+ }
+
+ /* Seconds? */
+ if (isdigit((int)*str)) {
+ start = str;
+ if (!parse_int(str, &str, &i, "seconds", 0, 60)) {
+ return NULL;
+ }
+
+ if (*str == '.' && !parse_int(str + 1, &str, &i,
+ "seconds fraction",
+ 0, 999)) {
+ return NULL;
+ }
+
+ if (!isspace((int)*str)) {
+ zc_error_prev_line("space expected after seconds");
+ return NULL;
+ }
+
+ if (sscanf(start, "%lf", &d) != 1) {
+ zc_error_prev_line("error parsing seconds");
+ }
+
+ if (d < 0.0 || d > 60.0) {
+ zc_error_prev_line(
+ "seconds not in range 0.0 .. 60.0");
+ }
+
+ secs = (int)(d * 1000.0 + 0.5);
+ ++str;
+ }
+
+ switch (*str) {
+ case 'N':
+ case 'n':
+ lat = ((uint32_t)1 << 31) +
+ (deg * 3600000 + min * 60000 + secs);
+ break;
+ case 'E':
+ case 'e':
+ lon = ((uint32_t)1 << 31) +
+ (deg * 3600000 + min * 60000 + secs);
+ break;
+ case 'S':
+ case 's':
+ lat = ((uint32_t)1 << 31) -
+ (deg * 3600000 + min * 60000 + secs);
+ break;
+ case 'W':
+ case 'w':
+ lon = ((uint32_t)1 << 31) -
+ (deg * 3600000 + min * 60000 + secs);
+ break;
+ default:
+ zc_error_prev_line(
+ "invalid latitude/longtitude: '%c'", *str);
+ return NULL;
+ }
+ ++str;
+
+ if (lat != 0 && lon != 0) {
+ break;
+ }
+
+ if (!isspace((int)*str)) {
+ zc_error_prev_line("space expected after"
+ " latitude/longitude");
+ return NULL;
+ }
+ ++str;
+ }
+
+ /* Altitude */
+ if (*str == '\0') {
+ zc_error_prev_line("unexpected end of LOC data");
+ return NULL;
+ }
+
+ if (!isspace((int)*str)) {
+ zc_error_prev_line("space expected before altitude");
+ return NULL;
+ }
+ ++str;
+
+ start = str;
+
+ /* Sign */
+ if (*str == '+' || *str == '-') {
+ ++str;
+ }
+
+ /* Meters of altitude... */
+ int ret = strtol(str, &str, 10);
+ UNUSED(ret); // Result checked in following switch
+
+ switch (*str) {
+ case ' ':
+ case '\0':
+ case 'm':
+ break;
+ case '.':
+ if (!parse_int(str + 1, &str, &i, "altitude fraction", 0, 99)) {
+ return NULL;
+ }
+ if (!isspace((int)*str) && *str != '\0' && *str != 'm') {
+ zc_error_prev_line("altitude fraction must be a number");
+ return NULL;
+ }
+ break;
+ default:
+ zc_error_prev_line("altitude must be expressed in meters");
+ return NULL;
+ }
+ if (!isspace((int)*str) && *str != '\0') {
+ ++str;
+ }
+
+ if (sscanf(start, "%lf", &d) != 1) {
+ zc_error_prev_line("error parsing altitude");
+ }
+
+ alt = (uint32_t)(10000000.0 + d * 100 + 0.5);
+
+ if (!isspace((int)*str) && *str != '\0') {
+ zc_error_prev_line("unexpected character after altitude");
+ return NULL;
+ }
+
+ /* Now parse size, horizontal precision and vertical precision if any */
+ for (i = 1; isspace((int)*str) && i <= 3; i++) {
+ vszhpvp[i] = precsize_aton(str + 1, &str);
+
+ if (!isspace((int)*str) && *str != '\0') {
+ zc_error_prev_line("invalid size or precision");
+ return NULL;
+ }
+ }
+
+ /* Allocate required space... */
+ r = alloc_rdata(16);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ p = (uint32_t *)(r + 1);
+
+ memmove(p, vszhpvp, 4);
+ write_uint32(p + 1, lat);
+ write_uint32(p + 2, lon);
+ write_uint32(p + 3, alt);
+
+ return r;
+}
+
+/*
+ * Convert an APL RR RDATA element.
+ */
+uint16_t * zparser_conv_apl_rdata(char *str)
+{
+ int negated = 0;
+ uint16_t address_family;
+ uint8_t prefix;
+ uint8_t maximum_prefix;
+ uint8_t length;
+ uint8_t address[IP6ADDRLEN];
+ char *colon = strchr(str, ':');
+ char *slash = strchr(str, '/');
+ int af;
+ int rc;
+ uint16_t rdlength;
+ uint16_t *r;
+ uint8_t *t;
+ char *end;
+ long p;
+
+ if (!colon) {
+ zc_error_prev_line("address family separator is missing");
+ return NULL;
+ }
+ if (!slash) {
+ zc_error_prev_line("prefix separator is missing");
+ return NULL;
+ }
+
+ *colon = '\0';
+ *slash = '\0';
+
+ if (*str == '!') {
+ negated = 1;
+ ++str;
+ }
+
+ if (strcmp(str, "1") == 0) {
+ address_family = htons(1);
+ af = AF_INET;
+ length = sizeof(in_addr_t);
+ maximum_prefix = length * 8;
+ } else if (strcmp(str, "2") == 0) {
+ address_family = htons(2);
+ af = AF_INET6;
+ length = IP6ADDRLEN;
+ maximum_prefix = length * 8;
+ } else {
+ zc_error_prev_line("invalid address family '%s'", str);
+ return NULL;
+ }
+
+ rc = inet_pton(af, colon + 1, address);
+ if (rc == 0) {
+ zc_error_prev_line("invalid address '%s'", colon + 1);
+ return NULL;
+ } else if (rc == -1) {
+ char ebuf[256];
+ zc_error_prev_line("inet_pton failed: %s",
+ strerror_r(errno, ebuf, sizeof(ebuf)));
+ return NULL;
+ }
+
+ /* Strip trailing zero octets. */
+ while (length > 0 && address[length - 1] == 0) {
+ --length;
+ }
+
+
+ p = strtol(slash + 1, &end, 10);
+ if (p < 0 || p > maximum_prefix) {
+ zc_error_prev_line("prefix not in the range 0 .. %d",
+ maximum_prefix);
+ return NULL;
+ } else if (*end != '\0') {
+ zc_error_prev_line("invalid prefix '%s'", slash + 1);
+ return NULL;
+ }
+ prefix = (uint8_t) p;
+
+ rdlength = (sizeof(address_family) + sizeof(prefix) + sizeof(length)
+ + length);
+ r = alloc_rdata(rdlength);
+ if (r == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ t = (uint8_t *)(r + 1);
+
+ memcpy(t, &address_family, sizeof(address_family));
+ t += sizeof(address_family);
+ memcpy(t, &prefix, sizeof(prefix));
+ t += sizeof(prefix);
+ memcpy(t, &length, sizeof(length));
+ if (negated) {
+ *t |= APL_NEGATION_MASK;
+ }
+ t += sizeof(length);
+ memcpy(t, address, length);
+
+ return r;
+}
+
+/*
+ * Below some function that also convert but not to wireformat
+ * but to "normal" (int,long,char) types
+ */
+
+uint32_t zparser_ttl2int(const char *ttlstr, int *error)
+{
+ /* convert a ttl value to a integer
+ * return the ttl in a int
+ * -1 on error
+ */
+
+ uint32_t ttl;
+ const char *t;
+
+ ttl = strtottl(ttlstr, &t);
+ if (*t != 0) {
+ zc_error_prev_line("invalid TTL value: %s", ttlstr);
+ *error = 1;
+ }
+
+ return ttl;
+}
+
+void zadd_rdata_wireformat(uint16_t *data)
+{
+ parser->temporary_items[parser->rdata_count].raw_data = data;
+ parser->rdata_count++;
+}
+
+/**
+ * Used for TXT RR's to grow with undefined number of strings.
+ */
+void zadd_rdata_txt_wireformat(uint16_t *data, int first)
+{
+ dbg_zp("Adding text!\n");
+// hex_print(data + 1, data[0]);
+ knot_rdata_item_t *rd;
+
+ /* First STR in str_seq, allocate 65K in first unused rdata
+ * else find last used rdata */
+ if (first) {
+ rd = &parser->temporary_items[parser->rdata_count];
+// if ((rd->data = (uint8_t *) region_alloc(parser->rr_region,
+// sizeof(uint8_t) + 65535 * sizeof(uint8_t))) == NULL) {
+// zc_error_prev_line("Could not allocate memory for TXT RR");
+// return;
+// }
+ rd->raw_data = alloc_rdata(65535 * sizeof(uint8_t));
+ if (rd->raw_data == NULL) {
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ }
+ parser->rdata_count++;
+ rd->raw_data[0] = 0;
+ } else {
+// assert(0);
+ rd = &parser->temporary_items[parser->rdata_count-1];
+ }
+
+ if ((size_t)rd->raw_data[0] + (size_t)data[0] > 65535) {
+ zc_error_prev_line("too large rdata element");
+ return;
+ }
+
+ memcpy((uint8_t *)rd->raw_data + 2 + rd->raw_data[0],
+ data + 1, data[0]);
+ rd->raw_data[0] += data[0];
+ free(data);
+ dbg_zp("Item after add\n");
+// hex_print(rd->raw_data + 1, rd->raw_data[0]);
+}
+
+void zadd_rdata_domain(knot_dname_t *dname)
+{
+ knot_dname_retain(dname);
+// printf("Adding rdata name: %s %p\n", dname->name, dname);
+ parser->temporary_items[parser->rdata_count].dname = dname;
+ parser->rdata_count++;
+}
+
+void parse_unknown_rdata(uint16_t type, uint16_t *wireformat)
+{
+ dbg_rdata("parsing unknown rdata for type: %d\n", type);
+// buffer_type packet;
+ uint16_t size;
+ ssize_t rdata_count;
+ ssize_t i;
+ knot_rdata_item_t *items = NULL;
+
+ if (wireformat) {
+ size = *wireformat;
+ } else {
+ return;
+ }
+
+// buffer_create_from(&packet, wireformat + 1, *wireformat);
+ rdata_count = rdata_wireformat_to_rdata_atoms(wireformat + 1, type,
+ size, &items);
+// dbg_rdata("got %d items\n", rdata_count);
+ dbg_rdata("wf to items returned error: %s (%d)\n",
+ error_to_str(knot_zcompile_error_msgs, rdata_count),
+ rdata_count);
+ if (rdata_count < 0) {
+ zc_error_prev_line("bad unknown RDATA\n");
+ /*!< \todo leaks */
+ return;
+ }
+
+ for (i = 0; i < rdata_count; ++i) {
+ if (rdata_atom_is_domain(type, i)) {
+ zadd_rdata_domain(items[i].dname);
+ } else {
+ //XXX won't this create size two times?
+ zadd_rdata_wireformat((uint16_t *)items[i].raw_data);
+ }
+ }
+ free(items);
+ /* Free wireformat */
+ free(wireformat);
+}
+
+void set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
+ uint16_t index)
+{
+ /*
+ * The bits are counted from left to right, so bit #0 is the
+ * left most bit.
+ */
+ uint8_t window = index / 256;
+ uint8_t bit = index % 256;
+
+ bits[window][bit / 8] |= (1 << (7 - bit % 8));
+}
+
diff --git a/src/zcompile/parser-util.h b/src/zcompile/parser-util.h
new file mode 100644
index 0000000..57258dc
--- /dev/null
+++ b/src/zcompile/parser-util.h
@@ -0,0 +1,357 @@
+/*!
+ * \file parser-util.h
+ *
+ * \author NLnet Labs
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ * Minor modifications by CZ.NIC, z.s.p.o.
+ *
+ * \brief Zone compiler utility functions.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOTD_PARSER_UTIL_H_
+#define _KNOTD_PARSER_UTIL_H_
+
+#include <assert.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "zcompile/zcompile.h"
+#include "libknot/util/descriptor.h"
+
+int inet_pton4(const char *src, uint8_t *dst);
+int inet_pton6(const char *src, uint8_t *dst);
+//int my_b32_pton(const char *src, uint8_t *target, size_t tsize);
+const char *inet_ntop4(const u_char *src, char *dst, size_t size);
+const char *inet_ntop6(const u_char *src, char *dst, size_t size);
+int inet_pton(int af, const char *src, void *dst);
+void b64_initialize_rmap();
+int b64_pton_do(char const *src, uint8_t *target, size_t targsize);
+int b64_pton_len(char const *src);
+int b64_pton(char const *src, uint8_t *target, size_t targsize);
+void set_bit(uint8_t bits[], size_t index);
+uint32_t strtoserial(const char *nptr, const char **endptr);
+void write_uint32(void *dst, uint32_t data);
+uint32_t strtottl(const char *nptr, const char **endptr);
+time_t mktime_from_utc(const struct tm *tm);
+
+/*!< Conversions from text to wire. */
+/*!
+ * \brief Converts hex text format to wireformat.
+ *
+ * \param hex String to be converted.
+ * \param len Length of string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_hex(const char *hex, size_t len);
+
+/*!
+ * \brief Converts hex text format with length to wireformat.
+ *
+ * \param hex String to be converted/.
+ * \param len Length of string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_hex_length(const char *hex, size_t len);
+
+/*!
+ * \brief Converts time string to wireformat.
+ *
+ * \param time Time string to be converted.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_time(const char *time);
+/*!
+ * \brief Converts a protocol and a list of service port numbers
+ * (separated by spaces) in the rdata to wireformat
+ *
+ * \param protostr Protocol string.
+ * \param servicestr Service string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_services(const char *protostr, char *servicestr);
+
+/*!
+ * \brief Converts serial to wireformat.
+ *
+ * \param serialstr Serial string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_serial(const char *serialstr);
+/*!
+ * \brief Converts period to wireformat.
+ *
+ * \param periodstr Period string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_period(const char *periodstr);
+
+/*!
+ * \brief Converts short int to wireformat.
+ *
+ * \param text String containing short int.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_short(const char *text);
+
+/*!
+ * \brief Converts long int to wireformat.
+ *
+ * \param text String containing long int.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_long(const char *text);
+
+/*!
+ * \brief Converts byte to wireformat.
+ *
+ * \param text String containing byte.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_byte(const char *text);
+
+/*!
+ * \brief Converts A rdata string to wireformat.
+ *
+ * \param text String containing A rdata.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_a(const char *text);
+
+/*!
+ * \brief Converts AAAA rdata string to wireformat.
+ *
+ * \param text String containing AAAA rdata.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_aaaa(const char *text);
+
+/*!
+ * \brief Converts text string to wireformat.
+ *
+ * \param text Text string.
+ * \param len Length of string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_text(const char *text, size_t len);
+
+/*!
+ * \brief Converts domain name string to wireformat.
+ *
+ * \param name Domain name string.
+ * \param len Length of string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_dns_name(const uint8_t* name, size_t len);
+
+/*!
+ * \brief Converts base32 encoded string to wireformat.
+ * TODO consider replacing with our implementation.
+ *
+ * \param b32 Base32 encoded string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_b32(const char *b32);
+
+/*!
+ * \brief Converts base64 encoded string to wireformat.
+ * TODO consider replacing with our implementation.
+ *
+ * \param b64 Base64 encoded string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_b64(const char *b64);
+
+/*!
+ * \brief Converts RR type string to wireformat.
+ *
+ * \param rr RR type string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_rrtype(const char *rr);
+
+/*!
+ * \brief Converts NXT string to wireformat.
+ *
+ * \param nxtbits NXT string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_nxt(uint8_t *nxtbits);
+
+/*!
+ * \brief Converts NSEC bitmap to wireformat.
+ *
+ * \param nsecbits[][] NSEC bits.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_nsec(uint8_t nsecbits[NSEC_WINDOW_COUNT]
+ [NSEC_WINDOW_BITS_SIZE]);
+/*!
+ * \brief Converts LOC string to wireformat.
+ *
+ * \param str LOC string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_loc(char *str);
+
+/*!
+ * \brief Converts algorithm string to wireformat.
+ *
+ * \param algstr Algorithm string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_algorithm(const char *algstr);
+
+/*!
+ * \brief Converts certificate type string to wireformat.
+ *
+ * \param typestr Certificate type mnemonic string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_certificate_type(const char *typestr);
+
+/*!
+ * \brief Converts APL data to wireformat.
+ *
+ * \param str APL data string.
+ *
+ * \return Converted wireformat.
+ */
+uint16_t *zparser_conv_apl_rdata(char *str);
+
+/*!
+ * \brief Parses unknown rdata.
+ *
+ * \param type Type of data.
+ * \param wireformat Wireformat of data.
+ *
+ * \return Converted wireformat.
+ */
+void parse_unknown_rdata(uint16_t type, uint16_t *wireformat);
+
+/*!
+ * \brief Converts TTL string to int.
+ *
+ * \param ttlstr String
+ * \param error Error code.
+ *
+ * \return Converted wireformat.
+ */
+uint32_t zparser_ttl2int(const char *ttlstr, int* error);
+
+/*!
+ * \brief Adds wireformat to temporary list of rdata items.
+ *
+ * \param data Wireformat to be added.
+ */
+void zadd_rdata_wireformat(uint16_t *data);
+
+/*!
+ * \brief Adds TXT wireformat to temporary list of rdata items.
+ *
+ * \param data Wireformat to be added.
+ * \param first This is first text to be added.
+ */
+void zadd_rdata_txt_wireformat(uint16_t *data, int first);
+
+/*!
+ * \brief Cleans after using zadd_rdata_txt_wireformat().
+ */
+void zadd_rdata_txt_clean_wireformat();
+
+/*!
+ * \brief Adds domain name to temporary list of rdata items.
+ *
+ * \param domain Domain name to be added.
+ */
+void zadd_rdata_domain(knot_dname_t *domain);
+
+/*!
+ * \brief Sets bit in NSEC bitmap.
+ *
+ * \param bits[][] NSEC bitmaps.
+ * \param index Index on which bit is to be set.
+ */
+void set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
+ uint16_t index);
+
+/*!
+ * \brief Allocate and init wireformat.
+ *
+ * \param data Data to be copied into newly created wireformat.
+ * \param size Size of data.
+ *
+ * \return Allocated wireformat.
+ */
+uint16_t *alloc_rdata_init(const void *data, size_t size);
+uint16_t rrsig_type_covered(knot_rrset_t *rrset);
+
+
+#endif /* _KNOTD_PARSER_UTIL_H_ */
+
+/*! @} */
diff --git a/src/zcompile/tests/unittests_zp_main.c b/src/zcompile/tests/unittests_zp_main.c
new file mode 100644
index 0000000..5d8c5e9
--- /dev/null
+++ b/src/zcompile/tests/unittests_zp_main.c
@@ -0,0 +1,62 @@
+/* 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 "knot/common.h"
+#include "common/libtap/tap_unit.h"
+
+// Units to test
+#include "zcompile_tests.c"
+
+// Run all loaded units
+int main(int argc, char *argv[])
+{
+ // Open log
+ //log_init(LOG_UPTO(LOG_ERR), LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING));
+
+ // Build test set
+ unit_api *tests[] = {
+ &zoneparser_tests_api, //! Zoneparser unit
+ NULL
+ };
+
+ // Plan number of tests
+ int id = 0;
+ int test_count = 0;
+ note("Units:");
+ while (tests[id] != NULL) {
+ note("- %s : %d tests", tests[id]->name,
+ tests[id]->count(argc, argv));
+ test_count += tests[id]->count(argc, argv);
+ ++id;
+ }
+
+ plan(test_count);
+
+ // Run tests
+ id = 0;
+ while (tests[id] != NULL) {
+ diag("Testing unit: %s", tests[id]->name);
+ tests[id]->run(argc, argv);
+ ++id;
+ }
+
+ //log_close();
+
+ // Evaluate
+ return exit_status();
+}
+
diff --git a/src/zcompile/tests/zcompile_tests.c b/src/zcompile/tests/zcompile_tests.c
new file mode 100644
index 0000000..5d3dce6
--- /dev/null
+++ b/src/zcompile/tests/zcompile_tests.c
@@ -0,0 +1,425 @@
+/* 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 "libknot/zone/zone.h"
+#include "knot/zone/zone-load.h"
+#include "knot/common.h"
+#include "libknot/rrset.h"
+#include "libknot/util/descriptor.h"
+#include "zcompile/zcompile.h"
+
+#ifdef TEST_WITH_LDNS
+#include "ldns/ldns.h"
+#endif
+
+static int zoneparser_tests_count(int argc, char *argv[]);
+static int zoneparser_tests_run(int argc, char *argv[]);
+
+/*
+ * Unit API.
+ */
+unit_api zoneparser_tests_api = {
+ "Zoneparser",
+ &zoneparser_tests_count,
+ &zoneparser_tests_run
+};
+
+#ifdef TEST_WITH_LDNS
+/*
+ * Unit implementation.
+ */static int compare_wires_simple_zp(uint8_t *wire1,
+ uint8_t *wire2, uint count)
+{
+ int i = 0;
+ while (i < count &&
+ wire1[i] == wire2[i]) {
+ i++;
+ }
+ return (!(count == i));
+}
+
+/* compares only one rdata */
+static int compare_rr_rdata_silent(knot_rdata_t *rdata, ldns_rr *rr,
+ uint16_t type)
+{
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ for (int i = 0; i < rdata->count; i++) {
+ /* TODO check for ldns "descriptors" as well */
+ if (desc->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME ||
+ desc->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME) {
+ assert(ldns_rr_rdf(rr, i));
+ if (rdata->items[i].dname->size !=
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) {
+ return 1;
+ }
+ if (compare_wires_simple_zp(rdata->items[i].dname->name,
+ ldns_rdf_data(ldns_rr_rdf(rr, i)),
+ rdata->items[i].dname->size) != 0) {
+ return 1;
+ }
+ } else {
+ if (ldns_rr_rdf(rr, i) == NULL &&
+ rdata->items[i].raw_data[0] != 0) {
+ return 1;
+ } else {
+ continue;
+ }
+ if (rdata->items[i].raw_data[0] !=
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) {
+
+ /* ldns stores the size including the
+ * length, dnslib does not */
+ if (abs(rdata->items[i].raw_data[0] -
+ ldns_rdf_size(ldns_rr_rdf(rr, i))) != 1) {
+ return 1;
+ }
+ }
+ if (compare_wires_simple_zp((uint8_t *)
+ (rdata->items[i].raw_data + 1),
+ ldns_rdf_data(ldns_rr_rdf(rr, i)),
+ rdata->items[i].raw_data[0]) != 0) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int compare_rrset_w_ldns_rrset(const knot_rrset_t *rrset,
+ ldns_rr_list *rrs,
+ char check_rdata, char verbose)
+{
+ /* We should have only one rrset from ldns, although it is
+ * represented as rr_list ... */
+
+ /* TODO errors */
+
+ assert(rrs);
+ assert(rrset);
+
+ ldns_rr_list_sort(rrs);
+
+ /* compare headers */
+
+ ldns_rr *rr = ldns_rr_list_rr(rrs, 0);
+
+ if (rrset->owner->size != ldns_rdf_size(ldns_rr_owner(rr))) {
+ diag("RRSet owner names differ in length");
+ if (!verbose) {
+ return 1;
+ }
+ diag("ldns: %d, dnslib: %d", ldns_rdf_size(ldns_rr_owner(rr)),
+ rrset->owner->size);
+ diag("%s", knot_dname_to_str(rrset->owner));
+ diag("%s", ldns_rdf_data(ldns_rr_owner(rr)));
+ return 1;
+ }
+
+ if (compare_wires_simple_zp(rrset->owner->name,
+ ldns_rdf_data(ldns_rr_owner(rr)),
+ rrset->owner->size) != 0) {
+ diag("RRSet owner wireformats differ");
+ return 1;
+ }
+
+ if (rrset->type != ldns_rr_get_type(rr)) {
+ diag("RRset types differ");
+ if (!verbose) {
+ return 1;
+ }
+ diag("Dnslib type: %d Ldns type: %d", rrset->type,
+ ldns_rr_get_type(rr));
+ return 1;
+ }
+
+ if (rrset->rclass != ldns_rr_get_class(rr)) {
+ diag("RRset classes differ");
+ return 1;
+ }
+
+ if (rrset->ttl != ldns_rr_ttl(rr)) {
+ diag("RRset TTLs differ");
+ if (!verbose) {
+ return 1;
+ }
+ diag("dnslib: %d ldns: %d", rrset->ttl, ldns_rr_ttl(rr));
+ return 1;
+ }
+
+ if (!check_rdata) {
+ return 0;
+ }
+
+ /* compare rdatas */
+
+ /* sort dnslib rdata */
+
+ knot_rdata_t *tmp_rdata = rrset->rdata;
+
+ rr = ldns_rr_list_pop_rr(rrs);
+
+ char found;
+
+ while (rr != NULL) {
+ found = 0;
+ tmp_rdata = rrset->rdata;
+ while (!found &&
+ tmp_rdata->next != rrset->rdata) {
+ if (compare_rr_rdata_silent(tmp_rdata, rr,
+ rrset->type) == 0) {
+ found = 1;
+ }
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ if (!found &&
+ compare_rr_rdata_silent(tmp_rdata, rr, rrset->type) == 0) {
+ found = 1;
+ }
+
+ /* remove the found rdata from list */
+ if (!found) {
+ diag("RRsets rdata differ");
+ return 1;
+ }
+ ldns_rr_free(rr);
+
+ rr = ldns_rr_list_pop_rr(rrs);
+ }
+
+ return 0;
+}
+
+int compare_zones(knot_zone_contents_t *zone,
+ ldns_rr_list *ldns_list, char verbose)
+{
+ /* TODO currently test fail when encountering first error -
+ * it should finish going through the zone */
+ knot_rrset_t *tmp_rrset = NULL;
+
+ knot_dname_t *tmp_dname = NULL;
+
+ knot_node_t *node = NULL;
+
+ ldns_rr_list *ldns_rrset = ldns_rr_list_pop_rrset(ldns_list);
+
+ if (ldns_rrset == NULL) {
+ diag("Error: empty node");
+ return 1;
+ }
+
+ ldns_rr *rr = NULL;
+
+ /*
+ * Following cycle works like this: First, we get RR from ldns rrset,
+ * then we search for the node containing the rrset, then we get the
+ * rrset, which is then compared with whole ldns rrset.
+ */
+
+ /* ldns_rr_list_pop_rrset should pop the first rrset */
+ while (ldns_rrset != NULL) {
+ rr = ldns_rr_list_rr(ldns_rrset, 0);
+ tmp_dname =
+ knot_dname_new_from_wire(ldns_rdf_data(ldns_rr_owner(rr)),
+ ldns_rdf_size(ldns_rr_owner(rr)),
+ NULL);
+
+ node = knot_zone_contents_get_node(zone, tmp_dname);
+
+ if (node == NULL) {
+ node = knot_zone_contents_get_nsec3_node(zone,
+ tmp_dname);
+ }
+
+ if (node == NULL) {
+ diag("Could not find node");
+ diag("%s", knot_dname_to_str(tmp_dname));
+ return 1;
+ }
+
+ knot_dname_free(&tmp_dname);
+
+ tmp_rrset = knot_node_get_rrset(node,
+ ldns_rr_get_type(ldns_rr_list_rr(ldns_rrset,
+ 0)));
+
+ if (tmp_rrset == NULL &&
+ (uint)(ldns_rr_get_type(ldns_rr_list_rr(ldns_rrset, 0))) !=
+ (uint)KNOT_RRTYPE_RRSIG) {
+ diag("Could not find rrset");
+ if (!verbose) {
+ return 1;
+ }
+ ldns_rr_list_print(stdout, ldns_rrset);
+ diag("%s", knot_dname_to_str(node->owner));
+ return 1;
+ } else if ((uint)(ldns_rr_get_type(ldns_rr_list_rr(ldns_rrset,
+ 0))) ==
+ (uint)KNOT_RRTYPE_RRSIG) {
+ knot_rrset_t *rrsigs = NULL;
+ /* read type covered from ldns rrset */
+ for (int i = 0; i < ldns_rrset->_rr_count; i++) {
+ uint16_t type_covered =
+ ldns_rdf_data(ldns_rr_rdf(
+ ldns_rr_list_rr(ldns_rrset, i), 0))[1];
+
+ /*
+ * Dnslib stores RRSIGs separately -
+ * we have to find get it from its "parent"
+ * rrset.
+ */
+
+ tmp_rrset = knot_node_get_rrset(node,
+ type_covered);
+
+ if (tmp_rrset == NULL) {
+ if (!verbose) {
+ return 1;
+ }
+ diag("following rrset "
+ "could not be found");
+ ldns_rr_list_print(stdout, ldns_rrset);
+ return 1;
+ }
+
+ if (rrsigs == NULL) {
+ rrsigs = tmp_rrset->rrsigs;
+ } else {
+ knot_rrset_merge((void *)&rrsigs,
+ (void *)&(tmp_rrset->rrsigs));
+ }
+ }
+ tmp_rrset = rrsigs;
+ }
+
+/* diag("dnslib type: %d", tmp_rrset->type);
+ diag("dnslib dname: %s", tmp_rrset->owner->name);
+
+ diag("ldns type: %d",
+ ldns_rr_get_type(ldns_rr_list_rr(ldns_rrset, 0)));
+ diag("ldns dname : %s", ldns_rdf_data(ldns_rr_owner(
+ ldns_rr_list_rr(ldns_rrset, 0)))); */
+
+// knot_rrset_dump(tmp_rrset, 1);
+
+ if (compare_rrset_w_ldns_rrset(tmp_rrset, ldns_rrset,
+ 1, 0) != 0) {
+ diag("RRSets did not match");
+// knot_rrset_dump(tmp_rrset, 1);
+ return 1;
+ }
+
+ ldns_rr_list_deep_free(ldns_rrset);
+
+ ldns_rrset = ldns_rr_list_pop_rrset(ldns_list);
+
+ if (ldns_rrset == NULL) {
+ ldns_rrset = ldns_rr_list_pop_rrset(ldns_list);
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+static int test_zoneparser_zone_read(const char *origin, const char *filename,
+ const char *outfile)
+{
+#ifndef TEST_WITH_LDNS
+ diag("Zoneparser tests without usage of ldns are not implemented");
+ return 0;
+#endif
+
+#ifdef TEST_WITH_LDNS
+ /* Calls zcompile. */
+ parser = zparser_create();
+ int ret = zone_read(origin, filename, outfile, 0);
+ if (ret != 0) {
+ diag("Could not load zone from file: %s", filename);
+ return 0;
+ }
+
+ knot_zone_t *dnsl_zone = NULL;
+ zloader_t *loader = NULL;
+ if (knot_zload_open(&loader, outfile) != 0) {
+ diag("Could not create zone loader.\n");
+ return 0;
+ }
+ dnsl_zone = knot_zload_load(loader);
+ remove(outfile);
+ if (!dnsl_zone) {
+ diag("Could not load dumped zone.\n");
+ return 0;
+ }
+
+ ldns_zone *ldns_zone = NULL;
+ FILE *f = fopen(filename, "r");
+ if (ldns_zone_new_frm_fp(&ldns_zone, f, NULL,
+ 0, LDNS_RR_CLASS_IN) != LDNS_STATUS_OK) {
+ diag("Could not load zone from file: %s (ldns)", filename);
+ return 0;
+ }
+
+// ldns_zone_sort(ldns_zone);
+
+ /*
+ * LDNS stores SOA record independently - create a list with all
+ * records in it.
+ */
+
+ ldns_rr_list *ldns_list = ldns_zone_rrs(ldns_zone);
+
+ ldns_rr_list_push_rr(ldns_list, ldns_zone_soa(ldns_zone));
+
+ if (compare_zones(dnsl_zone->contents, ldns_list, 0) != 0) {
+ return 0;
+ }
+
+ knot_zone_deep_free(&dnsl_zone, 0);
+ ldns_zone_free(ldns_zone);
+ fclose(f);
+ return 1;
+#endif
+}
+
+static const int ZONEPARSER_TEST_COUNT = 1;
+
+/*! API: return number of tests. */
+static int zoneparser_tests_count(int argc, char *argv[])
+{
+ return ZONEPARSER_TEST_COUNT;
+}
+
+/*! API: run tests. */
+static int zoneparser_tests_run(int argc, char *argv[])
+{
+ if (argc == 3) {
+ ok(test_zoneparser_zone_read(argv[1], argv[2],
+ "foo_test_zone"),
+ "zoneparser: read (%s)",
+ argv[2]);
+ } else {
+ diag("Wrong parameters\n usage: "
+ "knot-zcompile-unittests origin zonefile");
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/zcompile/zcompile-error.c b/src/zcompile/zcompile-error.c
new file mode 100644
index 0000000..9357cde
--- /dev/null
+++ b/src/zcompile/zcompile-error.c
@@ -0,0 +1,52 @@
+/* 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 "zcompile/zcompile-error.h"
+
+#include "common/errors.h"
+
+/*! \brief Table linking error messages to error codes. */
+const error_table_t knot_zcompile_error_msgs[KNOTDZCOMPILE_ERROR_COUNT] = {
+
+ /* Mapped errors. */
+ {KNOTDZCOMPILE_EOK, "OK"},
+ {KNOTDZCOMPILE_ENOMEM, "Not enough memory."},
+ {KNOTDZCOMPILE_EINVAL, "Invalid parameter passed."},
+ {KNOTDZCOMPILE_ENOTSUP, "Parameter not supported."},
+ {KNOTDZCOMPILE_EBUSY, "Requested resource is busy."},
+ {KNOTDZCOMPILE_EAGAIN,
+ "The system lacked the necessary resource, try again."},
+ {KNOTDZCOMPILE_EACCES,
+ "Permission to perform requested operation is denied."},
+ {KNOTDZCOMPILE_ECONNREFUSED, "Connection is refused."},
+ {KNOTDZCOMPILE_EISCONN, "Already connected."},
+ {KNOTDZCOMPILE_EADDRINUSE, "Address already in use."},
+ {KNOTDZCOMPILE_ENOENT, "Resource not found."},
+ {KNOTDZCOMPILE_ERANGE, "Value is out of range."},
+
+ /* Custom errors. */
+ {KNOTDZCOMPILE_ERROR, "Generic error."},
+ {KNOTDZCOMPILE_EBRDATA, "Malformed RDATA."},
+ {KNOTDZCOMPILE_ESOA, "Multiple SOA records."},
+ {KNOTDZCOMPILE_EBADSOA, "SOA record has different owner "
+ "than in config - parser will not continue!"},
+ {KNOTDZCOMPILE_EBADNODE, "Error handling node."},
+ {KNOTDZCOMPILE_EZONEINVAL, "Invalid zone file."},
+ {KNOTDZCOMPILE_EPARSEFAIL, "Parser failed."},
+ {KNOTDZCOMPILE_ENOIPV6, "IPv6 support disabled."},
+ {KNOTDZCOMPILE_ESYNT, "Parser syntactic error."},
+ {KNOTDZCOMPILE_ERROR, 0}
+};
diff --git a/src/zcompile/zcompile-error.h b/src/zcompile/zcompile-error.h
new file mode 100644
index 0000000..c6d999c
--- /dev/null
+++ b/src/zcompile/zcompile-error.h
@@ -0,0 +1,90 @@
+/*!
+ * \file zcompile-error.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Error codes and function for getting error message.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOTD_ZCOMPILE_ERROR_H_
+#define _KNOTD_ZCOMPILE_ERROR_H_
+
+#include "common/errors.h"
+
+/*!
+ * \brief Error codes used in the server.
+ *
+ * Some viable errors are directly mapped
+ * to libc errno codes.
+ */
+enum knot_zcompile_error {
+
+ /* Directly mapped error codes. */
+ KNOTDZCOMPILE_EOK = 0,
+ KNOTDZCOMPILE_ENOMEM = -ENOMEM, /*!< \brief Out of memory. */
+ KNOTDZCOMPILE_EINVAL = -EINVAL, /*!< \brief Invalid parameter passed. */
+ /*!
+ * \brief Parameter not supported.
+ */
+ KNOTDZCOMPILE_ENOTSUP = -ENOTSUP,
+ KNOTDZCOMPILE_EBUSY = -EBUSY, /*!< \brief Requested resource is busy. */
+ /*!
+ * \brief OS lacked necessary resources.
+ */
+ KNOTDZCOMPILE_EAGAIN = -EAGAIN,
+ KNOTDZCOMPILE_EACCES = -EACCES, /*!< \brief Permission is denied. */
+ /*!
+ * \brief Connection is refused.
+ */
+ KNOTDZCOMPILE_ECONNREFUSED = -ECONNREFUSED,
+ KNOTDZCOMPILE_EISCONN = -EISCONN, /*!< \brief Already connected. */
+ /*!
+ * \brief Address already in use.
+ */
+ KNOTDZCOMPILE_EADDRINUSE = -EADDRINUSE,
+ KNOTDZCOMPILE_ENOENT = -ENOENT, /*!< \brief Resource not found. */
+ KNOTDZCOMPILE_ERANGE = -ERANGE, /*!< \brief Value is out of range. */
+
+ /* Custom error codes. */
+ KNOTDZCOMPILE_ERROR = -16384, /*!< \brief Generic error. */
+ KNOTDZCOMPILE_ESYNT, /*!< \brief Syntax error. */
+ KNOTDZCOMPILE_EBADNODE, /*!< \brief Node error. */
+ KNOTDZCOMPILE_EBRDATA, /*!< \brief RDATA error. */
+ KNOTDZCOMPILE_EBADSOA, /*!< \brief SOA owner error. */
+ KNOTDZCOMPILE_ESOA, /*!< \brief Multiple SOA records. */
+
+ KNOTDZCOMPILE_EZONEINVAL, /*!< \brief Invalid zone file. */
+ KNOTDZCOMPILE_EPARSEFAIL, /*!< \brief Parser fail. */
+ KNOTDZCOMPILE_ENOIPV6, /*! \brief No IPv6 support. */
+
+ KNOTDZCOMPILE_ERROR_COUNT = 22
+};
+
+typedef enum knot_zcompile_error knot_zcompile_error_t;
+
+/*! \brief Table linking error messages to error codes. */
+extern const error_table_t knot_zcompile_error_msgs[KNOTDZCOMPILE_ERROR_COUNT];
+
+#endif /* _KNOTD_ZCOMPILE_ERROR_H_ */
+
+/*! @} */
diff --git a/src/zcompile/zcompile.c b/src/zcompile/zcompile.c
new file mode 100644
index 0000000..ec3cbe2
--- /dev/null
+++ b/src/zcompile/zcompile.c
@@ -0,0 +1,639 @@
+/*!
+ * \file zcompile.c
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>. Minor portions of code taken from
+ * NSD.
+ *
+ * \brief Zone compiler.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/* 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 <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "common/base32hex.h"
+#include "zcompile/zcompile.h"
+#include "zcompile/parser-util.h"
+#include "knot/zone/zone-dump-text.h"
+#include "zparser.h"
+#include "zcompile/zcompile-error.h"
+#include "knot/zone/zone-dump.h"
+#include "libknot/libknot.h"
+#include "libknot/util/utils.h"
+
+/* Some global flags... */
+static int vflag = 0;
+/* if -v then print progress each 'progress' RRs */
+static int progress = 10000;
+
+/* Total errors counter */
+static long int totalerrors = 0;
+static long int totalrrs = 0;
+
+extern FILE *zp_get_in(void *scanner);
+
+//#define ZP_DEBUG
+
+#ifdef ZP_DEBUG
+#define dbg_zp(msg...) fprintf(stderr, msg)
+#else
+#define dbg_zp(msg...)
+#endif
+
+/*!
+ * \brief Adds RRSet to list.
+ *
+ * \param head Head of list.
+ * \param rrsig RRSet to be added.
+ */
+static int rrset_list_add(rrset_list_t **head, knot_rrset_t *rrsig)
+{
+ if (*head == NULL) {
+ *head = malloc(sizeof(rrset_list_t));
+ if (*head == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+ (*head)->next = NULL;
+ (*head)->data = rrsig;
+ } else {
+ rrset_list_t *tmp = malloc(sizeof(*tmp));
+ if (tmp == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+ tmp->next = *head;
+ tmp->data = rrsig;
+ *head = tmp;
+ }
+
+ return KNOTDZCOMPILE_EOK;
+}
+
+/*!
+ * \brief Deletes RRSet list. Sets pointer to NULL.
+ *
+ * \param head Head of list to be deleted.
+ */
+static void rrset_list_delete(rrset_list_t **head)
+{
+ rrset_list_t *tmp;
+ if (*head == NULL) {
+ return;
+ }
+
+ while (*head != NULL) {
+ tmp = *head;
+ *head = (*head)->next;
+ free(tmp);
+ }
+
+ *head = NULL;
+}
+
+static int find_rrset_for_rrsig_in_zone(knot_zone_contents_t *zone,
+ knot_rrset_t *rrsig)
+{
+ assert(rrsig != NULL);
+ assert(rrsig->rdata->items[0].raw_data);
+
+ knot_node_t *tmp_node = NULL;
+
+ if (rrsig->type != KNOT_RRTYPE_NSEC3) {
+ tmp_node = knot_zone_contents_get_node(zone, rrsig->owner);
+ } else {
+ tmp_node = knot_zone_contents_get_nsec3_node(zone,
+ rrsig->owner);
+ }
+
+ if (tmp_node == NULL) {
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
+ knot_rrset_t *tmp_rrset =
+ knot_node_get_rrset(tmp_node, rrsig->type);
+
+ if (tmp_rrset == NULL) {
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
+ if (tmp_rrset->rrsigs != NULL) {
+ knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &tmp_node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ knot_rrset_free(&rrsig);
+ } else {
+ knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &tmp_node,
+ KNOT_RRSET_DUPL_SKIP, 1);
+ }
+
+ return KNOTDZCOMPILE_EOK;
+}
+
+static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
+ knot_node_t *node,
+ knot_rrset_t *rrsig)
+{
+ assert(rrsig != NULL);
+ assert(rrsig->rdata->items[0].raw_data);
+ assert(node);
+
+ assert(knot_dname_compare(rrsig->owner, node->owner) == 0);
+
+ knot_rrset_t *tmp_rrset =
+ knot_node_get_rrset(node, rrsig_type_covered(rrsig));
+
+ if (tmp_rrset == NULL) {
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
+ if (tmp_rrset->rrsigs != NULL) {
+ if (knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &node,
+ KNOT_RRSET_DUPL_MERGE, 1) < 0) {
+ return KNOTDZCOMPILE_EINVAL;
+ }
+ knot_rrset_free(&rrsig);
+ } else {
+ if (knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &node,
+ KNOT_RRSET_DUPL_SKIP, 1) < 0) {
+ return KNOTDZCOMPILE_EINVAL;
+ }
+ }
+
+ assert(tmp_rrset->rrsigs != NULL);
+
+ return KNOTDZCOMPILE_EOK;
+}
+
+static knot_node_t *create_node(knot_zone_contents_t *zone,
+ knot_rrset_t *current_rrset,
+ int (*node_add_func)(knot_zone_contents_t *zone, knot_node_t *node,
+ int create_parents, uint8_t, int),
+ knot_node_t *(*node_get_func)(const knot_zone_contents_t *zone,
+ const knot_dname_t *owner))
+{
+ knot_node_t *node =
+ knot_node_new(current_rrset->owner, NULL, 0);
+ if (node_add_func(zone, node, 1, 0, 1) != 0) {
+ return NULL;
+ }
+
+ current_rrset->owner = node->owner;
+
+ return node;
+}
+
+static void process_rrsigs_in_node(knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ rrset_list_t *tmp = parser->node_rrsigs;
+ while (tmp != NULL) {
+ if (find_rrset_for_rrsig_in_node(zone, node,
+ tmp->data) != 0) {
+ rrset_list_add(&parser->rrsig_orphans,
+ tmp->data);
+ parser->rrsig_orphan_count++;
+ }
+ tmp = tmp->next;
+ }
+}
+
+int process_rr(void)
+{
+ knot_zone_t *zone = parser->current_zone;
+ assert(zone != NULL);
+ knot_zone_contents_t *contents = knot_zone_get_contents(zone);
+ assert(contents != NULL);
+ knot_rrset_t *current_rrset = parser->current_rrset;
+ knot_rrset_t *rrset;
+ knot_rrtype_descriptor_t *descriptor =
+ knot_rrtype_descriptor_by_type(current_rrset->type);
+
+ dbg_zp("%s\n", knot_dname_to_str(parser->current_rrset->owner));
+ dbg_zp("type: %s\n", knot_rrtype_to_string(parser->current_rrset->type));
+ dbg_zp("rdata count: %d\n", parser->current_rrset->rdata->count);
+// hex_print(parser->current_rrset->rdata->items[0].raw_data,
+// parser->current_rrset->rdata->items[0].raw_data[0]);
+
+ if (descriptor->fixed_items) {
+ assert(current_rrset->rdata->count == descriptor->length);
+ }
+
+ assert(current_rrset->rdata->count > 0);
+
+ assert(knot_dname_is_fqdn(current_rrset->owner));
+
+ int (*node_add_func)(knot_zone_contents_t *, knot_node_t *, int,
+ uint8_t, int);
+ knot_node_t *(*node_get_func)(const knot_zone_contents_t *,
+ const knot_dname_t *);
+
+
+ /* If we have RRSIG of NSEC3 type first node will have
+ * to be created in NSEC3 part of the zone */
+
+ uint16_t type_covered = 0;
+ if (current_rrset->type == KNOT_RRTYPE_RRSIG) {
+ type_covered = rrsig_type_covered(current_rrset);
+ }
+
+ if (current_rrset->type != KNOT_RRTYPE_NSEC3 &&
+ type_covered != KNOT_RRTYPE_NSEC3) {
+ node_add_func = &knot_zone_contents_add_node;
+ node_get_func = &knot_zone_contents_get_node;
+ } else {
+ node_add_func = &knot_zone_contents_add_nsec3_node;
+ node_get_func = &knot_zone_contents_get_nsec3_node;
+ }
+
+ if ((current_rrset->type == KNOT_RRTYPE_SOA) && (zone != NULL)) {
+ if (knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA) != NULL) {
+ /* Receiving another SOA. */
+ if (!knot_rrset_compare(current_rrset,
+ knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA), KNOT_RRSET_COMPARE_WHOLE)) {
+ return KNOTDZCOMPILE_ESOA;
+ } else {
+ zc_warning_prev_line("encountered identical "
+ "extra SOA record");
+ return KNOTDZCOMPILE_EOK;
+ }
+ }
+ }
+
+ /*!< \todo Make sure the maximum RDLENGTH does not exceed 65535 bytes.*/
+
+ if (current_rrset->type == KNOT_RRTYPE_SOA) {
+ if (knot_dname_compare(current_rrset->owner,
+ parser->origin_from_config) != 0) {
+ zc_error_prev_line("SOA record has a different "
+ "owner than the one specified "
+ "in config! \n");
+ /* Such SOA cannot even be added, because
+ * it would not be in the zone apex. */
+ return KNOTDZCOMPILE_EBADSOA;
+ }
+ }
+
+ if (current_rrset->type == KNOT_RRTYPE_RRSIG) {
+ /*!< \todo Still a leak somewhere. */
+ knot_rrset_t *tmp_rrsig =
+ knot_rrset_new(current_rrset->owner,
+ KNOT_RRTYPE_RRSIG,
+ current_rrset->rclass,
+ current_rrset->ttl);
+ if (tmp_rrsig == NULL) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ if (knot_rrset_add_rdata(tmp_rrsig,
+ current_rrset->rdata) != 0) {
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+
+ if (parser->last_node &&
+ knot_dname_compare(parser->last_node->owner,
+ current_rrset->owner) != 0) {
+ /* RRSIG is first in the node, so we have to create it
+ * before we return
+ */
+ if (parser->node_rrsigs != NULL) {
+ process_rrsigs_in_node(contents,
+ parser->last_node);
+ rrset_list_delete(&parser->node_rrsigs);
+ }
+
+ if ((parser->last_node = create_node(contents,
+ current_rrset, node_add_func,
+ node_get_func)) == NULL) {
+ knot_rrset_free(&tmp_rrsig);
+ return KNOTDZCOMPILE_EBADNODE;
+ }
+ }
+
+ if (rrset_list_add(&parser->node_rrsigs, tmp_rrsig) != 0) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ return KNOTDZCOMPILE_EOK;
+ }
+
+ assert(current_rrset->type != KNOT_RRTYPE_RRSIG);
+
+ 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(parser->last_node->owner,
+ current_rrset->owner) ==
+ 0) {
+ node = parser->last_node;
+ } else {
+ if (parser->last_node && parser->node_rrsigs) {
+ process_rrsigs_in_node(contents,
+ parser->last_node);
+ }
+
+ rrset_list_delete(&parser->node_rrsigs);
+
+ /* new node */
+ node = node_get_func(contents, current_rrset->owner);
+ }
+
+ if (node == NULL) {
+ if (parser->last_node && parser->node_rrsigs) {
+ process_rrsigs_in_node(contents,
+ parser->last_node);
+ }
+
+ if ((node = create_node(contents, current_rrset,
+ node_add_func,
+ node_get_func)) == NULL) {
+ return KNOTDZCOMPILE_EBADNODE;
+ }
+ }
+
+ rrset = knot_node_get_rrset(node, current_rrset->type);
+ if (!rrset) {
+ rrset = knot_rrset_new(current_rrset->owner,
+ current_rrset->type,
+ current_rrset->rclass,
+ current_rrset->ttl);
+ if (rrset == NULL) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ if (knot_rrset_add_rdata(rrset, current_rrset->rdata) != 0) {
+ free(rrset);
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+
+ /* I chose skip, but there should not really be
+ * any rrset to skip */
+ if (knot_zone_contents_add_rrset(contents, rrset, &node,
+ KNOT_RRSET_DUPL_SKIP, 1) < 0) {
+ free(rrset);
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+ } else {
+ if (current_rrset->type !=
+ KNOT_RRTYPE_RRSIG && rrset->ttl !=
+ current_rrset->ttl) {
+ zc_error_prev_line(
+ "TTL does not match the TTL of the RRset");
+ }
+
+ if (knot_zone_contents_add_rrset(contents, current_rrset,
+ &node,
+ KNOT_RRSET_DUPL_MERGE, 1) < 0) {
+ return KNOTDZCOMPILE_EBRDATA;
+ }
+ }
+
+ if (vflag > 1 && totalrrs > 0 && (totalrrs % progress == 0)) {
+ zc_error_prev_line("Total errors: %ld\n", totalrrs);
+ }
+
+ parser->last_node = node;
+
+ ++totalrrs;
+
+ return KNOTDZCOMPILE_EOK;
+}
+
+static uint find_rrsets_orphans(knot_zone_contents_t *zone, rrset_list_t
+ *head)
+{
+ uint found_rrsets = 0;
+ while (head != NULL) {
+ if (find_rrset_for_rrsig_in_zone(zone, head->data) == 0) {
+ found_rrsets += 1;
+ dbg_zp("RRSET succesfully found: owner %s type %s\n",
+ knot_dname_to_str(head->data->owner),
+ knot_rrtype_to_string(head->data->type));
+ }
+ else { /* we can throw it away now */
+ knot_rrset_free(&head->data);
+ }
+ head = head->next;
+ }
+ return found_rrsets;
+}
+
+/*
+ *
+ * Opens a zone file.
+ *
+ * Returns:
+ *
+ * - pointer to the parser structure
+ * - NULL on error and errno set
+ *
+ */
+static int zone_open(const char *filename, uint32_t ttl, uint16_t rclass,
+ knot_node_t *origin, void *scanner, knot_dname_t *origin_from_config)
+{
+ /* Open the zone file... */
+ if (strcmp(filename, "-") == 0) {
+ zp_set_in(stdin, scanner);
+ filename = "<stdin>";
+ } else {
+ FILE *f = fopen(filename, "r");
+ if (f == NULL) {
+ return 0;
+ }
+ zp_set_in(f, scanner);
+ if (zp_get_in(scanner) == 0) {
+ return 0;
+ }
+ }
+
+// int fd = fileno(zp_get_in(scanner));
+// if (fd == -1) {
+// return 0;
+// }
+
+// if (fcntl(fd, F_SETLK, knot_file_lock(F_RDLCK, SEEK_SET)) == -1) {
+// fprintf(stderr, "Could not lock zone file for read!\n");
+// return 0;
+// }
+
+ zparser_init(filename, ttl, rclass, origin, origin_from_config);
+
+ return 1;
+}
+
+/*
+ * Reads the specified zone into the memory
+ *
+ */
+int zone_read(const char *name, const char *zonefile, const char *outfile,
+ int semantic_checks)
+{
+ if (!outfile) {
+ zc_error_prev_line("Missing output file for '%s'\n",
+ zonefile);
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
+ /* Check that we can write to outfile. */
+ FILE *f = fopen(outfile, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "Cannot write zone db to file '%s'\n",
+ outfile);
+ return KNOTDZCOMPILE_EINVAL;
+ }
+ fclose(f);
+
+
+// char ebuf[256];
+
+ knot_dname_t *dname =
+ knot_dname_new_from_str(name, strlen(name), NULL);
+ if (dname == NULL) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ knot_node_t *origin_node = knot_node_new(dname, NULL, 0);
+
+ /*!< \todo Another copy is probably not needed. */
+ knot_dname_t *origin_from_config =
+ knot_dname_new_from_str(name, strlen(name), NULL);
+ if (origin_from_config == NULL) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ //assert(origin_node->next == NULL);
+
+ assert(knot_node_parent(origin_node, 0) == NULL);
+ if (origin_node == NULL) {
+ knot_dname_release(dname);
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ void *scanner = NULL;
+ zp_lex_init(&scanner);
+ if (scanner == NULL) {
+ return KNOTDZCOMPILE_ENOMEM;
+ }
+
+ if (!zone_open(zonefile, 3600, KNOT_CLASS_IN, origin_node, scanner,
+ origin_from_config)) {
+ zc_error_prev_line("Cannot open '%s'\n",
+ zonefile);
+ zparser_free();
+ return KNOTDZCOMPILE_EZONEINVAL;
+ }
+
+ if (zp_parse(scanner) != 0) {
+// int fd = fileno(zp_get_in(scanner));
+// if (fcntl(fd, F_SETLK,
+// knot_file_lock(F_UNLCK, SEEK_SET)) == -1) {
+// return KNOTDZCOMPILE_EACCES;
+// }
+
+ FILE *in_file = (FILE *)zp_get_in(scanner);
+ fclose(in_file);
+ zp_lex_destroy(scanner);
+
+ return KNOTDZCOMPILE_ESYNT;
+ }
+
+ knot_zone_contents_t *contents =
+ knot_zone_get_contents(parser->current_zone);
+
+ FILE *in_file = (FILE *)zp_get_in(scanner);
+ fclose(in_file);
+ zp_lex_destroy(scanner);
+
+ /* Unlock zone file. */
+// int fd = fileno(zp_get_in(scanner));
+// if (fcntl(fd, F_SETLK, knot_file_lock(F_UNLCK, SEEK_SET)) == -1) {
+// fprintf(stderr, "Could not lock zone file for read!\n");
+// return 0;
+// }
+
+ dbg_zp("zp complete %p\n", parser->current_zone);
+
+ if (parser->last_node && parser->node_rrsigs != NULL) {
+ /* assign rrsigs to last node in the zone*/
+ process_rrsigs_in_node(contents,
+ parser->last_node);
+ rrset_list_delete(&parser->node_rrsigs);
+ }
+
+ dbg_zp("zone parsed\n");
+
+ if (!(parser->current_zone &&
+ knot_node_rrset(parser->current_zone->contents->apex,
+ KNOT_RRTYPE_SOA))) {
+ zc_error_prev_line("Zone file does not contain SOA record!\n");
+ knot_zone_deep_free(&parser->current_zone, 1);
+ zparser_free();
+ return KNOTDZCOMPILE_EZONEINVAL;
+ }
+
+ uint found_orphans;
+ found_orphans = find_rrsets_orphans(contents,
+ parser->rrsig_orphans);
+
+ dbg_zp("%u orphans found\n", found_orphans);
+
+ rrset_list_delete(&parser->rrsig_orphans);
+
+ if (found_orphans != parser->rrsig_orphan_count) {
+ fprintf(stderr,
+ "There are unassigned RRSIGs in the zone!\n");
+ parser->errors++;
+ }
+
+ knot_zone_contents_adjust(contents, 0);
+
+ dbg_zp("rdata adjusted\n");
+
+ if (parser->errors != 0) {
+ fprintf(stderr,
+ "Parser finished with error, not dumping the zone!\n");
+ } else {
+ if (knot_zdump_binary(contents,
+ outfile, semantic_checks,
+ zonefile) != 0) {
+ fprintf(stderr, "Could not dump zone!\n");
+ totalerrors++;
+ }
+ dbg_zp("zone dumped.\n");
+ }
+
+ /* This is *almost* unnecessary */
+ knot_zone_deep_free(&(parser->current_zone), 1);
+
+ fflush(stdout);
+ totalerrors += parser->errors;
+ zparser_free();
+
+ return totalerrors;
+}
+
+/*! @} */
diff --git a/src/zcompile/zcompile.h b/src/zcompile/zcompile.h
new file mode 100644
index 0000000..33dd3ee
--- /dev/null
+++ b/src/zcompile/zcompile.h
@@ -0,0 +1,207 @@
+/*!
+ * \file zoneparser.h
+ *
+ * \author modifications by Jan Kadlec <jan.kadlec@nic.cz>, most of the code
+ * by NLnet Labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief Zone compiler.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOTD_ZONEPARSER_H_
+#define _KNOTD_ZONEPARSER_H_
+
+#include <stdio.h>
+
+#include "libknot/dname.h"
+#include "libknot/rrset.h"
+#include "libknot/zone/node.h"
+#include "libknot/rdata.h"
+#include "libknot/zone/zone.h"
+#include "libknot/zone/dname-table.h"
+#include "libknot/zone/dname-table.h"
+#include "common/slab/slab.h"
+
+#define MAXRDATALEN 64 /*!< Maximum number of RDATA items. */
+#define MAXLABELLEN 63 /*!< Maximum label length. */
+#define MAXDOMAINLEN 255 /*!< Maximum domain name length */
+#define MAX_RDLENGTH 65535 /*!< Maximum length of RDATA item */
+#define MAXTOKENSLEN 512 /*!< Maximum number of tokens per entry. */
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+#define ROOT (const uint8_t *)"\001" /*!< Root domain name. */
+
+#define NSEC_WINDOW_COUNT 256 /*!< Number of NSEC windows. */
+#define NSEC_WINDOW_BITS_COUNT 256 /*!< Number of bits in NSEC window. */
+/*! \brief Size of NSEC window in bytes. */
+#define NSEC_WINDOW_BITS_SIZE (NSEC_WINDOW_BITS_COUNT / 8)
+
+/*
+ * RFC 4025 - codes for different types that IPSECKEY can hold.
+ */
+#define IPSECKEY_NOGATEWAY 0
+#define IPSECKEY_IP4 1
+#define IPSECKEY_IP6 2
+#define IPSECKEY_DNAME 3
+
+#define LINEBUFSZ 1024 /*!< Buffer size for one line in zone file. */
+
+struct lex_data {
+ size_t len; /*!< holds the label length */
+ char *str; /*!< holds the data */
+};
+
+#define DEFAULT_TTL 3600
+
+int yylex_destroy(void *scanner);
+int zp_parse(void *scanner);
+void zp_set_in(FILE *f, void *scanner);
+int zp_lex_init(void **scanner);
+int zp_lex_destroy(void *scanner);
+
+/*! \todo Implement ZoneDB. */
+typedef void namedb_type;
+
+/*!
+ * \brief One-purpose linked list holding pointers to RRSets.
+ */
+struct rrset_list {
+ knot_rrset_t *data; /*!< List data. */
+ struct rrset_list *next; /*!< Next node. */
+};
+
+typedef struct rrset_list rrset_list_t;
+
+/*!
+ * \brief Main zoneparser structure.
+ */
+struct zparser {
+ const char *filename; /*!< File with zone. */
+ uint32_t default_ttl; /*!< Default TTL. */
+ uint16_t default_class; /*!< Default class. */
+ knot_zone_t *current_zone; /*!< Current zone. */
+ knot_node_t *origin; /*!< Origin node. */
+ knot_dname_t *prev_dname; /*!< Previous dname. */
+ knot_dname_t *origin_from_config; /*!< Zone origin from config. */
+ knot_node_t *default_apex; /*!< Zone default apex. */
+
+ knot_node_t *last_node; /*!< Last processed node. */
+
+ char *dname_str; /*!< Temporary dname. */
+
+ int error_occurred; /*!< Error occured flag */
+ unsigned int errors; /*!< Number of errors. */
+ unsigned int line; /*!< Current line */
+
+ knot_rrset_t *current_rrset; /*!< Current RRSet. */
+ knot_rdata_item_t *temporary_items; /*!< Temporary rdata items. */
+
+ /*!
+ * \brief list of RRSIGs that were not inside their nodes in zone file
+ */
+ rrset_list_t *rrsig_orphans;
+ unsigned long rrsig_orphan_count; /*!< RRSIG orphan count */
+
+ knot_dname_t *root_domain; /*!< Root domain name. */
+ slab_cache_t *parser_slab; /*!< Slab for parser. */
+ rrset_list_t *node_rrsigs; /*!< List of RRSIGs in current node. */
+
+ int rdata_count; /*!< Count of parsed rdata. */
+};
+
+typedef struct zparser zparser_type;
+
+extern zparser_type *parser;
+
+extern void zc_error_prev_line(const char *fmt, ...);
+
+/* used in zonec.lex */
+
+void zc_error_prev_line(const char *fmt, ...);
+void zc_warning_prev_line(const char *fmt, ...);
+
+/*!
+ * \brief Does all the processing of RR - saves to zone, assigns RRSIGs etc.
+ */
+int process_rr();
+
+/*!
+ * \brief Parses and creates zone from given file.
+ *
+ * \param name Origin domain name string.
+ * \param zonefile File containing the zone.
+ * \param outfile File to save dump of the zone to.
+ * \param semantic_checks Enables or disables sematic checks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int zone_read(const char *name, const char *zonefile, const char *outfile,
+ int semantic_checks);
+
+/*!
+ * \brief Creates zparser instance.
+ *
+ *
+ * \return Created zparser instance.
+ */
+zparser_type *zparser_create();
+
+/*!
+ * \brief Inits zoneparser structure.
+ *
+ * \param filename Name of file with zone.
+ * \param ttl Default TTL.
+ * \param rclass Default class.
+ * \param origin Zone origin.
+ */
+void zparser_init(const char *filename, uint32_t ttl, uint16_t rclass,
+ knot_node_t *origin, knot_dname_t *owner_from_config);
+
+/*!
+ * \brief Frees zoneparser structure.
+ *
+ */
+void zparser_free();
+
+int save_dnames_in_table(knot_dname_table_t *table,
+ knot_rrset_t *rrset);
+
+#endif /* _KNOTD_ZONEPARSER_H_ */
+
+/*! @} */
diff --git a/src/zcompile/zcompile_main.c b/src/zcompile/zcompile_main.c
new file mode 100644
index 0000000..cb862f8
--- /dev/null
+++ b/src/zcompile/zcompile_main.c
@@ -0,0 +1,116 @@
+/* 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 <unistd.h>
+#include <stdlib.h>
+
+#include "zcompile/zcompile.h"
+#include "zcompile/zcompile-error.h"
+#include "common/errors.h"
+#include <config.h>
+
+static void help(int argc, char **argv)
+{
+ printf("Usage: %s [parameters] origin zonefile\n",
+ argv[0]);
+ printf("Parameters:\n"
+ " -o <outfile> Override output file.\n"
+ " -v Verbose mode - additional runtime information.\n"
+ " -s Enable semantic checks.\n"
+ " -V Print version of the server.\n"
+ " -h Print help and usage.\n");
+}
+
+int main(int argc, char **argv)
+{
+ // Parse command line arguments
+ int c = 0;
+ int verbose = 0;
+ int semantic_checks = 0;
+ const char* origin = 0;
+ const char* zonefile = 0;
+ const char* outfile = 0;
+ while ((c = getopt (argc, argv, "o:vVsh")) != -1) {
+ switch (c)
+ {
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
+ return 0;
+ case 's':
+ semantic_checks = 1;
+ break;
+ case 'h':
+ case '?':
+ default:
+ if (optopt == 'o') {
+ fprintf (stderr,
+ "Option -%c requires an argument.\n",
+ optopt);
+ }
+ help(argc, argv);
+ return 1;
+ }
+ }
+
+ UNUSED(verbose);
+
+ // Check if there's at least two remaining non-option
+ if (argc - optind < 2) {
+ help(argc, argv);
+ return 1;
+ }
+
+ origin = argv[optind];
+ zonefile = argv[optind + 1];
+
+ // Initialize log (no output)
+ //log_init(0);
+ //log_levels_set(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_DEBUG));
+
+ printf("Parsing file '%s', origin '%s' ...\n",
+ zonefile, origin);
+
+ parser = zparser_create();
+ if (!parser) {
+ fprintf(stderr, "Failed to create parser.\n");
+ //log_close();
+ return 1;
+ }
+
+ int error = zone_read(origin, zonefile, outfile, semantic_checks);
+
+ if (error) {
+ /* FIXME! */
+// if (error < 0) {
+// fprintf(stderr, "Finished with error: %s.\n",
+// error_to_str(knot_zcompile_error_msgs, error));
+// } else {
+// fprintf(stderr, "Finished with %u errors.\n");
+// }
+ } else {
+ printf("Compilation successful.\n");
+ }
+ //log_close();
+
+ return error;
+}
diff --git a/src/zcompile/zlexer.l b/src/zcompile/zlexer.l
new file mode 100644
index 0000000..c6c01fb
--- /dev/null
+++ b/src/zcompile/zlexer.l
@@ -0,0 +1,531 @@
+%{
+/*!
+ * \file zlexer.l
+ *
+ * \author minor modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the code by NLnet Labs
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief lexical analyzer for (DNS) zone files.
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#include "common.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+
+#include "zcompile/zcompile.h"
+#include "libknot/dname.h"
+#include "zcompile/parser-descriptor.h"
+#include "zparser.h"
+
+#define YY_NO_INPUT
+
+/* Utils */
+extern void zc_error(const char *fmt, ...);
+extern void zc_warning(const char *fmt, ...);
+
+void strip_string(char *str)
+{
+ char *start = str;
+ char *end = str + strlen(str) - 1;
+
+ while (isspace(*start))
+ ++start;
+ if (start > end) {
+ /* Completely blank. */
+ str[0] = '\0';
+ } else {
+ while (isspace(*end))
+ --end;
+ *++end = '\0';
+
+ if (str != start)
+ memmove(str, start, end - start + 1);
+ }
+}
+
+int hexdigit_to_int(char ch)
+{
+ switch (ch) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ default:
+ abort();
+ }
+}
+
+extern uint32_t strtottl(const char *nptr, const char **endptr);
+
+#define YY_NO_UNPUT
+#define MAXINCLUDES 10
+
+#define scanner yyscanner
+extern int zp_lex(YYSTYPE *lvalp, void *scanner);
+
+#if 0
+#define LEXOUT(s) printf s /* used ONLY when debugging */
+#else
+#define LEXOUT(s)
+#endif
+
+enum lexer_state {
+ EXPECT_OWNER,
+ PARSING_OWNER,
+ PARSING_TTL_CLASS_TYPE,
+ PARSING_RDATA
+};
+
+static YY_BUFFER_STATE include_stack[MAXINCLUDES];
+static zparser_type zparser_stack[MAXINCLUDES];
+static int include_stack_ptr = 0;
+
+static void pop_parser_state(void *scanner);
+static void push_parser_state(FILE *input, void *scanner);
+static int parse_token(void *scanner, int token, char *in_str,
+ enum lexer_state *lexer_state);
+
+
+/*!< \todo does not compile */
+#ifndef yy_set_bol // compat definition, for flex 2.4.6
+#define yy_set_bol(at_bol) \
+{ \
+ if (!yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
+}
+#endif
+
+%}
+
+%option nounput
+%option reentrant bison-bridge
+%option prefix = "zp_"
+%option outfile = "lex.yy.c"
+
+SPACE [ \t]
+LETTER [a-zA-Z]
+NEWLINE [\n\r]
+ZONESTR [^ \t\n\r();.\"\$]
+DOLLAR \$
+COMMENT ;
+DOT \.
+BIT [^\]\n]|\\.
+ANY [^\"\n\\]|\\.
+
+%x incl bitlabel quotedstring
+
+%%
+ static int paren_open = 0;
+ static enum lexer_state lexer_state = EXPECT_OWNER;
+
+{SPACE}*{COMMENT}.* /* ignore */
+^{DOLLAR}TTL { lexer_state = PARSING_RDATA; return DOLLAR_TTL; }
+^{DOLLAR}ORIGIN { lexer_state = PARSING_RDATA; return DOLLAR_ORIGIN; }
+
+ /*
+ * Handle $INCLUDE directives. See
+ * http://dinosaur.compilertools.net/flex/flex_12.html#SEC12.
+ */
+^{DOLLAR}INCLUDE {
+ BEGIN(incl);
+}
+<incl>\n |
+<incl><<EOF>> {
+ int error_occurred = parser->error_occurred;
+ BEGIN(INITIAL);
+ zc_error("missing file name in $INCLUDE directive");
+ yy_set_bol(1); /* Set beginning of line, so "^" rules match. */
+ ++parser->line;
+ parser->error_occurred = error_occurred;
+}
+<incl>.+ {
+ char *tmp;
+ /*! \todo pointer to origin. */
+ void *origin = parser->origin;
+ /* domain_type *origin = parser->origin; */
+ int error_occurred = parser->error_occurred;
+
+ BEGIN(INITIAL);
+ if (include_stack_ptr >= MAXINCLUDES ) {
+ zc_error("includes nested too deeply, skipped (>%d)",
+ MAXINCLUDES);
+ } else {
+ FILE *input;
+
+ /* Remove trailing comment. */
+ tmp = strrchr(yytext, ';');
+ if (tmp) {
+ *tmp = '\0';
+ }
+ strip_string(yytext);
+
+ /* Parse origin for include file. */
+ tmp = strrchr(yytext, ' ');
+ if (!tmp) {
+ tmp = strrchr(yytext, '\t');
+ }
+ if (tmp) {
+ /* split the original yytext */
+ *tmp = '\0';
+ strip_string(yytext);
+
+ /*! \todo knot_dname_new_from_wire() (dname.h)
+ * which knot_node to pass as node?
+ */
+ knot_dname_t *dname;
+ dname = knot_dname_new_from_wire((uint8_t*)tmp + 1,
+ strlen(tmp + 1),
+ NULL);
+ if (!dname) {
+ zc_error("incorrect include origin '%s'",
+ tmp + 1);
+ } else {
+ /*! \todo insert to zonedb. */
+ /* origin = domain_table_insert(
+ parser->db->domains, dname); */
+ }
+ }
+
+ if (strlen(yytext) == 0) {
+ zc_error("missing file name in $INCLUDE directive");
+ } else if (!(input = fopen(yytext, "r"))) {
+ char ebuf[256];
+ zc_error("cannot open include file '%s': %s",
+ yytext, strerror_r(errno, ebuf, sizeof(ebuf)));
+ } else {
+ /* Initialize parser for include file. */
+ char *filename = strdup(yytext);
+ push_parser_state(input, scanner); /* Destroys yytext. */
+ parser->filename = filename;
+ parser->line = 1;
+ parser->origin = origin;
+ lexer_state = EXPECT_OWNER;
+ }
+ }
+
+ parser->error_occurred = error_occurred;
+}
+<INITIAL><<EOF>> {
+ yy_set_bol(1); /* Set beginning of line, so "^" rules match. */
+ if (include_stack_ptr == 0) {
+ // from: http://stackoverflow.com/questions/1756275/bison-end-of-file
+ static int once = 0;
+ once++;
+ if (once > 1) {
+ yyterminate();
+ } else {
+ return NL;
+ }
+ } else {
+ fclose(yyin);
+ pop_parser_state(scanner);
+ }
+}
+^{DOLLAR}{LETTER}+ { zc_warning("Unknown directive: %s", yytext); }
+{DOT} {
+ LEXOUT((". "));
+ return parse_token(scanner, '.', yytext, &lexer_state);
+}
+@ {
+ LEXOUT(("@ "));
+ return parse_token(scanner, '@', yytext, &lexer_state);
+}
+\\# {
+ LEXOUT(("\\# "));
+ return parse_token(scanner, URR, yytext, &lexer_state);
+}
+{NEWLINE} {
+ ++parser->line;
+ if (!paren_open) {
+ lexer_state = EXPECT_OWNER;
+ LEXOUT(("NL\n"));
+ return NL;
+ } else {
+ LEXOUT(("SP "));
+ return SP;
+ }
+}
+\( {
+ if (paren_open) {
+ zc_error("nested parentheses");
+ yyterminate();
+ }
+ LEXOUT(("( "));
+ paren_open = 1;
+ return SP;
+}
+\) {
+ if (!paren_open) {
+ zc_error("closing parentheses without opening parentheses");
+ yyterminate();
+ }
+ LEXOUT((") "));
+ paren_open = 0;
+ return SP;
+}
+{SPACE}+ {
+ if (!paren_open && lexer_state == EXPECT_OWNER) {
+ lexer_state = PARSING_TTL_CLASS_TYPE;
+ LEXOUT(("PREV "));
+ return PREV;
+ }
+ if (lexer_state == PARSING_OWNER) {
+ lexer_state = PARSING_TTL_CLASS_TYPE;
+ }
+ LEXOUT(("SP "));
+ return SP;
+}
+
+ /* Bitlabels. Strip leading and ending brackets. */
+\\\[ { BEGIN(bitlabel); }
+<bitlabel><<EOF>> {
+ zc_error("EOF inside bitlabel");
+ BEGIN(INITIAL);
+}
+<bitlabel>{BIT}* { yymore(); }
+<bitlabel>\n { ++parser->line; yymore(); }
+<bitlabel>\] {
+ BEGIN(INITIAL);
+ yytext[yyleng - 1] = '\0';
+ return parse_token(scanner, BITLAB, yytext, &lexer_state);
+}
+
+ /* Quoted strings. Strip leading and ending quotes. */
+\" { BEGIN(quotedstring); LEXOUT(("\" ")); }
+<quotedstring><<EOF>> {
+ zc_error("EOF inside quoted string");
+ BEGIN(INITIAL);
+}
+<quotedstring>{ANY}* { LEXOUT(("STR ")); yymore(); }
+<quotedstring>\n { ++parser->line; yymore(); }
+<quotedstring>\" {
+ LEXOUT(("\" "));
+ BEGIN(INITIAL);
+ yytext[yyleng - 1] = '\0';
+ return parse_token(scanner, STR, yytext, &lexer_state);
+}
+
+({ZONESTR}|\\.|\\\n)+ {
+ /* Any allowed word. */
+ return parse_token(scanner, STR, yytext, &lexer_state);
+}
+. {
+ zc_error("unknown character '%c' (\\%03d) seen - is this a zonefile?",
+ (int) yytext[0], (int) yytext[0]);
+}
+%%
+
+/*
+ * Analyze "word" to see if it matches an RR type, possibly by using
+ * the "TYPExxx" notation. If it matches, the corresponding token is
+ * returned and the TYPE parameter is set to the RR type value.
+ */
+static int
+rrtype_to_token(const char *word, uint16_t *type)
+{
+ uint16_t t = parser_rrtype_from_string(word);
+ if (t != 0) {
+ parser_rrtype_descriptor_t *entry = 0;
+ entry = parser_rrtype_descriptor_by_type(t);
+ *type = t;
+
+ /*! \todo entry should return associated token.
+ see nsd/dns.c */
+ return entry->token;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Remove \DDD constructs from the input. See RFC 1035, section 5.1.
+ */
+static size_t
+zoctet(char *text)
+{
+ /*
+ * s follows the string, p lags behind and rebuilds the new
+ * string
+ */
+ char *s;
+ char *p;
+
+ for (s = p = text; *s; ++s, ++p) {
+ assert(p <= s);
+ if (s[0] != '\\') {
+ /* Ordinary character. */
+ *p = *s;
+ } else if (isdigit((int)s[1]) && isdigit((int)s[2]) && isdigit((int)s[3])) {
+ /* \DDD escape. */
+ int val = (hexdigit_to_int(s[1]) * 100 +
+ hexdigit_to_int(s[2]) * 10 +
+ hexdigit_to_int(s[3]));
+ if (0 <= val && val <= 255) {
+ s += 3;
+ *p = val;
+ } else {
+ zc_warning("text escape \\DDD overflow");
+ *p = *++s;
+ }
+ } else if (s[1] != '\0') {
+ /* \X where X is any character, keep X. */
+ *p = *++s;
+ } else {
+ /* Trailing backslash, ignore it. */
+ zc_warning("trailing backslash ignored");
+ --p;
+ }
+ }
+ *p = '\0';
+ return p - text;
+}
+
+static int parse_token(void *scanner, int token, char *in_str,
+ enum lexer_state *lexer_state)
+{
+ size_t len = 0;
+ char *str = NULL;
+
+ struct yyguts_t *yyg = (struct yyguts_t *)scanner;
+
+ if (*lexer_state == EXPECT_OWNER) {
+ *lexer_state = PARSING_OWNER;
+ } else if (*lexer_state == PARSING_TTL_CLASS_TYPE) {
+ const char *t;
+ int token;
+ uint16_t rrclass;
+
+ /* type */
+ token = rrtype_to_token(in_str, &yylval->type);
+ if (token != 0) {
+ *lexer_state = PARSING_RDATA;
+ LEXOUT(("%d[%s] ", token, in_str));
+ return token;
+ }
+
+ /* class */
+ rrclass = parser_rrclass_from_string(in_str);
+ if (rrclass != 0) {
+ yylval->rclass = rrclass;
+ LEXOUT(("CLASS "));
+ return T_RRCLASS;
+ }
+
+ /* ttl */
+ yylval->ttl = strtottl(in_str, &t);
+ if (*t == '\0') {
+ LEXOUT(("TTL "));
+ return T_TTL;
+ }
+ }
+
+ str = strdup(yytext);
+ if (str == NULL) {
+ /* Out of memory */
+ ERR_ALLOC_FAILED;
+ return NO_MEM;
+ }
+ len = zoctet(str);
+
+ yylval->data.str = str;
+ assert(yylval->data.str != NULL);
+ yylval->data.len = len;
+
+ if (strcmp(yytext, ".") == 0) {
+ free(str);
+ yylval->data.str=".";
+ } else if (strcmp(str, "@") == 0) {
+ free(str);
+ yylval->data.str="@";
+ } else if (strcmp(str, "\\#") == 0) {
+ free(str);
+ yylval->data.str="\\#";
+ }
+
+ LEXOUT(("%d[%s] ", token, in_str));
+ return token;
+}
+
+/*
+ * Saves the file specific variables on the include stack.
+ */
+static void push_parser_state(FILE *input, void *scanner)
+{
+ struct yyguts_t *yyg = (struct yyguts_t *)scanner;
+ zparser_stack[include_stack_ptr].filename = parser->filename;
+ zparser_stack[include_stack_ptr].line = parser->line;
+ zparser_stack[include_stack_ptr].origin = parser->origin;
+ include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
+ yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE, scanner),
+ scanner);
+ ++include_stack_ptr;
+}
+
+/*
+ * Restores the file specific variables from the include stack.
+ */
+void pop_parser_state(void *scanner)
+{
+ struct yyguts_t *yyg = (struct yyguts_t *)scanner;
+ --include_stack_ptr;
+ parser->filename = zparser_stack[include_stack_ptr].filename;
+ parser->line = zparser_stack[include_stack_ptr].line;
+ parser->origin = zparser_stack[include_stack_ptr].origin;
+ yy_delete_buffer(YY_CURRENT_BUFFER, scanner);
+ yy_switch_to_buffer(include_stack[include_stack_ptr], scanner);
+}
diff --git a/src/zcompile/zparser.y b/src/zcompile/zparser.y
new file mode 100644
index 0000000..6f081e5
--- /dev/null
+++ b/src/zcompile/zparser.y
@@ -0,0 +1,1730 @@
+%{
+/*!
+ * \file zparser.y
+ *
+ * \author modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * notable changes: normal allocation, parser is reentrant.
+ * most of the code by NLnet Labs
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \brief yacc grammar for (DNS) zone files
+ *
+ * \addtogroup zoneparser
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#include "common.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "zcompile/parser-util.h"
+
+#include "libknot/libknot.h"
+#include "zcompile/zcompile.h"
+#include "zcompile/parser-descriptor.h"
+#include "zcompile/zcompile-error.h"
+#include "zparser.h"
+
+/* these need to be global, otherwise they cannot be used inside yacc */
+zparser_type *parser;
+
+#ifdef __cplusplus
+extern "C"
+#endif /* __cplusplus */
+int zp_wrap(void);
+
+/* this hold the nxt bits */
+static uint8_t nxtbits[16];
+static int dlv_warn = 1;
+
+/* 256 windows of 256 bits (32 bytes) */
+/* still need to reset the bastard somewhere */
+static uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE];
+
+/* hold the highest rcode seen in a NSEC rdata , BUG #106 */
+uint16_t nsec_highest_rcode;
+
+void zp_error(void *scanner, const char *message);
+int zp_lex(YYSTYPE *lvalp, void *scanner);
+
+/* helper functions */
+void zc_error(const char *fmt, ...);
+void zc_warning(const char *fmt, ...);
+void zc_error_prev_line(const char *fmt, ...);
+void zc_warning_prev_line(const char *fmt, ...);
+
+#define NSEC3
+#ifdef NSEC3
+/* parse nsec3 parameters and add the (first) rdata elements */
+static void
+nsec3_add_params(const char* hash_algo_str, const char* flag_str,
+ const char* iter_str, const char* salt_str, int salt_len);
+#endif /* NSEC3 */
+
+knot_dname_t *error_dname; //XXX used to be const
+knot_dname_t *error_domain;
+
+%}
+%union {
+ knot_dname_t *domain;
+ knot_dname_t *dname;
+ struct lex_data data;
+ uint32_t ttl;
+ uint16_t rclass;
+ uint16_t type;
+ uint16_t *unknown;
+}
+
+%pure-parser
+%parse-param {void *scanner}
+%lex-param {void *scanner}
+%name-prefix = "zp_"
+
+/*
+ * Tokens to represent the known RR types of DNS.
+ */
+%token <type> T_A T_NS T_MX T_TXT T_CNAME T_AAAA T_PTR T_NXT T_KEY T_SOA T_SIG
+%token <type> T_SRV T_CERT T_LOC T_MD T_MF T_MB T_MG T_MR T_NULL T_WKS T_HINFO
+%token <type> T_MINFO T_RP T_AFSDB T_X25 T_ISDN T_RT T_NSAP T_NSAP_PTR T_PX
+%token <type> T_GPOS T_EID T_NIMLOC T_ATMA T_NAPTR T_KX T_A6 T_DNAME T_SINK
+%token <type> T_OPT T_APL T_UINFO T_UID T_GID T_UNSPEC T_TKEY T_TSIG T_IXFR
+%token <type> T_AXFR T_MAILB T_MAILA T_DS T_DLV T_SSHFP T_RRSIG T_NSEC T_DNSKEY
+%token <type> T_SPF T_NSEC3 T_IPSECKEY T_DHCID T_NSEC3PARAM
+
+/* other tokens */
+%token DOLLAR_TTL DOLLAR_ORIGIN NL SP NO_MEM
+%token <data> STR PREV BITLAB
+%token <ttl> T_TTL
+%token <rclass> T_RRCLASS
+
+/* unknown RRs */
+%token URR
+%token <type> T_UTYPE
+
+%type <type> type_and_rdata
+%type <domain> owner dname abs_dname
+%type <dname> rel_dname label
+%type <data> wire_dname wire_abs_dname wire_rel_dname wire_label
+%type <data> concatenated_str_seq str_sp_seq str_dot_seq dotted_str
+%type <data> nxt_seq nsec_more
+%type <unknown> rdata_unknown
+
+%%
+lines: /* empty file */
+ | lines line
+ ;
+
+line: NL
+ | sp NL
+ | NO_MEM {
+ zc_error_prev_line("Parser ran out of memory!");
+ YYABORT;
+ }
+ | PREV NL {} /* Lines containing only whitespace. */
+ | ttl_directive
+ {
+ parser->error_occurred = 0;
+ }
+ | origin_directive
+ {
+ parser->error_occurred = 0;
+ }
+ | rr
+ { /* rr should be fully parsed */
+ if (!parser->error_occurred) {
+ /*!< \todo assign error to error occurred */
+ /*! \todo Make sure this does not crash */
+ if (parser->current_rrset->owner == NULL) {
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ knot_rdata_t *tmp_rdata = knot_rdata_new();
+ if (tmp_rdata == NULL) {
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+
+ if (knot_rdata_set_items(tmp_rdata,
+ parser->temporary_items,
+ parser->rdata_count) != 0) {
+ knot_rdata_free(&tmp_rdata);
+ knot_rrset_deep_free(&(parser->current_rrset), 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone), 1);
+ YYABORT;
+ }
+
+ assert(parser->current_rrset->rdata == NULL);
+ if (knot_rrset_add_rdata(parser->current_rrset, tmp_rdata)
+ != 0) {
+ fprintf(stderr, "Could not add rdata!\n");
+ }
+// tmp_rdata->next = tmp_rdata;
+// parser->current_rrset->rdata = tmp_rdata;
+
+ if (!knot_dname_is_fqdn(parser->current_rrset->owner)) {
+ knot_dname_t *tmp_dname =
+ knot_dname_cat(parser->current_rrset->owner,
+ parser->root_domain);
+ if (tmp_dname == NULL) {
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+// knot_rrset_set_owner(parser->current_rrset, tmp_dname);
+ }
+
+ assert(parser->current_rrset->owner != NULL);
+ knot_dname_retain(parser->current_rrset->owner);
+ int ret = 0;
+ if ((ret = process_rr()) != 0) {
+ char *name =
+ knot_dname_to_str(parser->current_rrset->owner);
+ fprintf(stderr, "Error: could not process RRSet\n"
+ "owner: %s reason: %s\n",
+ name,
+ error_to_str(knot_zcompile_error_msgs, ret));
+ free(name);
+
+ /* If the owner is not already in the table, free it. */
+// if (dnslib_dname_table_find_dname(parser->dname_table,
+// parser->current_rrset->owner) == NULL) {
+// dnslib_dname_free(&parser->
+// current_rrset->owner);
+// } /* This would never happen */
+
+ if (ret == KNOTDZCOMPILE_EBADSOA) {
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ } else {
+ YYABORT;
+ /* Free rdata, it will not be added
+ * and hence cannot be
+ * freed with rest of the zone. */
+/* knot_rdata_deep_free(&tmp_rdata,
+ parser->
+ current_rrset->type,
+ 0); */
+ }
+ }
+ } else {
+ /* Error occured. This could either be lack of memory, or one
+ * of the converting function was not able to convert. */
+ if (parser->error_occurred == KNOTDZCOMPILE_ENOMEM) {
+ /* Ran out of memory in converting functions. */
+ fprintf(stderr, "Parser ran out "
+ "of memory, aborting!\n");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ }
+
+// printf("Current rrset name: %p (%s)\n", parser->current_rrset->owner->name,
+// knot_dname_to_str(parser->current_rrset->owner));
+// getchar();
+
+// knot_dname_release(parser->current_rrset->owner);
+
+ parser->current_rrset->type = 0;
+ parser->rdata_count = 0;
+ parser->current_rrset->rdata = NULL;
+ parser->error_occurred = 0;
+ }
+ | error NL
+ ;
+
+/* needed to cope with ( and ) in arbitary places */
+sp: SP
+ | sp SP
+ ;
+
+trail: NL
+ | sp NL
+ ;
+
+ttl_directive: DOLLAR_TTL sp STR trail
+ {
+ parser->default_ttl = zparser_ttl2int($3.str,
+ &(parser->error_occurred));
+ if (parser->error_occurred == 1) {
+ parser->default_ttl = DEFAULT_TTL;
+ parser->error_occurred = 0;
+ }
+
+ free($3.str);
+ }
+ ;
+
+
+
+origin_directive: DOLLAR_ORIGIN sp abs_dname trail
+ {
+ knot_node_t *origin_node = knot_node_new($3 ,NULL, 0);
+ if (parser->origin != NULL) {
+// knot_node_free(&parser->origin, 1);
+ }
+ parser->origin = origin_node;
+ }
+ | DOLLAR_ORIGIN sp rel_dname trail
+ {
+ zc_error_prev_line("$ORIGIN directive requires"
+ "absolute domain name");
+ }
+ ;
+
+rr: owner classttl type_and_rdata
+ {
+ /* Save the pointer, it might get freed! */
+ parser->current_rrset->owner = $1;
+// parser->current_rrset->owner = $1;
+// printf("new owner assigned: %p\n", $1);
+ parser->current_rrset->type = $3;
+ }
+ ;
+
+owner: dname sp
+ {
+// char *name = knot_dname_to_str($1);
+// printf("Totally new dname: %p %s\n", $1,
+// name);
+// free(name);
+ if (parser->prev_dname != NULL) {
+ // knot_dname_release(parser->prev_dname);
+ }
+ parser->prev_dname = $1;//knot_dname_deep_copy($1);
+// knot_dname_retain(parser->prev_dname);
+ $$ = $1;
+ }
+ | PREV
+ {
+// printf("Name from prev_dname!: %p %s\n", parser->prev_dname,
+// knot_dname_to_str(parser->prev_dname));
+ knot_dname_retain(parser->prev_dname);
+ $$ = parser->prev_dname;//knot_dname_deep_copy(parser->prev_dname);
+ }
+ ;
+
+classttl: /* empty - fill in the default, def. ttl and IN class */
+ {
+ parser->current_rrset->ttl = parser->default_ttl;
+ parser->current_rrset->rclass = parser->default_class;
+ }
+ | T_RRCLASS sp /* no ttl */
+ {
+ parser->current_rrset->ttl = parser->default_ttl;
+ parser->current_rrset->rclass = $1;
+ }
+ | T_TTL sp /* no class */
+ {
+ parser->current_rrset->ttl = $1;
+ parser->current_rrset->rclass = parser->default_class;
+ }
+ | T_TTL sp T_RRCLASS sp /* the lot */
+ {
+ parser->current_rrset->ttl = $1;
+ parser->current_rrset->rclass = $3;
+ }
+ | T_RRCLASS sp T_TTL sp /* the lot - reversed */
+ {
+ parser->current_rrset->ttl = $3;
+ parser->current_rrset->rclass = $1;
+ }
+ ;
+
+dname: abs_dname
+ | rel_dname
+ {
+ if ($1 == error_dname) {
+ $$ = error_domain;
+ } else if ($1->size + parser->origin->owner->size - 1 >
+ MAXDOMAINLEN) {
+ zc_error("domain name exceeds %d character limit",
+ MAXDOMAINLEN);
+ $$ = error_domain;
+ } else {
+ $$ = knot_dname_cat($1,
+ parser->origin->owner);
+// printf("leak: %s\n", knot_dname_to_str($$));
+// getchar();
+ }
+ }
+ ;
+
+abs_dname: '.'
+ {
+ $$ = parser->root_domain;
+ /* TODO how about concatenation now? */
+ }
+ | '@'
+ {
+ $$ = parser->origin->owner;
+ }
+ | rel_dname '.'
+ {
+ if ($1 != error_dname) {
+ $$ = $1;
+ } else {
+ $$ = error_domain;
+ }
+ }
+ ;
+
+label: STR
+ {
+ if ($1.len > MAXLABELLEN) {
+ zc_error("label exceeds %d character limit", MAXLABELLEN);
+ $$ = error_dname;
+ } else {
+// printf("%s\n", $1.str);
+ $$ = knot_dname_new_from_str($1.str, $1.len, NULL);
+// printf("Creating new (label): %s %p\n", $1.str, $$);
+// printf("new: %p %s\n", $$, $1.str);
+ $$->ref.count = 0;
+ }
+
+ free($1.str);
+
+ }
+ | BITLAB
+ {
+ zc_error("bitlabels are not supported."
+ "RFC2673 has status experimental.");
+ $$ = error_dname;
+ }
+ ;
+
+rel_dname: label
+ | rel_dname '.' label
+ {
+ if ($1 == error_dname || $3 == error_dname) {
+ $$ = error_dname;
+ } else if ($1->size + $3->size - 1 > MAXDOMAINLEN) {
+ zc_error("domain name exceeds %d character limit",
+ MAXDOMAINLEN);
+ $$ = error_dname;
+ } else {
+ $$ = knot_dname_cat($1, $3);
+// knot_dname_release($1); /*!< \todo check! */
+ knot_dname_free(&$3);
+ }
+ }
+ ;
+
+/*
+ * Some dnames in rdata are handled as opaque blobs
+ */
+
+wire_dname: wire_abs_dname
+ | wire_rel_dname
+ ;
+
+wire_abs_dname: '.'
+ {
+ char *result = malloc(2 * sizeof(char));
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ result[0] = 0;
+ result[1] = '\0';
+ $$.str = result;
+ $$.len = 1;
+ }
+ | wire_rel_dname '.'
+ {
+ char *result = malloc($1.len + 2 * sizeof(char));
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(result, $1.str, $1.len);
+ result[$1.len] = 0;
+ result[$1.len+1] = '\0';
+ $$.str = result;
+ $$.len = $1.len + 1;
+
+ free($1.str);
+;
+ }
+ ;
+
+wire_label: STR
+ {
+ char *result = malloc($1.len + sizeof(char));
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+
+ if ($1.len > MAXLABELLEN)
+ zc_error("label exceeds %d character limit", MAXLABELLEN);
+
+ /* make label anyway */
+ result[0] = $1.len;
+ memcpy(result+1, $1.str, $1.len);
+
+ $$.str = result;
+ $$.len = $1.len + 1;
+
+ free($1.str);
+ }
+ ;
+
+wire_rel_dname: wire_label
+ | wire_rel_dname '.' wire_label
+ {
+ if ($1.len + $3.len - 3 > MAXDOMAINLEN)
+ zc_error("domain name exceeds %d character limit",
+ MAXDOMAINLEN);
+
+ /* make dname anyway */
+ $$.len = $1.len + $3.len;
+ $$.str = malloc($$.len + sizeof(char));
+ if ($$.str == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy($$.str, $1.str, $1.len);
+ memcpy($$.str + $1.len, $3.str, $3.len);
+ $$.str[$$.len] = '\0';
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+
+
+str_seq: STR
+ {
+ zadd_rdata_txt_wireformat(zparser_conv_text($1.str, $1.len), 1);
+
+ free($1.str);
+ }
+ | str_seq sp STR
+ {
+ zadd_rdata_txt_wireformat(zparser_conv_text($3.str, $3.len), 0);
+// zc_warning("multiple TXT entries are currently not supported!");
+
+ free($3.str);
+ }
+ ;
+
+/*
+ * Generate a single string from multiple STR tokens, separated by
+ * spaces or dots.
+ */
+concatenated_str_seq: STR
+ | '.'
+ {
+ $$.len = 1;
+ $$.str = strdup(".");
+ }
+ | concatenated_str_seq sp STR
+ {
+ $$.len = $1.len + $3.len + 1;
+ $$.str = malloc($$.len + 1);
+ if ($$.str == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+
+ memcpy($$.str, $1.str, $1.len);
+ memcpy($$.str + $1.len, " ", 1);
+ memcpy($$.str + $1.len + 1, $3.str, $3.len);
+ $$.str[$$.len] = '\0';
+
+ free($1.str);
+ free($3.str);
+ }
+ | concatenated_str_seq '.' STR
+ {
+ $$.len = $1.len + $3.len + 1;
+ $$.str = malloc($$.len + 1);
+ if ($$.str == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy($$.str, $1.str, $1.len);
+ memcpy($$.str + $1.len, ".", 1);
+ memcpy($$.str + $1.len + 1, $3.str, $3.len);
+
+ free($1.str);
+ free($3.str);
+
+ $$.str[$$.len] = '\0';
+ }
+ ;
+
+/* used to convert a nxt list of types */
+nxt_seq: STR
+ {
+ uint16_t type = knot_rrtype_from_string($1.str);
+ if (type != 0 && type < 128) {
+ set_bit(nxtbits, type);
+ } else {
+ zc_error("bad type %d in NXT record", (int) type);
+ }
+
+ free($1.str);
+ }
+ | nxt_seq sp STR
+ {
+ uint16_t type = knot_rrtype_from_string($3.str);
+ if (type != 0 && type < 128) {
+ set_bit(nxtbits, type);
+ } else {
+ zc_error("bad type %d in NXT record", (int) type);
+ }
+
+ free($3.str);
+ }
+ ;
+
+nsec_more: SP nsec_more
+ {
+ }
+ | NL
+ {
+ }
+ | STR nsec_seq
+ {
+ uint16_t type = knot_rrtype_from_string($1.str);
+ if (type != 0) {
+ if (type > nsec_highest_rcode) {
+ nsec_highest_rcode = type;
+ }
+ set_bitnsec(nsecbits, type);
+ } else {
+ zc_error("bad type %d in NSEC record", (int) type);
+ }
+
+ free($1.str);
+ }
+ ;
+
+nsec_seq: NL
+ | SP nsec_more
+ ;
+
+/*
+ * Sequence of STR tokens separated by spaces. The spaces are not
+ * preserved during concatenation.
+ */
+str_sp_seq: STR
+ | str_sp_seq sp STR
+ {
+ char *result = malloc($1.len + $3.len + 1);
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ fprintf(stderr, "Parser ran out of memory, aborting!\n");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(result, $1.str, $1.len);
+ memcpy(result + $1.len, $3.str, $3.len);
+ $$.str = result;
+ $$.len = $1.len + $3.len;
+ $$.str[$$.len] = '\0';
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+/*
+ * Sequence of STR tokens separated by dots. The dots are not
+ * preserved during concatenation.
+ */
+str_dot_seq: STR
+ | str_dot_seq '.' STR
+ {
+ char *result = malloc($1.len + $3.len + 1);
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ fprintf(stderr, "Parser ran out of memory, aborting!\n");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(result, $1.str, $1.len);
+ memcpy(result + $1.len, $3.str, $3.len);
+ $$.str = result;
+ $$.len = $1.len + $3.len;
+ $$.str[$$.len] = '\0';
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+/*
+ * A string that can contain dots.
+ */
+dotted_str: STR
+ | '.'
+ {
+ $$.str = ".";
+ $$.len = 1;
+ }
+ | dotted_str '.'
+ {
+ char *result = malloc($1.len + 2);
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ fprintf(stderr, "Parser ran out of memory, aborting!\n");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(result, $1.str, $1.len);
+ result[$1.len] = '.';
+ $$.str = result;
+ $$.len = $1.len + 1;
+ $$.str[$$.len] = '\0';
+
+ free($1.str);
+ }
+ | dotted_str '.' STR
+ {
+ char *result = malloc($1.len + $3.len + 2);
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ fprintf(stderr, "Parser ran out of memory, aborting!\n");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(result, $1.str, $1.len);
+ result[$1.len] = '.';
+ memcpy(result + $1.len + 1, $3.str, $3.len);
+ $$.str = result;
+ $$.len = $1.len + $3.len + 1;
+ $$.str[$$.len] = '\0';
+
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+/* define what we can parse */
+type_and_rdata:
+ /*
+ * All supported RR types. We don't support NULL and types marked obsolete.
+ */
+ T_A sp rdata_a
+ | T_A sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NS sp rdata_domain_name
+ | T_NS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_MD sp rdata_domain_name { zc_warning_prev_line("MD is obsolete"); }
+ | T_MD sp rdata_unknown
+ {
+ zc_warning_prev_line("MD is obsolete");
+ $$ = $1; parse_unknown_rdata($1, $3);
+ }
+ | T_MF sp rdata_domain_name { zc_warning_prev_line("MF is obsolete"); }
+ | T_MF sp rdata_unknown
+ {
+ zc_warning_prev_line("MF is obsolete");
+ $$ = $1;
+ parse_unknown_rdata($1, $3);
+ }
+ | T_CNAME sp rdata_domain_name
+ | T_CNAME sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_SOA sp rdata_soa
+ | T_SOA sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_MB sp rdata_domain_name { zc_warning_prev_line("MB is obsolete"); }
+ | T_MB sp rdata_unknown
+ {
+ zc_warning_prev_line("MB is obsolete");
+ $$ = $1;
+ parse_unknown_rdata($1, $3);
+ }
+ | T_MG sp rdata_domain_name
+ | T_MG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_MR sp rdata_domain_name
+ | T_MR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ /* NULL */
+ | T_WKS sp rdata_wks
+ | T_WKS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_PTR sp rdata_domain_name
+ | T_PTR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_HINFO sp rdata_hinfo
+ | T_HINFO sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_MINFO sp rdata_minfo /* Experimental */
+ | T_MINFO sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_MX sp rdata_mx
+ | T_MX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_TXT sp rdata_txt
+ | T_TXT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_SPF sp rdata_txt
+ | T_SPF sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_RP sp rdata_rp /* RFC 1183 */
+ | T_RP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_AFSDB sp rdata_afsdb /* RFC 1183 */
+ | T_AFSDB sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_X25 sp rdata_x25 /* RFC 1183 */
+ | T_X25 sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_ISDN sp rdata_isdn /* RFC 1183 */
+ | T_ISDN sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_IPSECKEY sp rdata_ipseckey /* RFC 4025 */
+ | T_IPSECKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_DHCID sp rdata_dhcid
+ | T_DHCID sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_RT sp rdata_rt /* RFC 1183 */
+ | T_RT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NSAP sp rdata_nsap /* RFC 1706 */
+ | T_NSAP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_SIG sp rdata_rrsig
+ | T_SIG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_KEY sp rdata_dnskey
+ | T_KEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_PX sp rdata_px /* RFC 2163 */
+ | T_PX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_AAAA sp rdata_aaaa
+ | T_AAAA sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_LOC sp rdata_loc
+ | T_LOC sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NXT sp rdata_nxt
+ | T_NXT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_SRV sp rdata_srv
+ | T_SRV sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NAPTR sp rdata_naptr /* RFC 2915 */
+ | T_NAPTR sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_KX sp rdata_kx /* RFC 2230 */
+ | T_KX sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_CERT sp rdata_cert /* RFC 2538 */
+ | T_CERT sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_DNAME sp rdata_domain_name /* RFC 2672 */
+ | T_DNAME sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_APL trail /* RFC 3123 */
+ | T_APL sp rdata_apl /* RFC 3123 */
+ | T_APL sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_DS sp rdata_ds
+ | T_DS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_DLV sp rdata_dlv
+ {
+ if (dlv_warn) {
+ dlv_warn = 0;
+ zc_warning_prev_line("DLV is experimental");
+ }
+ }
+ | T_DLV sp rdata_unknown
+ {
+ if (dlv_warn) {
+ dlv_warn = 0;
+ zc_warning_prev_line("DLV is experimental");
+ }
+ $$ = $1;
+ parse_unknown_rdata($1, $3);
+ }
+ | T_SSHFP sp rdata_sshfp
+ | T_SSHFP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_RRSIG sp rdata_rrsig
+ | T_RRSIG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NSEC sp rdata_nsec
+ | T_NSEC sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NSEC3 sp rdata_nsec3
+ | T_NSEC3 sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_NSEC3PARAM sp rdata_nsec3_param
+ | T_NSEC3PARAM sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_DNSKEY sp rdata_dnskey
+ | T_DNSKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+ | T_UTYPE sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); }
+
+ | STR error NL
+ {
+ zc_error_prev_line("unrecognized RR type '%s'", $1.str);
+ free($1.str);
+ }
+ | NO_MEM
+ {
+ zc_error_prev_line("parser ran out of memory!");
+ YYABORT;
+ }
+ ;
+
+/*
+ *
+ * below are all the definition for all the different rdata
+ *
+ */
+
+rdata_a: dotted_str trail
+ {
+ zadd_rdata_wireformat(zparser_conv_a($1.str));
+ free($1.str);
+ }
+ ;
+
+rdata_domain_name: dname trail
+ {
+ /* convert a single dname record */
+ if ($1 != NULL) {
+ if (!knot_dname_is_fqdn($1)) {
+ knot_dname_cat($1, parser->root_domain);
+// parser->current_rrset->owner =
+// knot_dname_cat($1, parser->root_domain);
+ }
+ }
+ zadd_rdata_domain($1);
+ }
+ ;
+
+rdata_soa: dname sp dname sp STR sp STR sp STR sp STR sp STR trail
+ {
+ /* convert the soa data */
+ if (!knot_dname_is_fqdn($1)) {
+ knot_dname_cat($1, parser->root_domain);
+// parser->current_rrset->owner =
+// knot_dname_cat($1, parser->root_domain);
+
+ }
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+// parser->current_rrset->owner =
+// knot_dname_cat($3, parser->root_domain);
+
+ }
+ zadd_rdata_domain($1); /* prim. ns */
+ zadd_rdata_domain($3); /* email */
+ zadd_rdata_wireformat(zparser_conv_serial($5.str)); /* serial */
+ zadd_rdata_wireformat(zparser_conv_period($7.str)); /* refresh */
+ zadd_rdata_wireformat(zparser_conv_period($9.str)); /* retry */
+ zadd_rdata_wireformat(zparser_conv_period($11.str)); /* expire */
+ zadd_rdata_wireformat(zparser_conv_period($13.str)); /* minimum */
+
+ free($5.str);
+ free($7.str);
+ free($9.str);
+ free($11.str);
+ free($13.str);
+ }
+ ;
+
+rdata_wks: dotted_str sp STR sp concatenated_str_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_a($1.str)); /* address */
+ zadd_rdata_wireformat(zparser_conv_services($3.str, $5.str));
+ /* protocol and services */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ }
+ ;
+
+rdata_hinfo: STR sp STR trail
+ {
+ zadd_rdata_wireformat(zparser_conv_text($1.str, $1.len)); /* CPU */
+ zadd_rdata_wireformat(zparser_conv_text($3.str, $3.len)); /* OS*/
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+rdata_minfo: dname sp dname trail
+ {
+ if (!knot_dname_is_fqdn($1)) {
+
+ knot_dname_cat($1, parser->root_domain);
+
+ }
+ if (!knot_dname_is_fqdn($3)) {
+
+ knot_dname_cat($3, parser->root_domain);
+
+ }
+
+ /* convert a single dname record */
+ zadd_rdata_domain($1);
+ zadd_rdata_domain($3);
+ }
+ ;
+
+rdata_mx: STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* priority */
+ zadd_rdata_domain($3); /* MX host */
+
+ free($1.str);
+ }
+ ;
+
+rdata_txt: str_seq trail
+ {
+ ; //zadd_rdata_txt_clean_wireformat();
+ }
+ ;
+
+/* RFC 1183 */
+rdata_rp: dname sp dname trail
+ {
+ if (!knot_dname_is_fqdn($1)) {
+ knot_dname_cat($1, parser->root_domain);
+ }
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+
+ zadd_rdata_domain($1); /* mbox d-name */
+ zadd_rdata_domain($3); /* txt d-name */
+ }
+ ;
+
+/* RFC 1183 */
+rdata_afsdb: STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* subtype */
+ zadd_rdata_domain($3); /* domain name */
+
+ free($1.str);
+ }
+ ;
+
+/* RFC 1183 */
+rdata_x25: STR trail
+ {
+ zadd_rdata_wireformat(zparser_conv_text($1.str, $1.len));
+ /* X.25 address. */
+
+ free($1.str);
+ }
+ ;
+
+/* RFC 1183 */
+rdata_isdn: STR trail
+ {
+ zadd_rdata_wireformat(zparser_conv_text($1.str, $1.len));
+ /* address */
+
+ free($1.str);
+ }
+ | STR sp STR trail
+ {
+ zadd_rdata_wireformat(zparser_conv_text($1.str, $1.len));
+ /* address */
+ zadd_rdata_wireformat(zparser_conv_text($3.str, $3.len));
+ /* sub-address */
+
+ free($1.str);
+ free($3.str);
+ }
+ ;
+
+/* RFC 1183 */
+rdata_rt: STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* preference */
+ zadd_rdata_domain($3); /* intermediate host */
+
+ free($1.str);
+ }
+ ;
+
+/* RFC 1706 */
+rdata_nsap: str_dot_seq trail
+ {
+ /* String must start with "0x" or "0X". */
+ if (strncasecmp($1.str, "0x", 2) != 0) {
+ zc_error_prev_line("NSAP rdata must start with '0x'");
+ } else {
+ zadd_rdata_wireformat(zparser_conv_hex($1.str + 2,
+ $1.len - 2));
+ /* NSAP */
+ }
+
+ free($1.str);
+ }
+ ;
+
+/* RFC 2163 */
+rdata_px: STR sp dname sp dname trail
+ {
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+ if (!knot_dname_is_fqdn($5)) {
+ knot_dname_cat($5, parser->root_domain);
+ }
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* preference */
+ zadd_rdata_domain($3); /* MAP822 */
+ zadd_rdata_domain($5); /* MAPX400 */
+
+ free($1.str);
+ }
+ ;
+
+rdata_aaaa: dotted_str trail
+ {
+ zadd_rdata_wireformat(zparser_conv_aaaa($1.str));
+ /* IPv6 address */
+
+ free($1.str);
+ }
+ ;
+
+rdata_loc: concatenated_str_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_loc($1.str)); /* Location */
+
+ free($1.str);
+ }
+ ;
+
+rdata_nxt: dname sp nxt_seq trail
+ {
+ if (!knot_dname_is_fqdn($1)) {
+ knot_dname_cat($1, parser->root_domain);
+ }
+ zadd_rdata_domain($1); /* nxt name */
+ zadd_rdata_wireformat(zparser_conv_nxt(nxtbits)); /* nxt bitlist */
+ memset(nxtbits, 0, sizeof(nxtbits));
+ }
+ ;
+
+rdata_srv: STR sp STR sp STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($7)) {
+ knot_dname_cat($7, parser->root_domain);
+
+ }
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* prio */
+ zadd_rdata_wireformat(zparser_conv_short($3.str)); /* weight */
+ zadd_rdata_wireformat(zparser_conv_short($5.str)); /* port */
+ zadd_rdata_domain($7); /* target name */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ }
+ ;
+
+/* RFC 2915 */
+rdata_naptr: STR sp STR sp STR sp STR sp STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($11)) {
+ knot_dname_cat($11, parser->root_domain);
+
+ }
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* order */
+ zadd_rdata_wireformat(zparser_conv_short($3.str)); /* preference */
+ zadd_rdata_wireformat(zparser_conv_text($5.str, $5.len));
+ /* flags */
+ zadd_rdata_wireformat(zparser_conv_text($7.str, $7.len));
+ /* service */
+ zadd_rdata_wireformat(zparser_conv_text($9.str, $9.len));
+ /* regexp */
+ zadd_rdata_domain($11); /* target name */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ free($9.str);
+ }
+ ;
+
+/* RFC 2230 */
+rdata_kx: STR sp dname trail
+ {
+ if (!knot_dname_is_fqdn($3)) {
+ knot_dname_cat($3, parser->root_domain);
+ }
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* preference */
+ zadd_rdata_domain($3); /* exchanger */
+
+ free($1.str);
+ }
+ ;
+
+/* RFC 2538 */
+rdata_cert: STR sp STR sp STR sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_certificate_type($1.str));
+ /* type */
+ zadd_rdata_wireformat(zparser_conv_short($3.str)); /* key tag */
+ zadd_rdata_wireformat(zparser_conv_algorithm($5.str));
+ /* algorithm */
+ zadd_rdata_wireformat(zparser_conv_b64($7.str));
+ /* certificate or CRL */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ }
+ ;
+
+/* RFC 3123 */
+rdata_apl: rdata_apl_seq trail
+ ;
+
+rdata_apl_seq: dotted_str
+ {
+ zadd_rdata_wireformat(zparser_conv_apl_rdata($1.str));
+
+ free($1.str);
+ }
+ | rdata_apl_seq sp dotted_str
+ {
+ zadd_rdata_wireformat(zparser_conv_apl_rdata($3.str));
+
+ free($3.str);
+ }
+ ;
+
+rdata_ds: STR sp STR sp STR sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* keytag */
+ zadd_rdata_wireformat(zparser_conv_algorithm($3.str)); /* alg */
+ zadd_rdata_wireformat(zparser_conv_byte($5.str)); /* type */
+ zadd_rdata_wireformat(zparser_conv_hex($7.str, $7.len)); /* hash */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ }
+ ;
+
+rdata_dlv: STR sp STR sp STR sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* keytag */
+ zadd_rdata_wireformat(zparser_conv_algorithm($3.str)); /* alg */
+ zadd_rdata_wireformat(zparser_conv_byte($5.str)); /* type */
+ zadd_rdata_wireformat(zparser_conv_hex($7.str, $7.len)); /* hash */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ }
+ ;
+
+rdata_sshfp: STR sp STR sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_byte($1.str)); /* alg */
+ zadd_rdata_wireformat(zparser_conv_byte($3.str)); /* fp type */
+ zadd_rdata_wireformat(zparser_conv_hex($5.str, $5.len)); /* hash */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ }
+ ;
+
+rdata_dhcid: str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_b64($1.str)); /* data blob */
+
+ free($1.str);
+ }
+ ;
+
+rdata_rrsig: STR sp STR sp STR sp STR sp STR sp STR
+ sp STR sp wire_dname sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_rrtype($1.str));
+ /* rr covered */
+ zadd_rdata_wireformat(zparser_conv_algorithm($3.str)); /* alg */
+ zadd_rdata_wireformat(zparser_conv_byte($5.str)); /* # labels */
+ zadd_rdata_wireformat(zparser_conv_period($7.str));
+ /* # orig TTL */
+ zadd_rdata_wireformat(zparser_conv_time($9.str)); /* sig exp */
+ zadd_rdata_wireformat(zparser_conv_time($11.str)); /* sig inc */
+ zadd_rdata_wireformat(zparser_conv_short($13.str)); /* key id */
+/* zadd_rdata_wireformat(zparser_conv_dns_name((const uint8_t*)
+ $15.str,
+ $15.len));*/
+ knot_dname_t *dname =
+ knot_dname_new_from_wire((uint8_t *)$15.str, $15.len, NULL);
+ if (dname == NULL) {
+ parser->error_occurred = KNOTDZCOMPILE_ENOMEM;
+ } else {
+ knot_dname_cat(dname, parser->root_domain);
+ }
+
+ zadd_rdata_domain(dname);
+ /* sig name */
+ zadd_rdata_wireformat(zparser_conv_b64($17.str)); /* sig data */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ free($9.str);
+ free($11.str);
+ free($13.str);
+ free($15.str);
+ free($17.str);
+ }
+ ;
+
+rdata_nsec: wire_dname nsec_seq
+ {
+/* zadd_rdata_wireformat(zparser_conv_dns_name((const uint8_t*)
+ $1.str,
+ $1.len));*/
+
+ knot_dname_t *dname =
+ knot_dname_new_from_wire((uint8_t *)$1.str, $1.len, NULL);
+
+ free($1.str);
+
+ knot_dname_cat(dname, parser->root_domain);
+
+ zadd_rdata_domain(dname);
+ /* nsec name */
+ zadd_rdata_wireformat(zparser_conv_nsec(nsecbits));
+ /* nsec bitlist */
+ memset(nsecbits, 0, sizeof(nsecbits));
+ nsec_highest_rcode = 0;
+ }
+ ;
+
+rdata_nsec3: STR sp STR sp STR sp STR sp STR nsec_seq
+ {
+#ifdef NSEC3
+ nsec3_add_params($1.str, $3.str, $5.str, $7.str, $7.len);
+
+/* knot_dname_t *dname =
+ knot_dname_new_from_str($9.str, $9.len, NULL);
+
+ zadd_rdata_domain(dname); */
+
+ zadd_rdata_wireformat(zparser_conv_b32($9.str));
+ /* next hashed name */
+ zadd_rdata_wireformat(zparser_conv_nsec(nsecbits));
+ /* nsec bitlist */
+ memset(nsecbits, 0, sizeof(nsecbits));
+ nsec_highest_rcode = 0;
+#else
+ zc_error_prev_line("nsec3 not supported");
+#endif /* NSEC3 */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ free($9.str);
+ }
+ ;
+
+rdata_nsec3_param: STR sp STR sp STR sp STR trail
+ {
+#ifdef NSEC3
+ nsec3_add_params($1.str, $3.str, $5.str, $7.str, $7.len);
+#else
+ zc_error_prev_line("nsec3 not supported");
+#endif /* NSEC3 */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ }
+ ;
+
+rdata_dnskey: STR sp STR sp STR sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_short($1.str)); /* flags */
+ zadd_rdata_wireformat(zparser_conv_byte($3.str)); /* proto */
+ zadd_rdata_wireformat(zparser_conv_algorithm($5.str)); /* alg */
+ zadd_rdata_wireformat(zparser_conv_b64($7.str)); /* hash */
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+ }
+ ;
+
+rdata_ipsec_base: STR sp STR sp STR sp dotted_str
+ {
+ knot_dname_t* name = 0;
+ zadd_rdata_wireformat(zparser_conv_byte($1.str)); /* precedence */
+ zadd_rdata_wireformat(zparser_conv_byte($3.str));
+ /* gateway type */
+ zadd_rdata_wireformat(zparser_conv_byte($5.str)); /* algorithm */
+ switch(atoi($3.str)) {
+ case IPSECKEY_NOGATEWAY:
+ zadd_rdata_wireformat(alloc_rdata_init("", 0));
+ break;
+ case IPSECKEY_IP4:
+ zadd_rdata_wireformat(zparser_conv_a($7.str));
+ break;
+ case IPSECKEY_IP6:
+ zadd_rdata_wireformat(zparser_conv_aaaa($7.str));
+ break;
+ case IPSECKEY_DNAME:
+ /* convert and insert the dname */
+ if(strlen($7.str) == 0)
+ zc_error_prev_line("IPSECKEY must specify"
+ "gateway name");
+ name = knot_dname_new_from_wire((uint8_t*)$7.str + 1,
+ strlen($7.str + 1),
+ NULL);
+ if(!name) {
+ zc_error_prev_line("IPSECKEY bad gateway"
+ "dname %s", $7.str);
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ if($7.str[strlen($7.str)-1] != '.') {
+ knot_dname_t* tmpd =
+ knot_dname_new_from_wire(name->name,
+ name->size,
+ NULL);
+ if (tmpd == NULL) {
+ zc_error_prev_line("Could not create dname!");
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ name = knot_dname_cat(tmpd,
+ knot_node_parent(parser->origin, 0)->owner);
+ }
+
+ free($1.str);
+ free($3.str);
+ free($5.str);
+ free($7.str);
+
+ uint8_t* dncpy = malloc(sizeof(uint8_t) * name->size);
+ if (dncpy == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&(parser->current_rrset),
+ 0, 0, 0);
+ knot_zone_deep_free(&(parser->current_zone),
+ 1);
+ YYABORT;
+ }
+ memcpy(dncpy, name->name, name->size);
+ zadd_rdata_wireformat((uint16_t *)dncpy);
+ //knot_dname_free(&name);
+ break;
+ default:
+ zc_error_prev_line("unknown IPSECKEY gateway type");
+ }
+ }
+ ;
+
+rdata_ipseckey: rdata_ipsec_base sp str_sp_seq trail
+ {
+ zadd_rdata_wireformat(zparser_conv_b64($3.str)); /* public key */
+
+ free($3.str);
+ }
+ | rdata_ipsec_base trail
+ ;
+
+rdata_unknown: URR sp STR sp str_sp_seq trail
+ {
+ /* $2 is the number of octects, currently ignored */
+ $$ = zparser_conv_hex($5.str, $5.len);
+ free($5.str);
+ free($3.str);
+ }
+ | URR sp STR trail
+ {
+ $$ = zparser_conv_hex("", 0);
+ free($3.str);
+ }
+ | URR error NL
+ {
+ $$ = zparser_conv_hex("", 0);
+ }
+ ;
+%%
+
+int zp_wrap(void)
+{
+ return 1;
+}
+
+/*
+ * Create the parser.
+ */
+zparser_type *zparser_create()
+{
+ zparser_type *result = malloc(sizeof(zparser_type));
+ if (result == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ result->temporary_items = malloc(MAXRDATALEN *
+ sizeof(knot_rdata_item_t));
+ if (result->temporary_items == NULL) {
+ ERR_ALLOC_FAILED;
+ free(result);
+ return NULL;
+ }
+
+ result->current_rrset = knot_rrset_new(NULL, 0, 0, 0);
+ if (result->current_rrset == NULL) {
+ ERR_ALLOC_FAILED;
+ free(result->temporary_items);
+ free(result);
+ return NULL;
+ }
+
+ result->root_domain = knot_dname_new_from_str(".", 1, NULL);
+// printf("THE NEW ROOT: %p\n", result->root_domain);
+ if (result->root_domain == NULL) {
+ ERR_ALLOC_FAILED;
+ free(result->temporary_items);
+ free(result->current_rrset);
+ free(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+/*
+ * Initialize the parser for a new zone file.
+ */
+void
+zparser_init(const char *filename, uint32_t ttl, uint16_t rclass,
+ knot_node_t *origin, knot_dname_t *origin_from_config)
+{
+ memset(nxtbits, 0, sizeof(nxtbits));
+ memset(nsecbits, 0, sizeof(nsecbits));
+ nsec_highest_rcode = 0;
+
+ parser->current_zone = NULL;
+ parser->prev_dname = NULL;
+
+ parser->default_ttl = ttl;
+ parser->default_class = rclass;
+
+ parser->origin = origin;
+ parser->prev_dname = NULL;//parser->origin->owner;
+
+ parser->default_apex = origin;
+ parser->error_occurred = 0;
+ parser->errors = 0;
+ parser->line = 1;
+ parser->filename = filename;
+ parser->rdata_count = 0;
+ parser->origin_from_config = origin_from_config;
+
+ parser->last_node = origin;
+// parser->root_domain = NULL;
+
+ /* Create zone */
+ parser->current_zone = knot_zone_new(origin, 0, 1);
+
+ parser->node_rrsigs = NULL;
+ parser->rrsig_orphans = NULL;
+ parser->rrsig_orphan_count = 0;
+
+ parser->current_rrset->rclass = parser->default_class;
+ parser->current_rrset->rdata = NULL;
+}
+
+
+void zparser_free()
+{
+// knot_dname_release(parser->root_domain);
+// knot_dname_release(parser->prev_dname);
+ knot_dname_free(&parser->origin_from_config);
+ free(parser->temporary_items);
+ if (parser->current_rrset != NULL) {
+ free(parser->current_rrset);
+ }
+ free(parser);
+}
+
+void
+yyerror(void *scanner, const char *message)
+{
+ zc_error("%s", message);
+}
+
+static void
+error_va_list(unsigned line, const char *fmt, va_list args)
+{
+ if (parser->filename) {
+ fprintf(stderr, "%s:%u: ", parser->filename, line);
+ }
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+
+ ++parser->errors;
+ parser->error_occurred = 1;
+}
+
+/* the line counting sux, to say the least
+ * with this grose hack we try do give sane
+ * numbers back */
+void
+zc_error_prev_line(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ error_va_list(parser->line - 1, fmt, args);
+ va_end(args);
+}
+
+void
+zc_error(const char *fmt, ...)
+{
+ /* send an error message to stderr */
+ va_list args;
+ va_start(args, fmt);
+ error_va_list(parser->line, fmt, args);
+ va_end(args);
+}
+
+static void
+warning_va_list(unsigned line, const char *fmt, va_list args)
+{
+ if (parser->filename) {
+ fprintf(stderr, "%s:%u: ", parser->filename, line);
+ }
+ fprintf(stderr, "warning: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+}
+
+void
+zc_warning_prev_line(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ warning_va_list(parser->line - 1, fmt, args);
+ va_end(args);
+}
+
+void
+zc_warning(const char *fmt, ... )
+{
+ va_list args;
+ va_start(args, fmt);
+ warning_va_list(parser->line, fmt, args);
+ va_end(args);
+}
+
+#ifdef NSEC3
+static void
+nsec3_add_params(const char* hashalgo_str, const char* flag_str,
+ const char* iter_str, const char* salt_str, int salt_len)
+{
+ zadd_rdata_wireformat(zparser_conv_byte(hashalgo_str));
+ zadd_rdata_wireformat(zparser_conv_byte(flag_str));
+ zadd_rdata_wireformat(zparser_conv_short(iter_str));
+
+ /* salt */
+ if(strcmp(salt_str, "-") != 0)
+ zadd_rdata_wireformat(zparser_conv_hex_length(salt_str,
+ salt_len));
+ else
+ zadd_rdata_wireformat(alloc_rdata_init("", 1));
+
+}
+#endif /* NSEC3 */