summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:53:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:53:35 +0000
commit69c6a41ffb878ef98c9378ed4b1634a404cfaa7f (patch)
treeb2a4f704565d62fbb129ab9dc3b35977c50e6e7f /tests
parentInitial commit. (diff)
downloadknot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.tar.xz
knot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.zip
Adding upstream version 2.7.6.upstream/2.7.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am240
-rw-r--r--tests/Makefile.in2383
-rw-r--r--tests/contrib/test_base32hex.c267
-rw-r--r--tests/contrib/test_base64.c237
-rw-r--r--tests/contrib/test_dynarray.c81
-rw-r--r--tests/contrib/test_heap.c166
-rw-r--r--tests/contrib/test_net.c759
-rw-r--r--tests/contrib/test_net_shortwrite.c151
-rw-r--r--tests/contrib/test_qp-trie.c202
-rw-r--r--tests/contrib/test_siphash.c135
-rw-r--r--tests/contrib/test_sockaddr.c234
-rw-r--r--tests/contrib/test_string.c59
-rw-r--r--tests/contrib/test_strtonum.c156
-rw-r--r--tests/contrib/test_time.c203
-rw-r--r--tests/contrib/test_wire_ctx.c287
-rw-r--r--tests/knot/semantic_check_data/cdnskey.cds10
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid10
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid.param10
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocdnskey9
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocds9
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nodnskey9
-rw-r--r--tests/knot/semantic_check_data/cdnskey.two13
-rw-r--r--tests/knot/semantic_check_data/cname_extra_01.zone18
-rw-r--r--tests/knot/semantic_check_data/cname_extra_02.signed76
-rw-r--r--tests/knot/semantic_check_data/cname_multiple.zone15
-rw-r--r--tests/knot/semantic_check_data/different_signer_name.signed58
-rw-r--r--tests/knot/semantic_check_data/dname_apex_nsec3.signed23
-rw-r--r--tests/knot/semantic_check_data/dname_children.zone16
-rw-r--r--tests/knot/semantic_check_data/dnskey_param_error.signed70
-rw-r--r--tests/knot/semantic_check_data/duplicate.signature19
-rw-r--r--tests/knot/semantic_check_data/invalid_ds.signed106
-rw-r--r--tests/knot/semantic_check_data/missing.signed20
-rw-r--r--tests/knot/semantic_check_data/missing_glue_01.zone14
-rw-r--r--tests/knot/semantic_check_data/missing_glue_02.zone16
-rw-r--r--tests/knot/semantic_check_data/missing_glue_03.zone15
-rw-r--r--tests/knot/semantic_check_data/missing_ns.zone10
-rw-r--r--tests/knot/semantic_check_data/no_error_delegaton_bitmap.signed66
-rw-r--r--tests/knot/semantic_check_data/no_error_nsec3_delegation.signed78
-rw-r--r--tests/knot/semantic_check_data/no_error_nsec3_optout.signed98
-rw-r--r--tests/knot/semantic_check_data/no_error_wildcard_glue.zone18
-rw-r--r--tests/knot/semantic_check_data/no_rrsig.signed48
-rw-r--r--tests/knot/semantic_check_data/no_rrsig_with_delegation.signed61
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_01.signed80
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_02.signed94
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_03.signed94
-rw-r--r--tests/knot/semantic_check_data/nsec3_ds.signed109
-rw-r--r--tests/knot/semantic_check_data/nsec3_missing.signed120
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout.signed81
-rw-r--r--tests/knot/semantic_check_data/nsec3_param_invalid.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec_broken_chain_01.signed72
-rw-r--r--tests/knot/semantic_check_data/nsec_broken_chain_02.signed73
-rw-r--r--tests/knot/semantic_check_data/nsec_missing.signed67
-rw-r--r--tests/knot/semantic_check_data/nsec_multiple.signed74
-rw-r--r--tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed73
-rw-r--r--tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed73
-rw-r--r--tests/knot/semantic_check_data/rrsig_rdata_ttl.signed58
-rw-r--r--tests/knot/semantic_check_data/rrsig_signed.signed62
-rw-r--r--tests/knot/semantic_check_data/rrsig_ttl.signed58
-rw-r--r--tests/knot/test_acl.c200
-rw-r--r--tests/knot/test_changeset.c188
-rw-r--r--tests/knot/test_conf.c210
-rw-r--r--tests/knot/test_conf.h45
-rw-r--r--tests/knot/test_conf_tools.c123
-rw-r--r--tests/knot/test_confdb.c489
-rw-r--r--tests/knot/test_confio.c1009
-rw-r--r--tests/knot/test_dthreads.c145
-rw-r--r--tests/knot/test_fdset.c152
-rw-r--r--tests/knot/test_journal.c746
-rw-r--r--tests/knot/test_kasp_db.c137
-rw-r--r--tests/knot/test_node.c154
-rw-r--r--tests/knot/test_process_query.c189
-rw-r--r--tests/knot/test_query_module.c83
-rw-r--r--tests/knot/test_requestor.c164
-rw-r--r--tests/knot/test_semantic_check.in129
-rw-r--r--tests/knot/test_server.c68
-rw-r--r--tests/knot/test_server.h86
-rw-r--r--tests/knot/test_worker_pool.c152
-rw-r--r--tests/knot/test_worker_queue.c57
-rw-r--r--tests/knot/test_zone-tree.c118
-rw-r--r--tests/knot/test_zone-update.c336
-rw-r--r--tests/knot/test_zone_events.c99
-rw-r--r--tests/knot/test_zone_serial.c134
-rw-r--r--tests/knot/test_zone_timers.c102
-rw-r--r--tests/knot/test_zonedb.c115
-rw-r--r--tests/libdnssec/sample_keys.h407
-rw-r--r--tests/libdnssec/test_binary.c93
-rw-r--r--tests/libdnssec/test_crypto.c37
-rw-r--r--tests/libdnssec/test_key.c212
-rw-r--r--tests/libdnssec/test_key_algorithm.c84
-rw-r--r--tests/libdnssec/test_key_ds.c116
-rw-r--r--tests/libdnssec/test_keyid.c81
-rw-r--r--tests/libdnssec/test_keystore_pkcs11.c484
-rw-r--r--tests/libdnssec/test_keystore_pkcs8.c219
-rw-r--r--tests/libdnssec/test_keystore_pkcs8_dir.c151
-rw-r--r--tests/libdnssec/test_keytag.c61
-rw-r--r--tests/libdnssec/test_list.c127
-rw-r--r--tests/libdnssec/test_nsec_bitmap.c102
-rw-r--r--tests/libdnssec/test_nsec_hash.c114
-rw-r--r--tests/libdnssec/test_random.c82
-rw-r--r--tests/libdnssec/test_shared_bignum.c128
-rw-r--r--tests/libdnssec/test_shared_dname.c79
-rw-r--r--tests/libdnssec/test_sign.c184
-rw-r--r--tests/libdnssec/test_sign_der.c202
-rw-r--r--tests/libdnssec/test_tsig.c145
-rw-r--r--tests/libknot/test_control.c221
-rw-r--r--tests/libknot/test_cookies.c177
-rw-r--r--tests/libknot/test_db.c287
-rw-r--r--tests/libknot/test_descriptor.c361
-rw-r--r--tests/libknot/test_dname.c584
-rw-r--r--tests/libknot/test_edns.c484
-rw-r--r--tests/libknot/test_edns_ecs.c271
-rw-r--r--tests/libknot/test_endian.c70
-rw-r--r--tests/libknot/test_lookup.c66
-rw-r--r--tests/libknot/test_pkt.c199
-rw-r--r--tests/libknot/test_rdata.c60
-rw-r--r--tests/libknot/test_rdataset.c205
-rw-r--r--tests/libknot/test_rrset-wire.c264
-rw-r--r--tests/libknot/test_rrset.c117
-rw-r--r--tests/libknot/test_tsig.c204
-rw-r--r--tests/libknot/test_wire.c46
-rw-r--r--tests/libknot/test_yparser.c280
-rw-r--r--tests/libknot/test_ypschema.c417
-rw-r--r--tests/libknot/test_yptrafo.c403
-rw-r--r--tests/libzscanner/TESTS80
-rw-r--r--tests/libzscanner/data/00-0_general.in24
-rw-r--r--tests/libzscanner/data/00-0_general.out68
-rw-r--r--tests/libzscanner/data/00-1_general.in7
-rw-r--r--tests/libzscanner/data/00-1_general.out2
-rw-r--r--tests/libzscanner/data/00-2_general.in1
-rw-r--r--tests/libzscanner/data/00-2_general.out2
-rw-r--r--tests/libzscanner/data/00-3_general.in4
-rw-r--r--tests/libzscanner/data/00-3_general.out6
-rw-r--r--tests/libzscanner/data/00-4_general.in0
-rw-r--r--tests/libzscanner/data/00-4_general.out0
-rw-r--r--tests/libzscanner/data/01_owner.in37
-rw-r--r--tests/libzscanner/data/01_owner.out138
-rw-r--r--tests/libzscanner/data/02_class.in10
-rw-r--r--tests/libzscanner/data/02_class.out16
-rw-r--r--tests/libzscanner/data/03_rrttl.in26
-rw-r--r--tests/libzscanner/data/03_rrttl.out100
-rw-r--r--tests/libzscanner/data/04-0_ORIGIN.in29
-rw-r--r--tests/libzscanner/data/04-0_ORIGIN.out86
-rw-r--r--tests/libzscanner/data/04-1_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-1_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-2_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-2_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-3_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-3_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-4_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-4_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-5_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-5_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-6_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-6_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-7_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-7_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-8_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-8_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-9_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-9_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/05-0_TTL.in36
-rw-r--r--tests/libzscanner/data/05-0_TTL.out92
-rw-r--r--tests/libzscanner/data/05-1_TTL.in4
-rw-r--r--tests/libzscanner/data/05-1_TTL.out2
-rw-r--r--tests/libzscanner/data/05-2_TTL.in4
-rw-r--r--tests/libzscanner/data/05-2_TTL.out2
-rw-r--r--tests/libzscanner/data/05-3_TTL.in4
-rw-r--r--tests/libzscanner/data/05-3_TTL.out2
-rw-r--r--tests/libzscanner/data/05-4_TTL.in4
-rw-r--r--tests/libzscanner/data/05-4_TTL.out2
-rw-r--r--tests/libzscanner/data/06-0_INCLUDE.in29
-rw-r--r--tests/libzscanner/data/06-0_INCLUDE.out138
-rw-r--r--tests/libzscanner/data/06-1_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-1_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-2_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-2_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-3_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-3_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-4_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-4_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-5_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-5_INCLUDE.out0
-rw-r--r--tests/libzscanner/data/06-6_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-6_INCLUDE.out4
-rw-r--r--tests/libzscanner/data/06-7_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-7_INCLUDE.out4
-rw-r--r--tests/libzscanner/data/06-8_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-8_INCLUDE.out12
-rw-r--r--tests/libzscanner/data/07-0-rdata.in1
-rw-r--r--tests/libzscanner/data/07-0-rdata.out2
-rw-r--r--tests/libzscanner/data/07-1-rdata.in1
-rw-r--r--tests/libzscanner/data/07-1-rdata.out2
-rw-r--r--tests/libzscanner/data/07-2-rdata.in1
-rw-r--r--tests/libzscanner/data/07-2-rdata.out6
-rw-r--r--tests/libzscanner/data/07-3-rdata.in1
-rw-r--r--tests/libzscanner/data/07-3-rdata.out6
-rw-r--r--tests/libzscanner/data/07-4-rdata.in1
-rw-r--r--tests/libzscanner/data/07-4-rdata.out6
-rw-r--r--tests/libzscanner/data/10_A.in19
-rw-r--r--tests/libzscanner/data/10_A.out50
-rw-r--r--tests/libzscanner/data/11_AAAA.in21
-rw-r--r--tests/libzscanner/data/11_AAAA.out62
-rw-r--r--tests/libzscanner/data/12_TXT.in37
-rw-r--r--tests/libzscanner/data/12_TXT.out138
-rw-r--r--tests/libzscanner/data/13_SPF.in14
-rw-r--r--tests/libzscanner/data/13_SPF.out32
-rw-r--r--tests/libzscanner/data/14_NS.in38
-rw-r--r--tests/libzscanner/data/14_NS.out144
-rw-r--r--tests/libzscanner/data/15_CNAME.in14
-rw-r--r--tests/libzscanner/data/15_CNAME.out32
-rw-r--r--tests/libzscanner/data/16_PTR.in14
-rw-r--r--tests/libzscanner/data/16_PTR.out32
-rw-r--r--tests/libzscanner/data/17_DNAME.in14
-rw-r--r--tests/libzscanner/data/17_DNAME.out32
-rw-r--r--tests/libzscanner/data/18_MX.in23
-rw-r--r--tests/libzscanner/data/18_MX.out62
-rw-r--r--tests/libzscanner/data/19_AFSDB.in14
-rw-r--r--tests/libzscanner/data/19_AFSDB.out32
-rw-r--r--tests/libzscanner/data/20_RT.in14
-rw-r--r--tests/libzscanner/data/20_RT.out32
-rw-r--r--tests/libzscanner/data/21_KX.in14
-rw-r--r--tests/libzscanner/data/21_KX.out32
-rw-r--r--tests/libzscanner/data/22_HINFO.in26
-rw-r--r--tests/libzscanner/data/22_HINFO.out88
-rw-r--r--tests/libzscanner/data/23_MINFO.in18
-rw-r--r--tests/libzscanner/data/23_MINFO.out48
-rw-r--r--tests/libzscanner/data/24_RP.in14
-rw-r--r--tests/libzscanner/data/24_RP.out32
-rw-r--r--tests/libzscanner/data/25_SOA.in31
-rw-r--r--tests/libzscanner/data/25_SOA.out74
-rw-r--r--tests/libzscanner/data/26_SRV.in25
-rw-r--r--tests/libzscanner/data/26_SRV.out66
-rw-r--r--tests/libzscanner/data/27_NAPTR.in20
-rw-r--r--tests/libzscanner/data/27_NAPTR.out56
-rw-r--r--tests/libzscanner/data/28_TYPE.in27
-rw-r--r--tests/libzscanner/data/28_TYPE.out64
-rw-r--r--tests/libzscanner/data/29_CERT.in58
-rw-r--r--tests/libzscanner/data/29_CERT.out244
-rw-r--r--tests/libzscanner/data/30_KEY.in31
-rw-r--r--tests/libzscanner/data/30_KEY.out86
-rw-r--r--tests/libzscanner/data/31_DNSKEY.in32
-rw-r--r--tests/libzscanner/data/31_DNSKEY.out92
-rw-r--r--tests/libzscanner/data/32_APL.in30
-rw-r--r--tests/libzscanner/data/32_APL.out104
-rw-r--r--tests/libzscanner/data/33_DS.in23
-rw-r--r--tests/libzscanner/data/33_DS.out62
-rw-r--r--tests/libzscanner/data/34_SSHFP.in21
-rw-r--r--tests/libzscanner/data/34_SSHFP.out54
-rw-r--r--tests/libzscanner/data/35_IPSECKEY.in29
-rw-r--r--tests/libzscanner/data/35_IPSECKEY.out94
-rw-r--r--tests/libzscanner/data/36_RRSIG.in45
-rw-r--r--tests/libzscanner/data/36_RRSIG.out134
-rw-r--r--tests/libzscanner/data/37_NSEC.in20
-rw-r--r--tests/libzscanner/data/37_NSEC.out64
-rw-r--r--tests/libzscanner/data/38_DHCID.in26
-rw-r--r--tests/libzscanner/data/38_DHCID.out72
-rw-r--r--tests/libzscanner/data/39_NSEC3.in46
-rw-r--r--tests/libzscanner/data/39_NSEC3.out144
-rw-r--r--tests/libzscanner/data/40_NSEC3PARAM.in23
-rw-r--r--tests/libzscanner/data/40_NSEC3PARAM.out58
-rw-r--r--tests/libzscanner/data/41_TLSA.in21
-rw-r--r--tests/libzscanner/data/41_TLSA.out54
-rw-r--r--tests/libzscanner/data/42_LOC.in64
-rw-r--r--tests/libzscanner/data/42_LOC.out248
-rw-r--r--tests/libzscanner/data/43_EUI48.in22
-rw-r--r--tests/libzscanner/data/43_EUI48.out60
-rw-r--r--tests/libzscanner/data/44_EUI64.in22
-rw-r--r--tests/libzscanner/data/44_EUI64.out60
-rw-r--r--tests/libzscanner/data/45_NID.in14
-rw-r--r--tests/libzscanner/data/45_NID.out32
-rw-r--r--tests/libzscanner/data/46_L32.in21
-rw-r--r--tests/libzscanner/data/46_L32.out54
-rw-r--r--tests/libzscanner/data/47_L64.in23
-rw-r--r--tests/libzscanner/data/47_L64.out62
-rw-r--r--tests/libzscanner/data/48_LP.in14
-rw-r--r--tests/libzscanner/data/48_LP.out32
-rw-r--r--tests/libzscanner/data/49_CDS.in23
-rw-r--r--tests/libzscanner/data/49_CDS.out62
-rw-r--r--tests/libzscanner/data/50_CDNSKEY.in32
-rw-r--r--tests/libzscanner/data/50_CDNSKEY.out92
-rw-r--r--tests/libzscanner/data/51_URI.in22
-rw-r--r--tests/libzscanner/data/51_URI.out60
-rw-r--r--tests/libzscanner/data/52_CAA.in23
-rw-r--r--tests/libzscanner/data/52_CAA.out70
-rw-r--r--tests/libzscanner/data/includes/include19
-rw-r--r--tests/libzscanner/data/includes/include26
-rw-r--r--tests/libzscanner/data/includes/include30
-rw-r--r--tests/libzscanner/data/includes/include41
-rw-r--r--tests/libzscanner/data/includes/include51
-rw-r--r--tests/libzscanner/data/includes/include61
-rw-r--r--tests/libzscanner/processing.c173
-rw-r--r--tests/libzscanner/processing.h29
-rw-r--r--tests/libzscanner/test_zscanner.in41
-rw-r--r--tests/libzscanner/zscanner-tool.c251
-rw-r--r--tests/modules/test_onlinesign.c203
-rw-r--r--tests/modules/test_rrl.c167
-rw-r--r--tests/tap/basic.c605
-rw-r--r--tests/tap/basic.h132
-rw-r--r--tests/tap/files.c66
-rw-r--r--tests/tap/files.h44
-rw-r--r--tests/tap/float.c67
-rw-r--r--tests/tap/float.h39
-rw-r--r--tests/tap/libtap.sh246
-rw-r--r--tests/tap/macros.h85
-rw-r--r--tests/tap/runtests.c1393
-rw-r--r--tests/utils/test_cert.c223
-rw-r--r--tests/utils/test_lookup.c136
309 files changed, 30433 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..86f30f2
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,240 @@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/libdnssec \
+ -I$(top_srcdir)/src/libdnssec/shared \
+ $(gnutls_CFLAGS)
+
+LDADD = \
+ libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la
+
+if HAVE_DAEMON
+LDADD += \
+ $(top_builddir)/src/libknotd.la
+endif HAVE_DAEMON
+
+EXTRA_DIST = \
+ tap/libtap.sh \
+ libdnssec/sample_keys.h \
+ knot/semantic_check_data \
+ knot/test_semantic_check.in \
+ libzscanner/data \
+ libzscanner/test_zscanner.in \
+ libzscanner/TESTS
+
+check_LTLIBRARIES = libtap.la
+
+libtap_la_SOURCES = \
+ tap/basic.c \
+ tap/basic.h \
+ tap/files.c \
+ tap/files.h \
+ tap/float.c \
+ tap/float.h \
+ tap/macros.h
+
+EXTRA_PROGRAMS = tap/runtests
+
+tap_runtests_LDADD =
+
+check_PROGRAMS = \
+ contrib/test_base32hex \
+ contrib/test_base64 \
+ contrib/test_dynarray \
+ contrib/test_heap \
+ contrib/test_net \
+ contrib/test_net_shortwrite \
+ contrib/test_qp-trie \
+ contrib/test_siphash \
+ contrib/test_sockaddr \
+ contrib/test_string \
+ contrib/test_strtonum \
+ contrib/test_time \
+ contrib/test_wire_ctx
+
+check_PROGRAMS += \
+ libdnssec/test_binary \
+ libdnssec/test_crypto \
+ libdnssec/test_key \
+ libdnssec/test_key_algorithm \
+ libdnssec/test_key_ds \
+ libdnssec/test_keyid \
+ libdnssec/test_keystore_pkcs11 \
+ libdnssec/test_keystore_pkcs8 \
+ libdnssec/test_keystore_pkcs8_dir \
+ libdnssec/test_keytag \
+ libdnssec/test_list \
+ libdnssec/test_nsec_bitmap \
+ libdnssec/test_nsec_hash \
+ libdnssec/test_random \
+ libdnssec/test_sign \
+ libdnssec/test_sign_der \
+ libdnssec/test_shared_bignum \
+ libdnssec/test_shared_dname \
+ libdnssec/test_tsig
+
+if HAVE_DAEMON
+check_PROGRAMS += \
+ knot/test_acl \
+ knot/test_changeset \
+ knot/test_conf \
+ knot/test_conf_tools \
+ knot/test_confdb \
+ knot/test_confio \
+ knot/test_dthreads \
+ knot/test_fdset \
+ knot/test_journal \
+ knot/test_kasp_db \
+ knot/test_node \
+ knot/test_process_query \
+ knot/test_query_module \
+ knot/test_requestor \
+ knot/test_server \
+ knot/test_worker_pool \
+ knot/test_worker_queue \
+ knot/test_zone-tree \
+ knot/test_zone-update \
+ knot/test_zone_events \
+ knot/test_zone_serial \
+ knot/test_zone_timers \
+ knot/test_zonedb
+
+knot_test_acl_SOURCES = \
+ knot/test_acl.c \
+ knot/test_conf.h
+
+knot_test_conf_SOURCES = \
+ knot/test_conf.c \
+ knot/test_conf.h
+
+knot_test_confdb_SOURCES = \
+ knot/test_confdb.c \
+ knot/test_conf.h
+
+knot_test_confio_SOURCES = \
+ knot/test_confio.c \
+ knot/test_conf.h
+
+knot_test_process_query_SOURCES = \
+ knot/test_process_query.c \
+ knot/test_server.h \
+ knot/test_conf.h
+endif HAVE_DAEMON
+
+check_PROGRAMS += \
+ libknot/test_control \
+ libknot/test_cookies \
+ libknot/test_db \
+ libknot/test_descriptor \
+ libknot/test_dname \
+ libknot/test_edns \
+ libknot/test_edns_ecs \
+ libknot/test_endian \
+ libknot/test_lookup \
+ libknot/test_pkt \
+ libknot/test_rdata \
+ libknot/test_rdataset \
+ libknot/test_rrset \
+ libknot/test_rrset-wire \
+ libknot/test_tsig \
+ libknot/test_yparser \
+ libknot/test_ypschema \
+ libknot/test_yptrafo \
+ libknot/test_wire
+
+if HAVE_LIBUTILS
+check_PROGRAMS += \
+ utils/test_cert \
+ utils/test_lookup
+endif HAVE_LIBUTILS
+
+if HAVE_DAEMON
+if STATIC_MODULE_onlinesign
+check_PROGRAMS += \
+ modules/test_onlinesign
+else
+if SHARED_MODULE_onlinesign
+check_PROGRAMS += \
+ modules/test_onlinesign
+endif
+endif
+
+if STATIC_MODULE_rrl
+check_PROGRAMS += \
+ modules/test_rrl
+else
+if SHARED_MODULE_rrl
+check_PROGRAMS += \
+ modules/test_rrl
+endif
+endif
+endif HAVE_DAEMON
+
+libdnssec_test_keystore_pkcs11_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DLIBDIR='"$(libdir)"'
+
+if HAVE_LIBUTILS
+utils_test_lookup_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(libedit_CFLAGS)
+
+utils_test_lookup_LDADD = \
+ libtap.la \
+ $(top_builddir)/src/libknotus.la \
+ $(libedit_LIBS)
+
+utils_test_cert_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(libedit_CFLAGS)
+
+utils_test_cert_LDADD = \
+ libtap.la \
+ $(top_builddir)/src/libknotus.la \
+ $(libedit_LIBS)
+endif HAVE_LIBUTILS
+
+EXTRA_PROGRAMS += libzscanner/zscanner-tool
+
+libzscanner_zscanner_tool_SOURCES = \
+ libzscanner/zscanner-tool.c \
+ libzscanner/processing.h \
+ libzscanner/processing.c
+
+check_SCRIPTS = \
+ libzscanner/test_zscanner
+
+edit = $(SED) \
+ -e 's|@top_srcdir[@]|$(abs_top_srcdir)|g' \
+ -e 's|@top_builddir[@]|$(abs_top_builddir)|g'
+
+if HAVE_LIBUTILS
+check_SCRIPTS += \
+ knot/test_semantic_check
+
+knot/test_semantic_check:
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+endif HAVE_LIBUTILS
+
+libzscanner/test_zscanner: libzscanner/zscanner-tool
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+
+CLEANFILES = $(check_SCRIPTS) $(EXTRA_PROGRAMS) runtests.log
+
+check-compile: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+
+AM_V_RUNTESTS = $(am__v_RUNTESTS_@AM_V@)
+am__v_RUNTESTS_ = $(am__v_RUNTESTS_@AM_DEFAULT_V@)
+am__v_RUNTESTS_0 =
+am__v_RUNTESTS_1 = RET=$$?; if [ "$$RET" != "0" ]; then cat "$(builddir)/runtests.log"; exit $$RET; fi
+check-local: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+ @$(top_builddir)/tests/tap/runtests -s $(srcdir) -b $(builddir) \
+ -L $(builddir)/runtests.log $(check_PROGRAMS) $(check_SCRIPTS); \
+ $(AM_V_RUNTESTS)
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644
index 0000000..3ec7322
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,2383 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 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@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+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@
+@HAVE_DAEMON_TRUE@am__append_1 = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la
+
+EXTRA_PROGRAMS = tap/runtests$(EXEEXT) \
+ libzscanner/zscanner-tool$(EXEEXT)
+check_PROGRAMS = contrib/test_base32hex$(EXEEXT) \
+ contrib/test_base64$(EXEEXT) contrib/test_dynarray$(EXEEXT) \
+ contrib/test_heap$(EXEEXT) contrib/test_net$(EXEEXT) \
+ contrib/test_net_shortwrite$(EXEEXT) \
+ contrib/test_qp-trie$(EXEEXT) contrib/test_siphash$(EXEEXT) \
+ contrib/test_sockaddr$(EXEEXT) contrib/test_string$(EXEEXT) \
+ contrib/test_strtonum$(EXEEXT) contrib/test_time$(EXEEXT) \
+ contrib/test_wire_ctx$(EXEEXT) libdnssec/test_binary$(EXEEXT) \
+ libdnssec/test_crypto$(EXEEXT) libdnssec/test_key$(EXEEXT) \
+ libdnssec/test_key_algorithm$(EXEEXT) \
+ libdnssec/test_key_ds$(EXEEXT) libdnssec/test_keyid$(EXEEXT) \
+ libdnssec/test_keystore_pkcs11$(EXEEXT) \
+ libdnssec/test_keystore_pkcs8$(EXEEXT) \
+ libdnssec/test_keystore_pkcs8_dir$(EXEEXT) \
+ libdnssec/test_keytag$(EXEEXT) libdnssec/test_list$(EXEEXT) \
+ libdnssec/test_nsec_bitmap$(EXEEXT) \
+ libdnssec/test_nsec_hash$(EXEEXT) \
+ libdnssec/test_random$(EXEEXT) libdnssec/test_sign$(EXEEXT) \
+ libdnssec/test_sign_der$(EXEEXT) \
+ libdnssec/test_shared_bignum$(EXEEXT) \
+ libdnssec/test_shared_dname$(EXEEXT) \
+ libdnssec/test_tsig$(EXEEXT) $(am__EXEEXT_1) \
+ libknot/test_control$(EXEEXT) libknot/test_cookies$(EXEEXT) \
+ libknot/test_db$(EXEEXT) libknot/test_descriptor$(EXEEXT) \
+ libknot/test_dname$(EXEEXT) libknot/test_edns$(EXEEXT) \
+ libknot/test_edns_ecs$(EXEEXT) libknot/test_endian$(EXEEXT) \
+ libknot/test_lookup$(EXEEXT) libknot/test_pkt$(EXEEXT) \
+ libknot/test_rdata$(EXEEXT) libknot/test_rdataset$(EXEEXT) \
+ libknot/test_rrset$(EXEEXT) libknot/test_rrset-wire$(EXEEXT) \
+ libknot/test_tsig$(EXEEXT) libknot/test_yparser$(EXEEXT) \
+ libknot/test_ypschema$(EXEEXT) libknot/test_yptrafo$(EXEEXT) \
+ libknot/test_wire$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_3) \
+ $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6)
+@HAVE_DAEMON_TRUE@am__append_2 = \
+@HAVE_DAEMON_TRUE@ knot/test_acl \
+@HAVE_DAEMON_TRUE@ knot/test_changeset \
+@HAVE_DAEMON_TRUE@ knot/test_conf \
+@HAVE_DAEMON_TRUE@ knot/test_conf_tools \
+@HAVE_DAEMON_TRUE@ knot/test_confdb \
+@HAVE_DAEMON_TRUE@ knot/test_confio \
+@HAVE_DAEMON_TRUE@ knot/test_dthreads \
+@HAVE_DAEMON_TRUE@ knot/test_fdset \
+@HAVE_DAEMON_TRUE@ knot/test_journal \
+@HAVE_DAEMON_TRUE@ knot/test_kasp_db \
+@HAVE_DAEMON_TRUE@ knot/test_node \
+@HAVE_DAEMON_TRUE@ knot/test_process_query \
+@HAVE_DAEMON_TRUE@ knot/test_query_module \
+@HAVE_DAEMON_TRUE@ knot/test_requestor \
+@HAVE_DAEMON_TRUE@ knot/test_server \
+@HAVE_DAEMON_TRUE@ knot/test_worker_pool \
+@HAVE_DAEMON_TRUE@ knot/test_worker_queue \
+@HAVE_DAEMON_TRUE@ knot/test_zone-tree \
+@HAVE_DAEMON_TRUE@ knot/test_zone-update \
+@HAVE_DAEMON_TRUE@ knot/test_zone_events \
+@HAVE_DAEMON_TRUE@ knot/test_zone_serial \
+@HAVE_DAEMON_TRUE@ knot/test_zone_timers \
+@HAVE_DAEMON_TRUE@ knot/test_zonedb
+
+@HAVE_LIBUTILS_TRUE@am__append_3 = \
+@HAVE_LIBUTILS_TRUE@ utils/test_cert \
+@HAVE_LIBUTILS_TRUE@ utils/test_lookup
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__append_4 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__append_5 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__append_6 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@ modules/test_rrl
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__append_7 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@ modules/test_rrl
+
+@HAVE_LIBUTILS_TRUE@am__append_8 = \
+@HAVE_LIBUTILS_TRUE@ knot/test_semantic_check
+
+subdir = tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cc_clang.m4 \
+ $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/ax_compare_version.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+libtap_la_LIBADD =
+am__dirstamp = $(am__leading_dot)dirstamp
+am_libtap_la_OBJECTS = tap/basic.lo tap/files.lo tap/float.lo
+libtap_la_OBJECTS = $(am_libtap_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+@HAVE_DAEMON_TRUE@am__EXEEXT_1 = knot/test_acl$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_changeset$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_conf$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_conf_tools$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_confdb$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_confio$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_dthreads$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_fdset$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_journal$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_kasp_db$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_node$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_process_query$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_query_module$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_requestor$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_server$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_worker_pool$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_worker_queue$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone-tree$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone-update$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_events$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_serial$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_timers$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zonedb$(EXEEXT)
+@HAVE_LIBUTILS_TRUE@am__EXEEXT_2 = utils/test_cert$(EXEEXT) \
+@HAVE_LIBUTILS_TRUE@ utils/test_lookup$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__EXEEXT_3 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__EXEEXT_4 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__EXEEXT_5 = modules/test_rrl$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__EXEEXT_6 = modules/test_rrl$(EXEEXT)
+contrib_test_base32hex_SOURCES = contrib/test_base32hex.c
+contrib_test_base32hex_OBJECTS = contrib/test_base32hex.$(OBJEXT)
+contrib_test_base32hex_LDADD = $(LDADD)
+contrib_test_base32hex_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_base64_SOURCES = contrib/test_base64.c
+contrib_test_base64_OBJECTS = contrib/test_base64.$(OBJEXT)
+contrib_test_base64_LDADD = $(LDADD)
+contrib_test_base64_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_dynarray_SOURCES = contrib/test_dynarray.c
+contrib_test_dynarray_OBJECTS = contrib/test_dynarray.$(OBJEXT)
+contrib_test_dynarray_LDADD = $(LDADD)
+contrib_test_dynarray_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_heap_SOURCES = contrib/test_heap.c
+contrib_test_heap_OBJECTS = contrib/test_heap.$(OBJEXT)
+contrib_test_heap_LDADD = $(LDADD)
+contrib_test_heap_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_net_SOURCES = contrib/test_net.c
+contrib_test_net_OBJECTS = contrib/test_net.$(OBJEXT)
+contrib_test_net_LDADD = $(LDADD)
+contrib_test_net_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_net_shortwrite_SOURCES = contrib/test_net_shortwrite.c
+contrib_test_net_shortwrite_OBJECTS = \
+ contrib/test_net_shortwrite.$(OBJEXT)
+contrib_test_net_shortwrite_LDADD = $(LDADD)
+contrib_test_net_shortwrite_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_qp_trie_SOURCES = contrib/test_qp-trie.c
+contrib_test_qp_trie_OBJECTS = contrib/test_qp-trie.$(OBJEXT)
+contrib_test_qp_trie_LDADD = $(LDADD)
+contrib_test_qp_trie_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_siphash_SOURCES = contrib/test_siphash.c
+contrib_test_siphash_OBJECTS = contrib/test_siphash.$(OBJEXT)
+contrib_test_siphash_LDADD = $(LDADD)
+contrib_test_siphash_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_sockaddr_SOURCES = contrib/test_sockaddr.c
+contrib_test_sockaddr_OBJECTS = contrib/test_sockaddr.$(OBJEXT)
+contrib_test_sockaddr_LDADD = $(LDADD)
+contrib_test_sockaddr_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_string_SOURCES = contrib/test_string.c
+contrib_test_string_OBJECTS = contrib/test_string.$(OBJEXT)
+contrib_test_string_LDADD = $(LDADD)
+contrib_test_string_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_strtonum_SOURCES = contrib/test_strtonum.c
+contrib_test_strtonum_OBJECTS = contrib/test_strtonum.$(OBJEXT)
+contrib_test_strtonum_LDADD = $(LDADD)
+contrib_test_strtonum_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_time_SOURCES = contrib/test_time.c
+contrib_test_time_OBJECTS = contrib/test_time.$(OBJEXT)
+contrib_test_time_LDADD = $(LDADD)
+contrib_test_time_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+contrib_test_wire_ctx_SOURCES = contrib/test_wire_ctx.c
+contrib_test_wire_ctx_OBJECTS = contrib/test_wire_ctx.$(OBJEXT)
+contrib_test_wire_ctx_LDADD = $(LDADD)
+contrib_test_wire_ctx_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am__knot_test_acl_SOURCES_DIST = knot/test_acl.c knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_acl_OBJECTS = knot/test_acl.$(OBJEXT)
+knot_test_acl_OBJECTS = $(am_knot_test_acl_OBJECTS)
+knot_test_acl_LDADD = $(LDADD)
+knot_test_acl_DEPENDENCIES = libtap.la $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_changeset_SOURCES = knot/test_changeset.c
+knot_test_changeset_OBJECTS = knot/test_changeset.$(OBJEXT)
+knot_test_changeset_LDADD = $(LDADD)
+knot_test_changeset_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am__knot_test_conf_SOURCES_DIST = knot/test_conf.c knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_conf_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_conf.$(OBJEXT)
+knot_test_conf_OBJECTS = $(am_knot_test_conf_OBJECTS)
+knot_test_conf_LDADD = $(LDADD)
+knot_test_conf_DEPENDENCIES = libtap.la $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_conf_tools_SOURCES = knot/test_conf_tools.c
+knot_test_conf_tools_OBJECTS = knot/test_conf_tools.$(OBJEXT)
+knot_test_conf_tools_LDADD = $(LDADD)
+knot_test_conf_tools_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am__knot_test_confdb_SOURCES_DIST = knot/test_confdb.c \
+ knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_confdb_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_confdb.$(OBJEXT)
+knot_test_confdb_OBJECTS = $(am_knot_test_confdb_OBJECTS)
+knot_test_confdb_LDADD = $(LDADD)
+knot_test_confdb_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am__knot_test_confio_SOURCES_DIST = knot/test_confio.c \
+ knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_confio_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_confio.$(OBJEXT)
+knot_test_confio_OBJECTS = $(am_knot_test_confio_OBJECTS)
+knot_test_confio_LDADD = $(LDADD)
+knot_test_confio_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_dthreads_SOURCES = knot/test_dthreads.c
+knot_test_dthreads_OBJECTS = knot/test_dthreads.$(OBJEXT)
+knot_test_dthreads_LDADD = $(LDADD)
+knot_test_dthreads_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_fdset_SOURCES = knot/test_fdset.c
+knot_test_fdset_OBJECTS = knot/test_fdset.$(OBJEXT)
+knot_test_fdset_LDADD = $(LDADD)
+knot_test_fdset_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_journal_SOURCES = knot/test_journal.c
+knot_test_journal_OBJECTS = knot/test_journal.$(OBJEXT)
+knot_test_journal_LDADD = $(LDADD)
+knot_test_journal_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_kasp_db_SOURCES = knot/test_kasp_db.c
+knot_test_kasp_db_OBJECTS = knot/test_kasp_db.$(OBJEXT)
+knot_test_kasp_db_LDADD = $(LDADD)
+knot_test_kasp_db_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_node_SOURCES = knot/test_node.c
+knot_test_node_OBJECTS = knot/test_node.$(OBJEXT)
+knot_test_node_LDADD = $(LDADD)
+knot_test_node_DEPENDENCIES = libtap.la $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am__knot_test_process_query_SOURCES_DIST = knot/test_process_query.c \
+ knot/test_server.h knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_process_query_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_process_query.$(OBJEXT)
+knot_test_process_query_OBJECTS = \
+ $(am_knot_test_process_query_OBJECTS)
+knot_test_process_query_LDADD = $(LDADD)
+knot_test_process_query_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_query_module_SOURCES = knot/test_query_module.c
+knot_test_query_module_OBJECTS = knot/test_query_module.$(OBJEXT)
+knot_test_query_module_LDADD = $(LDADD)
+knot_test_query_module_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_requestor_SOURCES = knot/test_requestor.c
+knot_test_requestor_OBJECTS = knot/test_requestor.$(OBJEXT)
+knot_test_requestor_LDADD = $(LDADD)
+knot_test_requestor_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_server_SOURCES = knot/test_server.c
+knot_test_server_OBJECTS = knot/test_server.$(OBJEXT)
+knot_test_server_LDADD = $(LDADD)
+knot_test_server_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_worker_pool_SOURCES = knot/test_worker_pool.c
+knot_test_worker_pool_OBJECTS = knot/test_worker_pool.$(OBJEXT)
+knot_test_worker_pool_LDADD = $(LDADD)
+knot_test_worker_pool_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_worker_queue_SOURCES = knot/test_worker_queue.c
+knot_test_worker_queue_OBJECTS = knot/test_worker_queue.$(OBJEXT)
+knot_test_worker_queue_LDADD = $(LDADD)
+knot_test_worker_queue_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zone_tree_SOURCES = knot/test_zone-tree.c
+knot_test_zone_tree_OBJECTS = knot/test_zone-tree.$(OBJEXT)
+knot_test_zone_tree_LDADD = $(LDADD)
+knot_test_zone_tree_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zone_update_SOURCES = knot/test_zone-update.c
+knot_test_zone_update_OBJECTS = knot/test_zone-update.$(OBJEXT)
+knot_test_zone_update_LDADD = $(LDADD)
+knot_test_zone_update_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zone_events_SOURCES = knot/test_zone_events.c
+knot_test_zone_events_OBJECTS = knot/test_zone_events.$(OBJEXT)
+knot_test_zone_events_LDADD = $(LDADD)
+knot_test_zone_events_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zone_serial_SOURCES = knot/test_zone_serial.c
+knot_test_zone_serial_OBJECTS = knot/test_zone_serial.$(OBJEXT)
+knot_test_zone_serial_LDADD = $(LDADD)
+knot_test_zone_serial_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zone_timers_SOURCES = knot/test_zone_timers.c
+knot_test_zone_timers_OBJECTS = knot/test_zone_timers.$(OBJEXT)
+knot_test_zone_timers_LDADD = $(LDADD)
+knot_test_zone_timers_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+knot_test_zonedb_SOURCES = knot/test_zonedb.c
+knot_test_zonedb_OBJECTS = knot/test_zonedb.$(OBJEXT)
+knot_test_zonedb_LDADD = $(LDADD)
+knot_test_zonedb_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_binary_SOURCES = libdnssec/test_binary.c
+libdnssec_test_binary_OBJECTS = libdnssec/test_binary.$(OBJEXT)
+libdnssec_test_binary_LDADD = $(LDADD)
+libdnssec_test_binary_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_crypto_SOURCES = libdnssec/test_crypto.c
+libdnssec_test_crypto_OBJECTS = libdnssec/test_crypto.$(OBJEXT)
+libdnssec_test_crypto_LDADD = $(LDADD)
+libdnssec_test_crypto_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_key_SOURCES = libdnssec/test_key.c
+libdnssec_test_key_OBJECTS = libdnssec/test_key.$(OBJEXT)
+libdnssec_test_key_LDADD = $(LDADD)
+libdnssec_test_key_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_key_algorithm_SOURCES = libdnssec/test_key_algorithm.c
+libdnssec_test_key_algorithm_OBJECTS = \
+ libdnssec/test_key_algorithm.$(OBJEXT)
+libdnssec_test_key_algorithm_LDADD = $(LDADD)
+libdnssec_test_key_algorithm_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_key_ds_SOURCES = libdnssec/test_key_ds.c
+libdnssec_test_key_ds_OBJECTS = libdnssec/test_key_ds.$(OBJEXT)
+libdnssec_test_key_ds_LDADD = $(LDADD)
+libdnssec_test_key_ds_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_keyid_SOURCES = libdnssec/test_keyid.c
+libdnssec_test_keyid_OBJECTS = libdnssec/test_keyid.$(OBJEXT)
+libdnssec_test_keyid_LDADD = $(LDADD)
+libdnssec_test_keyid_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_keystore_pkcs11_SOURCES = \
+ libdnssec/test_keystore_pkcs11.c
+libdnssec_test_keystore_pkcs11_OBJECTS = libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.$(OBJEXT)
+libdnssec_test_keystore_pkcs11_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs11_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_keystore_pkcs8_SOURCES = \
+ libdnssec/test_keystore_pkcs8.c
+libdnssec_test_keystore_pkcs8_OBJECTS = \
+ libdnssec/test_keystore_pkcs8.$(OBJEXT)
+libdnssec_test_keystore_pkcs8_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs8_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_keystore_pkcs8_dir_SOURCES = \
+ libdnssec/test_keystore_pkcs8_dir.c
+libdnssec_test_keystore_pkcs8_dir_OBJECTS = \
+ libdnssec/test_keystore_pkcs8_dir.$(OBJEXT)
+libdnssec_test_keystore_pkcs8_dir_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs8_dir_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_keytag_SOURCES = libdnssec/test_keytag.c
+libdnssec_test_keytag_OBJECTS = libdnssec/test_keytag.$(OBJEXT)
+libdnssec_test_keytag_LDADD = $(LDADD)
+libdnssec_test_keytag_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_list_SOURCES = libdnssec/test_list.c
+libdnssec_test_list_OBJECTS = libdnssec/test_list.$(OBJEXT)
+libdnssec_test_list_LDADD = $(LDADD)
+libdnssec_test_list_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_nsec_bitmap_SOURCES = libdnssec/test_nsec_bitmap.c
+libdnssec_test_nsec_bitmap_OBJECTS = \
+ libdnssec/test_nsec_bitmap.$(OBJEXT)
+libdnssec_test_nsec_bitmap_LDADD = $(LDADD)
+libdnssec_test_nsec_bitmap_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_nsec_hash_SOURCES = libdnssec/test_nsec_hash.c
+libdnssec_test_nsec_hash_OBJECTS = libdnssec/test_nsec_hash.$(OBJEXT)
+libdnssec_test_nsec_hash_LDADD = $(LDADD)
+libdnssec_test_nsec_hash_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_random_SOURCES = libdnssec/test_random.c
+libdnssec_test_random_OBJECTS = libdnssec/test_random.$(OBJEXT)
+libdnssec_test_random_LDADD = $(LDADD)
+libdnssec_test_random_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_shared_bignum_SOURCES = libdnssec/test_shared_bignum.c
+libdnssec_test_shared_bignum_OBJECTS = \
+ libdnssec/test_shared_bignum.$(OBJEXT)
+libdnssec_test_shared_bignum_LDADD = $(LDADD)
+libdnssec_test_shared_bignum_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_shared_dname_SOURCES = libdnssec/test_shared_dname.c
+libdnssec_test_shared_dname_OBJECTS = \
+ libdnssec/test_shared_dname.$(OBJEXT)
+libdnssec_test_shared_dname_LDADD = $(LDADD)
+libdnssec_test_shared_dname_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_sign_SOURCES = libdnssec/test_sign.c
+libdnssec_test_sign_OBJECTS = libdnssec/test_sign.$(OBJEXT)
+libdnssec_test_sign_LDADD = $(LDADD)
+libdnssec_test_sign_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_sign_der_SOURCES = libdnssec/test_sign_der.c
+libdnssec_test_sign_der_OBJECTS = libdnssec/test_sign_der.$(OBJEXT)
+libdnssec_test_sign_der_LDADD = $(LDADD)
+libdnssec_test_sign_der_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libdnssec_test_tsig_SOURCES = libdnssec/test_tsig.c
+libdnssec_test_tsig_OBJECTS = libdnssec/test_tsig.$(OBJEXT)
+libdnssec_test_tsig_LDADD = $(LDADD)
+libdnssec_test_tsig_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_control_SOURCES = libknot/test_control.c
+libknot_test_control_OBJECTS = libknot/test_control.$(OBJEXT)
+libknot_test_control_LDADD = $(LDADD)
+libknot_test_control_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_cookies_SOURCES = libknot/test_cookies.c
+libknot_test_cookies_OBJECTS = libknot/test_cookies.$(OBJEXT)
+libknot_test_cookies_LDADD = $(LDADD)
+libknot_test_cookies_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_db_SOURCES = libknot/test_db.c
+libknot_test_db_OBJECTS = libknot/test_db.$(OBJEXT)
+libknot_test_db_LDADD = $(LDADD)
+libknot_test_db_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_descriptor_SOURCES = libknot/test_descriptor.c
+libknot_test_descriptor_OBJECTS = libknot/test_descriptor.$(OBJEXT)
+libknot_test_descriptor_LDADD = $(LDADD)
+libknot_test_descriptor_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_dname_SOURCES = libknot/test_dname.c
+libknot_test_dname_OBJECTS = libknot/test_dname.$(OBJEXT)
+libknot_test_dname_LDADD = $(LDADD)
+libknot_test_dname_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_edns_SOURCES = libknot/test_edns.c
+libknot_test_edns_OBJECTS = libknot/test_edns.$(OBJEXT)
+libknot_test_edns_LDADD = $(LDADD)
+libknot_test_edns_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_edns_ecs_SOURCES = libknot/test_edns_ecs.c
+libknot_test_edns_ecs_OBJECTS = libknot/test_edns_ecs.$(OBJEXT)
+libknot_test_edns_ecs_LDADD = $(LDADD)
+libknot_test_edns_ecs_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_endian_SOURCES = libknot/test_endian.c
+libknot_test_endian_OBJECTS = libknot/test_endian.$(OBJEXT)
+libknot_test_endian_LDADD = $(LDADD)
+libknot_test_endian_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_lookup_SOURCES = libknot/test_lookup.c
+libknot_test_lookup_OBJECTS = libknot/test_lookup.$(OBJEXT)
+libknot_test_lookup_LDADD = $(LDADD)
+libknot_test_lookup_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_pkt_SOURCES = libknot/test_pkt.c
+libknot_test_pkt_OBJECTS = libknot/test_pkt.$(OBJEXT)
+libknot_test_pkt_LDADD = $(LDADD)
+libknot_test_pkt_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_rdata_SOURCES = libknot/test_rdata.c
+libknot_test_rdata_OBJECTS = libknot/test_rdata.$(OBJEXT)
+libknot_test_rdata_LDADD = $(LDADD)
+libknot_test_rdata_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_rdataset_SOURCES = libknot/test_rdataset.c
+libknot_test_rdataset_OBJECTS = libknot/test_rdataset.$(OBJEXT)
+libknot_test_rdataset_LDADD = $(LDADD)
+libknot_test_rdataset_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_rrset_SOURCES = libknot/test_rrset.c
+libknot_test_rrset_OBJECTS = libknot/test_rrset.$(OBJEXT)
+libknot_test_rrset_LDADD = $(LDADD)
+libknot_test_rrset_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_rrset_wire_SOURCES = libknot/test_rrset-wire.c
+libknot_test_rrset_wire_OBJECTS = libknot/test_rrset-wire.$(OBJEXT)
+libknot_test_rrset_wire_LDADD = $(LDADD)
+libknot_test_rrset_wire_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_tsig_SOURCES = libknot/test_tsig.c
+libknot_test_tsig_OBJECTS = libknot/test_tsig.$(OBJEXT)
+libknot_test_tsig_LDADD = $(LDADD)
+libknot_test_tsig_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_wire_SOURCES = libknot/test_wire.c
+libknot_test_wire_OBJECTS = libknot/test_wire.$(OBJEXT)
+libknot_test_wire_LDADD = $(LDADD)
+libknot_test_wire_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_yparser_SOURCES = libknot/test_yparser.c
+libknot_test_yparser_OBJECTS = libknot/test_yparser.$(OBJEXT)
+libknot_test_yparser_LDADD = $(LDADD)
+libknot_test_yparser_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_ypschema_SOURCES = libknot/test_ypschema.c
+libknot_test_ypschema_OBJECTS = libknot/test_ypschema.$(OBJEXT)
+libknot_test_ypschema_LDADD = $(LDADD)
+libknot_test_ypschema_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+libknot_test_yptrafo_SOURCES = libknot/test_yptrafo.c
+libknot_test_yptrafo_OBJECTS = libknot/test_yptrafo.$(OBJEXT)
+libknot_test_yptrafo_LDADD = $(LDADD)
+libknot_test_yptrafo_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+am_libzscanner_zscanner_tool_OBJECTS = \
+ libzscanner/zscanner-tool.$(OBJEXT) \
+ libzscanner/processing.$(OBJEXT)
+libzscanner_zscanner_tool_OBJECTS = \
+ $(am_libzscanner_zscanner_tool_OBJECTS)
+libzscanner_zscanner_tool_LDADD = $(LDADD)
+libzscanner_zscanner_tool_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+modules_test_onlinesign_SOURCES = modules/test_onlinesign.c
+modules_test_onlinesign_OBJECTS = modules/test_onlinesign.$(OBJEXT)
+modules_test_onlinesign_LDADD = $(LDADD)
+modules_test_onlinesign_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+modules_test_rrl_SOURCES = modules/test_rrl.c
+modules_test_rrl_OBJECTS = modules/test_rrl.$(OBJEXT)
+modules_test_rrl_LDADD = $(LDADD)
+modules_test_rrl_DEPENDENCIES = libtap.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+tap_runtests_SOURCES = tap/runtests.c
+tap_runtests_OBJECTS = tap/runtests.$(OBJEXT)
+tap_runtests_DEPENDENCIES =
+utils_test_cert_SOURCES = utils/test_cert.c
+utils_test_cert_OBJECTS = utils/utils_test_cert-test_cert.$(OBJEXT)
+am__DEPENDENCIES_1 =
+@HAVE_LIBUTILS_TRUE@utils_test_cert_DEPENDENCIES = libtap.la \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1)
+utils_test_lookup_SOURCES = utils/test_lookup.c
+utils_test_lookup_OBJECTS = \
+ utils/utils_test_lookup-test_lookup.$(OBJEXT)
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_DEPENDENCIES = libtap.la \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src
+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) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libtap_la_SOURCES) contrib/test_base32hex.c \
+ contrib/test_base64.c contrib/test_dynarray.c \
+ contrib/test_heap.c contrib/test_net.c \
+ contrib/test_net_shortwrite.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_string.c contrib/test_strtonum.c \
+ contrib/test_time.c contrib/test_wire_ctx.c \
+ $(knot_test_acl_SOURCES) knot/test_changeset.c \
+ $(knot_test_conf_SOURCES) knot/test_conf_tools.c \
+ $(knot_test_confdb_SOURCES) $(knot_test_confio_SOURCES) \
+ knot/test_dthreads.c knot/test_fdset.c knot/test_journal.c \
+ knot/test_kasp_db.c knot/test_node.c \
+ $(knot_test_process_query_SOURCES) knot/test_query_module.c \
+ knot/test_requestor.c knot/test_server.c \
+ knot/test_worker_pool.c knot/test_worker_queue.c \
+ knot/test_zone-tree.c knot/test_zone-update.c \
+ knot/test_zone_events.c knot/test_zone_serial.c \
+ knot/test_zone_timers.c knot/test_zonedb.c \
+ libdnssec/test_binary.c libdnssec/test_crypto.c \
+ libdnssec/test_key.c libdnssec/test_key_algorithm.c \
+ libdnssec/test_key_ds.c libdnssec/test_keyid.c \
+ libdnssec/test_keystore_pkcs11.c \
+ libdnssec/test_keystore_pkcs8.c \
+ libdnssec/test_keystore_pkcs8_dir.c libdnssec/test_keytag.c \
+ libdnssec/test_list.c libdnssec/test_nsec_bitmap.c \
+ libdnssec/test_nsec_hash.c libdnssec/test_random.c \
+ libdnssec/test_shared_bignum.c libdnssec/test_shared_dname.c \
+ libdnssec/test_sign.c libdnssec/test_sign_der.c \
+ libdnssec/test_tsig.c libknot/test_control.c \
+ libknot/test_cookies.c libknot/test_db.c \
+ libknot/test_descriptor.c libknot/test_dname.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_rdata.c libknot/test_rdataset.c \
+ libknot/test_rrset.c libknot/test_rrset-wire.c \
+ libknot/test_tsig.c libknot/test_wire.c libknot/test_yparser.c \
+ libknot/test_ypschema.c libknot/test_yptrafo.c \
+ $(libzscanner_zscanner_tool_SOURCES) modules/test_onlinesign.c \
+ modules/test_rrl.c tap/runtests.c utils/test_cert.c \
+ utils/test_lookup.c
+DIST_SOURCES = $(libtap_la_SOURCES) contrib/test_base32hex.c \
+ contrib/test_base64.c contrib/test_dynarray.c \
+ contrib/test_heap.c contrib/test_net.c \
+ contrib/test_net_shortwrite.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_string.c contrib/test_strtonum.c \
+ contrib/test_time.c contrib/test_wire_ctx.c \
+ $(am__knot_test_acl_SOURCES_DIST) knot/test_changeset.c \
+ $(am__knot_test_conf_SOURCES_DIST) knot/test_conf_tools.c \
+ $(am__knot_test_confdb_SOURCES_DIST) \
+ $(am__knot_test_confio_SOURCES_DIST) knot/test_dthreads.c \
+ knot/test_fdset.c knot/test_journal.c knot/test_kasp_db.c \
+ knot/test_node.c $(am__knot_test_process_query_SOURCES_DIST) \
+ knot/test_query_module.c knot/test_requestor.c \
+ knot/test_server.c knot/test_worker_pool.c \
+ knot/test_worker_queue.c knot/test_zone-tree.c \
+ knot/test_zone-update.c knot/test_zone_events.c \
+ knot/test_zone_serial.c knot/test_zone_timers.c \
+ knot/test_zonedb.c libdnssec/test_binary.c \
+ libdnssec/test_crypto.c libdnssec/test_key.c \
+ libdnssec/test_key_algorithm.c libdnssec/test_key_ds.c \
+ libdnssec/test_keyid.c libdnssec/test_keystore_pkcs11.c \
+ libdnssec/test_keystore_pkcs8.c \
+ libdnssec/test_keystore_pkcs8_dir.c libdnssec/test_keytag.c \
+ libdnssec/test_list.c libdnssec/test_nsec_bitmap.c \
+ libdnssec/test_nsec_hash.c libdnssec/test_random.c \
+ libdnssec/test_shared_bignum.c libdnssec/test_shared_dname.c \
+ libdnssec/test_sign.c libdnssec/test_sign_der.c \
+ libdnssec/test_tsig.c libknot/test_control.c \
+ libknot/test_cookies.c libknot/test_db.c \
+ libknot/test_descriptor.c libknot/test_dname.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_rdata.c libknot/test_rdataset.c \
+ libknot/test_rrset.c libknot/test_rrset-wire.c \
+ libknot/test_tsig.c libknot/test_wire.c libknot/test_yparser.c \
+ libknot/test_ypschema.c libknot/test_yptrafo.c \
+ $(libzscanner_zscanner_tool_SOURCES) modules/test_onlinesign.c \
+ modules/test_rrl.c tap/runtests.c utils/test_cert.c \
+ utils/test_lookup.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_CLANG_VERSION = @CC_CLANG_VERSION@
+CFLAGS = @CFLAGS@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+external_lmdb_LIBS = @external_lmdb_LIBS@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/libdnssec \
+ -I$(top_srcdir)/src/libdnssec/shared \
+ $(gnutls_CFLAGS)
+
+LDADD = libtap.la $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libshared.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__append_1)
+EXTRA_DIST = \
+ tap/libtap.sh \
+ libdnssec/sample_keys.h \
+ knot/semantic_check_data \
+ knot/test_semantic_check.in \
+ libzscanner/data \
+ libzscanner/test_zscanner.in \
+ libzscanner/TESTS
+
+check_LTLIBRARIES = libtap.la
+libtap_la_SOURCES = \
+ tap/basic.c \
+ tap/basic.h \
+ tap/files.c \
+ tap/files.h \
+ tap/float.c \
+ tap/float.h \
+ tap/macros.h
+
+tap_runtests_LDADD =
+@HAVE_DAEMON_TRUE@knot_test_acl_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_acl.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_conf_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_conf.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_confdb_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_confdb.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_confio_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_confio.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_process_query_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_process_query.c \
+@HAVE_DAEMON_TRUE@ knot/test_server.h \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+libdnssec_test_keystore_pkcs11_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DLIBDIR='"$(libdir)"'
+
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_CPPFLAGS = \
+@HAVE_LIBUTILS_TRUE@ $(AM_CPPFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libedit_CFLAGS)
+
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_LDADD = \
+@HAVE_LIBUTILS_TRUE@ libtap.la \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS)
+
+@HAVE_LIBUTILS_TRUE@utils_test_cert_CPPFLAGS = \
+@HAVE_LIBUTILS_TRUE@ $(AM_CPPFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libedit_CFLAGS)
+
+@HAVE_LIBUTILS_TRUE@utils_test_cert_LDADD = \
+@HAVE_LIBUTILS_TRUE@ libtap.la \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS)
+
+libzscanner_zscanner_tool_SOURCES = \
+ libzscanner/zscanner-tool.c \
+ libzscanner/processing.h \
+ libzscanner/processing.c
+
+check_SCRIPTS = libzscanner/test_zscanner $(am__append_8)
+edit = $(SED) \
+ -e 's|@top_srcdir[@]|$(abs_top_srcdir)|g' \
+ -e 's|@top_builddir[@]|$(abs_top_builddir)|g'
+
+CLEANFILES = $(check_SCRIPTS) $(EXTRA_PROGRAMS) runtests.log
+AM_V_RUNTESTS = $(am__v_RUNTESTS_@AM_V@)
+am__v_RUNTESTS_ = $(am__v_RUNTESTS_@AM_DEFAULT_V@)
+am__v_RUNTESTS_0 =
+am__v_RUNTESTS_1 = RET=$$?; if [ "$$RET" != "0" ]; then cat "$(builddir)/runtests.log"; exit $$RET; fi
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(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) --foreign tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/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: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-checkLTLIBRARIES:
+ -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES)
+ @list='$(check_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+tap/$(am__dirstamp):
+ @$(MKDIR_P) tap
+ @: > tap/$(am__dirstamp)
+tap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) tap/$(DEPDIR)
+ @: > tap/$(DEPDIR)/$(am__dirstamp)
+tap/basic.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+tap/files.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+tap/float.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+
+libtap.la: $(libtap_la_OBJECTS) $(libtap_la_DEPENDENCIES) $(EXTRA_libtap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libtap_la_OBJECTS) $(libtap_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_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
+contrib/$(am__dirstamp):
+ @$(MKDIR_P) contrib
+ @: > contrib/$(am__dirstamp)
+contrib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/$(DEPDIR)
+ @: > contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/test_base32hex.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base32hex$(EXEEXT): $(contrib_test_base32hex_OBJECTS) $(contrib_test_base32hex_DEPENDENCIES) $(EXTRA_contrib_test_base32hex_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base32hex$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base32hex_OBJECTS) $(contrib_test_base32hex_LDADD) $(LIBS)
+contrib/test_base64.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base64$(EXEEXT): $(contrib_test_base64_OBJECTS) $(contrib_test_base64_DEPENDENCIES) $(EXTRA_contrib_test_base64_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base64$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base64_OBJECTS) $(contrib_test_base64_LDADD) $(LIBS)
+contrib/test_dynarray.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_dynarray$(EXEEXT): $(contrib_test_dynarray_OBJECTS) $(contrib_test_dynarray_DEPENDENCIES) $(EXTRA_contrib_test_dynarray_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_dynarray$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_dynarray_OBJECTS) $(contrib_test_dynarray_LDADD) $(LIBS)
+contrib/test_heap.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_heap$(EXEEXT): $(contrib_test_heap_OBJECTS) $(contrib_test_heap_DEPENDENCIES) $(EXTRA_contrib_test_heap_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_heap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_heap_OBJECTS) $(contrib_test_heap_LDADD) $(LIBS)
+contrib/test_net.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_net$(EXEEXT): $(contrib_test_net_OBJECTS) $(contrib_test_net_DEPENDENCIES) $(EXTRA_contrib_test_net_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_net$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_net_OBJECTS) $(contrib_test_net_LDADD) $(LIBS)
+contrib/test_net_shortwrite.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_net_shortwrite$(EXEEXT): $(contrib_test_net_shortwrite_OBJECTS) $(contrib_test_net_shortwrite_DEPENDENCIES) $(EXTRA_contrib_test_net_shortwrite_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_net_shortwrite$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_net_shortwrite_OBJECTS) $(contrib_test_net_shortwrite_LDADD) $(LIBS)
+contrib/test_qp-trie.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_qp-trie$(EXEEXT): $(contrib_test_qp_trie_OBJECTS) $(contrib_test_qp_trie_DEPENDENCIES) $(EXTRA_contrib_test_qp_trie_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_qp-trie$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_qp_trie_OBJECTS) $(contrib_test_qp_trie_LDADD) $(LIBS)
+contrib/test_siphash.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_siphash$(EXEEXT): $(contrib_test_siphash_OBJECTS) $(contrib_test_siphash_DEPENDENCIES) $(EXTRA_contrib_test_siphash_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_siphash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_siphash_OBJECTS) $(contrib_test_siphash_LDADD) $(LIBS)
+contrib/test_sockaddr.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_sockaddr$(EXEEXT): $(contrib_test_sockaddr_OBJECTS) $(contrib_test_sockaddr_DEPENDENCIES) $(EXTRA_contrib_test_sockaddr_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_sockaddr$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_sockaddr_OBJECTS) $(contrib_test_sockaddr_LDADD) $(LIBS)
+contrib/test_string.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_string$(EXEEXT): $(contrib_test_string_OBJECTS) $(contrib_test_string_DEPENDENCIES) $(EXTRA_contrib_test_string_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_string$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_string_OBJECTS) $(contrib_test_string_LDADD) $(LIBS)
+contrib/test_strtonum.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_strtonum$(EXEEXT): $(contrib_test_strtonum_OBJECTS) $(contrib_test_strtonum_DEPENDENCIES) $(EXTRA_contrib_test_strtonum_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_strtonum$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_strtonum_OBJECTS) $(contrib_test_strtonum_LDADD) $(LIBS)
+contrib/test_time.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_time$(EXEEXT): $(contrib_test_time_OBJECTS) $(contrib_test_time_DEPENDENCIES) $(EXTRA_contrib_test_time_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_time$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_time_OBJECTS) $(contrib_test_time_LDADD) $(LIBS)
+contrib/test_wire_ctx.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_wire_ctx$(EXEEXT): $(contrib_test_wire_ctx_OBJECTS) $(contrib_test_wire_ctx_DEPENDENCIES) $(EXTRA_contrib_test_wire_ctx_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_wire_ctx$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_wire_ctx_OBJECTS) $(contrib_test_wire_ctx_LDADD) $(LIBS)
+knot/$(am__dirstamp):
+ @$(MKDIR_P) knot
+ @: > knot/$(am__dirstamp)
+knot/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/$(DEPDIR)
+ @: > knot/$(DEPDIR)/$(am__dirstamp)
+knot/test_acl.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_acl$(EXEEXT): $(knot_test_acl_OBJECTS) $(knot_test_acl_DEPENDENCIES) $(EXTRA_knot_test_acl_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_acl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_acl_OBJECTS) $(knot_test_acl_LDADD) $(LIBS)
+knot/test_changeset.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_changeset$(EXEEXT): $(knot_test_changeset_OBJECTS) $(knot_test_changeset_DEPENDENCIES) $(EXTRA_knot_test_changeset_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_changeset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_changeset_OBJECTS) $(knot_test_changeset_LDADD) $(LIBS)
+knot/test_conf.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_conf$(EXEEXT): $(knot_test_conf_OBJECTS) $(knot_test_conf_DEPENDENCIES) $(EXTRA_knot_test_conf_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_conf$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_conf_OBJECTS) $(knot_test_conf_LDADD) $(LIBS)
+knot/test_conf_tools.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_conf_tools$(EXEEXT): $(knot_test_conf_tools_OBJECTS) $(knot_test_conf_tools_DEPENDENCIES) $(EXTRA_knot_test_conf_tools_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_conf_tools$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_conf_tools_OBJECTS) $(knot_test_conf_tools_LDADD) $(LIBS)
+knot/test_confdb.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_confdb$(EXEEXT): $(knot_test_confdb_OBJECTS) $(knot_test_confdb_DEPENDENCIES) $(EXTRA_knot_test_confdb_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_confdb$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_confdb_OBJECTS) $(knot_test_confdb_LDADD) $(LIBS)
+knot/test_confio.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_confio$(EXEEXT): $(knot_test_confio_OBJECTS) $(knot_test_confio_DEPENDENCIES) $(EXTRA_knot_test_confio_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_confio$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_confio_OBJECTS) $(knot_test_confio_LDADD) $(LIBS)
+knot/test_dthreads.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_dthreads$(EXEEXT): $(knot_test_dthreads_OBJECTS) $(knot_test_dthreads_DEPENDENCIES) $(EXTRA_knot_test_dthreads_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_dthreads$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_dthreads_OBJECTS) $(knot_test_dthreads_LDADD) $(LIBS)
+knot/test_fdset.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_fdset$(EXEEXT): $(knot_test_fdset_OBJECTS) $(knot_test_fdset_DEPENDENCIES) $(EXTRA_knot_test_fdset_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_fdset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_fdset_OBJECTS) $(knot_test_fdset_LDADD) $(LIBS)
+knot/test_journal.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_journal$(EXEEXT): $(knot_test_journal_OBJECTS) $(knot_test_journal_DEPENDENCIES) $(EXTRA_knot_test_journal_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_journal$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_journal_OBJECTS) $(knot_test_journal_LDADD) $(LIBS)
+knot/test_kasp_db.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_kasp_db$(EXEEXT): $(knot_test_kasp_db_OBJECTS) $(knot_test_kasp_db_DEPENDENCIES) $(EXTRA_knot_test_kasp_db_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_kasp_db$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_kasp_db_OBJECTS) $(knot_test_kasp_db_LDADD) $(LIBS)
+knot/test_node.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_node$(EXEEXT): $(knot_test_node_OBJECTS) $(knot_test_node_DEPENDENCIES) $(EXTRA_knot_test_node_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_node$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_node_OBJECTS) $(knot_test_node_LDADD) $(LIBS)
+knot/test_process_query.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_process_query$(EXEEXT): $(knot_test_process_query_OBJECTS) $(knot_test_process_query_DEPENDENCIES) $(EXTRA_knot_test_process_query_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_process_query$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_process_query_OBJECTS) $(knot_test_process_query_LDADD) $(LIBS)
+knot/test_query_module.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_query_module$(EXEEXT): $(knot_test_query_module_OBJECTS) $(knot_test_query_module_DEPENDENCIES) $(EXTRA_knot_test_query_module_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_query_module$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_query_module_OBJECTS) $(knot_test_query_module_LDADD) $(LIBS)
+knot/test_requestor.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_requestor$(EXEEXT): $(knot_test_requestor_OBJECTS) $(knot_test_requestor_DEPENDENCIES) $(EXTRA_knot_test_requestor_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_requestor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_requestor_OBJECTS) $(knot_test_requestor_LDADD) $(LIBS)
+knot/test_server.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_server$(EXEEXT): $(knot_test_server_OBJECTS) $(knot_test_server_DEPENDENCIES) $(EXTRA_knot_test_server_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_server$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_server_OBJECTS) $(knot_test_server_LDADD) $(LIBS)
+knot/test_worker_pool.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_worker_pool$(EXEEXT): $(knot_test_worker_pool_OBJECTS) $(knot_test_worker_pool_DEPENDENCIES) $(EXTRA_knot_test_worker_pool_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_worker_pool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_worker_pool_OBJECTS) $(knot_test_worker_pool_LDADD) $(LIBS)
+knot/test_worker_queue.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_worker_queue$(EXEEXT): $(knot_test_worker_queue_OBJECTS) $(knot_test_worker_queue_DEPENDENCIES) $(EXTRA_knot_test_worker_queue_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_worker_queue$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_worker_queue_OBJECTS) $(knot_test_worker_queue_LDADD) $(LIBS)
+knot/test_zone-tree.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone-tree$(EXEEXT): $(knot_test_zone_tree_OBJECTS) $(knot_test_zone_tree_DEPENDENCIES) $(EXTRA_knot_test_zone_tree_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone-tree$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_tree_OBJECTS) $(knot_test_zone_tree_LDADD) $(LIBS)
+knot/test_zone-update.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone-update$(EXEEXT): $(knot_test_zone_update_OBJECTS) $(knot_test_zone_update_DEPENDENCIES) $(EXTRA_knot_test_zone_update_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone-update$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_update_OBJECTS) $(knot_test_zone_update_LDADD) $(LIBS)
+knot/test_zone_events.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_events$(EXEEXT): $(knot_test_zone_events_OBJECTS) $(knot_test_zone_events_DEPENDENCIES) $(EXTRA_knot_test_zone_events_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_events$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_events_OBJECTS) $(knot_test_zone_events_LDADD) $(LIBS)
+knot/test_zone_serial.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_serial$(EXEEXT): $(knot_test_zone_serial_OBJECTS) $(knot_test_zone_serial_DEPENDENCIES) $(EXTRA_knot_test_zone_serial_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_serial$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_serial_OBJECTS) $(knot_test_zone_serial_LDADD) $(LIBS)
+knot/test_zone_timers.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_timers$(EXEEXT): $(knot_test_zone_timers_OBJECTS) $(knot_test_zone_timers_DEPENDENCIES) $(EXTRA_knot_test_zone_timers_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_timers$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_timers_OBJECTS) $(knot_test_zone_timers_LDADD) $(LIBS)
+knot/test_zonedb.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zonedb$(EXEEXT): $(knot_test_zonedb_OBJECTS) $(knot_test_zonedb_DEPENDENCIES) $(EXTRA_knot_test_zonedb_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zonedb$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zonedb_OBJECTS) $(knot_test_zonedb_LDADD) $(LIBS)
+libdnssec/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec
+ @: > libdnssec/$(am__dirstamp)
+libdnssec/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/$(DEPDIR)
+ @: > libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/test_binary.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_binary$(EXEEXT): $(libdnssec_test_binary_OBJECTS) $(libdnssec_test_binary_DEPENDENCIES) $(EXTRA_libdnssec_test_binary_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_binary$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_binary_OBJECTS) $(libdnssec_test_binary_LDADD) $(LIBS)
+libdnssec/test_crypto.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_crypto$(EXEEXT): $(libdnssec_test_crypto_OBJECTS) $(libdnssec_test_crypto_DEPENDENCIES) $(EXTRA_libdnssec_test_crypto_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_crypto$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_crypto_OBJECTS) $(libdnssec_test_crypto_LDADD) $(LIBS)
+libdnssec/test_key.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key$(EXEEXT): $(libdnssec_test_key_OBJECTS) $(libdnssec_test_key_DEPENDENCIES) $(EXTRA_libdnssec_test_key_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_OBJECTS) $(libdnssec_test_key_LDADD) $(LIBS)
+libdnssec/test_key_algorithm.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key_algorithm$(EXEEXT): $(libdnssec_test_key_algorithm_OBJECTS) $(libdnssec_test_key_algorithm_DEPENDENCIES) $(EXTRA_libdnssec_test_key_algorithm_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key_algorithm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_algorithm_OBJECTS) $(libdnssec_test_key_algorithm_LDADD) $(LIBS)
+libdnssec/test_key_ds.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key_ds$(EXEEXT): $(libdnssec_test_key_ds_OBJECTS) $(libdnssec_test_key_ds_DEPENDENCIES) $(EXTRA_libdnssec_test_key_ds_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key_ds$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_ds_OBJECTS) $(libdnssec_test_key_ds_LDADD) $(LIBS)
+libdnssec/test_keyid.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keyid$(EXEEXT): $(libdnssec_test_keyid_OBJECTS) $(libdnssec_test_keyid_DEPENDENCIES) $(EXTRA_libdnssec_test_keyid_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keyid$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keyid_OBJECTS) $(libdnssec_test_keyid_LDADD) $(LIBS)
+libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.$(OBJEXT): \
+ libdnssec/$(am__dirstamp) libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keystore_pkcs11$(EXEEXT): $(libdnssec_test_keystore_pkcs11_OBJECTS) $(libdnssec_test_keystore_pkcs11_DEPENDENCIES) $(EXTRA_libdnssec_test_keystore_pkcs11_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keystore_pkcs11$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keystore_pkcs11_OBJECTS) $(libdnssec_test_keystore_pkcs11_LDADD) $(LIBS)
+libdnssec/test_keystore_pkcs8.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keystore_pkcs8$(EXEEXT): $(libdnssec_test_keystore_pkcs8_OBJECTS) $(libdnssec_test_keystore_pkcs8_DEPENDENCIES) $(EXTRA_libdnssec_test_keystore_pkcs8_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keystore_pkcs8$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keystore_pkcs8_OBJECTS) $(libdnssec_test_keystore_pkcs8_LDADD) $(LIBS)
+libdnssec/test_keystore_pkcs8_dir.$(OBJEXT): \
+ libdnssec/$(am__dirstamp) libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keystore_pkcs8_dir$(EXEEXT): $(libdnssec_test_keystore_pkcs8_dir_OBJECTS) $(libdnssec_test_keystore_pkcs8_dir_DEPENDENCIES) $(EXTRA_libdnssec_test_keystore_pkcs8_dir_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keystore_pkcs8_dir$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keystore_pkcs8_dir_OBJECTS) $(libdnssec_test_keystore_pkcs8_dir_LDADD) $(LIBS)
+libdnssec/test_keytag.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keytag$(EXEEXT): $(libdnssec_test_keytag_OBJECTS) $(libdnssec_test_keytag_DEPENDENCIES) $(EXTRA_libdnssec_test_keytag_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keytag$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keytag_OBJECTS) $(libdnssec_test_keytag_LDADD) $(LIBS)
+libdnssec/test_list.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_list$(EXEEXT): $(libdnssec_test_list_OBJECTS) $(libdnssec_test_list_DEPENDENCIES) $(EXTRA_libdnssec_test_list_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_list$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_list_OBJECTS) $(libdnssec_test_list_LDADD) $(LIBS)
+libdnssec/test_nsec_bitmap.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_nsec_bitmap$(EXEEXT): $(libdnssec_test_nsec_bitmap_OBJECTS) $(libdnssec_test_nsec_bitmap_DEPENDENCIES) $(EXTRA_libdnssec_test_nsec_bitmap_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_nsec_bitmap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_nsec_bitmap_OBJECTS) $(libdnssec_test_nsec_bitmap_LDADD) $(LIBS)
+libdnssec/test_nsec_hash.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_nsec_hash$(EXEEXT): $(libdnssec_test_nsec_hash_OBJECTS) $(libdnssec_test_nsec_hash_DEPENDENCIES) $(EXTRA_libdnssec_test_nsec_hash_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_nsec_hash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_nsec_hash_OBJECTS) $(libdnssec_test_nsec_hash_LDADD) $(LIBS)
+libdnssec/test_random.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_random$(EXEEXT): $(libdnssec_test_random_OBJECTS) $(libdnssec_test_random_DEPENDENCIES) $(EXTRA_libdnssec_test_random_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_random$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_random_OBJECTS) $(libdnssec_test_random_LDADD) $(LIBS)
+libdnssec/test_shared_bignum.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_shared_bignum$(EXEEXT): $(libdnssec_test_shared_bignum_OBJECTS) $(libdnssec_test_shared_bignum_DEPENDENCIES) $(EXTRA_libdnssec_test_shared_bignum_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_shared_bignum$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_shared_bignum_OBJECTS) $(libdnssec_test_shared_bignum_LDADD) $(LIBS)
+libdnssec/test_shared_dname.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_shared_dname$(EXEEXT): $(libdnssec_test_shared_dname_OBJECTS) $(libdnssec_test_shared_dname_DEPENDENCIES) $(EXTRA_libdnssec_test_shared_dname_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_shared_dname$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_shared_dname_OBJECTS) $(libdnssec_test_shared_dname_LDADD) $(LIBS)
+libdnssec/test_sign.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_sign$(EXEEXT): $(libdnssec_test_sign_OBJECTS) $(libdnssec_test_sign_DEPENDENCIES) $(EXTRA_libdnssec_test_sign_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_sign$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_sign_OBJECTS) $(libdnssec_test_sign_LDADD) $(LIBS)
+libdnssec/test_sign_der.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_sign_der$(EXEEXT): $(libdnssec_test_sign_der_OBJECTS) $(libdnssec_test_sign_der_DEPENDENCIES) $(EXTRA_libdnssec_test_sign_der_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_sign_der$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_sign_der_OBJECTS) $(libdnssec_test_sign_der_LDADD) $(LIBS)
+libdnssec/test_tsig.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_tsig$(EXEEXT): $(libdnssec_test_tsig_OBJECTS) $(libdnssec_test_tsig_DEPENDENCIES) $(EXTRA_libdnssec_test_tsig_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_tsig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_tsig_OBJECTS) $(libdnssec_test_tsig_LDADD) $(LIBS)
+libknot/$(am__dirstamp):
+ @$(MKDIR_P) libknot
+ @: > libknot/$(am__dirstamp)
+libknot/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/$(DEPDIR)
+ @: > libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/test_control.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_control$(EXEEXT): $(libknot_test_control_OBJECTS) $(libknot_test_control_DEPENDENCIES) $(EXTRA_libknot_test_control_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_control$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_control_OBJECTS) $(libknot_test_control_LDADD) $(LIBS)
+libknot/test_cookies.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_cookies$(EXEEXT): $(libknot_test_cookies_OBJECTS) $(libknot_test_cookies_DEPENDENCIES) $(EXTRA_libknot_test_cookies_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_cookies$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_cookies_OBJECTS) $(libknot_test_cookies_LDADD) $(LIBS)
+libknot/test_db.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_db$(EXEEXT): $(libknot_test_db_OBJECTS) $(libknot_test_db_DEPENDENCIES) $(EXTRA_libknot_test_db_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_db$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_db_OBJECTS) $(libknot_test_db_LDADD) $(LIBS)
+libknot/test_descriptor.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_descriptor$(EXEEXT): $(libknot_test_descriptor_OBJECTS) $(libknot_test_descriptor_DEPENDENCIES) $(EXTRA_libknot_test_descriptor_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_descriptor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_descriptor_OBJECTS) $(libknot_test_descriptor_LDADD) $(LIBS)
+libknot/test_dname.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_dname$(EXEEXT): $(libknot_test_dname_OBJECTS) $(libknot_test_dname_DEPENDENCIES) $(EXTRA_libknot_test_dname_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_dname$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_dname_OBJECTS) $(libknot_test_dname_LDADD) $(LIBS)
+libknot/test_edns.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_edns$(EXEEXT): $(libknot_test_edns_OBJECTS) $(libknot_test_edns_DEPENDENCIES) $(EXTRA_libknot_test_edns_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_edns$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_edns_OBJECTS) $(libknot_test_edns_LDADD) $(LIBS)
+libknot/test_edns_ecs.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_edns_ecs$(EXEEXT): $(libknot_test_edns_ecs_OBJECTS) $(libknot_test_edns_ecs_DEPENDENCIES) $(EXTRA_libknot_test_edns_ecs_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_edns_ecs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_edns_ecs_OBJECTS) $(libknot_test_edns_ecs_LDADD) $(LIBS)
+libknot/test_endian.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_endian$(EXEEXT): $(libknot_test_endian_OBJECTS) $(libknot_test_endian_DEPENDENCIES) $(EXTRA_libknot_test_endian_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_endian$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_endian_OBJECTS) $(libknot_test_endian_LDADD) $(LIBS)
+libknot/test_lookup.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_lookup$(EXEEXT): $(libknot_test_lookup_OBJECTS) $(libknot_test_lookup_DEPENDENCIES) $(EXTRA_libknot_test_lookup_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_lookup$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_lookup_OBJECTS) $(libknot_test_lookup_LDADD) $(LIBS)
+libknot/test_pkt.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_pkt$(EXEEXT): $(libknot_test_pkt_OBJECTS) $(libknot_test_pkt_DEPENDENCIES) $(EXTRA_libknot_test_pkt_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_pkt$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_pkt_OBJECTS) $(libknot_test_pkt_LDADD) $(LIBS)
+libknot/test_rdata.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rdata$(EXEEXT): $(libknot_test_rdata_OBJECTS) $(libknot_test_rdata_DEPENDENCIES) $(EXTRA_libknot_test_rdata_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rdata$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rdata_OBJECTS) $(libknot_test_rdata_LDADD) $(LIBS)
+libknot/test_rdataset.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rdataset$(EXEEXT): $(libknot_test_rdataset_OBJECTS) $(libknot_test_rdataset_DEPENDENCIES) $(EXTRA_libknot_test_rdataset_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rdataset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rdataset_OBJECTS) $(libknot_test_rdataset_LDADD) $(LIBS)
+libknot/test_rrset.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rrset$(EXEEXT): $(libknot_test_rrset_OBJECTS) $(libknot_test_rrset_DEPENDENCIES) $(EXTRA_libknot_test_rrset_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rrset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rrset_OBJECTS) $(libknot_test_rrset_LDADD) $(LIBS)
+libknot/test_rrset-wire.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rrset-wire$(EXEEXT): $(libknot_test_rrset_wire_OBJECTS) $(libknot_test_rrset_wire_DEPENDENCIES) $(EXTRA_libknot_test_rrset_wire_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rrset-wire$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rrset_wire_OBJECTS) $(libknot_test_rrset_wire_LDADD) $(LIBS)
+libknot/test_tsig.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_tsig$(EXEEXT): $(libknot_test_tsig_OBJECTS) $(libknot_test_tsig_DEPENDENCIES) $(EXTRA_libknot_test_tsig_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_tsig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_tsig_OBJECTS) $(libknot_test_tsig_LDADD) $(LIBS)
+libknot/test_wire.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_wire$(EXEEXT): $(libknot_test_wire_OBJECTS) $(libknot_test_wire_DEPENDENCIES) $(EXTRA_libknot_test_wire_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_wire$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_wire_OBJECTS) $(libknot_test_wire_LDADD) $(LIBS)
+libknot/test_yparser.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_yparser$(EXEEXT): $(libknot_test_yparser_OBJECTS) $(libknot_test_yparser_DEPENDENCIES) $(EXTRA_libknot_test_yparser_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_yparser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_yparser_OBJECTS) $(libknot_test_yparser_LDADD) $(LIBS)
+libknot/test_ypschema.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_ypschema$(EXEEXT): $(libknot_test_ypschema_OBJECTS) $(libknot_test_ypschema_DEPENDENCIES) $(EXTRA_libknot_test_ypschema_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_ypschema$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_ypschema_OBJECTS) $(libknot_test_ypschema_LDADD) $(LIBS)
+libknot/test_yptrafo.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_yptrafo$(EXEEXT): $(libknot_test_yptrafo_OBJECTS) $(libknot_test_yptrafo_DEPENDENCIES) $(EXTRA_libknot_test_yptrafo_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_yptrafo$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_yptrafo_OBJECTS) $(libknot_test_yptrafo_LDADD) $(LIBS)
+libzscanner/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner
+ @: > libzscanner/$(am__dirstamp)
+libzscanner/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner/$(DEPDIR)
+ @: > libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/zscanner-tool.$(OBJEXT): libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/processing.$(OBJEXT): libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+
+libzscanner/zscanner-tool$(EXEEXT): $(libzscanner_zscanner_tool_OBJECTS) $(libzscanner_zscanner_tool_DEPENDENCIES) $(EXTRA_libzscanner_zscanner_tool_DEPENDENCIES) libzscanner/$(am__dirstamp)
+ @rm -f libzscanner/zscanner-tool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libzscanner_zscanner_tool_OBJECTS) $(libzscanner_zscanner_tool_LDADD) $(LIBS)
+modules/$(am__dirstamp):
+ @$(MKDIR_P) modules
+ @: > modules/$(am__dirstamp)
+modules/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) modules/$(DEPDIR)
+ @: > modules/$(DEPDIR)/$(am__dirstamp)
+modules/test_onlinesign.$(OBJEXT): modules/$(am__dirstamp) \
+ modules/$(DEPDIR)/$(am__dirstamp)
+
+modules/test_onlinesign$(EXEEXT): $(modules_test_onlinesign_OBJECTS) $(modules_test_onlinesign_DEPENDENCIES) $(EXTRA_modules_test_onlinesign_DEPENDENCIES) modules/$(am__dirstamp)
+ @rm -f modules/test_onlinesign$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(modules_test_onlinesign_OBJECTS) $(modules_test_onlinesign_LDADD) $(LIBS)
+modules/test_rrl.$(OBJEXT): modules/$(am__dirstamp) \
+ modules/$(DEPDIR)/$(am__dirstamp)
+
+modules/test_rrl$(EXEEXT): $(modules_test_rrl_OBJECTS) $(modules_test_rrl_DEPENDENCIES) $(EXTRA_modules_test_rrl_DEPENDENCIES) modules/$(am__dirstamp)
+ @rm -f modules/test_rrl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(modules_test_rrl_OBJECTS) $(modules_test_rrl_LDADD) $(LIBS)
+tap/runtests.$(OBJEXT): tap/$(am__dirstamp) \
+ tap/$(DEPDIR)/$(am__dirstamp)
+
+tap/runtests$(EXEEXT): $(tap_runtests_OBJECTS) $(tap_runtests_DEPENDENCIES) $(EXTRA_tap_runtests_DEPENDENCIES) tap/$(am__dirstamp)
+ @rm -f tap/runtests$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tap_runtests_OBJECTS) $(tap_runtests_LDADD) $(LIBS)
+utils/$(am__dirstamp):
+ @$(MKDIR_P) utils
+ @: > utils/$(am__dirstamp)
+utils/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/$(DEPDIR)
+ @: > utils/$(DEPDIR)/$(am__dirstamp)
+utils/utils_test_cert-test_cert.$(OBJEXT): utils/$(am__dirstamp) \
+ utils/$(DEPDIR)/$(am__dirstamp)
+
+utils/test_cert$(EXEEXT): $(utils_test_cert_OBJECTS) $(utils_test_cert_DEPENDENCIES) $(EXTRA_utils_test_cert_DEPENDENCIES) utils/$(am__dirstamp)
+ @rm -f utils/test_cert$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(utils_test_cert_OBJECTS) $(utils_test_cert_LDADD) $(LIBS)
+utils/utils_test_lookup-test_lookup.$(OBJEXT): utils/$(am__dirstamp) \
+ utils/$(DEPDIR)/$(am__dirstamp)
+
+utils/test_lookup$(EXEEXT): $(utils_test_lookup_OBJECTS) $(utils_test_lookup_DEPENDENCIES) $(EXTRA_utils_test_lookup_DEPENDENCIES) utils/$(am__dirstamp)
+ @rm -f utils/test_lookup$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(utils_test_lookup_OBJECTS) $(utils_test_lookup_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f contrib/*.$(OBJEXT)
+ -rm -f knot/*.$(OBJEXT)
+ -rm -f libdnssec/*.$(OBJEXT)
+ -rm -f libknot/*.$(OBJEXT)
+ -rm -f libzscanner/*.$(OBJEXT)
+ -rm -f modules/*.$(OBJEXT)
+ -rm -f tap/*.$(OBJEXT)
+ -rm -f tap/*.lo
+ -rm -f utils/*.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base32hex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base64.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_dynarray.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_heap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net_shortwrite.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_qp-trie.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_siphash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_sockaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_string.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_strtonum.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_time.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_wire_ctx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_acl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_changeset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf_tools.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_dthreads.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_fdset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_journal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_kasp_db.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_node.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_process_query.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_query_module.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_requestor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_pool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-tree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-update.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_events.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_serial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_timers.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zonedb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_binary.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_algorithm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_ds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keyid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs8_dir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keytag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_bitmap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_random.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_bignum.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_dname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign_der.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_tsig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_cookies.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_db.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_descriptor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_dname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns_ecs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_endian.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_lookup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_pkt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdataset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset-wire.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_tsig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_wire.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yparser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_ypschema.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yptrafo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/processing.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/zscanner-tool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_onlinesign.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_rrl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/basic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/files.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/float.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/runtests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/utils_test_cert-test_cert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.o: libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.o -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.o `test -f 'libdnssec/test_keystore_pkcs11.c' || echo '$(srcdir)/'`libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.o `test -f 'libdnssec/test_keystore_pkcs11.c' || echo '$(srcdir)/'`libdnssec/test_keystore_pkcs11.c
+
+libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.obj: libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.obj -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.obj `if test -f 'libdnssec/test_keystore_pkcs11.c'; then $(CYGPATH_W) 'libdnssec/test_keystore_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/libdnssec/test_keystore_pkcs11.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_test_keystore_pkcs11-test_keystore_pkcs11.obj `if test -f 'libdnssec/test_keystore_pkcs11.c'; then $(CYGPATH_W) 'libdnssec/test_keystore_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/libdnssec/test_keystore_pkcs11.c'; fi`
+
+utils/utils_test_cert-test_cert.o: utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_cert-test_cert.o -MD -MP -MF utils/$(DEPDIR)/utils_test_cert-test_cert.Tpo -c -o utils/utils_test_cert-test_cert.o `test -f 'utils/test_cert.c' || echo '$(srcdir)/'`utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_cert-test_cert.Tpo utils/$(DEPDIR)/utils_test_cert-test_cert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_cert.c' object='utils/utils_test_cert-test_cert.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_cert-test_cert.o `test -f 'utils/test_cert.c' || echo '$(srcdir)/'`utils/test_cert.c
+
+utils/utils_test_cert-test_cert.obj: utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_cert-test_cert.obj -MD -MP -MF utils/$(DEPDIR)/utils_test_cert-test_cert.Tpo -c -o utils/utils_test_cert-test_cert.obj `if test -f 'utils/test_cert.c'; then $(CYGPATH_W) 'utils/test_cert.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_cert.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_cert-test_cert.Tpo utils/$(DEPDIR)/utils_test_cert-test_cert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_cert.c' object='utils/utils_test_cert-test_cert.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_cert-test_cert.obj `if test -f 'utils/test_cert.c'; then $(CYGPATH_W) 'utils/test_cert.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_cert.c'; fi`
+
+utils/utils_test_lookup-test_lookup.o: utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_lookup-test_lookup.o -MD -MP -MF utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo -c -o utils/utils_test_lookup-test_lookup.o `test -f 'utils/test_lookup.c' || echo '$(srcdir)/'`utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_lookup.c' object='utils/utils_test_lookup-test_lookup.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_lookup-test_lookup.o `test -f 'utils/test_lookup.c' || echo '$(srcdir)/'`utils/test_lookup.c
+
+utils/utils_test_lookup-test_lookup.obj: utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_lookup-test_lookup.obj -MD -MP -MF utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo -c -o utils/utils_test_lookup-test_lookup.obj `if test -f 'utils/test_lookup.c'; then $(CYGPATH_W) 'utils/test_lookup.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_lookup.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_lookup.c' object='utils/utils_test_lookup-test_lookup.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_lookup-test_lookup.obj `if test -f 'utils/test_lookup.c'; then $(CYGPATH_W) 'utils/test_lookup.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_lookup.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf contrib/.libs contrib/_libs
+ -rm -rf knot/.libs knot/_libs
+ -rm -rf libdnssec/.libs libdnssec/_libs
+ -rm -rf libknot/.libs libknot/_libs
+ -rm -rf libzscanner/.libs libzscanner/_libs
+ -rm -rf modules/.libs modules/_libs
+ -rm -rf tap/.libs tap/_libs
+ -rm -rf utils/.libs utils/_libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ 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-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ 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"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS) \
+ $(check_SCRIPTS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+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)
+ -rm -f contrib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/$(am__dirstamp)
+ -rm -f knot/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/$(am__dirstamp)
+ -rm -f libdnssec/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/$(am__dirstamp)
+ -rm -f libknot/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/$(am__dirstamp)
+ -rm -f libzscanner/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libzscanner/$(am__dirstamp)
+ -rm -f modules/$(DEPDIR)/$(am__dirstamp)
+ -rm -f modules/$(am__dirstamp)
+ -rm -f tap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f tap/$(am__dirstamp)
+ -rm -f utils/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf contrib/$(DEPDIR) knot/$(DEPDIR) libdnssec/$(DEPDIR) libknot/$(DEPDIR) libzscanner/$(DEPDIR) modules/$(DEPDIR) tap/$(DEPDIR) utils/$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+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 contrib/$(DEPDIR) knot/$(DEPDIR) libdnssec/$(DEPDIR) libknot/$(DEPDIR) libzscanner/$(DEPDIR) modules/$(DEPDIR) tap/$(DEPDIR) utils/$(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:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \
+ clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \
+ clean-libtool cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic 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-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ 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 tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+@HAVE_LIBUTILS_TRUE@knot/test_semantic_check:
+@HAVE_LIBUTILS_TRUE@ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+@HAVE_LIBUTILS_TRUE@ @chmod +x $(top_builddir)/tests/$@
+
+libzscanner/test_zscanner: libzscanner/zscanner-tool
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+
+check-compile: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+check-local: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+ @$(top_builddir)/tests/tap/runtests -s $(srcdir) -b $(builddir) \
+ -L $(builddir)/runtests.log $(check_PROGRAMS) $(check_SCRIPTS); \
+ $(AM_V_RUNTESTS)
+
+# 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/tests/contrib/test_base32hex.c b/tests/contrib/test_base32hex.c
new file mode 100644
index 0000000..4c52fad
--- /dev/null
+++ b/tests/contrib/test_base32hex.c
@@ -0,0 +1,267 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base32hex.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define BUF_LEN 256
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 8) * 5)
+
+int main(int argc, char *argv[])
+{
+ plan(67);
+
+ int32_t ret;
+ uint8_t in[BUF_LEN], ref[BUF_LEN], out[BUF_LEN], out2[BUF_LEN], *out3;
+ uint32_t in_len, ref_len;
+
+ // 0. test invalid input
+ ret = base32hex_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "base32hex_encode: NULL input buffer");
+ ret = base32hex_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "base32hex_encode: NULL output buffer");
+ ret = base32hex_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base32hex_encode: input buffer too large");
+ ret = base32hex_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base32hex_encode: output buffer too small");
+
+ ret = base32hex_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "base32hex_encode_alloc: NULL input buffer");
+ ret = base32hex_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "base32hex_encode_alloc: input buffer too large");
+ ret = base32hex_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "base32hex_encode_alloc: NULL output buffer");
+
+ ret = base32hex_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "base32hex_decode: NULL input buffer");
+ ret = base32hex_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "base32hex_decode: NULL output buffer");
+ ret = base32hex_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base32hex_decode: input buffer too large");
+ ret = base32hex_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "base32hex_decode: output buffer too small");
+
+ ret = base32hex_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "base32hex_decode_alloc: NULL input buffer");
+ ret = base32hex_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "base32hex_decode_aloc: input buffer too large");
+ ret = base32hex_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "base32hex_decode_alloc: NULL output buffer");
+
+ // 1. test vector -> ENC -> DEC
+ strlcpy((char *)in, "", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "1. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "1. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "1. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "1. test vector - DEC output content");
+ }
+
+ // 2. test vector -> ENC -> DEC
+ strlcpy((char *)in, "f", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "co======", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "2. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "2. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "2. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "2. test vector - DEC output content");
+ }
+
+ // 3. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpng====", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "3. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "3. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "3. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "3. test vector - DEC output content");
+ }
+
+ // 4. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmu===", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "4. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "4. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "4. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "4. test vector - DEC output content");
+ }
+
+ // 5. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foob", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuog=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "5. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "5. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "5. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "5. test vector - DEC output content");
+ }
+
+ // 6. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fooba", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuoj1", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "6. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "6. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "6. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "6. test vector - DEC output content");
+ }
+
+ // 7. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foobar", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuoj1e8======", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "7. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "7. test vector - ENC output content");
+ }
+ ret = base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "7. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "7. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = base32hex_decode((uint8_t *)"AAAAAA==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 2");
+ ret = base32hex_decode((uint8_t *)"AAA=====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 5");
+ ret = base32hex_decode((uint8_t *)"A=======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 7");
+ ret = base32hex_decode((uint8_t *)"========", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 8");
+ ret = base32hex_decode((uint8_t *)"AAAAA=A=", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 2");
+ ret = base32hex_decode((uint8_t *)"AA=A====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 5");
+ ret = base32hex_decode((uint8_t *)"=A======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 7");
+ ret = base32hex_decode((uint8_t *)"CO======CO======", 16, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Two octects with padding");
+
+ // Bad data length
+ ret = base32hex_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 1");
+ ret = base32hex_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 2");
+ ret = base32hex_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 3");
+ ret = base32hex_decode((uint8_t *)"AAAA", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 4");
+ ret = base32hex_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 5");
+ ret = base32hex_decode((uint8_t *)"AAAAAA", 6, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 6");
+ ret = base32hex_decode((uint8_t *)"AAAAAAA", 7, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 7");
+ ret = base32hex_decode((uint8_t *)"AAAAAAAAA", 9, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 9");
+
+ // Bad data character
+ ret = base32hex_decode((uint8_t *)"AAAAAAA$", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar");
+ ret = base32hex_decode((uint8_t *)"AAAAAAA ", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character space");
+ ret = base32hex_decode((uint8_t *)"AAAAAA$A", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 7");
+ ret = base32hex_decode((uint8_t *)"AAAAA$AA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 6");
+ ret = base32hex_decode((uint8_t *)"AAAA$AAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 5");
+ ret = base32hex_decode((uint8_t *)"AAA$AAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 4");
+ ret = base32hex_decode((uint8_t *)"AA$AAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 3");
+ ret = base32hex_decode((uint8_t *)"A$AAAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 2");
+ ret = base32hex_decode((uint8_t *)"$AAAAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 1");
+
+ return 0;
+}
diff --git a/tests/contrib/test_base64.c b/tests/contrib/test_base64.c
new file mode 100644
index 0000000..45e4e55
--- /dev/null
+++ b/tests/contrib/test_base64.c
@@ -0,0 +1,237 @@
+/* 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base64.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define BUF_LEN 256
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3)
+
+int main(int argc, char *argv[])
+{
+ plan(52);
+
+ int32_t ret;
+ uint8_t in[BUF_LEN], ref[BUF_LEN], out[BUF_LEN], out2[BUF_LEN], *out3;
+ uint32_t in_len, ref_len;
+
+ // 0. test invalid input
+ ret = base64_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "base64_encode: NULL input buffer");
+ ret = base64_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "base64_encode: NULL output buffer");
+ ret = base64_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base64_encode: input buffer too large");
+ ret = base64_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base64_encode: output buffer too small");
+
+ ret = base64_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "base64_encode_alloc: NULL input buffer");
+ ret = base64_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "base64_encode_alloc: input buffer too large");
+ ret = base64_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "base64_encode_alloc: NULL output buffer");
+
+ ret = base64_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "base64_decode: NULL input buffer");
+ ret = base64_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "base64_decode: NULL output buffer");
+ ret = base64_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "base64_decode: input buffer too large");
+ ret = base64_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "base64_decode: output buffer too small");
+
+ ret = base64_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "base64_decode_alloc: NULL input buffer");
+ ret = base64_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "base64_decode_aloc: input buffer too large");
+ ret = base64_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "base64_decode_alloc: NULL output buffer");
+
+ // 1. test vector -> ENC -> DEC
+ strlcpy((char *)in, "", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "1. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "1. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "1. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "1. test vector - DEC output content");
+ }
+
+ // 2. test vector -> ENC -> DEC
+ strlcpy((char *)in, "f", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zg==", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "2. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "2. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "2. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "2. test vector - DEC output content");
+ }
+
+ // 3. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm8=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "3. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "3. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "3. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "3. test vector - DEC output content");
+ }
+
+ // 4. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9v", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "4. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "4. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "4. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "4. test vector - DEC output content");
+ }
+
+ // 5. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foob", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYg==", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "5. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "5. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "5. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "5. test vector - DEC output content");
+ }
+
+ // 6. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fooba", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmE=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "6. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "6. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "6. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "6. test vector - DEC output content");
+ }
+
+ // 7. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foobar", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmFy", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "7. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "7. test vector - ENC output content");
+ }
+ ret = base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "7. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "7. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = base64_decode((uint8_t *)"A===", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 3");
+ ret = base64_decode((uint8_t *)"====", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 4");
+ ret = base64_decode((uint8_t *)"AA=A", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding character on position 2");
+ ret = base64_decode((uint8_t *)"Zg==Zg==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Two quartets with padding");
+
+ // Bad data length
+ ret = base64_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 1");
+ ret = base64_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 2");
+ ret = base64_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 3");
+ ret = base64_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 5");
+
+ // Bad data character
+ ret = base64_decode((uint8_t *)"AAA$", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character dollar");
+ ret = base64_decode((uint8_t *)"AAA ", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character space");
+
+ return 0;
+}
diff --git a/tests/contrib/test_dynarray.c b/tests/contrib/test_dynarray.c
new file mode 100644
index 0000000..3e4c57f
--- /dev/null
+++ b/tests/contrib/test_dynarray.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "contrib/dynarray.h"
+
+#define test_capacity 5
+// minimum 3
+
+typedef struct {
+ int x;
+ int x2;
+} quadrate_t;
+
+dynarray_declare(q, quadrate_t, DYNARRAY_VISIBILITY_STATIC, test_capacity);
+dynarray_define(q, quadrate_t, DYNARRAY_VISIBILITY_STATIC);
+
+static q_dynarray_t q_fill(size_t howmany)
+{
+ quadrate_t q = { 0 };
+ q_dynarray_t qd = { 0 };
+
+ for (size_t i = 0; i < howmany; i++) {
+ q.x2 = q.x * q.x;
+ q_dynarray_add(&qd, &q);
+ q.x++;
+ }
+ return qd;
+}
+
+static void check_arr(q_dynarray_t *q, size_t count, size_t index, const char *msg)
+{
+ quadrate_t *arr = q->arr(q);
+ ok(arr[index].x == index && arr[index].x2 == index * index,
+ "%s check: index %zu", msg, index);
+
+ size_t i = 0;
+ dynarray_foreach(q, quadrate_t, p, *q) {
+ ok(p->x == i && p->x2 == i * i, "%s foreach: index %zu", msg, i);
+ i++;
+ }
+
+ ok(i == count, "%s foreach: whole array", msg);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // first fill
+ q_dynarray_t q = q_fill(test_capacity - 1);
+ check_arr(&q, test_capacity - 1, test_capacity - 3, "initial");
+ q_dynarray_free(&q);
+
+ // second fill
+ q = q_fill(test_capacity + 3);
+ check_arr(&q, test_capacity + 3, test_capacity + 1, "second");
+ q_dynarray_free(&q);
+
+ // third fill
+ q = q_fill(test_capacity * 5);
+ check_arr(&q, test_capacity * 5, test_capacity * 4, "third");
+ q_dynarray_free(&q);
+
+ return 0;
+}
diff --git a/tests/contrib/test_heap.c b/tests/contrib/test_heap.c
new file mode 100644
index 0000000..e49204c
--- /dev/null
+++ b/tests/contrib/test_heap.c
@@ -0,0 +1,166 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/ucw/heap.h"
+
+static void seed_random(void)
+{
+ unsigned short int seed[3] = { 0 };
+
+ FILE *f = fopen("/dev/urandom", "r");
+ if (f) {
+ if (fread(seed, sizeof(seed), 1, f) != 1) {
+ diag("failed to seed random source");
+ }
+ fclose(f);
+ }
+
+ diag("seed %hu %hu %hu", seed[0], seed[1], seed[2]);
+ seed48(seed);
+}
+
+struct value {
+ heap_val_t _heap;
+ int data;
+};
+
+static int value_cmp(void *_a, void *_b)
+{
+ const struct value *a = _a;
+ const struct value *b = _b;
+ return (a->data - b->data);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ seed_random();
+
+ static const int VALUE_COUNT = 1000;
+ static const int VALUE_RANGE = 950;
+ static const int VALUE_REPLACE = 300;
+ static const int VALUE_DELETE = 100;
+
+ struct heap heap;
+ heap_init(&heap, value_cmp, 0);
+
+ ok(EMPTY_HEAP(&heap), "heap is empty");
+
+ // fill the heap with random values (with duplicates)
+
+ struct value *values = calloc(VALUE_COUNT, sizeof(struct value));
+ assert(values);
+ assert(VALUE_RANGE < VALUE_COUNT);
+
+ bool valid = true;
+ for (int i = 0; i < VALUE_COUNT; i++) {
+ values[i].data = lrand48() % VALUE_RANGE;
+ if (heap_insert(&heap, &values[i]._heap) == 0) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_insert");
+ ok(!EMPTY_HEAP(&heap), "heap is non-empty");
+
+ // exercise heap_insert
+
+ valid = true;
+ for (int i = 0; i < VALUE_COUNT; i++) {
+ int pos = heap_find(&heap, &values[i]._heap);
+ if (*HELEMENT(&heap, pos) != &values[i]._heap) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_find");
+
+ // exercise heap_replace
+
+ assert(VALUE_REPLACE <= VALUE_COUNT);
+ struct value *replaced = calloc(VALUE_REPLACE, sizeof(struct value));
+ assert(replaced);
+
+ valid = true;
+ for (int i = 0; i < VALUE_REPLACE; i++) {
+ replaced[i].data = lrand48() % VALUE_RANGE;
+ int pos = heap_find(&heap, &values[i]._heap);
+ if (pos < 1) {
+ valid = false;
+ continue;
+ }
+
+ heap_replace(&heap, pos, &replaced[i]._heap);
+ int newpos = heap_find(&heap, &replaced[i]._heap);
+ if (newpos < 1) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_replace");
+
+ // exercise heap_delete
+
+ assert(VALUE_REPLACE + VALUE_DELETE < VALUE_COUNT);
+
+ valid = true;
+ for (int i = 0; i < VALUE_DELETE; i++) {
+ heap_val_t *value = &values[i + VALUE_REPLACE]._heap;
+ int pos = heap_find(&heap, value);
+ if (pos < 1) {
+ valid = false;
+ continue;
+
+ }
+ heap_delete(&heap, pos);
+ pos = heap_find(&heap, value);
+ if (pos != 0) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_delete");
+
+ // exercise item retrieval
+
+ assert(VALUE_COUNT > VALUE_DELETE);
+
+ valid = true;
+ int current = -1;
+ for (int i = 0; i < VALUE_COUNT - VALUE_DELETE; i++) {
+ struct value *val = (struct value *)*HHEAD(&heap);
+ heap_delmin(&heap);
+ if (current <= val->data) {
+ current = val->data;
+ } else {
+ valid = false;
+ }
+ }
+
+ ok(valid, "heap ordering");
+ ok(EMPTY_HEAP(&heap), "heap_delmin");
+
+ free(replaced);
+ free(values);
+ heap_deinit(&heap);
+
+ return 0;
+}
diff --git a/tests/contrib/test_net.c b/tests/contrib/test_net.c
new file mode 100644
index 0000000..9583362
--- /dev/null
+++ b/tests/contrib/test_net.c
@@ -0,0 +1,759 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "libknot/errcode.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+#undef ENABLE_NET_UNREACHABLE_TEST
+//#define ENABLE_NET_UNREACHABLE_TEST
+
+const int TIMEOUT = 5000;
+const int TIMEOUT_SHORT = 500;
+
+/*!
+ * \brief Get loopback socket address with unset port.
+ */
+static struct sockaddr_storage addr_local(void)
+{
+ struct sockaddr_storage addr = { 0 };
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_addr = in6addr_loopback;
+
+ return addr;
+}
+
+#ifdef ENABLE_NET_UNREACHABLE_TEST
+/*!
+ * \brief Get unreachable IPv6 address.
+ *
+ * Allocated from 100::/64 (Discard-Only Address Block).
+ */
+static struct sockaddr_storage addr_unreachable(void)
+{
+ struct sockaddr_storage addr = { 0 };
+ sockaddr_set(&addr, AF_INET6, "100::b1ac:h01e", 42);
+
+ return addr;
+}
+#endif
+
+/*!
+ * \brief Get address of a socket.
+ */
+static struct sockaddr_storage addr_from_socket(int sock)
+{
+ struct sockaddr_storage addr = { 0 };
+ socklen_t len = sizeof(addr);
+ getsockname(sock, (struct sockaddr *)&addr, &len);
+
+ return addr;
+}
+
+static const char *socktype_name(int type)
+{
+ switch (type) {
+ case SOCK_STREAM: return "TCP";
+ case SOCK_DGRAM: return "UDP";
+ default: return "unknown";
+ }
+}
+
+static bool socktype_is_stream(int type)
+{
+ return type == SOCK_STREAM;
+}
+
+/* -- mock server ---------------------------------------------------------- */
+
+#define LISTEN_BACKLOG 5
+
+struct server_ctx;
+typedef struct server_ctx server_ctx_t;
+
+typedef void (*server_cb)(int sock, void *data);
+
+/*!
+ * \brief Server context.
+ */
+struct server_ctx {
+ int sock;
+ int type;
+ bool terminate;
+ server_cb handler;
+ void *handler_data;
+
+ pthread_t thr;
+ pthread_mutex_t mx;
+};
+
+static int poll_read(int sock)
+{
+ struct pollfd pfd = { .fd = sock, .events = POLLIN };
+ return poll(&pfd, 1, TIMEOUT);
+}
+
+static void server_handle(server_ctx_t *ctx)
+{
+ int remote = ctx->sock;
+
+ assert(ctx->type == SOCK_STREAM || ctx->type == SOCK_DGRAM);
+
+ if (socktype_is_stream(ctx->type)) {
+ remote = accept(ctx->sock, 0, 0);
+ if (remote < 0) {
+ return;
+ }
+ }
+
+ pthread_mutex_lock(&ctx->mx);
+ server_cb handler = ctx->handler;
+ pthread_mutex_unlock(&ctx->mx);
+ handler(remote, ctx->handler_data);
+
+ if (socktype_is_stream(ctx->type)) {
+ close(remote);
+ }
+}
+
+/*!
+ * \brief Simple server.
+ *
+ * Terminated when a one-byte message is delivered.
+ */
+static void *server_main(void *_ctx)
+{
+ server_ctx_t *ctx = _ctx;
+
+ for (;;) {
+ pthread_mutex_lock(&ctx->mx);
+ bool terminate = ctx->terminate;
+ pthread_mutex_unlock(&ctx->mx);
+ if (terminate) {
+ break;
+ }
+
+ int r = poll_read(ctx->sock);
+ if (r == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ break;
+ }
+ } else if (r == 0) {
+ continue;
+ }
+
+ assert(r == 1);
+ server_handle(ctx);
+ }
+
+ return NULL;
+}
+
+static bool server_start(server_ctx_t *ctx, int sock, int type,
+ server_cb handler, void *handler_data)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->sock = sock;
+ ctx->type = type;
+ ctx->handler = handler;
+ ctx->handler_data = handler_data;
+
+ ctx->terminate = false;
+
+ pthread_mutex_init(&ctx->mx, NULL);
+ return (pthread_create(&ctx->thr, NULL, server_main, ctx) == 0);
+}
+
+static void server_stop(server_ctx_t *ctx)
+{
+ pthread_mutex_lock(&ctx->mx);
+ ctx->terminate = true;
+ pthread_mutex_unlock(&ctx->mx);
+
+ pthread_kill(ctx->thr, SIGUSR1);
+ pthread_join(ctx->thr, NULL);
+}
+
+/* -- tests ---------------------------------------------------------------- */
+
+static void handler_echo(int sock, void *_server)
+{
+ server_ctx_t *server = _server;
+ uint8_t buffer[16] = { 0 };
+
+ struct sockaddr_storage remote = { 0 };
+ struct sockaddr_storage *addr = NULL;
+ if (!socktype_is_stream(server->type)) {
+ addr = &remote;
+ }
+
+ int in = net_recv(sock, buffer, sizeof(buffer), addr, TIMEOUT);
+ if (in <= 0) {
+ return;
+ }
+
+ net_send(sock, buffer, in, (struct sockaddr *)addr, TIMEOUT);
+}
+
+static void test_connected_one(const struct sockaddr_storage *server_addr,
+ const struct sockaddr_storage *source_addr,
+ int type, const char *name, const char *addr_name)
+{
+ int r;
+
+ int client = net_connected_socket(type, (struct sockaddr *)server_addr, NULL);
+ ok(client >= 0, "%s, %s: client, create connected socket", name, addr_name);
+
+ const uint8_t out[] = "test message";
+ const size_t out_len = sizeof(out);
+ if (socktype_is_stream(type)) {
+ r = net_stream_send(client, out, out_len, TIMEOUT);
+ } else {
+ r = net_dgram_send(client, out, out_len, NULL);
+ }
+ ok(r == out_len, "%s, %s: client, send message", name, addr_name);
+
+ r = net_is_connected(client);
+ ok(r, "%s, %s: client, is connected", name, addr_name);
+
+ uint8_t in[128] = { 0 };
+ if (socktype_is_stream(type)) {
+ r = net_stream_recv(client, in, sizeof(in), TIMEOUT);
+ } else {
+ r = net_dgram_recv(client, in, sizeof(in), TIMEOUT);
+ }
+ ok(r == out_len && memcmp(out, in, out_len) == 0,
+ "%s, %s: client, receive message", name, addr_name);
+
+ close(client);
+}
+
+static void test_connected(int type)
+{
+ const char *name = socktype_name(type);
+ const struct sockaddr_storage empty_addr = { 0 };
+ const struct sockaddr_storage local_addr = addr_local();
+
+ int r;
+
+ // setup server
+
+ int server = net_bound_socket(type, (struct sockaddr *)&local_addr, 0);
+ ok(server >= 0, "%s: server, create bound socket", name);
+
+ if (socktype_is_stream(type)) {
+ r = listen(server, LISTEN_BACKLOG);
+ ok(r == 0, "%s: server, start listening", name);
+ }
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, type, handler_echo, &server_ctx);
+ ok(r, "%s: server, start", name);
+
+ const struct sockaddr_storage server_addr = addr_from_socket(server);
+
+ // connected socket, send and receive
+
+ test_connected_one(&server_addr, NULL, type, name, "NULL source");
+ test_connected_one(&server_addr, &empty_addr, type, name, "zero source");
+ test_connected_one(&server_addr, &local_addr, type, name, "valid source");
+
+ // cleanup
+
+ server_stop(&server_ctx);
+ close(server);
+}
+
+static void handler_noop(int sock, void *data)
+{
+}
+
+static void test_unconnected(void)
+{
+ int r = 0;
+ int sock = -1;
+ const struct sockaddr_storage local = addr_local();
+
+ uint8_t buffer[] = { 'k', 'n', 'o', 't' };
+ ssize_t buffer_len = sizeof(buffer);
+
+ // server
+
+ int server = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)&local, 0);
+ ok(server >= 0, "UDP, create server socket");
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, SOCK_DGRAM, handler_noop, NULL);
+ ok(r, "UDP, start server");
+
+ // UDP
+
+ sock = net_unbound_socket(SOCK_DGRAM, (struct sockaddr *)&local);
+ ok(sock >= 0, "UDP, create unbound socket");
+
+ ok(!net_is_connected(sock), "UDP, is not connected");
+
+ r = net_dgram_send(sock, buffer, buffer_len, NULL);
+ ok(r == KNOT_ECONN, "UDP, send failure on unconnected socket");
+
+ r = net_dgram_recv(sock, buffer, buffer_len, TIMEOUT_SHORT);
+ ok(r == KNOT_ETIMEOUT, "UDP, receive timeout on unconnected socket");
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ r = net_dgram_send(sock, buffer, buffer_len, (struct sockaddr *)&server_addr);
+ ok(r == buffer_len, "UDP, send on defined address");
+
+ close(sock);
+
+ // TCP
+
+ sock = net_unbound_socket(SOCK_STREAM, (struct sockaddr *)&local);
+ ok(sock >= 0, "TCP, create unbound socket");
+
+ ok(!net_is_connected(sock), "TCP, is not connected");
+
+#ifdef __linux__
+ const int expected = KNOT_ECONN;
+ const char *expected_msg = "failure";
+ const int expected_timeout = TIMEOUT;
+#else
+ const int expected = KNOT_ETIMEOUT;
+ const char *expected_msg = "timeout";
+ const int expected_timeout = TIMEOUT_SHORT;
+#endif
+
+ r = net_stream_send(sock, buffer, buffer_len, expected_timeout);
+ ok(r == expected, "TCP, send %s on unconnected socket", expected_msg);
+
+ r = net_stream_recv(sock, buffer, sizeof(buffer), expected_timeout);
+ ok(r == expected, "TCP, receive %s on unconnected socket", expected_msg);
+
+ close(sock);
+
+ // server termination
+
+ server_stop(&server_ctx);
+ close(server);
+}
+
+static void test_refused(void)
+{
+ int r = -1;
+
+ struct sockaddr_storage addr = { 0 };
+ uint8_t buffer[1] = { 0 };
+ int server, client;
+
+ // unreachable remote
+
+#ifdef ENABLE_NET_UNREACHABLE_TEST
+ addr = addr_unreachable();
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL);
+ ok(client >= 0, "client, connected");
+
+ tv = TIMEOUT_SHORT;
+ r = net_stream_send(client, (uint8_t *)"", 1, &tv);
+ ok(r == KNOT_ETIMEOUT, "client, timeout on write");
+ close(client);
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL);
+ ok(client >= 0, "client, connected");
+
+ tv = TIMEOUT_SHORT;
+ r = net_stream_recv(client, buffer, sizeof(buffer), &tv);
+ ok(r == KNOT_ETIMEOUT, "client, timeout on read");
+ close(client);
+#else
+ skip("unreachable tests disabled");
+#endif
+
+ // listening, not accepting
+
+ addr = addr_local();
+ server = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&addr, 0);
+ ok(server >= 0, "server, create server");
+ addr = addr_from_socket(server);
+
+ r = listen(server, LISTEN_BACKLOG);
+ ok(r == 0, "server, start listening");
+
+ client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr, NULL);
+ ok(client >= 0, "client, connect");
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ ok(r == 1, "client, successful write");
+
+ r = net_stream_recv(client, buffer, sizeof(buffer), TIMEOUT_SHORT);
+ ok(r == KNOT_ETIMEOUT, "client, timeout on read");
+
+ close(client);
+
+ // listening, closed immediately
+
+ client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr, NULL);
+ ok(client >= 0, "client, connect");
+
+ r = close(server);
+ ok(r == 0, "server, close socket");
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ ok(r == KNOT_ECONN, "client, refused on write");
+
+ close(client);
+}
+
+struct dns_handler_ctx {
+ const uint8_t *expected;
+ int len;
+ bool raw;
+ bool success;
+};
+
+static void _sync(int remote, int send)
+{
+ uint8_t buf[1] = { 0 };
+ int r;
+ if (send) {
+ r = net_stream_send(remote, buf, sizeof(buf), TIMEOUT);
+ } else {
+ r = net_stream_recv(remote, buf, sizeof(buf), TIMEOUT);
+
+ }
+ assert(r == sizeof(buf));
+ (void)r;
+}
+
+static void sync_signal(int remote)
+{
+ _sync(remote, true);
+}
+
+static void sync_wait(int remote)
+{
+ _sync(remote, false);
+}
+
+static void handler_dns(int sock, void *_ctx)
+{
+ struct dns_handler_ctx *ctx = _ctx;
+
+ uint8_t in[16] = { 0 };
+ int in_len = 0;
+
+ sync_signal(sock);
+
+ if (ctx->raw) {
+ in_len = net_stream_recv(sock, in, sizeof(in), TIMEOUT);
+ } else {
+ in_len = net_dns_tcp_recv(sock, in, sizeof(in), TIMEOUT);
+ }
+
+ ctx->success = in_len == ctx->len &&
+ (ctx->len < 0 || memcmp(in, ctx->expected, in_len) == 0);
+}
+
+static void dns_send_hello(int sock)
+{
+ net_dns_tcp_send(sock, (uint8_t *)"wimbgunts", 9, TIMEOUT);
+}
+
+static void dns_send_fragmented(int sock)
+{
+ struct fragment { const uint8_t *data; size_t len; };
+
+ const struct fragment fragments[] = {
+ { (uint8_t *)"\x00", 1 },
+ { (uint8_t *)"\x08""qu", 3 },
+ { (uint8_t *)"oopisk", 6 },
+ { NULL }
+ };
+
+ for (const struct fragment *f = fragments; f->len > 0; f++) {
+ net_stream_send(sock, f->data, f->len, TIMEOUT);
+ }
+}
+
+static void dns_send_incomplete(int sock)
+{
+ net_stream_send(sock, (uint8_t *)"\x00\x08""korm", 6, TIMEOUT);
+}
+
+static void dns_send_trailing(int sock)
+{
+ net_stream_send(sock, (uint8_t *)"\x00\x05""bloitxx", 9, TIMEOUT);
+}
+
+static void test_dns_tcp(void)
+{
+ struct testcase {
+ const char *name;
+ const uint8_t *expected;
+ size_t expected_len;
+ bool expected_raw;
+ void (*send_callback)(int sock);
+ };
+
+ const struct testcase testcases[] = {
+ { "single DNS", (uint8_t *)"wimbgunts", 9, false, dns_send_hello },
+ { "single RAW", (uint8_t *)"\x00\x09""wimbgunts", 11, true, dns_send_hello },
+ { "fragmented", (uint8_t *)"quoopisk", 8, false, dns_send_fragmented },
+ { "incomplete", NULL, KNOT_ECONN, false, dns_send_incomplete },
+ { "trailing garbage", (uint8_t *)"bloit", 5, false, dns_send_trailing },
+ { NULL }
+ };
+
+ for (const struct testcase *t = testcases; t->name != NULL; t++) {
+ struct dns_handler_ctx handler_ctx = {
+ .expected = t->expected,
+ .len = t->expected_len,
+ .raw = t->expected_raw,
+ .success = false
+ };
+
+ struct sockaddr_storage addr = addr_local();
+ int server = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&addr, 0);
+ ok(server >= 0, "%s, server, create socket", t->name);
+
+ int r = listen(server, LISTEN_BACKLOG);
+ ok(r == 0, "%s, server, start listening", t->name);
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, SOCK_STREAM, handler_dns, &handler_ctx);
+ ok(r, "%s, server, start handler", t->name);
+
+ addr = addr_from_socket(server);
+ int client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr, NULL);
+ ok(client >= 0, "%s, client, create connected socket", t->name);
+
+ sync_wait(client);
+ t->send_callback(client);
+
+ close(client);
+ server_stop(&server_ctx);
+ close(server);
+
+ ok(handler_ctx.success, "%s, expected result", t->name);
+ }
+}
+
+static bool socket_is_blocking(int sock)
+{
+ return fcntl(sock, F_GETFL, O_NONBLOCK) == 0;
+}
+
+static void test_nonblocking_mode(int type)
+{
+ const char *name = socktype_name(type);
+ const struct sockaddr_storage addr = addr_local();
+
+ int client = net_unbound_socket(type, (struct sockaddr *)&addr);
+ ok(client >= 0, "%s: unbound, create", name);
+ ok(!socket_is_blocking(client), "%s: unbound, nonblocking mode", name);
+ close(client);
+
+ int server = net_bound_socket(type, (struct sockaddr *)&addr, 0);
+ ok(server >= 0, "%s: bound, create", name);
+ ok(!socket_is_blocking(server), "%s: bound, nonblocking mode", name);
+
+ if (socktype_is_stream(type)) {
+ int r = listen(server, LISTEN_BACKLOG);
+ ok(r == 0, "%s: bound, start listening", name);
+ }
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ client = net_connected_socket(type, (struct sockaddr *)&server_addr, NULL);
+ ok(client >= 0, "%s: connected, create", name);
+ ok(!socket_is_blocking(client), "%s: connected, nonblocking mode", name);
+
+ close(client);
+ close(server);
+}
+
+static void test_nonblocking_accept(void)
+{
+ int r;
+
+ // create server
+
+ struct sockaddr_storage addr_server = addr_local();
+
+ int server = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&addr_server, 0);
+ ok(server >= 0, "server, create socket");
+
+ r = listen(server, LISTEN_BACKLOG);
+ ok(r == 0, "server, start listening");
+
+ addr_server = addr_from_socket(server);
+
+ // create client
+
+ int client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr_server, NULL);
+ ok(client >= 0, "client, create connected socket");
+
+ struct sockaddr_storage addr_client = addr_from_socket(client);
+
+ // accept connection
+
+ r = poll_read(server);
+ ok(r == 1, "server, pending connection");
+
+ struct sockaddr_storage addr_accepted = { 0 };
+ int accepted = net_accept(server, &addr_accepted);
+ ok(accepted >= 0, "server, accept connection");
+
+ ok(!socket_is_blocking(accepted), "accepted, nonblocking mode");
+
+ ok(sockaddr_cmp((struct sockaddr *)&addr_client,
+ (struct sockaddr *)&addr_accepted) == 0,
+ "accepted, correct address");
+
+ close(client);
+
+ // client reconnect
+
+ close(client);
+ client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr_server, NULL);
+ ok(client >= 0, "client, reconnect");
+
+ r = poll_read(server);
+ ok(r == 1, "server, pending connection");
+
+ accepted = net_accept(server, NULL);
+ ok(accepted >= 0, "server, accept connection (no remote address)");
+
+ ok(!socket_is_blocking(accepted), "accepted, nonblocking mode");
+
+ // cleanup
+
+ close(client);
+ close(server);
+}
+
+static void test_socket_types(void)
+{
+ struct sockaddr_storage addr = addr_local();
+
+ struct testcase {
+ const char *name;
+ int type;
+ bool is_stream;
+ };
+
+ const struct testcase testcases[] = {
+ { "UDP", SOCK_DGRAM, false },
+ { "TCP", SOCK_STREAM, true },
+ { NULL }
+ };
+
+ for (const struct testcase *t = testcases; t->name != NULL; t++) {
+ int sock = net_unbound_socket(t->type, (struct sockaddr *)&addr);
+ ok(sock >= 0, "%s, create socket", t->name);
+
+ is_int(t->type, net_socktype(sock), "%s, socket type", t->name);
+
+ ok(net_is_stream(sock) == t->is_stream, "%s, is stream", t->name);
+
+ close(sock);
+ }
+
+ is_int(AF_UNSPEC, net_socktype(-1), "invalid, socket type");
+ ok(!net_is_stream(-1), "invalid, is stream");
+}
+
+static void test_bind_multiple(void)
+{
+ const struct sockaddr_storage addr = addr_local();
+
+ // bind first socket
+
+ int sock_one = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)&addr, NET_BIND_MULTIPLE);
+ if (sock_one == KNOT_ENOTSUP) {
+ skip("not supported on this system");
+ return;
+ }
+ ok(sock_one >= 0, "bind first socket");
+
+ // bind second socket to the same address
+
+ const struct sockaddr_storage addr_one = addr_from_socket(sock_one);
+ int sock_two = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)&addr_one, NET_BIND_MULTIPLE);
+ ok(sock_two >= 0, "bind second socket");
+
+ // compare sockets
+
+ ok(sock_one != sock_two, "descriptors are different");
+
+ const struct sockaddr_storage addr_two = addr_from_socket(sock_two);
+ ok(sockaddr_cmp((struct sockaddr *)&addr_one,
+ (struct sockaddr *)&addr_two) == 0,
+ "addresses are the same");
+
+ close(sock_one);
+ close(sock_two);
+}
+
+static void signal_noop(int sig)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ signal(SIGUSR1, signal_noop);
+
+ diag("nonblocking mode");
+ test_nonblocking_mode(SOCK_DGRAM);
+ test_nonblocking_mode(SOCK_STREAM);
+ test_nonblocking_accept();
+
+ diag("socket types");
+ test_socket_types();
+
+ diag("connected sockets");
+ test_connected(SOCK_DGRAM);
+ test_connected(SOCK_STREAM);
+
+ diag("unconnected sockets");
+ test_unconnected();
+
+ diag("refused connections");
+ test_refused();
+
+ diag("DNS messages over TCP");
+ test_dns_tcp();
+
+ diag("flag NET_BIND_MULTIPLE");
+ test_bind_multiple();
+
+ return 0;
+}
diff --git a/tests/contrib/test_net_shortwrite.c b/tests/contrib/test_net_shortwrite.c
new file mode 100644
index 0000000..37b80ac
--- /dev/null
+++ b/tests/contrib/test_net_shortwrite.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "libknot/errcode.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+const int TIMEOUT = 2000;
+
+static struct sockaddr_storage localhost(void)
+{
+ struct sockaddr_storage addr = { 0 };
+
+ struct addrinfo *res = NULL;
+ if (getaddrinfo(NULL, "0", NULL, &res) == 0) {
+ memcpy(&addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ return addr;
+}
+
+struct data {
+ int server_fd;
+ uint8_t *buffer;
+ size_t size;
+ int result;
+};
+
+static void *thr_receive(void *data)
+{
+ struct data *d = data;
+
+ struct pollfd pfd = { .fd = d->server_fd, .events = POLLIN };
+ int r = poll(&pfd, 1, TIMEOUT);
+ if (r != 1) {
+ d->result = KNOT_ETIMEOUT;
+ return NULL;
+ }
+
+ int client = accept(d->server_fd, NULL, NULL);
+ if (client < 0) {
+ d->result = KNOT_ECONN;
+ return NULL;
+ }
+
+ d->result = net_dns_tcp_recv(client, d->buffer, d->size, TIMEOUT);
+
+ close(client);
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ int r;
+
+ // create TCP server
+
+ struct sockaddr_storage addr = localhost();
+ int server = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&addr, 0);
+ ok(server >= 0, "server: bind socket");
+
+ r = listen(server, 0);
+ ok(r == 0, "server: start listening");
+
+ struct sockaddr *sa = (struct sockaddr *)&addr;
+ socklen_t salen = sockaddr_len(sa);
+ r = getsockname(server, sa, &salen);
+ ok(r == 0, "server: get bound address");
+
+ // create TCP client
+
+ int client = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr, NULL);
+ ok(client >= 0, "client: connect to server");
+
+ int optval = 8192;
+ socklen_t optlen = sizeof(optval);
+ r = setsockopt(client, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
+ ok(r == 0, "client: configure small send buffer");
+
+ // accept TCP connection on the background
+
+ uint8_t recvbuf[UINT16_MAX] = { 0 };
+ struct data recv_data = {
+ .server_fd = server,
+ .buffer = recvbuf,
+ .size = sizeof(recvbuf)
+ };
+
+ pthread_t thr;
+ r = pthread_create(&thr, NULL, thr_receive, &recv_data);
+ ok(r == 0, "server: start receiver thread");
+
+ // send message (should handle partial-write correctly)
+
+ uint8_t sndbuf[UINT16_MAX];
+ for (size_t i = 0; i < sizeof(sndbuf); i++) {
+ sndbuf[i] = i;
+ }
+ r = net_dns_tcp_send(client, sndbuf, sizeof(sndbuf), TIMEOUT);
+ ok(r == sizeof(sndbuf), "client: net_dns_tcp_send() with short-write");
+
+ // receive message
+
+ r = pthread_join(thr, NULL);
+ ok(r == 0, "server: wait for receiver thread to terminate");
+
+ ok(recv_data.result == sizeof(recvbuf) &&
+ memcmp(sndbuf, recvbuf, sizeof(recvbuf)) == 0,
+ "server: net_dns_tcp_recv() complete and valid data");
+
+ // clean up
+
+ if (server >= 0) {
+ close(server);
+ }
+
+ if (client >= 0) {
+ close(client);
+ }
+
+ return 0;
+}
diff --git a/tests/contrib/test_qp-trie.c b/tests/contrib/test_qp-trie.c
new file mode 100644
index 0000000..f1440f2
--- /dev/null
+++ b/tests/contrib/test_qp-trie.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <time.h>
+#include <tap/basic.h>
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/macros.h"
+#include "contrib/string.h"
+#include "libknot/errcode.h"
+
+/* UCW array sorting defines. */
+#define ASORT_PREFIX(X) str_key_##X
+#define ASORT_KEY_TYPE char*
+#define ASORT_LT(x, y) (strcmp((x), (y)) < 0)
+#include "contrib/ucw/array-sort.h"
+
+/* Constants. */
+#define KEY_MAXLEN 64
+
+/*! \brief Generate random key. */
+static const char *alphabet = "abcdefghijklmn0123456789";
+static char *str_key_rand(size_t len)
+{
+ char *s = malloc(len);
+ memset(s, 0, len);
+ for (unsigned i = 0; i < len - 1; ++i) {
+ s[i] = alphabet[rand() % strlen(alphabet)];
+ }
+ return s;
+}
+
+/* \brief Check lesser or equal result. */
+static bool str_key_get_leq(trie_t *trie, char **keys, size_t i, size_t size)
+{
+ static char key_buf[KEY_MAXLEN];
+
+ int ret = 0;
+ trie_val_t *val = NULL;
+ const char *key = keys[i];
+ size_t key_len = strlen(key) + 1;
+ memcpy(key_buf, key, key_len);
+
+ /* Count equal first keys. */
+ size_t first_key_count = 1;
+ for (size_t k = 1; k < size; ++k) {
+ if (strcmp(keys[0], keys[k]) == 0) {
+ first_key_count += 1;
+ } else {
+ break;
+ }
+ }
+
+ /* Before current key. */
+ key_buf[key_len - 2] -= 1;
+ if (i < first_key_count) {
+ ret = trie_get_leq(trie, key_buf, key_len, &val);
+ if (ret != KNOT_ENOENT) {
+ diag("%s: leq for key BEFORE %zu/'%s' ret = %d", __func__, i, keys[i], ret);
+ return false; /* No key before first. */
+ }
+ } else {
+ ret = trie_get_leq(trie, key_buf, key_len, &val);
+ if (ret < KNOT_EOK || strcmp(*val, key_buf) > 0) {
+ diag("%s: '%s' is not before the key %zu/'%s'", __func__, (char*)*val, i, keys[i]);
+ return false; /* Found key must be LEQ than searched. */
+ }
+ }
+
+ /* Current key. */
+ key_buf[key_len - 2] += 1;
+ ret = trie_get_leq(trie, key_buf, key_len, &val);
+ if (! (ret == KNOT_EOK && val && strcmp(*val, key_buf) == 0)) {
+ diag("%s: leq for key %zu/'%s' ret = %d", __func__, i, keys[i], ret);
+ return false; /* Must find equal match. */
+ }
+
+ /* After the current key. */
+ key_buf[key_len - 2] += 1;
+ ret = trie_get_leq(trie, key_buf, key_len, &val);
+ if (! (ret >= KNOT_EOK && strcmp(*val, key_buf) <= 0)) {
+ diag("%s: leq for key AFTER %zu/'%s' ret = %d %s", __func__, i, keys[i], ret, (char*)*val);
+ return false; /* Every key must have its LEQ match. */
+ }
+
+ return true;
+
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Random keys. */
+ srand(time(NULL));
+ unsigned key_count = 100000;
+ char **keys = malloc(sizeof(char*) * key_count);
+ for (unsigned i = 0; i < key_count; ++i) {
+ keys[i] = str_key_rand(KEY_MAXLEN);
+ }
+
+ /* Sort random keys. */
+ str_key_sort(keys, key_count);
+
+ /* Create trie */
+ trie_val_t *val = NULL;
+ trie_t *trie = trie_create(NULL);
+ ok(trie != NULL, "trie: create");
+
+ /* Insert keys */
+ bool passed = true;
+ size_t inserted = 0;
+ for (unsigned i = 0; i < key_count; ++i) {
+ val = trie_get_ins(trie, keys[i], strlen(keys[i]) + 1);
+ if (!val) {
+ passed = false;
+ break;
+ }
+ if (*val == NULL) {
+ *val = keys[i];
+ ++inserted;
+ }
+ }
+ ok(passed, "trie: insert");
+
+ /* Check total insertions against trie weight. */
+ is_int(trie_weight(trie), inserted, "trie: trie weight matches insertions");
+
+ /* Lookup all keys */
+ passed = true;
+ for (unsigned i = 0; i < key_count; ++i) {
+ val = trie_get_try(trie, keys[i], strlen(keys[i]) + 1);
+ if (val && (*val == keys[i] || strcmp(*val, keys[i]) == 0)) {
+ continue;
+ } else {
+ diag("trie: mismatch on element '%u'", i);
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "trie: lookup all keys");
+
+ /* Lesser or equal lookup. */
+ passed = true;
+ for (unsigned i = 0; i < key_count; ++i) {
+ if (!str_key_get_leq(trie, keys, i, key_count)) {
+ passed = false;
+ for (int off = -10; off < 10; ++off) {
+ int k = (int)i + off;
+ if (k < 0 || k >= key_count) {
+ continue;
+ }
+ diag("[%u/%d]: %s%s", i, off, off == 0?">":"",keys[k]);
+ }
+ break;
+ }
+ }
+ ok(passed, "trie: find lesser or equal for all keys");
+
+ /* Sorted iteration. */
+ char key_buf[KEY_MAXLEN] = {'\0'};
+ size_t iterated = 0;
+ trie_it_t *it = trie_it_begin(trie);
+ while (!trie_it_finished(it)) {
+ size_t cur_key_len = 0;
+ const char *cur_key = trie_it_key(it, &cur_key_len);
+ if (iterated > 0) { /* Only if previous exists. */
+ if (strcmp(key_buf, cur_key) > 0) {
+ diag("'%s' <= '%s' FAIL\n", key_buf, cur_key);
+ break;
+ }
+ }
+ ++iterated;
+ memcpy(key_buf, cur_key, cur_key_len);
+ trie_it_next(it);
+ }
+ is_int(inserted, iterated, "trie: sorted iteration");
+ trie_it_free(it);
+
+ /* Cleanup */
+ for (unsigned i = 0; i < key_count; ++i) {
+ free(keys[i]);
+ }
+ free(keys);
+ trie_free(trie);
+ return 0;
+}
diff --git a/tests/contrib/test_siphash.c b/tests/contrib/test_siphash.c
new file mode 100644
index 0000000..b7e2739
--- /dev/null
+++ b/tests/contrib/test_siphash.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+// Prevent possible linking with a system SiphHash (OpenBSD).
+#include "contrib/openbsd/siphash.c"
+
+// https://github.com/veorq/SipHash
+const uint8_t vectors24[64][8] = {
+ { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72 },
+ { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74 },
+ { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d },
+ { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85 },
+ { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf },
+ { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18 },
+ { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb },
+ { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab },
+ { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93 },
+ { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e },
+ { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a },
+ { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4 },
+ { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75 },
+ { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14 },
+ { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7 },
+ { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1 },
+ { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f },
+ { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69 },
+ { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b },
+ { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb },
+ { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe },
+ { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0 },
+ { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93 },
+ { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8 },
+ { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8 },
+ { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc },
+ { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17 },
+ { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f },
+ { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde },
+ { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6 },
+ { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad },
+ { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32 },
+ { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71 },
+ { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7 },
+ { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12 },
+ { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15 },
+ { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31 },
+ { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02 },
+ { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca },
+ { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a },
+ { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e },
+ { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad },
+ { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18 },
+ { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4 },
+ { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9 },
+ { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9 },
+ { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb },
+ { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0 },
+ { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6 },
+ { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7 },
+ { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee },
+ { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1 },
+ { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a },
+ { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81 },
+ { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f },
+ { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24 },
+ { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7 },
+ { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea },
+ { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60 },
+ { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66 },
+ { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c },
+ { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f },
+ { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5 },
+ { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95 },
+};
+
+static void block_test(SIPHASH_KEY *key, uint8_t *data, int data_len, int block_len)
+{
+ int count = data_len / block_len;
+ int rest = data_len % block_len;
+
+ SIPHASH_CTX ctx;
+ SipHash24_Init(&ctx, key);
+
+ for (int i = 0; i < count; i++) {
+ SipHash24_Update(&ctx, data + i * block_len, block_len);
+ }
+ SipHash24_Update(&ctx, data + count * block_len, rest);
+
+ uint64_t hash = SipHash24_End(&ctx);
+ ok(memcmp(&hash, vectors24[data_len], sizeof(uint64_t)) == 0,
+ "siphash24: %i-byte block updates", block_len);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ SIPHASH_KEY key;
+ memcpy(&key.k0, "\x00\x01\x02\x03\x04\x05\x06\x07", sizeof(uint64_t));
+ memcpy(&key.k1, "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", sizeof(uint64_t));
+
+ uint8_t data[64];
+ for (int i = 0; i < sizeof(data); i++) {
+ data[i] = i;
+ }
+
+ for (int data_len = 0; data_len < sizeof(data); data_len++) {
+ diag("data length %i", data_len);
+
+ uint64_t hash = SipHash24(&key, data, data_len);
+ ok(memcmp(&hash, vectors24[data_len], sizeof(uint64_t)) == 0,
+ "siphash24: 1-block update");
+
+ for (int block_len = 1; block_len <= 8; block_len++) {
+ block_test(&key, data, data_len, block_len);
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/contrib/test_sockaddr.c b/tests/contrib/test_sockaddr.c
new file mode 100644
index 0000000..5f80507
--- /dev/null
+++ b/tests/contrib/test_sockaddr.c
@@ -0,0 +1,234 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+
+struct sockaddr *SA(struct sockaddr_storage *ss)
+{
+ return (struct sockaddr *)ss;
+}
+
+static void test_sockaddr_is_any(void)
+{
+ struct sockaddr_storage invalid = { 0 };
+ ok(!sockaddr_is_any(SA(&invalid)), "sockaddr_is_any: invalid");
+
+ struct sockaddr_storage path = { 0 };
+ path.ss_family = AF_UNIX;
+ ok(!sockaddr_is_any(SA(&path)), "sockaddr_is_any: unix");
+
+ struct sockaddr_storage ipv4_local = { 0 };
+ sockaddr_set(&ipv4_local, AF_INET, "127.0.0.1", 0);
+ ok(!sockaddr_is_any(SA(&ipv4_local)), "sockaddr_is_any: IPv4 local");
+
+ struct sockaddr_storage ipv4_any = { 0 };
+ sockaddr_set(&ipv4_any, AF_INET, "0.0.0.0", 0);
+ ok(sockaddr_is_any(SA(&ipv4_any)), "sockaddr_is_any: IPv4 any");
+
+ struct sockaddr_storage ipv6_local = { 0 };
+ sockaddr_set(&ipv6_local, AF_INET6, "::1", 0);
+ ok(!sockaddr_is_any(SA(&ipv6_local)), "sockaddr_is_any: IPv6 local");
+
+ struct sockaddr_storage ipv6_any = { 0 };
+ sockaddr_set(&ipv6_any, AF_INET6, "::", 0);
+ ok(sockaddr_is_any(SA(&ipv6_any)), "sockaddr_is_any: IPv6 any");
+}
+
+static void check_sockaddr_set(struct sockaddr_storage *ss, int family,
+ const char *straddr, int port)
+{
+ int ret = sockaddr_set(ss, family, straddr, port);
+ is_int(KNOT_EOK, ret, "set address '%s'", straddr);
+}
+
+static void test_net_match(void)
+{
+ int ret;
+ struct sockaddr_storage t = { 0 };
+
+ // 127 dec ~ 01111111 bin
+ // 170 dec ~ 10101010 bin
+ struct sockaddr_storage ref4 = { 0 };
+ check_sockaddr_set(&ref4, AF_INET, "127.170.170.127", 0);
+
+ // 7F hex ~ 01111111 bin
+ // AA hex ~ 10101010 bin
+ struct sockaddr_storage ref6 = { 0 };
+ check_sockaddr_set(&ref6, AF_INET6, "7FAA::AA7F", 0);
+
+ ret = sockaddr_net_match(SA(&ref4), SA(&ref6), 32);
+ ok(ret == false, "match: family mismatch");
+
+ ret = sockaddr_net_match(NULL, SA(&ref4), 32);
+ ok(ret == false, "match: NULL first parameter");
+ ret = sockaddr_net_match(SA(&ref4), NULL, 32);
+ ok(ret == false, "match: NULL second parameter");
+
+ ret = sockaddr_net_match(SA(&ref4), SA(&ref4), -1);
+ ok(ret == true, "match: ipv4 - identity, auto full prefix");
+ ret = sockaddr_net_match(SA(&ref4), SA(&ref4), 31);
+ ok(ret == true, "match: ipv4 - identity, subnet");
+ ret = sockaddr_net_match(SA(&ref4), SA(&ref4), 32);
+ ok(ret == true, "match: ipv4 - identity, full prefix");
+ ret = sockaddr_net_match(SA(&ref4), SA(&ref4), 33);
+ ok(ret == true, "match: ipv4 - identity, prefix overflow");
+
+ ret = sockaddr_net_match(SA(&ref6), SA(&ref6), -1);
+ ok(ret == true, "match: ipv6 - identity, auto full prefix");
+ ret = sockaddr_net_match(SA(&ref6), SA(&ref6), 127);
+ ok(ret == true, "match: ipv6 - identity, subnet");
+ ret = sockaddr_net_match(SA(&ref6), SA(&ref6), 128);
+ ok(ret == true, "match: ipv6 - identity, full prefix");
+ ret = sockaddr_net_match(SA(&ref6), SA(&ref6), 129);
+ ok(ret == true, "match: ipv6 - identity, prefix overflow");
+
+ // 124 dec ~ 01111100 bin
+ check_sockaddr_set(&t, AF_INET, "124.0.0.0", 0);
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 5);
+ ok(ret == true, "match: ipv4 - first byte, shorter prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 6);
+ ok(ret == true, "match: ipv4 - first byte, precise prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 7);
+ ok(ret == false, "match: ipv4 - first byte, not match");
+
+ check_sockaddr_set(&t, AF_INET, "127.170.170.124", 0);
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 29);
+ ok(ret == true, "match: ipv4 - last byte, shorter prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 30);
+ ok(ret == true, "match: ipv4 - last byte, precise prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref4), 31);
+ ok(ret == false, "match: ipv4 - last byte, not match");
+
+ // 7C hex ~ 01111100 bin
+ check_sockaddr_set(&t, AF_INET6, "7CAA::", 0);
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 5);
+ ok(ret == true, "match: ipv6 - first byte, shorter prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 6);
+ ok(ret == true, "match: ipv6 - first byte, precise prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 7);
+ ok(ret == false, "match: ipv6 - first byte, not match");
+
+ check_sockaddr_set(&t, AF_INET6, "7FAA::AA7C", 0);
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 125);
+ ok(ret == true, "match: ipv6 - last byte, shorter prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 126);
+ ok(ret == true, "match: ipv6 - last byte, precise prefix");
+ ret = sockaddr_net_match(SA(&t), SA(&ref6), 127);
+ ok(ret == false, "match: ipv6 - last byte, not match");
+}
+
+static void test_range_match(void)
+{
+ bool ret;
+ struct sockaddr_storage t = { 0 };
+ struct sockaddr_storage min = { 0 };
+ struct sockaddr_storage max = { 0 };
+
+ // IPv4 tests.
+
+ check_sockaddr_set(&min, AF_INET, "0.0.0.0", 0);
+ check_sockaddr_set(&max, AF_INET, "255.255.255.255", 0);
+
+ check_sockaddr_set(&t, AF_INET, "0.0.0.0", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv4 max range - minimum");
+ check_sockaddr_set(&t, AF_INET, "255.255.255.255", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv4 max range - maximum");
+
+ check_sockaddr_set(&min, AF_INET, "1.13.113.213", 0);
+ check_sockaddr_set(&max, AF_INET, "2.24.124.224", 0);
+
+ check_sockaddr_set(&t, AF_INET, "1.12.113.213", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv4 middle range - negative far min");
+ check_sockaddr_set(&t, AF_INET, "1.13.113.212", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv4 middle range - negative close min");
+ check_sockaddr_set(&t, AF_INET, "1.13.113.213", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv4 middle range - minimum");
+ check_sockaddr_set(&t, AF_INET, "1.13.213.213", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv4 middle range - middle");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.224", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv4 middle range - max");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.225", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv4 middle range - negative close max");
+ check_sockaddr_set(&t, AF_INET, "2.25.124.225", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv4 middle range - negative far max");
+
+ // IPv6 tests.
+
+ check_sockaddr_set(&min, AF_INET6, "::0", 0);
+ check_sockaddr_set(&max, AF_INET6,
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", 0);
+
+ check_sockaddr_set(&t, AF_INET6, "::0", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv6 max range - minimum");
+ check_sockaddr_set(&t, AF_INET6,
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv6 max range - maximum");
+
+ check_sockaddr_set(&min, AF_INET6, "1:13::ABCD:200B", 0);
+ check_sockaddr_set(&max, AF_INET6, "2:A24::124:224", 0);
+
+ check_sockaddr_set(&t, AF_INET6, "1:12::BCD:2000", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv6 middle range - negative far min");
+ check_sockaddr_set(&t, AF_INET6, "1:13::ABCD:200A", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv6 middle range - negative close min");
+ check_sockaddr_set(&t, AF_INET6, "1:13::ABCD:200B", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv6 middle range - minimum");
+ check_sockaddr_set(&t, AF_INET6, "1:13:0:12:34:0:ABCD:200B", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv6 middle range - middle");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:224", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == true, "match: ipv6 middle range - max");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:225", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv6 middle range - negative close max");
+ check_sockaddr_set(&t, AF_INET6, "2:FA24::4:24", 0);
+ ret = sockaddr_range_match(SA(&t), SA(&min), SA(&max));
+ ok(ret == false, "match: ipv6 middle range - negative far max");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("sockaddr_is_any");
+ test_sockaddr_is_any();
+
+ diag("sockaddr_net_match");
+ test_net_match();
+
+ diag("sockaddr_range_match");
+ test_range_match();
+
+ return 0;
+}
diff --git a/tests/contrib/test_string.c b/tests/contrib/test_string.c
new file mode 100644
index 0000000..fca8b8a
--- /dev/null
+++ b/tests/contrib/test_string.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <stdlib.h>
+
+#include "contrib/string.h"
+
+static void test_strstrip(void)
+{
+ char *c = NULL;
+
+ c = strstrip("hello");
+ is_string("hello", c, "strstrip: no whitespace");
+ free(c);
+
+ c = strstrip("world \n");
+ is_string("world", c, "strstrip: trailing whitespace");
+ free(c);
+
+ c = strstrip(" \n banana");
+ is_string("banana", c, "strstrip: leading whitespace");
+ free(c);
+
+ c = strstrip(" \t hello world \n");
+ is_string("hello world", c, "strstrip: leading and trailing");
+ free(c);
+
+ c = strstrip("");
+ is_string("", c, "strstrip: empty string");
+ free(c);
+
+ c = strstrip(" ");
+ is_string("", c, "strstrip: just whitespaces");
+ free(c);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_strstrip();
+
+ return 0;
+}
diff --git a/tests/contrib/test_strtonum.c b/tests/contrib/test_strtonum.c
new file mode 100644
index 0000000..48f2559
--- /dev/null
+++ b/tests/contrib/test_strtonum.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <stdio.h>
+#include <stdlib.h>
+#include <tap/basic.h>
+
+#include "contrib/strtonum.h"
+#include "libdnssec/error.h"
+
+static void test_u8(const char *in, uint8_t expected, int errcode)
+{
+ uint8_t out = 0x11;
+ assert(expected != out);
+
+ ok(str_to_u8(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u8 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_u16(const char *in, uint16_t expected, int errcode)
+{
+ uint16_t out = 0x0101;
+ assert(expected != out);
+
+ ok(str_to_u16(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u16 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_u32(const char *in, uint32_t expected, int errcode)
+{
+ uint32_t out = 0x010101;
+ assert(expected != out);
+
+ ok(str_to_u32(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u32 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_int(const char *in, int expected, int errcode)
+{
+ int out = 12345;
+ assert(expected != out);
+
+ ok(str_to_int(in, &out, INT_MIN, INT_MAX) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_int %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_size(const char *in, size_t expected, size_t min, size_t max,
+ int errcode)
+{
+ size_t out = 12345;
+ assert(expected != out);
+
+ ok(str_to_size(in, &out, min, max) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_int %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+// mute warn_unused_result
+#define asprintf(args, ...) do { \
+ int r = (asprintf)(args, ##__VA_ARGS__); assert(r >= 0); (void)r; \
+} while (0);
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_u8("-1", 0, KNOT_EINVAL);
+ test_u8("256", 0, KNOT_ERANGE);
+ test_u8("0x1", 0, KNOT_EINVAL);
+ test_u8(" 1", 0, KNOT_EINVAL);
+ test_u8("1 ", 0, KNOT_EINVAL);
+ test_u8("0", 0, KNOT_EOK);
+ test_u8("42", 42, KNOT_EOK);
+ test_u8("+84", 84, KNOT_EOK);
+ test_u8("255", UINT8_MAX, KNOT_EOK);
+
+ test_u16("-1", 0, KNOT_EINVAL);
+ test_u16("65536", 0, KNOT_ERANGE);
+ test_u16("0x1", 0, KNOT_EINVAL);
+ test_u16(" 1", 0, KNOT_EINVAL);
+ test_u16("1 ", 0, KNOT_EINVAL);
+ test_u16("0", 0, KNOT_EOK);
+ test_u16("65280", 65280, KNOT_EOK);
+ test_u16("+256", 256, KNOT_EOK);
+ test_u16("65535", UINT16_MAX, KNOT_EOK);
+
+ test_u32("-1", 0, KNOT_EINVAL);
+ test_u32("4294967296", 0, KNOT_ERANGE);
+ test_u32("0x1", 0, KNOT_EINVAL);
+ test_u32(" 1", 0, KNOT_EINVAL);
+ test_u32("1 ", 0, KNOT_EINVAL);
+ test_u32("0", 0, KNOT_EOK);
+ test_u32("65280", 65280, KNOT_EOK);
+ test_u32("+256", 256, KNOT_EOK);
+ test_u32("4294967295", UINT32_MAX, KNOT_EOK);
+
+ test_size("-1", 0, 0, 1, KNOT_EINVAL);
+ test_size("4294967296", 0, 0, 1, KNOT_ERANGE);
+ test_size("0", 0, 1, 2, KNOT_ERANGE);
+ test_size("0x1", 0, 0, 1, KNOT_EINVAL);
+ test_size(" 1", 0, 0, 1, KNOT_EINVAL);
+ test_size("1 ", 0, 0, 1, KNOT_EINVAL);
+ test_size("0", 0, 0, 1, KNOT_EOK);
+ test_size("65280", 65280, 0, 65280, KNOT_EOK);
+ test_size("+256", 256, 0, 65280, KNOT_EOK);
+
+ char *int_under = NULL;
+ asprintf(&int_under, "%lld", (long long)INT_MIN - 1);
+ char *int_min = NULL;
+ asprintf(&int_min, "%lld", (long long)INT_MIN);
+ char *int_max = NULL;
+ asprintf(&int_max, "%lld", (long long)INT_MAX);
+ char *int_over = NULL;
+ asprintf(&int_over, "%lld", (long long)INT_MAX + 1);
+
+ test_int(int_under, 0, KNOT_ERANGE);
+ test_int(int_over, 0, KNOT_ERANGE);
+ test_int("0x1", 0, KNOT_EINVAL);
+ test_int(" 1", 0, KNOT_EINVAL);
+ test_int("1 ", 0, KNOT_EINVAL);
+ test_int(int_min, INT_MIN, KNOT_EOK);
+ test_int("0", 0, KNOT_EOK);
+ test_int("268435459", 268435459, KNOT_EOK);
+ test_int("+1073741827", 1073741827, KNOT_EOK);
+ test_int(int_max, INT_MAX, KNOT_EOK);
+
+ free(int_under);
+ free(int_min);
+ free(int_max);
+ free(int_over);
+
+ return 0;
+}
diff --git a/tests/contrib/test_time.c b/tests/contrib/test_time.c
new file mode 100644
index 0000000..a21f8c2
--- /dev/null
+++ b/tests/contrib/test_time.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <string.h>
+
+#include "contrib/time.h"
+
+static void test_now(void)
+{
+ struct timespec t = time_now();
+ ok(t.tv_sec != 0, "time_now() returns something");
+}
+
+static void test_diff(void)
+{
+ struct timespec t1 = { 10, 1000 };
+ struct timespec t2 = { 50, 1500 };
+ struct timespec t3 = { 70, 500 };
+
+ struct timespec res;
+
+ res = time_diff(&t1, &t2);
+ ok(res.tv_sec == 40 && res.tv_nsec == 500, "time_diff()");
+
+ res = time_diff(&t2, &t3);
+ ok(res.tv_sec == 19 && res.tv_nsec == 999999000, "time_diff() ns overflow");
+
+ res = time_diff(&t3, &t1);
+ ok(res.tv_sec == -60 && res.tv_nsec == 500, "time_diff() negative");
+
+ res = time_diff(&t2, &t1);
+ ok(res.tv_sec == -41 && res.tv_nsec == 999999500, "time_diff() negative");
+}
+
+static void test_diff_ms(void)
+{
+ struct timespec t1 = { 10, 1000 };
+ struct timespec t2 = { 50, 500 };
+
+ float ms = 0.0;
+
+ ms = time_diff_ms(&t1, &t2);
+ ok(39990.0 < ms && ms < 40010.0, "time_diff_ms()");
+
+ ms = time_diff_ms(&t2, &t1);
+ ok(-40010.0 < ms && ms < -39990.0, "time_diff_ms() negative");
+}
+
+static void test_knot_time(void)
+{
+ knot_time_t a = knot_time();
+ knot_time_t inf = 0;
+ knot_time_t c;
+ knot_timediff_t d;
+ int ret;
+
+ ok(a != 0, "knot time not zero");
+
+ ret = knot_time_cmp(a, a);
+ ok(ret == 0, "compare same times");
+
+ ret = knot_time_cmp(a - 1, a + 1);
+ ok(ret == -1, "compare smaller time");
+
+ ret = knot_time_cmp(a + 10, a - 10);
+ ok(ret == 1, "compare bigger time");
+
+ ret = knot_time_cmp(inf, inf);
+ ok(ret == 0, "compare two infinities");
+
+ ret = knot_time_cmp(a, inf);
+ ok(ret == -1, "compare time and infty");
+
+ ret = knot_time_cmp(inf, a);
+ ok(ret == 1, "compare infty and time");
+
+ c = knot_time_min(a, a);
+ ok(c == a, "take same time");
+
+ c = knot_time_min(a, a + 1);
+ ok(c == a, "take first smaller");
+
+ c = knot_time_min(a + 1, a);
+ ok(c == a, "take second smaller");
+
+ c = knot_time_min(inf, inf);
+ ok(c == inf, "take same infty");
+
+ c = knot_time_min(a, inf);
+ ok(c == a, "take first finite");
+
+ c = knot_time_min(inf, a);
+ ok(c == a, "take second finite");
+
+ d = knot_time_diff(a + 1, a);
+ ok(d == 1, "positive diff");
+
+ d = knot_time_diff(a, a + 1);
+ ok(d == -1, "negative diff");
+
+ d = knot_time_diff(inf, inf);
+ ok(d == KNOT_TIMEDIFF_MAX, "positive double infty diff");
+
+ d = knot_time_diff(inf, a);
+ ok(d == KNOT_TIMEDIFF_MAX, "positive infty diff");
+
+ d = knot_time_diff(a, inf);
+ ok(d == KNOT_TIMEDIFF_MIN, "negative infty diff");
+}
+
+static void test_time_parse_expect(int ret, knot_time_t res,
+ knot_time_t expected, const char *msg)
+{
+ ok(ret == 0, "time_parse %s ok", msg);
+ ok(res == expected, "time_parse %s result", msg);
+}
+
+static void test_time_parse(void)
+{
+ knot_time_t res;
+ int ret;
+
+ ret = knot_time_parse("", "", &res);
+ test_time_parse_expect(ret, res, 0, "nihilist");
+
+ ret = knot_time_parse("#", "12345", &res);
+ test_time_parse_expect(ret, res, 12345, "unix");
+
+ ret = knot_time_parse("+-#U", "-1h", &res);
+ test_time_parse_expect(ret, res, knot_time() - 3600, "hour");
+
+ ret = knot_time_parse("+-#u'nths'|+-#u'nutes'", "+1minutes", &res);
+ test_time_parse_expect(ret, res, knot_time() + 60, "minute");
+}
+
+static void test_time_print_expect(int ret, const char *res, int res_len,
+ const char *expected, const char *msg)
+{
+ ok(ret == 0, "time_print %s ok", msg);
+ ok(strncmp(res, expected, res_len) == 0, "time_print %s result", msg);
+}
+
+static void test_time_print(void)
+{
+ char buff[100];
+ int bufl = sizeof(buff);
+ int ret;
+ knot_time_t t = 44000, t2, big;
+
+ ret = knot_time_print(TIME_PRINT_UNIX, t, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "44000", "unix");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_RELSEC, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-10000", "relsec");
+
+ ret = knot_time_print(TIME_PRINT_ISO8601, t, buff, bufl);
+ buff[11] = '0', buff[12] = '0'; // zeroing 'hours' field to avoid locality issues
+ test_time_print_expect(ret, buff, bufl, "1970-01-01T00:13:20Z", "iso");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_HUMAN_MIXED, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-2h46m40s", "negative human mixed");
+ big = knot_time_add(knot_time(), 2 * 365 * 24 * 3600 + 1);
+ ret = knot_time_print(TIME_PRINT_HUMAN_MIXED, big, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "+2Y1s", "big human mixed");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_HUMAN_LOWER, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-2h46mi40s", "negative human lower");
+ big = knot_time_add(knot_time(), 2 * 365 * 24 * 3600 + 1);
+ ret = knot_time_print(TIME_PRINT_HUMAN_LOWER, big, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "+2y1s", "big human lower");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_now();
+ test_diff();
+ test_diff_ms();
+ test_knot_time();
+ test_time_parse();
+ test_time_print();
+
+ return 0;
+}
diff --git a/tests/contrib/test_wire_ctx.c b/tests/contrib/test_wire_ctx.c
new file mode 100644
index 0000000..93456c3
--- /dev/null
+++ b/tests/contrib/test_wire_ctx.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "libknot/errcode.h"
+#include "contrib/wire_ctx.h"
+
+#define OK(wire) { \
+ is_int(KNOT_EOK, (wire)->error, "check for no error"); \
+}
+
+#define NOK(wire, code) { \
+ is_int(code, (wire)->error, "check for error"); \
+}
+
+void ok_offset(wire_ctx_t *wire, size_t max, size_t i)
+{
+ wire_ctx_set_offset(wire, i);
+ OK(wire);
+ is_int(max - i, wire_ctx_available(wire), "get available %zu", max - i);
+ OK(wire);
+ is_int(i, wire_ctx_offset(wire), "get start position %zu", i);
+ OK(wire);
+}
+
+void nok_offset(wire_ctx_t *wire, size_t max)
+{
+ wire_ctx_set_offset(wire, max);
+ OK(wire);
+ wire_ctx_set_offset(wire, max + 1);
+ NOK(wire, KNOT_ERANGE);
+ is_int(0, wire_ctx_available(wire), "get available %i", 0);
+ NOK(wire, KNOT_ERANGE);
+ is_int(max, wire_ctx_offset(wire), "get last start position %zu", max);
+ NOK(wire, KNOT_ERANGE);
+}
+
+void offset_test(void)
+{
+ diag("offset operation");
+
+ const size_t LEN = 3;
+ uint8_t data[LEN];
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ // First free byte.
+ ok_offset(&wire, LEN, 0);
+ // Last free byte.
+ ok_offset(&wire, LEN, 2);
+ // First non-free byte.
+ ok_offset(&wire, LEN, 3);
+ // Invalid offset.
+ nok_offset(&wire, LEN);
+}
+
+void skip_test(void)
+{
+ diag("skip operation");
+
+ uint8_t data[3];
+
+ // Forward skips.
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_skip(&wire, 2);
+ OK(&wire);
+ is_int(2, wire_ctx_offset(&wire), "skip by offset %i", 2);
+
+ wire_ctx_skip(&wire, 1);
+ OK(&wire);
+ is_int(3, wire_ctx_offset(&wire), "skip by offset %i", 1);
+
+ // Out-of-bounds skip.
+ wire_ctx_skip(&wire, 1);
+ NOK(&wire, KNOT_ERANGE);
+ is_int(3, wire_ctx_offset(&wire), "out-of-bounds skip by %i", 1);
+
+ // Backward skips.
+
+ wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_set_offset(&wire, 3);
+ OK(&wire);
+
+ wire_ctx_skip(&wire, -2);
+ OK(&wire);
+ is_int(1, wire_ctx_offset(&wire), "skip by offset %i", -2);
+
+ wire_ctx_skip(&wire, -1);
+ OK(&wire);
+ is_int(0, wire_ctx_offset(&wire), "skip by offset %i", -1);
+
+ // Out-of-bounds skip.
+ wire_ctx_skip(&wire, -1);
+ NOK(&wire, KNOT_ERANGE);
+ is_int(0, wire_ctx_offset(&wire), "out-of-bounds skip by %i", -1);
+}
+
+void clear_test(void)
+{
+ diag("clear operation");
+
+ uint8_t data[] = { 1, 2, 3 };
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_clear(&wire, 10);
+ NOK(&wire, KNOT_ESPACE);
+ is_int(1, data[0], "no attempt to clear");
+
+ wire = wire_ctx_init(data, sizeof(data));
+ wire_ctx_clear(&wire, 3);
+ OK(&wire);
+ is_int(0, wire_ctx_available(&wire), "no space available");
+ for (int i = 0; i < sizeof(data); i++) {
+ is_int(0, data[i], "wire position %i is zero", i);
+ }
+}
+
+#define check_rw(size, value, ...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t data[sizeof(expect)] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ \
+ wire_ctx_write_u ## size(&wire, value); \
+ OK(&wire); \
+ ok(memcmp(data, expect, sizeof(expect)) == 0, "write %i value", size); \
+ is_int(size/8, wire_ctx_offset(&wire), "write %i offset", size); \
+ \
+ wire_ctx_set_offset(&wire, 0); \
+ OK(&wire); \
+ \
+ uint64_t num = wire_ctx_read_u ## size(&wire); \
+ OK(&wire); \
+ is_int(value, num, "read %i value", size); \
+ is_int(size/8, wire_ctx_offset(&wire), "read %i offset", size); \
+}
+
+#define check_general_rw(...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t data[sizeof(expect)] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ \
+ wire_ctx_write(&wire, expect, sizeof(expect)); \
+ OK(&wire); \
+ ok(memcmp(data, expect, sizeof(expect)) == 0, "write value"); \
+ is_int(sizeof(expect), wire_ctx_offset(&wire), "write offset"); \
+ \
+ wire_ctx_set_offset(&wire, 0); \
+ OK(&wire); \
+ \
+ uint8_t d[sizeof(expect)] = { 0 }; \
+ wire_ctx_read(&wire, d, sizeof(expect)); \
+ OK(&wire); \
+ ok(memcmp(d, expect, sizeof(expect)) == 0, "read value"); \
+ is_int(sizeof(expect), wire_ctx_offset(&wire), "read offset"); \
+}
+
+void read_write_test(void)
+{
+ diag("read and write operation");
+
+ check_rw( 8, 0x11, 0x11);
+ check_rw(16, 0x1122, 0x11, 0x22);
+ check_rw(32, 0x11223344, 0x11, 0x22, 0x33, 0x44);
+ check_rw(48, 0x112233445566, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
+ check_rw(64, 0x1122334455667788, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
+
+ check_general_rw(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x10);
+}
+
+#define check_rw_over(size) { \
+ uint8_t data[1] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_set_offset(&wire, 1); \
+ OK(&wire); \
+ \
+ wire_ctx_write_u ## size(&wire, 0); \
+ NOK(&wire, KNOT_ESPACE); \
+ is_int(1, wire_ctx_offset(&wire), "err write %i offset", size); \
+ \
+ wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_set_offset(&wire, 1); \
+ OK(&wire); \
+ \
+ uint64_t num = wire_ctx_read_u ## size(&wire); \
+ NOK(&wire, KNOT_EFEWDATA); \
+ is_int(0, num, "err read %i value", size); \
+ is_int(1, wire_ctx_offset(&wire), "err read %i offset", size); \
+}
+
+#define check_general_rw_over(void) { \
+ uint8_t data[1] = { 0 }; \
+ uint8_t d[2] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_write(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_ESPACE); \
+ is_int(0, wire_ctx_offset(&wire), "err write offset"); \
+ \
+ wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_read(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_EFEWDATA); \
+ is_int(0, wire_ctx_offset(&wire), "err read offset"); \
+}
+
+void read_write_overflow_test(void)
+{
+ diag("overflow read and write operation");
+
+ check_rw_over(8);
+ check_rw_over(16);
+ check_rw_over(32);
+ check_rw_over(48);
+ check_rw_over(64);
+
+ check_general_rw_over();
+}
+
+#define check_ro(size) { \
+ uint8_t data[8] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init_const(data, sizeof(data)); \
+ \
+ wire_ctx_write_u ## size(&wire, 0); \
+ NOK(&wire, KNOT_EACCES); \
+ is_int(0, wire_ctx_offset(&wire), "err write %i offset", size); \
+}
+
+#define check_general_ro(void) { \
+ uint8_t data[8] = { 0 }; \
+ uint8_t d[2] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init_const(data, sizeof(data)); \
+ \
+ wire_ctx_write(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_EACCES); \
+ is_int(0, wire_ctx_offset(&wire), "err write offset"); \
+}
+
+void write_readonly_test(void)
+{
+ diag("readonly write operation");
+
+ check_ro(8);
+ check_ro(16);
+ check_ro(32);
+ check_ro(48);
+ check_ro(64);
+
+ check_general_ro();
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ offset_test();
+ skip_test();
+ clear_test();
+ read_write_test();
+ read_write_overflow_test();
+ write_readonly_test();
+
+ return 0;
+}
diff --git a/tests/knot/semantic_check_data/cdnskey.cds b/tests/knot/semantic_check_data/cdnskey.cds
new file mode 100644
index 0000000..354ad81
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.cds
@@ -0,0 +1,10 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 CDS 53851 8 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.invalid b/tests/knot/semantic_check_data/cdnskey.invalid
new file mode 100644
index 0000000..9c971ab
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid
@@ -0,0 +1,10 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 CDS 53851 8 2 668159D684EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.invalid.param b/tests/knot/semantic_check_data/cdnskey.invalid.param
new file mode 100644
index 0000000..07f78db
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid.param
@@ -0,0 +1,10 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 CDS 53851 4 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.nocdnskey b/tests/knot/semantic_check_data/cdnskey.nocdnskey
new file mode 100644
index 0000000..40c3096
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocdnskey
@@ -0,0 +1,9 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDS 53851 8 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.nocds b/tests/knot/semantic_check_data/cdnskey.nocds
new file mode 100644
index 0000000..8966caf
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocds
@@ -0,0 +1,9 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.nodnskey b/tests/knot/semantic_check_data/cdnskey.nodnskey
new file mode 100644
index 0000000..8f8e458
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nodnskey
@@ -0,0 +1,9 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 CDS 53851 8 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cdnskey.two b/tests/knot/semantic_check_data/cdnskey.two
new file mode 100644
index 0000000..81c4a1f
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.two
@@ -0,0 +1,13 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns2.example.com. hostmaster.example.com. 2010135808 10800 3600 1209600 7200
+example.com. 3600 NS dns2.example.com.
+example.com. 3600 MX 10 mail.example.com.
+example.com. 3600 DNSKEY 256 3 8 AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVwYkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEeCUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+example.com. 3600 DNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 DNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYToARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5WmnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Tax7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCNbGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4NodQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQEHYAd/AP8YgaovS8N1fJyh0=
+example.com. 3600 CDNSKEY 257 3 8 AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKfjqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU80AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbmLIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpMCLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRndpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n/4lgdSiBtvByLCXoWEYIGRs=
+
+example.com. 3600 CDS 53851 8 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+example.com. 3600 CDS 53852 8 2 6F8129D687EC387C948E6F4B0AC9AA01481CCEBF7570AFEC582897E7725122D6
+dns2.example.com. 3600 A 192.0.2.1
diff --git a/tests/knot/semantic_check_data/cname_extra_01.zone b/tests/knot/semantic_check_data/cname_extra_01.zone
new file mode 100644
index 0000000..ae3f27a
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_extra_01.zone
@@ -0,0 +1,18 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111218 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ MX 10 mail
+
+dns1 A 192.0.2.1
+
+; error CNAME, node contains other records
+email CNAME mail
+ A 192.0.2.2
diff --git a/tests/knot/semantic_check_data/cname_extra_02.signed b/tests/knot/semantic_check_data/cname_extra_02.signed
new file mode 100644
index 0000000..724a8da
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_extra_02.signed
@@ -0,0 +1,76 @@
+email.example.com. 3600 IN CNAME mail.example.com.
+ 3600 RRSIG CNAME 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ IxkF8oqOEzhlZDSRBIi4448EGvQwxm0QDFE3
+ JExA4Byx2QaJvXo8LoCeyQxS/f9E6bXpXQk2
+ 4dgQxUrRZqnKEA== )
+ 86400 NSEC example.com. CNAME RRSIG NSEC A
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ iKA+5qsYA7A7JN7Df99aJnToYESjqordQgVj
+ yMS/1RVBYEGE4y3ggehzAxvc8bsNYnUwGeGt
+ vse5dMVKCcIaPA== )
+; CNAME extra record A
+email.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ DummySignatureDEADBEEFToYESjqordQgVj
+ yMS/1RVBYEGE4y3ggehzAxvc8bsNYnUwGeGt
+ vse5dMVKCcIaPA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ MT+QgXcsDzkrFgncNwFyH8lwXiOTpj1rnPgs
+ OUIOfIhyJyzT1hpozAHt+IWOPHUkKjBN1C5y
+ SwyTnlqwJtG0yw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ Mr0Gu7PUu9PsUBflhd8tMhcQ9+ve+z561/ml
+ kP6PL0MHgLg7V8KVmL2tc7+JAhSOVSpJ4BGQ
+ c9HKD15lFDFEgw== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ oEMpoEhi86OM/SdyobPEh90zF3c3FhOgv68j
+ paD5BLUsAntf3qU+KoIMb9iVglp+VTGrg0Ol
+ XdJ2D/xSMA+XHA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ WOtx+LBKbS2MOahlpDJMqgeH1TI5dZoQitmA
+ SOkDRlJgfPsiKeiaGMrnWN9xnPZOVr9MsInE
+ sKYjh6EZM1nuBQ== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224073150 31323 example.com.
+ nL4eJLv62C56wexu10DMPHqXCXSE/3vRe4es
+ 4e0e1CkY9bdj+LgLfgs7CH7UDNXFX2CxKxHd
+ mL4sp5AtaA8fnQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ nn2dG+ORbcNQWHT87ijfOddx0SKCSE+8hAxt
+ SiQQpxAzPw13CZmnbYas8uvFFtth6U689V3h
+ rMzzZcxQEA1z8w== )
+ 86400 NSEC email.example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ BFz1Z7dbBNgHXDOaufuCoIzGHbwyLUrA+Wad
+ QBPD9xCYkXHoHfvVOhtEeMR19Rz+fi6ottJI
+ 4AWItiobBC/DAQ== )
diff --git a/tests/knot/semantic_check_data/cname_multiple.zone b/tests/knot/semantic_check_data/cname_multiple.zone
new file mode 100644
index 0000000..971c34f
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_multiple.zone
@@ -0,0 +1,15 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+
+email CNAME mail
+email CNAME mail2
diff --git a/tests/knot/semantic_check_data/different_signer_name.signed b/tests/knot/semantic_check_data/different_signer_name.signed
new file mode 100644
index 0000000..60a7f08
--- /dev/null
+++ b/tests/knot/semantic_check_data/different_signer_name.signed
@@ -0,0 +1,58 @@
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224081051 29600 example.com.
+ fEcJTvz6hu2rhfrdc/pS2/P/OWhq1dZhnUGS
+ 6+bamvMIqfNuYasJzeMZp1zneEETiMf7h+0E
+ cdmHKHKdVTukCA== )
+ 86400 NSEC example.com. A RRSIG NSEC
+; different signer name in RRSIG
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224081051 29600 different.com.
+ V6veOuBzm7n82hwIBbln9lY3pxKR/h8d7okC
+ deEeaF6jpDt8aoTJjo0UbtEJJwBNKARKfe4F
+ 9U0NVIL0HnN2/Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224081051 29600 example.com.
+ Pej4N9K4S9PA8wV0Ks6KtqAblfd05eRmv8T9
+ fBnR8V3vCtYONryKOP3cFlMXr2quWDCh5iwF
+ CQU98zle4CI9fg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224081051 29600 example.com.
+ UY1XRRhfD6a9xOm/+FsAo2/uYG/HRWR4k33B
+ amre+LBqabcXm9qX8wOU2W1h/sK/v8WFRjIn
+ dnCCjY/iwixfGg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224081051 29600 example.com.
+ IWxMxnZfLFFrQ3xOujrKKedNvIiIXN9QCL/G
+ Y615FWxxQcufNEZmlRILStAKtVsRJTFSInBP
+ NoTix1W8vttyeA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081051 29600 example.com.
+ dA9WweUpYtqqCm18zk+Jv3Md7WtVlXuvSeX3
+ 5IHmbezmlzrPpHBs+gWdD6bnGwlRRcea7FkS
+ u0Nt96PkXVuXPA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081051 31323 example.com.
+ nvruQZ5cZZ9+/I6wAh5AjWlxomueuuFiFoZe
+ XYBFmB+VH+eWpWL2Lp4hgaQ6UyKaHAKqtzMC
+ pqVA7Vwz4HEftQ== )
diff --git a/tests/knot/semantic_check_data/dname_apex_nsec3.signed b/tests/knot/semantic_check_data/dname_apex_nsec3.signed
new file mode 100644
index 0000000..287fe35
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_apex_nsec3.signed
@@ -0,0 +1,23 @@
+;; Zone dump (Knot DNS 2.6.0)
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111216 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNAME bar.example.com.
+example.com. 0 CDNSKEY 257 3 13 p3J5T0YFTf9IGjHFhS5oFGGBDOjs25Tz29eT5sAK7WxXoapa4Vw3C9zBH/BdDH1RmeUR6OHPY1+x2NiouNm05g==
+example.com. 0 CDS 2073 13 2 B345AF792A41656301EC57A4CE7E03C02A5E3C8F422FAB2FCD67C25649DB1285
+example.com. 3600 DNSKEY 256 3 13 UthmrB0FXNo/yZ3N0cnG/OJxG0FR7CT6KadbK4n22rMzfwq87jnobJ0xOpC7aEpGgDbypR0rK+KIAbRv4Prfeg==
+example.com. 3600 DNSKEY 257 3 13 p3J5T0YFTf9IGjHFhS5oFGGBDOjs25Tz29eT5sAK7WxXoapa4Vw3C9zBH/BdDH1RmeUR6OHPY1+x2NiouNm05g==
+example.com. 0 NSEC3PARAM 1 0 10 90E4D95759B9FB50
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 13 2 3600 20670924135857 20171006122857 46856 example.com. I2YeGSYvj56eHe7bkbnOpziu18gAdwMGEH5ZAGiuZzL37M2lRB/2rtAxWpxP1G1+idFXahAx6q2X5pK1o/yZWQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20670924135857 20171006122857 46856 example.com. NULugLRW6r+y6AAJS2tcFt5BBWJMMxq3q0wvYirSvzzt5A6vwWC2ocJ2A2kftoDrIQWYHl1ppsxHTMsvw3NKfQ==
+example.com. 3600 RRSIG DNAME 13 2 3600 20670924135857 20171006122857 46856 example.com. PhUAnuLYFE3ySc9mMoExr+WoGf6it4/571GhjYyhgfUWQs1pE8WJismYG9NFle6Q3OlmDJSEWQwHBHxQmOeyKw==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20670924135857 20171006122857 2073 example.com. ZnhiPDiX/3RUW5rdLO8pKcIe+orbYloDD1mxhmfvfJ52IGdCV+okDu2M559pUB8Ihb/6H9DDl1qO5lTVGpVEMg==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20670924135857 20171006122857 46856 example.com. DxyzDQGkONU+NTxBswKZRWc3kPQxH929PaD5S4g6MoH6EJ/X52mb5om0oXpXLuOTEo2Cij8c3aghLN2h3eiw5Q==
+example.com. 0 RRSIG CDS 13 2 0 20670924135857 20171006122857 46856 example.com. 8tPTolSZPvZUSpIs+nb4x/QRrNFgZG0jqqWWnQH8zy3CRS4L91SU5Z08aVz632n35toWv+uqG8LXI0sLKm6M4g==
+example.com. 0 RRSIG CDNSKEY 13 2 0 20670924135857 20171006122857 46856 example.com. 80MTgMHXqc8Eb+3LPjl9fJ6sjNR40MPVKUfD/Qw0mzWHjz8ZOUDxNZViHnmyoz4JoorrO5b2yAx+ojpt+FyQLg==
+;; DNSSEC NSEC3 chain
+9sq7g935u0bb7md3rn34uh1aknnjhf1k.example.com. 86400 NSEC3 1 0 10 90E4D95759B9FB50 9SQ7G935U0BB7MD3RN34UH1AKNNJHF1K NS SOA DNAME RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+;; DNSSEC NSEC3 signatures
+9sq7g935u0bb7md3rn34uh1aknnjhf1k.example.com. 86400 RRSIG NSEC3 13 3 86400 20670924135857 20171006122857 46856 example.com. sFlwNYgq6HoBDlXp9vC0Ck5uJ76rJyf4zfdQmTnJ8aB/44XvDoQ+tGIn4ilVN2SxzyT1A4c/nWOyMMCVhjXARg==
+;; Written 17 records
+;; Time 2017-10-06 15:58:57 CEST
diff --git a/tests/knot/semantic_check_data/dname_children.zone b/tests/knot/semantic_check_data/dname_children.zone
new file mode 100644
index 0000000..5758833
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_children.zone
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+ AAAA 2001:DB8::1
+
+foo DNAME bar
+bar.foo A 192.0.0.1
diff --git a/tests/knot/semantic_check_data/dnskey_param_error.signed b/tests/knot/semantic_check_data/dnskey_param_error.signed
new file mode 100644
index 0000000..1a2e936
--- /dev/null
+++ b/tests/knot/semantic_check_data/dnskey_param_error.signed
@@ -0,0 +1,70 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 1 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 5 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 1 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/duplicate.signature b/tests/knot/semantic_check_data/duplicate.signature
new file mode 100644
index 0000000..77bf21f
--- /dev/null
+++ b/tests/knot/semantic_check_data/duplicate.signature
@@ -0,0 +1,19 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010112269 10800 3600 1209600 7200
+example.com. 3600 NS dns1.example.com.
+example.com. 3600 DNSKEY 256 3 7 AwEAAd0e6EjJ0PgChDpbjB9QtvJ0ZqwKC/j7wlEOOB9owqefH/taZ37w6QR8Ysvvv2058AflDcCP3qlaOXp+ogq7AhayA+K4kc/UQyTPCe2jXKlX9IB0KAsr8nO9UEXzjYuyBw80Ry86Xxmj7OGYRu8eRm3ruOjVJy8hCrEQ7680an303Iu3Ixnmo8lPTPPMg4dbFXZ/RW6Sanrr/Sy6fre87XY58yywqX9lZOh5eqBeZG9WvU/HycrDkx5AcwD5etVk98tVTnofShY34ePZWDmRHEtvBpMzNObdomgM5we+DawC6P+Z8PFeGz+OgN7WVzkjm+MmYAk1aIeLQyNIE8SyMts=
+example.com. 3600 DNSKEY 257 3 7 AwEAAea81n0wL09ZMejh806rJ0Km3MYC+ySPWnOmV70nEmONbnduRPpXWjYSqFmH5kldfNdCH403kI/YCMYDYBAPFPbhxuZuVBaJJqOQVsI4rpwj+XiANfGFAq9pZ0oA8iH7gSoNUCf6+g2hcP0ajYoqCjUZ7ZZQNytV/x6foW5t5PbyPNeAU1AEKxk2VSg1TMfkccZqTIx1ofS0N102Z4tOBn26judPqLc0tXMCJc7wgekqG04IGe7UWfk9xWtwo2SbtX9diErF8DJ93C17OWkb04n1xCm3i8/XZadA/HrBjfX/NvlHF8qnUQzzxN7UGrvBD4hE12R9ICj4YNFZViOTdvs=
+dns1.example.com. 3600 A 192.0.2.1
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 7 2 3600 20170403124401 20170403124311 40703 example.com. ltKNDw2O/sIwQUsv3UCKqOtZYvWNJ0mHo2xDxpzZXfAiMbgR4k7jBIkpSEpcBiBlH7EvWom7CYVigPu8Y+j/Jq4uv+wmVF47OVY3YZvuzfprWj+iOQwPlfDJfUPx+U+73SSsZ21B5/+auB5cada730B4gQKmldleVGg5aov4H2+BpEyrsSs2o79qiXNBzLPqrZEmT0nfUAvQC8xhFV/71I8Q5qtfa3vO6DLSOBmBUtAlGKqfpWoZ2w+QDdA6rtOe0haizTZUtghL2ut47bdTR253brhUccL6nnLc5//jTUBToIhmG/p698xLnU9BYnuHIi74xsb3hVr5b46W5gAGKw==
+example.com. 3600 RRSIG SOA 7 2 3600 20170403124401 20170403124311 40703 example.com. E9a+I8HDh6ycTVkFgOWkzbH7PWds7ewp1M5lUci13ZzMVWsJeFW7t1tLnnOtvz2H8pq5/BevPB8iZBA7rHH7GxoQ5P8xrxAO6HvuRZT8O4kYAWRZ0QHhMIvY8f6VqTyoOmzgIGt0nJ3BL/XJgxIiFrsiLyih6+dkckEu62F22+FFvlv49ufKkCo+EUQPCzo7ZYODc8xKWo97SmaADzjfz7Hq9UPHraUgLhNkfBDbI9YPCGKaJaAiqCBy/6ih3SyHxVPLcIz95okeo5AJVszFIS+8pNPZssJBpWsLKYyAGzs2dsliRwS9z+a3wkHXJIfbLX+r3kGhcG4lQMYDz9SrFA==
+example.com. 7200 RRSIG NSEC 7 2 7200 20170403124401 20170403124311 40703 example.com. poh0BT+nUD3sM05axVGC+k7jj1r3YVNcx4bn/0cviNxzCqLY9RGgImPWsmkTgbJpmCox9SHzpTqL8acIQDNZaciNH9WeYKvn7wkap3z6jtCuQRezM3nUx7E37fzbnNC8MUoWkV37y3FSmtiza9l1isrE5dGkNMOsBcPvIp5wrbQ+dH4cMdcgQuW+NDjee6czIeeYtyarBWhq30S2lxroh8VXlrFDTcbiIY4UoGzJDfevvsonNFQXc+p7qq2fU1fyU1e3Ugty9I23g6fLhLcrmflVbYpcgE8/02K4asu5D7x/dOq21OU/jJfeudk66l6CVw7c3Qh/N63jRn8SsCj0Sg==
+example.com. 3600 RRSIG DNSKEY 7 2 3600 20170403124401 20170403124311 5154 example.com. RmAPllqg+CEX+vj5KKmXGYsF8vqbqLoXSYqSOSWbvgWRazKaQU98fpJWdrmqylkR6Xa1fnbvliP4N/0MGremNejsNPvMsJ4GvpyM75Mb4BEf5mwwikW6xov9V/n1AN9grWofj/r5evsZYcxIR7naM9oxV6qJvy8fFIjthG805MO18bYk1/Och2x9TgUf6DTqKNBHQjk1AfrhVvpuLjdNnNT16Ak3izrCLOm2tuNTaflkYkD0n06ZIAsz1krJWztpncA2csnKQmdybSL95wZnFeb6nkmq+P5vk3PuTENIMURYMCNfzBHogLfbVG5HpDhaHkcM2zSe1qATbp9xRZujLw==
+dns1.example.com. 3600 RRSIG A 7 3 3600 20170403124401 20170403124311 40703 example.com. iYfVT+HPDqMyH9f8aLrzNK6sOCoo38tlRJ5tjiko0DOpsWp20LLgVQLvKsTs3SfdC0gYzMVQCgzfDMbAgrEvmm4ZEQT/NSUhcO2t08f6pABn6GSdoswFupi0LGdQmgj/MbOET02OTALh9I6g3Ir1+bF+C10GS/8CYqffO/52IEJylc6AzDCwAjfkI/55hsuv2H8Wp5cqEG5yAlL4fK+U2zQWEuAGOtGbEuzeKcEDV6iiAuFge7ClW+CbB3gQxEhDdx5TQNNAcpzHmum5yfsfcFkIezZqIzEvOQWg1nJVcLvYnuBqyMWv/uGbG4CxTDy49U9JB/6QfilMk38VVcitZA==
+dns1.example.com. 7200 RRSIG NSEC 7 3 7200 20170403124401 20170403124311 40703 example.com. gtRE8TafAp50tzk3rAub93X69pp4J7uPzXPM0UAAp97oVMqcqvuZh80fICLmKl7xShvBx+AYfV+2CoeMW66CXVHTP8CyIjLyi32EGgL75Y2xs55/lEOaMl8hREgxniopCWGX/5vjmY0SBdGWVQVyeQeb0DbTXFWQNw/1LUPueoM1zqGcHFKFt5Y1GidboUEDsNeCmG3ZzGV9/v9sVUezzDK53uaHm8Ojz6E4N7kg6qXDF32ZAxs0dDjh46bsaTNvMLCEXqO2imHx9Omc2wYyCt/roMoeYiulXQ7yHYt0yQuCYwqxxMqJ4z9jvLNdLxH3YZYV0CVUrNgNC/5vtUILsQ==
+dns1.example.com. 3600 RRSIG A 7 3 3600 20170216152943 20170216152853 45258 example.com. j7H3N22L+tqfwuSd4GhIwMyjrFSY3+kypIcOvg0Ipbj4pAHsJOJTiW454Ueq54G/0ntoHxgmGLv3d/EV9prMPPQz8eqtRcYFip2NuEF9bJsIG3SMy+0HolPK+8D7B0MOGFA2TExKNknS7sJy/Jn/yQrf7BHubC61zWnqB+vN7MNlJASXEvy3008oi4FScSsrAVIrZK+z7utY4exkCVfELC7flGenoyPDFR12y8WpN/Tk6q1H37x+EKaQgFj361Bm6f/InPKW8Npn/SNCIJ2DvSWAnj6+2n1mse0sC+rKhRIDMDopu7JzTjpVs9U/p9BY5dtH/3YvST4Vz3syqd1unA==
+;; DNSSEC NSEC chain
+example.com. 7200 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 7200 NSEC example.com. A RRSIG NSEC
+;; Written 13 records
+;; Time 2017-04-03 14:43:12 CEST
diff --git a/tests/knot/semantic_check_data/invalid_ds.signed b/tests/knot/semantic_check_data/invalid_ds.signed
new file mode 100644
index 0000000..2435014
--- /dev/null
+++ b/tests/knot/semantic_check_data/invalid_ds.signed
@@ -0,0 +1,106 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 5 3 ( 2BB183AF5F22588179A53B0A
+ 98631FAD1A292118 )
+ 3600 IN DS 60485 5 7 ( 2BB183AF5F22588179A53B0A
+ 98631FAD1A292118 )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 1 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ A RRSIG )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+
+
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
+
+
diff --git a/tests/knot/semantic_check_data/missing.signed b/tests/knot/semantic_check_data/missing.signed
new file mode 100644
index 0000000..75c7d22
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing.signed
@@ -0,0 +1,20 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 1081539379 3600 300 3600000 3600
+example.com. 3600 NS dns1.example.com.
+example.com. 3600 DNSKEY 256 3 7 AwEAAaBgc4O+4UWd7mzSyelnPb/le/x0q/E90B/xnlf56kgEFMvEGz++o6CMRXr5JfgyxDDsahxTwFoWu30KJry4MjgcwlETM63DFpIYtyDBsi8TlQFEp5NDrlYUWlPGiPfywZBVkHGFMcFct+5/ZalTzYIP39tDytZcPZ/IgQRQZA9qeHYIw51YX9IlNMalHFCtJyrpzCdo22FY/vwBwSbdCa+vzkH1Uu8JkIqyAvAGkuwVisgpMpzWhvJNi5WSAnQfwOcsYCftINAHdRXtuqyG+uU/RDcZ2psx+woi+mYzEPeYV9MEqWpDyIz7jS7e1hK/1o05+qY8Eu2gt4enRj9BQr0=
+example.com. 3600 DNSKEY 256 3 7 AwEAAfcfJSUnim+cR3YEc4VfdJ5W65GNlK0LQaAh6vAejH7uol8VYmXdlyz+wlhad+DyRM5Jl+XJVFMMyFUqWx+Q63DPRtl+TlN/2pWU2gsNHUoovFhFpdX1cQMVoxr2QLgsm1ASTeqvZV8Dn0xAlNRihNv877sTySjveaH0JpuVCMpe5DB1zVbAzLgDqFKAvwJCumdycp7RzMi9PqS1XtsEInKi+X/zZteTJbDO7l+tFt9/NgFxiaLgNo8Gz2oVBTQvAbjCDEi2mPA/YJrpOGZWNkB2L9HFSfzZih8BbgUI3Fh4lhS8XCVrfVV7K9YR2F5NBVi7h0Mk15hzNsSS7tRK1FM=
+example.com. 3600 DNSKEY 257 3 7 AwEAAcjdwuJkjM8G5rk967z1cJqF88BqpvN2GN/6Tj1XA5AbIx+33qy5JI6K43ehlT/neLizOCk/JyXaw8gcjQaDKcIy0vKysXvI6yK4PNgHTdzQunBqGTfvPDlXKCle550R/DJF2OZH/T7jgX2GhQlem6UB3A23n24YP50IzAmXK9RYdE/dMFXU5jEz+CjcHNkB8ZCb2VrKE9RDjY88vr6lyM2kPbvBtx4UaUSEzwlDMRc3Wf+dBWKm6mKWAPsHZM/cux+S2mca/cxEA1ngCgBBbm7824WjTXgDs14QWuwruMTqLPDujUYND5kbsiuhQsfEFGVq2UyhGEZG/NoIEEg7qLc=
+dns1.example.com. 3600 AAAA 2001:db8::3
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 7 2 3600 20840201000000 20160302125715 7242 example.com. B/6k7YAQGkiz6IkssLZblExgMZBE+Flkhv/leVgvM4RLPPpQ2znouYyrSbVCcU5irA7PFLbee5Mn1aWj2S57L8yGJjHBuamQSIO0GcvGcmXi1CrdaYXSofo3PtnKpM8/mG3+8RCUL5YhoxhTK4Y5gJrYGPkRPKsBTw2Qd2TUJFebtYgCuGN8Q3UwbeYPw89rNbqC3a7zsGwJoZKgDnm3NwCWcv6NRTcQA/H5v6T0/QvYbZpBMrjl3EiAWOccdUlQnALngGSzbJ2GnmK933VXYhuAoSKEN6thauOBSLkdCh9afkUzo/t7xhTJszo0F1uuavs8PYf3HjmdnMwdPMkUuQ==
+example.com. 3600 RRSIG SOA 7 2 3600 20840201000000 20160302125715 7242 example.com. dHmPqRl9snHFavwkkAFZqHDmvUrI3+e+dmEexqgW9txr30fbrkeGAp6ApdZlqJiDTJ/2q0UoyQxSYe/BzgV4gEBgTTgfmC7m9eHVLTD70KMlNuvwC4jkh1vWT1Zn6IFUsQtJ+54XfRTe/2VHyeK7saqsA/ARRZGOzk6To8CWxNCApUdLZMQO9UTX7uVcXKkPvfMvlhx1fmn4OE8ntwbY1oPosQb987N8V8x9Rb2hINr4DCkXNDydDZAh4vsZO0DHPlmyfkyNguQDmdgnDz1CVbJzguy8tqeMGT7CrwU8AmX3JADQTHoxjnWEidLLUa/gNDRFcRc5YMcdZyImHqdNZQ==
+example.com. 3600 RRSIG NSEC 7 2 3600 20840201000000 20160302125715 7242 example.com. bjw2G/BwF2xTP/QSkKqdr7byUS+nqMvfppuhZmH0VcysAKN2oqsV51bn7gWej6dnx0svtX7nCOlwdDFSCMJld5BGZFnAfhS+XVc/wTeZGMi1BkeJxlT3UbGLhf2DuLLyL969HPltL527vSysjBEmi7OsTlH+wXD6SW35ZClajNSRLjxrRpVHTGpA5uyyysHRYNXAKS3+SSc1N/Fovjgzi68exWD0BKTGia7Nf9Fn+bqvhbYh+pMHA7djPFJIsER3OCBx7H1KMxl6rap7Q3rC0I289xnnsOqRh/GnzSVgvobKWozOOs9XXNg+w9ioSos+kbzTxxSEvLKqNBgCbLjUHg==
+example.com. 3600 RRSIG DNSKEY 7 2 3600 20840201000000 20160302125715 37855 example.com. EHXazcQ27b5Cjqd1T8TAui2PrUqEq7cBxk45OA8BfBDmOuH2vXFVL+juCM2gCvQ+0oZmcJmpkjMCxUqQekXgxRy21PlEzJfR3VHRDSYCSogR9cCLw9T9OiFXugZAtYcLVXVHddKu0+t5yeQqdStgLBiz9EmeuPFYd/h/BxKI8FGx/TjHNzd06SKgxlZAT/vCGhEswgSIpxJm4Ju561vT2/Hh+NmD4jIKVf0OUSkfRVRbxpzMs0HaZx6s0T2mcL8so/rEXjSIORkw56Q7x3EmYQDxNJjoNo4nHKT0/pciCey+vHj9pxxaiave8wLBG96JpgJgSV7BG8TTbE2q62wX1Q==
+dns1.example.com. 3600 RRSIG AAAA 7 3 3600 20840201000000 20160302125715 7242 example.com. h0oZ7ghuhANB27zD+M1m0NyVUHND7g2qI1IfEKDjMzZ34wyqM0xWLm/Izln86ol4naDvJU3a7hsJS/95DdvW/s711Oi2nKhX/Hkjvnzu8WVcf5DvEKYQe/fyZ676hnwviKqFzwmfTAKgIuSvt2uZzJkpcyL8ZE6O/GdPrcR6rTuuDI30F4zXIWPmuMNLR2qJv59DwM0tZScLdmRGKGnZNpdxDvtbCsZrXBUPrOE5XpAw+fe8+oL3UEeKQZq8qFhVvegl4TAuk1a8CS+zG4E1ABQKp86J1h0G4l/ajmWqq2T59lHsBAOuX0IbKHEHIJzwRd9EV7LM59EtJVacx8ZCkw==
+dns1.example.com. 3600 RRSIG NSEC 7 3 3600 20840201000000 20160302125715 7242 example.com. Pq6F6akEhyIqch7vwWJ5C53FW1UW/Y8vseFqB6tzql5bnIYjEwikgiWR85uvSUNGvsjHbadBYiVh2i68k80ws/2LecQCvguSH+rMkqqY9go+pBh3pdiNlJaJZp3zJQ6+E35xOA+p0G5t84Et3satJl3OcpVthrdBKuotpDg4P+nOpfLHkI3FO5vehxs71HmESQli5JllhPNMH6WZfWsP74D4DgRjUpIK9hGznCeuZxJT5+S5wL4fzqb+P20W30bsQqMbo9GNdPy5AdbwZoEKJVoN3HC/sv03ScQzWUxjamHCQOeZFys25fFlh7+JU1xYSb3V/fPhUUuf7OsBVvn7+g==
+mail.example.com. 7200 RRSIG NSEC 7 3 7200 20840201000000 20160302125715 19578 example.com. gjNXoKVddN+Z3MmHXxs0v4Gv/3zaTAg0mBSLkSp8Ion6qKj/aR2y50QhNfZGVEZSmyerDiaVpfPMN+q9mwx+6xmv4/G97DkadXBYt5IXGR1fXhMCF+RLJb5ePYjQKSk2TfRMJAlk1Mowfvlp8rXFrT576y2F+IXKbpiJOdRt/13Wo5IUbw6LLFDOeZ3fUiZtBmoWTTjBnrGWYdb+ePcSXID+qM5TmRXqIOFceJvt/RhGZ5LYAchgM2sZDf4Asacxg6Z6vS2cA1opTLMAIu+cuEmq61cSJxWHblfXIpPMXFG+4i+nkCxEFxWyxt9edlAeHS/l2AiHQl5QeuzwEjFI2g==
+;; DNSSEC NSEC chain
+example.com. 3600 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 3600 NSEC example.com. AAAA RRSIG NSEC
+;; Written 14 records
+;; Time 2017-03-31 15:38:20 CEST
diff --git a/tests/knot/semantic_check_data/missing_glue_01.zone b/tests/knot/semantic_check_data/missing_glue_01.zone
new file mode 100644
index 0000000..6900d0e
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing_glue_01.zone
@@ -0,0 +1,14 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ NS dns2
+
+; no A records for NS
diff --git a/tests/knot/semantic_check_data/missing_glue_02.zone b/tests/knot/semantic_check_data/missing_glue_02.zone
new file mode 100644
index 0000000..1fcbc1f
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing_glue_02.zone
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ NS dns2
+
+dns1 A 192.0.2.1
+
+; missing A record for dns2
diff --git a/tests/knot/semantic_check_data/missing_glue_03.zone b/tests/knot/semantic_check_data/missing_glue_03.zone
new file mode 100644
index 0000000..4485d43
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing_glue_03.zone
@@ -0,0 +1,15 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+
+deleg NS ns1.deleg
+; missing A record for ns1.deleg
diff --git a/tests/knot/semantic_check_data/missing_ns.zone b/tests/knot/semantic_check_data/missing_ns.zone
new file mode 100644
index 0000000..7d71d78
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing_ns.zone
@@ -0,0 +1,10 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+; missing NS in SOA
diff --git a/tests/knot/semantic_check_data/no_error_delegaton_bitmap.signed b/tests/knot/semantic_check_data/no_error_delegaton_bitmap.signed
new file mode 100644
index 0000000..82f4261
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_delegaton_bitmap.signed
@@ -0,0 +1,66 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224170742 29600 example.com.
+ imfvFljZ29MFD+AVQgb8/jeBgRPGJHXWw7jv
+ lCijFIMnH2gvHeUbKZTQLuxegeQ62ZJsu7w6
+ RI4aS0fffncK8g== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224170742 29600 example.com.
+ pnKWGmJdaur8QVQQ+xo9SbpDvC4E1lvSW1o6
+ wOUdzE5AMqy9+jB73BCC6Ota+Bt7vBCpAU2L
+ WFq6sB7oFj3vUA== )
+ 86400 NSEC deleg.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224170742 29600 example.com.
+ b5bzGxN44wwyABHRjgBSY+YhN+mWbFJsyWJ0
+ e7FQ3QGKwQkgKiOnUOElGu9qWy5KinPOvTTm
+ 96SOBVuVxfTK3Q== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224170742 29600 example.com.
+ rHeZv1qbt/28Nu44kYsoQdawfXdiWzq0YM7f
+ mI6QevXbyLx2QvgwlhSCc+sAoVkmQg+448M8
+ N7CcSooQE6z1eg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224170742 31323 example.com.
+ TlUfbDLPLMwqxeiDwqX3vtN5HGxL8+JnEpF7
+ rgE6Knf3I0oI3oBYBPKpMBYnQXQHUPqoK3uo
+ MLngsROcqxwi6w== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 86400 NSEC dns1.example.com. NS RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224170742 29600 example.com.
+ O81uip6/VmZE6dhWYNNj5FH2CnatytEgFiLW
+ k886unXnF4/pXpSlwUfZ4iIcA8qY6BRw+AS+
+ 97Y6p2ACTw1/KA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224170742 29600 example.com.
+ qrtmPmS7uGQS9Ytb25zLhyfajR7X7sZWXcIU
+ T61PSMeJyAYsOHdPB7VHxCRSv7QWYyrK9mh2
+ ohpYTvvvo1iptg== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224170742 29600 example.com.
+ NJ0Z8NZPBykmhuU/fb4x3AVOdZc6YebfQyYD
+ PaDTxHpquIh5ThfVtmawIpA6eVFkSscbUFEL
+ rjeRsepFYI3uig== )
diff --git a/tests/knot/semantic_check_data/no_error_nsec3_delegation.signed b/tests/knot/semantic_check_data/no_error_nsec3_delegation.signed
new file mode 100644
index 0000000..8585171
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_nsec3_delegation.signed
@@ -0,0 +1,78 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ jRNMrWLfS4yzRHQOBxs6/GKWIzx6AZV5lyCm
+ 7bYTV9wS3owDJSQhJ7lft0WbBmUMtV3tP9Xr
+ Yc+yW48p2Vr+QQ== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/no_error_nsec3_optout.signed b/tests/knot/semantic_check_data/no_error_nsec3_optout.signed
new file mode 100644
index 0000000..4fde7f5
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_nsec3_optout.signed
@@ -0,0 +1,98 @@
+; Zone without any semantic error
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010111221 21600 3600 604800 86400
+ 3600 NS dns1.example.com.
+ 432000000 DNSKEY 256 3 7 (
+ AwEAAdwI5CT9W2+RYXwUeD9m4LT1dLInHKLynsjrIzNTgbLmr5bqC/eWE8c0OsZpZmUbY0u04CzO
+ 2SXnk7zsDzH8GvcUfJHFnC+ZGuK45DChwv+k6kUmwm5nJUVD2qye3mucPvHX1Ss2NOrd+xqKu/t2
+ fVAOWKAh+r4XNN5eKg5rmkl38/A90JtiLg6Imp/O83ZG//ZUykFal0sujTbVP8MaLTMsXwHE46xf
+ i1tZDqObyY8TQTf0CtDm7d3LJvlDT1IST5NRU32fOtRUqdnTiMLbQQufwe1cCreZ+P4ej8DeI4gv
+ N/8lRCSnBBOnmwIPq4bjEdxnK9WRKmfLIpoK+fdO3dk= )
+ 432000000 DNSKEY 257 3 7 (
+ AwEAAbWZLegQDKxDkaW1NvVwZ/nn0qsLEPW3nO+sJpExg52NvM5eugR02H0mwrULG5jAAHaAZ5C9
+ eXE3i3asxiyUIygx+/8n2VQIVml/iKfRgqoS6tEAZzL11DL0q/dWf8dF5A6yJQWqifGBXGRms3AU
+ Ec7pri7LfX3CBSqieiuHBAkqJVrJm6I73kduk4/tUEVEzJEmoNEHDDAAhpHhDo6g6ytzmdfTTNyz
+ VSWyAzn65+Eg2F3MaQOptoPsrIUdAud0UPCk49tdLUgaCAT0Kd1NICD9ANd+ba1mWmsWXkudAPsw
+ 4WNLFZQUxlEsVPg4IsxZyrl3cyGDeLnXLLg0lWTGgJE= )
+ 0 CDNSKEY 257 3 7 (
+ AwEAAbWZLegQDKxDkaW1NvVwZ/nn0qsLEPW3nO+sJpExg52NvM5eugR02H0mwrULG5jAAHaAZ5C9
+ eXE3i3asxiyUIygx+/8n2VQIVml/iKfRgqoS6tEAZzL11DL0q/dWf8dF5A6yJQWqifGBXGRms3AU
+ Ec7pri7LfX3CBSqieiuHBAkqJVrJm6I73kduk4/tUEVEzJEmoNEHDDAAhpHhDo6g6ytzmdfTTNyz
+ VSWyAzn65+Eg2F3MaQOptoPsrIUdAud0UPCk49tdLUgaCAT0Kd1NICD9ANd+ba1mWmsWXkudAPsw
+ 4WNLFZQUxlEsVPg4IsxZyrl3cyGDeLnXLLg0lWTGgJE= )
+ 0 CDS 53991 7 2 E97D72CCA0AC77B5515FF2DBE9A662B55E2647A47497A1166696BF94A9E02DE6
+ 0 NSEC3PARAM 1 0 10 7A148F1404032E16
+deleg.example.com. 3600 NS deleg.example.com.
+ 3600 A 192.0.2.1
+dns1.example.com. 3600 A 192.0.2.1
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 7 2 3600 (
+ 20670913080225 20170912080225 13839 example.com. VxLYvx9K8iCokjVJRDJbj8dyc6kinzf6
+ /iM/kjDSKQoSBlCRR3+pczeq9NE/qUWQkLzqGd8sTfaatXD8/8UW1L9v5wToxoBPnhfO92V1ExTCq1uH
+ cUeuAs5FEPRzHZDXyHD2P5/lOzVI3YjTaH5Ub9uIvpELNGpA3P+AWfyr2agglRAAcf8eFbjzUd6U1kjX
+ L7SuLz/XChsWlAvwPNj932nyZrqlce1m0AOVFkF/YriZ/uGrRnSioAXOEW7HaV9KNmCqx4qks1qfSjEH
+ B/AwIsrk+ZWfVsR/0EaAWnXcjd/RWe+4jN9aKWX9tomSC6G7TL71fiKwPZ4C0/mkwp1LaA== )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20670913080225 20170912080225 13839 example.com. IcI4kyxzAAKDj9skK4/OcTdyOfxS
+ +O3Y+kSuPOQnO1fFcJV0ZWOxGc2ElO4ecKyiYdwlEU0WRuSy5JEnfEqzFGjdUmmhmI95w61KdbGk
+ VCsSyGV0AsSSVNrIo0KNXAhfcoWkULXk0CsRc/5Fglt7bzaArdd5nyeNLHP26zcFdGhZAZDOS13o
+ 7g/mHrV2dqQZl1VTxvURoW20uAmVX48koPEeOz70Yh+YKG0z1209YhN4ASB57l26bxjdzS1FIaul
+ Wu+OZr7JmfDuQMkVdWJG31jmXeLDiEUhFFD6Po2cijr1xvvxE6cmedjabCTCT0+U5mnHVAlxGhdw
+ 0jBF3ec5ZQ== )
+ 432000000 RRSIG DNSKEY 7 2 432000000 (
+ 20670913080225 20170912080225 53991 example.com. DCBKQjY99s29NtqD3/+zoMpL5vh2
+ mvgufudFWkBl2tmiH90SPe7BTUaMCu+kwiOSJh3U6ToywvKG7i8Tn820BleAC78c8TDtSxrJdzTlxhOm
+ o29dUObmvwKzMBsyAaj/2mQfY9GZNYxem7OyaZjbdrV4Rb4ghyy7Rttg+xle6tVH76+/MCcsdBur1NrR
+ r8HXtV8Sd+h0GCYeQvhMi5btIJK0OAHStTyU5iCmLIh28rxxHfmU5aRFkzanT83Xj7VdoF3fmjIE9Uww
+ fWX6PUzIoxEfm5a+v0EGbzqGOEgCnQ7cJo3DQkx0ouZT1bWTJQ2Hv9i323aAB/boGLR7b2wHrg== )
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20670913080225 20170912080225 13839 example.com. bOrMfjlEwZhfbcGmy4XYve/qZ8BV
+ n0t7JIBlKP/t9oFeukvxSHZnj3iT3sXq94Ck2DTDhRSw98rKG5V0tlAR/XGOz9Mx/nrY/NW5zX+1
+ zty1+6naf09QQsWUwseuUPu5uz1aEB6sIGj2V9bQWuHZdkIQkiVzN9SgD3olKmjOLsJxRS/ycJ59
+ I5so/g4wfC/jD7+NE8clkLJ5ME226brQOv/3xEFJC/IGR92uSvkzN1r7F0UZEozgu1UGykj16HU7
+ x/8YqT57xWiTcuZoSP1KoU494nO47dDhvO0pdMnIvozFnSI3heEJSVcZFxtePJ5gIUEp+wxGrayb
+ N0dQmCmrzg== )
+ 0 RRSIG CDS 7 2 0 (
+ 20670913080225 20170912080225 13839 example.com. jckEGuG5vvRBpT0GIj/XXctC
+ TfcWnGRZv2Lj4kwZk14yKqrfFSB9G8glilIDdWeM0AvBYPFuEMTj34+HJxPbnTxaYYiX4wBpJfKB
+ mNbcVsJZeK5DtnuvOvCrTwdiEFqR+tG04uTVAh9TEBHude3zatgbbzYzFMP/N+XlERw7qm2im9iB
+ Jq++HjD+etKI7qYlJmcFzLvVNPR/sIHdDvl9tbwrjNpv/s2Df/KO1lrtMERiZMXHLWV70CstuDGj
+ fIM/dAV06MShs38qwaCC/CeLtKkj6pvOJqIJciRnc+7Ln86iSdSDyczD+Yp6aTsRpoIUgswU7FkS
+ pSKmkZdhW6+jmQ== )
+ 0 RRSIG CDNSKEY 7 2 0 (
+ 20670913080225 20170912080225 13839 example.com. ALnyl9KnQ/zTbq/OSzJ5k7I2JqjD
+ JGbxu7Kuzbh2fAG7RHnN7INvaK+YhZX7w9sUHQSIj62u37wr5HS+1zgAfSKTd5k/OQC6y2FB4c2A
+ D4MCb5FaE0zJ1KmjFc3uZnEKfTl8b30A0dunRLgVHZHSmd4R/nJ3yAvm2+LumW0htP5lhcc7Xoi8
+ FJQ6KYg/ske0GCsMm5Ye+xO7m2QPbkgyOgq901rTBRMLbodbRXb8ZAFJGtl/iYmKG6pqxzQadeFQ
+ PxsL1bsdTvYsxIicQ6CpIOmvx/hl2aDi2f+vvfA4dHd2W2EDB4mMvRK09HadmBXimEkVJ+Tl++rg
+ WpiekXzI1g== )
+dns1.example.com. 3600 RRSIG A 7 3 3600 (
+ 20670913080225 20170912080225 13839 example.com. tBFbAbT4K6Uv9LlTfGb+2jkg3vJm
+ D+PAqNW5PGhP26b+AMxOaVKPEVLD9oLhyrJv0Pt2IN/VVIvQ04fdqPwvS+z4XqZ4R5X5jb+vh5WY
+ 1UsprBwkx5gViGlv9tcjB1ryg+U5A6e7VYccYMSVc0qB8trbk+z9TBbFFs5cXNUDLrCLQQm2TVTq
+ zKvxASK6/DdI+ZQd9RHDCd7im4pVYBA+R/QgQzzNhyhzqXoKzy3AFvxPWw6MX+ugX9Ea5HAF4uUx
+ xQmXEKmPMQey95WKk9ruqLRuaxGlDUWSq355ppGVppiN2oa53w00oUfacwo9MNIgiDi9Av5EG5EC
+ gbvq8Ze8jw== )
+;; DNSSEC NSEC3 chain
+036n766anb525cqa642tmm3r4occikb7.example.com. 86400 NSEC3 1 0 10 7A148F1404032E16 BGV0LKAMJSOEC22ARUGDNLTTCB70AF7U A RRSIG
+bgv0lkamjsoec22arugdnlttcb70af7u.example.com. 86400 NSEC3 1 0 10 7A148F1404032E16 S57MPBJ9OEOU9OH6HSV2ANMCV8BHLRMR NS SOA RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+s57mpbj9oeou9oh6hsv2anmcv8bhlrmr.example.com. 86400 NSEC3 1 0 10 7A148F1404032E16 036N766ANB525CQA642TMM3R4OCCIKB7 NS
+;; DNSSEC NSEC3 signatures
+036n766anb525cqa642tmm3r4occikb7.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20670913080225 20170912080225 13839 example.com. Ej5/ql3a/6yjyk2PZfX9MhlnzclyrCnj
+ DpCF9dg2pqf/G4Z3rKef/PNbUPYgvvS/tmwyP+qb10BYvpH1Lp2bxSGrdPAfs5ge5KyCxa6rmwwP0eEc
+ C2AviCdbtJceB4fmxTvCt+2pgTCrWTnoWsaJYHlj5BRjaFXbqkCi954rIAy/YJoU9vqjysCZbfARastK
+ yt9rpeDePnKLsZRDP2ENi45m5dHLQQ6t3j45MCZXLi2Qp6Kh1FCkU6mYQNRhGpi2bi3dUCJYLp3CpchG
+ kQxFV3Jik0g0vMcpEW31rzx7WgWMpjAbW5DahIDd9f2Do/OmyjAjYZYFBPJtz2QV1PBNeg== )
+bgv0lkamjsoec22arugdnlttcb70af7u.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20670913080225 20170912080225 13839 example.com. huMYkoTMTSZdLdr06vWbvGaSih5L+pxc
+ fSMlmfww1NCJtJwm4WAyzegR2VAaGpok1gDxfhyPEpdO0WHZe2UbRpVMq/xuOWXCBXrHPzg9825687su
+ q4T+WY2nenwxi5bLs8S/v+w3VjOh6NlldNg9gg9xsmhcOghoBS2xpS9Mmj23y8U/J19uuTrQL4gb/Olz
+ gfGQ9JeLGArJfr7Sj+KcpLLqGolja0xdeWClCoDtcgZ3DilxpKuPkbQ/Dr1ejWOPXJIFaBaOV58CdpEK
+ omvQ5VMmdWaKZx+Uku/JELkeSiSJVxaC69JKAjDddbNUKHUnktJBFKHpsbRPAO1zHbxF8A== )
+s57mpbj9oeou9oh6hsv2anmcv8bhlrmr.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20670913080225 20170912080225 13839 example.com. NOIPazxgOpFTiExjI/CArS9iu1C0ZnXg
+ M9vEt1n/OX5g0HT0YLrvifSqLw/MN8W4t4xrSM+n/cGLUFcwP9+Zk3NeLrz9gCBmrneYZn+DCV3LF+lU
+ nKNc2lu9khFAHLwZCR+Wtj3bUpQREywqT7mmthlYGlN0wLfSTMSRc4HXlEzELx00tENA4kJy8HnPOdJU
+ rDjJPMa4mSxDBbgoGRkd6EhWtaUep1kQOR3sKqLYOBq8U1yARkGQuuLI/xcItjqRnVw5KYoUzyPtKoVg
+ 1RyCcQxd9wZp06joiMKKQhQCo2/S2Ez1pkBhc7GNxJ7wT1rLQbl2yf7Lupf2YFpfQkWzBQ== )
+
diff --git a/tests/knot/semantic_check_data/no_error_wildcard_glue.zone b/tests/knot/semantic_check_data/no_error_wildcard_glue.zone
new file mode 100644
index 0000000..3cf1e9f
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_wildcard_glue.zone
@@ -0,0 +1,18 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+
+dns1 A 1.2.3.4
+
+abc NS a.ns.abc
+0.ns.abc A 1.2.3.4
+*.ns.abc AAAA ::1
+
diff --git a/tests/knot/semantic_check_data/no_rrsig.signed b/tests/knot/semantic_check_data/no_rrsig.signed
new file mode 100644
index 0000000..6a3161b
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_rrsig.signed
@@ -0,0 +1,48 @@
+dns1.example.com. 3600 IN A 192.0.2.1
+ 86400 NSEC example.com. A NSEC
+; missing RRSIGs
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ ieEKhIV69ywg+YFSqdz0t17eE+PLl1eR4kpv
+ Mq6Q6TfjC7V5/PcFW6KRoP50RFp4m4cD0E7T
+ GpmpnPF++QV1Vw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ kYbAbCGzyWPBEfc0TH1calUiKsZi12MH3TNV
+ 7vtjOvIYEqeNmuJkrw899a7nOPNoahB6h7o/
+ DXuRlFqYYCC16Q== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224081310 29600 example.com.
+ PchT9RWRkLCMxWAQ3ut6LZlh4MYT4CkAPThQ
+ cnIn0ORi/fVgGzlifQ88xfEdEr1ZoXk9PlhT
+ 5b+wocBOl2HhGg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ JLcSyR8KgSicUou0c7Zs7Ol1DYiaQ8Lfyort
+ 8a+5OP3em3r3NH1nJkiVfs8+xdvUcGlGkbib
+ RKlfRWiIcOEalQ== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081310 31323 example.com.
+ EQMX5DPXhwa+blMRkzl+swUW3BtzpGJ5tGEU
+ hkH7bJfM51gIAO5qnUO/mMPnEA8b4dc20nnZ
+ 8j8lETDjqBLgDQ== )
diff --git a/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed b/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed
new file mode 100644
index 0000000..2c36b9b
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed
@@ -0,0 +1,61 @@
+ns.deleg.example.com. 3600 IN A 192.168.0.2
+deleg.example.com. 3600 IN NS ns.deleg.example.com.
+ 86400 NSEC dns1.example.com. NS NSEC
+; missing RRSIG for NSEC record
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ HhnlCtlIaZFVklpzVUnzm6AzFd65CSc4WCJL
+ f2o7Gkevu+HTnkiPN6gqtERC/BKJz1EKd2fC
+ KDyLxXw6KeTRAw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ rcHuZd9wTYykzis+9Z8uyqD8V9h22szf2bmE
+ GYNyJBlHZO0sOmys31xnvDfQ9sdk9hf1TUfB
+ 9ACGIF5lDHBEog== )
+ 86400 NSEC deleg.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224081610 29600 example.com.
+ YGSe1OINjOY3I8BY1EoxcOJsDZ/DjGCT5nqY
+ J6BBjTcbT5S1W61SN50xc2sGB4Q8F2KTotAe
+ arzn4DGDt9mOMw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ rdmqHOUXqhwrJusNt/7FTV+AtO/v6Md3LXzj
+ /QzR/pCADNC6ZA+FvqaOycnUxoryKk7PY3pM
+ 5ispCMuEx/1OGA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081610 31323 example.com.
+ jELyXsaJx+G4heZJ96dyE12hSyTNFazwWDkq
+ 1Mkja9/bTTdYAd+t8fhf/c35bUiTVJWMivJe
+ +YcCwqGf2U+2zw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ ln2xuvghOWBDOfyk19Wwtv3oc8+1go3WQuMf
+ vel5x/uHVx6voNA25cpFIQ6nPlCo8pmd5R3w
+ paMxgoQtBkzBcA== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224081610 29600 example.com.
+ EEKcIegUeyn/1FIgxHV+gSX3b/ygQPAcjD8g
+ aCt1yiO0B1xmVm09RJNxzCLaTKxQENhxIoUZ
+ 2l7250pBQnrlAQ== )
diff --git a/tests/knot/semantic_check_data/nsec3_chain_01.signed b/tests/knot/semantic_check_data/nsec3_chain_01.signed
new file mode 100644
index 0000000..cd90b9f
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_01.signed
@@ -0,0 +1,80 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ ; wrong next record
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+; RRSIGs for NSEC3s
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ PjEM7Bxxb7w67366fKCLkR9BVFAL0RI8RJCZ
+ 5aqoMVuy+ui7MLKxKT2LfeTHgBw1Cww1bbJw
+ Ip2zu0/ZGPfKzA== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ DUMMYDUMMYDUMMYgc7Jx/FgAlruRjwsS/YJa
+ sZRspDGZhSqK2daV5K0lmK+XL8BoOtp7aXtq
+ VER5XcWLOebCdw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ XsJa0IUE2ddTohJmiuNVd/Po1ZOK0PDCuU7/
+ CS0/wiZ5ZlxPdVUAYXuC7HhGH+ZPsqwZ4oUU
+ ToDbFqfdzmC1XQ== )
+
+; other zone data without error
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ u5QMvOSkZBUM5tLiEAq3A+x4Ha17ZsNUYqeI
+ SuYA1+NbaBDxAtT6scB9aeA4lOTQ0TZvpGFE
+ YF/XxGtqvwdZ8g== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ ELZOh4iS9DpAafa8NTaI/eNL3Qwy+lsmgrzF
+ 7jaoR5yOURl/RZSJY+m9Peaq4ALcROdGJ0O4
+ miSpdTIZsBSGZg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ sjxCP/grgOR+4vmXw7HU/hGSx5dS5QxM00IA
+ gZNJ6Lqf+4OSL3TEa1/qqRSFTl5uv3rqh5W4
+ 8p2JoT1ZkcJj6w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229140652 31323 example.com.
+ jrH48r1iPFRfbyIZWcARQrejVgrE9v8qqt4R
+ uPHjz5t7PYmZYH544SI9HtaWGkIJ9jzlxr5l
+ ikCWo1we50y9Lg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229140652 29600 example.com.
+ yIXzhQw8c/c/in+doXX5JmqoGiqoYD2Hhw6d
+ /aGXc5QLQqxyATXln02vkwt1d7DK/ha1vkfx
+ bvGdduXDQ7YZ+g== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ aMRzV/1+m9wQHWezSiwkmDEbnS85wB9dA5x/
+ u2P7NPsgwMnRdfpVIMfaVhSJH88i5OlLTvL1
+ sSK+RADpuoqnLA== )
diff --git a/tests/knot/semantic_check_data/nsec3_chain_02.signed b/tests/knot/semantic_check_data/nsec3_chain_02.signed
new file mode 100644
index 0000000..ca70cfa
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_02.signed
@@ -0,0 +1,94 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ ; wrong next
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938 ; wrong next
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ oErbN3Xw+0zAqkz5KC5nOsINblBc4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ ep1TVqEISn2ZOiBtizK2eyuuhsYyD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySginature6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
diff --git a/tests/knot/semantic_check_data/nsec3_chain_03.signed b/tests/knot/semantic_check_data/nsec3_chain_03.signed
new file mode 100644
index 0000000..80112f8
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_03.signed
@@ -0,0 +1,94 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A ; wrong next
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT ; wrong next
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignatureD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
diff --git a/tests/knot/semantic_check_data/nsec3_ds.signed b/tests/knot/semantic_check_data/nsec3_ds.signed
new file mode 100644
index 0000000..d37d400
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_ds.signed
@@ -0,0 +1,109 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 5 1 ( 2BB183AF5F22588179A53B0A
+ 98631FAD1A292118 )
+deleg.example.com. 3600 IN RRSIG DS 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 1 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ A RRSIG )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+
+
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
+
+
diff --git a/tests/knot/semantic_check_data/nsec3_missing.signed b/tests/knot/semantic_check_data/nsec3_missing.signed
new file mode 100644
index 0000000..4974956
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_missing.signed
@@ -0,0 +1,120 @@
+
+; extra record without corresponding NSEC3
+extra.example.com. 3600 IN A 1.2.3.4
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature12345678
+ 123456789123456789123456789123456789
+ lqhxunAbh08dsQ== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ NS )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN A 1.2.3.4
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG A 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignatureD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
+
+
diff --git a/tests/knot/semantic_check_data/nsec3_optout.signed b/tests/knot/semantic_check_data/nsec3_optout.signed
new file mode 100644
index 0000000..c9caa5d
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout.signed
@@ -0,0 +1,81 @@
+
+; insecure delegation, not covered by NSEC3 or opt-out
+zzz.example.com. 3600 IN NS zzz.example.com.
+ 3600 A 192.0.2.1
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ jRNMrWLfS4yzRHQOBxs6/GKWIzx6AZV5lyCm
+ 7bYTV9wS3owDJSQhJ7lft0WbBmUMtV3tP9Xr
+ Yc+yW48p2Vr+QQ== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/nsec3_param_invalid.signed b/tests/knot/semantic_check_data/nsec3_param_invalid.signed
new file mode 100644
index 0000000..c7d8d6d
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_param_invalid.signed
@@ -0,0 +1,70 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 4 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 1 15 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 4 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed
new file mode 100644
index 0000000..a3024d8
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed
@@ -0,0 +1,70 @@
+; example.com -- missing DNSKEY in type bitmap
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG NSEC3PARAM )
+; dns1.example.com
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ li23VC44fumpMHhKwWug2J1C2fwCMiwgofYO
+ DKydNYsJyYTlyi8ezLJ2KoBlCtOc4Fp0NbqS
+ aN8CKWh7fQVnkQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ Y8olY2OClZgC+QHnOhY52LONVOcctOnl8jNY
+ /c7sCHZO4TdPPDHDhpbVntQD+Vc4fUTx+cXY
+ GrF5sLbhddBJXg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ fx2rZzhyYrp1b4tNH1SmM852VbGEeZdKrD+f
+ ZoInny1m8sovb1J9ORtVbGkOYOnInDMLWMCX
+ fghHC2MafuFV+Q== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 31323 example.com.
+ TcNU6AlrYhJLrNlkfOPJzO6A77j6C39IPoP4
+ OfmY2ClA5Vx2JO0vQ4bIHR7GIW8fiMe6M6tt
+ ZwQImhVWdG414A== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160225083237 29600 example.com.
+ iY0WB0dN1hQXoctaMwvvXzn7paQt5xUyucT3
+ xwo6HAI8Y+OJlecUfOpkkQ9lqIfsqPTXmgbY
+ RieoZGrWR6ZvaQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ QptUkTNS7umNQ5V6Z9DyGl6z+rG7G3TFmHG8
+ p9HGaKifSxjwSFW0nZ9/s86XHQ8ql5+bQmPa
+ xw39ntBmQLVxfg== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ CPx6000z5m1zUUpVhki1u9U7P/WMr7PUJAk3
+ G0w3v+/Lw56mDzYzNuTpPzS0noe0LKuecqRu
+ m99KpLyLOx+9QA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ m2z+hx+8hTA7Phu6QzGJrq+o4MiURpda3fYm
+ 0wTDmXtfPKsHmojGr3kBlvUMg16s2gpvNyCL
+ MSlnJ+7KCkI+Mw== )
diff --git a/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed
new file mode 100644
index 0000000..e3e4940
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed
@@ -0,0 +1,70 @@
+; example.com
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+; dns1.example.com -- extra type in bitmap - NSEC
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG NSEC)
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ li23VC44fumpMHhKwWug2J1C2fwCMiwgofYO
+ DKydNYsJyYTlyi8ezLJ2KoBlCtOc4Fp0NbqS
+ aN8CKWh7fQVnkQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ Y8olY2OClZgC+QHnOhY52LONVOcctOnl8jNY
+ /c7sCHZO4TdPPDHDhpbVntQD+Vc4fUTx+cXY
+ GrF5sLbhddBJXg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ fx2rZzhyYrp1b4tNH1SmM852VbGEeZdKrD+f
+ ZoInny1m8sovb1J9ORtVbGkOYOnInDMLWMCX
+ fghHC2MafuFV+Q== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 31323 example.com.
+ TcNU6AlrYhJLrNlkfOPJzO6A77j6C39IPoP4
+ OfmY2ClA5Vx2JO0vQ4bIHR7GIW8fiMe6M6tt
+ ZwQImhVWdG414A== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160225083237 29600 example.com.
+ iY0WB0dN1hQXoctaMwvvXzn7paQt5xUyucT3
+ xwo6HAI8Y+OJlecUfOpkkQ9lqIfsqPTXmgbY
+ RieoZGrWR6ZvaQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ QptUkTNS7umNQ5V6Z9DyGl6z+rG7G3TFmHG8
+ p9HGaKifSxjwSFW0nZ9/s86XHQ8ql5+bQmPa
+ xw39ntBmQLVxfg== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ CPx6000z5m1zUUpVhki1u9U7P/WMr7PUJAk3
+ G0w3v+/Lw56mDzYzNuTpPzS0noe0LKuecqRu
+ m99KpLyLOx+9QA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ m2z+hx+8hTA7Phu6QzGJrq+o4MiURpda3fYm
+ 0wTDmXtfPKsHmojGr3kBlvUMg16s2gpvNyCL
+ MSlnJ+7KCkI+Mw== )
diff --git a/tests/knot/semantic_check_data/nsec_broken_chain_01.signed b/tests/knot/semantic_check_data/nsec_broken_chain_01.signed
new file mode 100644
index 0000000..cb41dce
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_broken_chain_01.signed
@@ -0,0 +1,72 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_broken_chain_02.signed b/tests/knot/semantic_check_data/nsec_broken_chain_02.signed
new file mode 100644
index 0000000..27d8ebb
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_broken_chain_02.signed
@@ -0,0 +1,73 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_missing.signed b/tests/knot/semantic_check_data/nsec_missing.signed
new file mode 100644
index 0000000..e901607
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_missing.signed
@@ -0,0 +1,67 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+; missing NSEC for www.example.com.
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_multiple.signed b/tests/knot/semantic_check_data/nsec_multiple.signed
new file mode 100644
index 0000000..fc8e41b
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_multiple.signed
@@ -0,0 +1,74 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed b/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed
new file mode 100644
index 0000000..058a0a3
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed
@@ -0,0 +1,73 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; extra AAAA type in NSEC bitmap
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC AAAA
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
diff --git a/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed b/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed
new file mode 100644
index 0000000..dafdc92
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed
@@ -0,0 +1,73 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; missing A type in NSEC bitmap
+www.example.com. 86400 NSEC example.com. RRSIG NSEC
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
diff --git a/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed b/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed
new file mode 100644
index 0000000..b626b45
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed
@@ -0,0 +1,58 @@
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 600 (
+ 20840201000000 20160201000000 29600 example.com.
+ f24sVhH1P/0mEMYTMbFLrWmJtl6kqZF6yzaS
+ TcyK6JhVM4sDT//YnjizJGsTVGSCelz3FxMj
+ LdiUm9AD05uY6A== )
+
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ FgQ4VD1yDeA+uvJ+o8e1F28ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ kINKkWiBvb9Dpb0vghlLhXyObSzsYYNsOqe9
+ pWJN4lI4F2O3T6biPTQPsq3mYMR+6x9gPr6v
+ ysEPHlGtLdTLag== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ LkagMndC+wJGlQycPDvNmCZ0/QuBB7Zo4UVZ
+ He5jzQrE3Hnq8tn+/QfJ/yn62qCZ87DETwTT
+ rGaLqOTYRb1isg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ YDJ1tQvNlv8Y7cGioq8nkbaETx7wmyJKqa0B
+ 8hDLClYA4nf9UtyVXqZCISa2PlgRdBc5GEEh
+ U5BuLr4wYXqEFA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ FPOm8y3e09jh0fv0ZaOecWbdIXDAoERVKdjz
+ qsg1Etop1n6nDhO/lW3pwOUe02Zq2vretu2W
+ DozlDr5E6ZoqPA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 31323 example.com.
+ cZTevjvA8UO9Tqet/pbsN0Peep6aN8heyxMK
+ XP/Twsj4u0DeClKeIN7pd7Gi7Aac/UV2dev/
+ x/90SM22VQVpeQ== )
diff --git a/tests/knot/semantic_check_data/rrsig_signed.signed b/tests/knot/semantic_check_data/rrsig_signed.signed
new file mode 100644
index 0000000..2798026
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_signed.signed
@@ -0,0 +1,62 @@
+dns1.example.com. 86400 RRSIG RRSIG 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ DummySignatureDEADBEEF8ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ kINKkWiBvb9Dpb0vghlLhXyObSzsYYNsOqe9
+ pWJN4lI4F2O3T6biPTQPsq3mYMR+6x9gPr6v
+ ysEPHlGtLdTLag== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ LkagMndC+wJGlQycPDvNmCZ0/QuBB7Zo4UVZ
+ He5jzQrE3Hnq8tn+/QfJ/yn62qCZ87DETwTT
+ rGaLqOTYRb1isg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ YDJ1tQvNlv8Y7cGioq8nkbaETx7wmyJKqa0B
+ 8hDLClYA4nf9UtyVXqZCISa2PlgRdBc5GEEh
+ U5BuLr4wYXqEFA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ FPOm8y3e09jh0fv0ZaOecWbdIXDAoERVKdjz
+ qsg1Etop1n6nDhO/lW3pwOUe02Zq2vretu2W
+ DozlDr5E6ZoqPA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 31323 example.com.
+ cZTevjvA8UO9Tqet/pbsN0Peep6aN8heyxMK
+ XP/Twsj4u0DeClKeIN7pd7Gi7Aac/UV2dev/
+ x/90SM22VQVpeQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ f24sVhH1P/0mEMYTMbFLrWmJtl6kqZF6yzaS
+ TcyK6JhVM4sDT//YnjizJGsTVGSCelz3FxMj
+ LdiUm9AD05uY6A== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ FgQ4VD1yDeA+uvJ+o8e1F28ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
diff --git a/tests/knot/semantic_check_data/rrsig_ttl.signed b/tests/knot/semantic_check_data/rrsig_ttl.signed
new file mode 100644
index 0000000..04be771
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_ttl.signed
@@ -0,0 +1,58 @@
+dns1.example.com. 3600 IN A 192.0.2.1
+ 600 RRSIG A 7 3 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ f24sVhH1P/0mEMYTMbFLrWmJtl6kqZF6yzaS
+ TcyK6JhVM4sDT//YnjizJGsTVGSCelz3FxMj
+ LdiUm9AD05uY6A== )
+
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ FgQ4VD1yDeA+uvJ+o8e1F28ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ kINKkWiBvb9Dpb0vghlLhXyObSzsYYNsOqe9
+ pWJN4lI4F2O3T6biPTQPsq3mYMR+6x9gPr6v
+ ysEPHlGtLdTLag== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ LkagMndC+wJGlQycPDvNmCZ0/QuBB7Zo4UVZ
+ He5jzQrE3Hnq8tn+/QfJ/yn62qCZ87DETwTT
+ rGaLqOTYRb1isg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ YDJ1tQvNlv8Y7cGioq8nkbaETx7wmyJKqa0B
+ 8hDLClYA4nf9UtyVXqZCISa2PlgRdBc5GEEh
+ U5BuLr4wYXqEFA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ FPOm8y3e09jh0fv0ZaOecWbdIXDAoERVKdjz
+ qsg1Etop1n6nDhO/lW3pwOUe02Zq2vretu2W
+ DozlDr5E6ZoqPA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 31323 example.com.
+ cZTevjvA8UO9Tqet/pbsN0Peep6aN8heyxMK
+ XP/Twsj4u0DeClKeIN7pd7Gi7Aac/UV2dev/
+ x/90SM22VQVpeQ== )
diff --git a/tests/knot/test_acl.c b/tests/knot/test_acl.c
new file mode 100644
index 0000000..be35607
--- /dev/null
+++ b/tests/knot/test_acl.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "test_conf.h"
+#include "libknot/libknot.h"
+#include "knot/updates/acl.h"
+#include "contrib/sockaddr.h"
+
+#define ZONE "example.zone"
+#define KEY1 "key1_md5"
+#define KEY2 "key2_md5"
+#define KEY3 "key3_sha256"
+
+static void check_sockaddr_set(struct sockaddr_storage *ss, int family,
+ const char *straddr, int port)
+{
+ int ret = sockaddr_set(ss, family, straddr, port);
+ ok(ret == 0, "set address '%s'", straddr);
+}
+
+static void test_acl_allowed(void)
+{
+ int ret;
+ conf_val_t acl;
+ struct sockaddr_storage addr = { 0 };
+
+ knot_dname_t *zone_name = knot_dname_from_str_alloc(ZONE);
+ ok(zone_name != NULL, "create zone dname");
+ knot_dname_t *key1_name = knot_dname_from_str_alloc(KEY1);
+ ok(key1_name != NULL, "create "KEY1);
+ knot_dname_t *key2_name = knot_dname_from_str_alloc(KEY2);
+ ok(key2_name != NULL, "create "KEY2);
+ knot_dname_t *key3_name = knot_dname_from_str_alloc(KEY3);
+ ok(key3_name != NULL, "create "KEY3);
+
+ knot_tsig_key_t key0 = { 0 };
+ knot_tsig_key_t key1 = { DNSSEC_TSIG_HMAC_MD5, key1_name };
+ knot_tsig_key_t key2 = { DNSSEC_TSIG_HMAC_MD5, key2_name };
+ knot_tsig_key_t key3 = { DNSSEC_TSIG_HMAC_SHA256, key3_name };
+
+ const char *conf_str =
+ "key:\n"
+ " - id: "KEY1"\n"
+ " algorithm: hmac-md5\n"
+ " secret: Zm9v\n"
+ " - id: "KEY2"\n"
+ " algorithm: hmac-md5\n"
+ " secret: Zm9v\n"
+ " - id: "KEY3"\n"
+ " algorithm: hmac-sha256\n"
+ " secret: Zm8=\n"
+ "\n"
+ "acl:\n"
+ " - id: acl_key_addr\n"
+ " address: [ 2001::1 ]\n"
+ " key: [ key1_md5 ]\n"
+ " action: [ transfer ]\n"
+ " - id: acl_deny\n"
+ " address: [ 240.0.0.2 ]\n"
+ " action: [ notify ]\n"
+ " deny: on\n"
+ " - id: acl_no_action_deny\n"
+ " address: [ 240.0.0.3 ]\n"
+ " deny: on\n"
+ " - id: acl_multi_addr\n"
+ " address: [ 192.168.1.1, 240.0.0.0/24 ]\n"
+ " action: [ notify, update ]\n"
+ " - id: acl_multi_key\n"
+ " key: [ key2_md5, key3_sha256 ]\n"
+ " action: [ notify, update ]\n"
+ " - id: acl_range_addr\n"
+ " address: [ 100.0.0.0-100.0.0.5, ::0-::5 ]\n"
+ " action: [ transfer ]\n"
+ "\n"
+ "zone:\n"
+ " - domain: "ZONE"\n"
+ " acl: [ acl_key_addr, acl_deny, acl_no_action_deny ]\n"
+ " acl: [ acl_multi_addr, acl_multi_key ]\n"
+ " acl: [ acl_range_addr ]";
+
+ ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NONE, &addr, &key1);
+ ok(ret == true, "Address, key, empty action");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key1);
+ ok(ret == true, "Address, key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key1);
+ ok(ret == false, "Address not match, key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0);
+ ok(ret == false, "Address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key2);
+ ok(ret == false, "Address match, key not match, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1);
+ ok(ret == false, "Address, key match, action not match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key0);
+ ok(ret == true, "Second address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1);
+ ok(ret == false, "Second address match, extra key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key0);
+ ok(ret == false, "Denied address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key0);
+ ok(ret == true, "Denied address match, no key, action not match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.3", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key0);
+ ok(ret == false, "Denied address match, no key, no action");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "1.1.1.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key3);
+ ok(ret == true, "Arbitrary address, second key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "100.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0);
+ ok(ret == true, "IPv4 address from range, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0);
+ ok(ret == true, "IPv6 address from range, no key, action match");
+
+ conf_free(conf());
+ knot_dname_free(zone_name, NULL);
+ knot_dname_free(key1_name, NULL);
+ knot_dname_free(key2_name, NULL);
+ knot_dname_free(key3_name, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("acl_allowed");
+ test_acl_allowed();
+
+ return 0;
+}
diff --git a/tests/knot/test_changeset.c b/tests/knot/test_changeset.c
new file mode 100644
index 0000000..16179ec
--- /dev/null
+++ b/tests/knot/test_changeset.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/errcode.h"
+#include "libknot/error.h"
+#include "knot/updates/changesets.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test with NULL changeset
+ ok(changeset_size(NULL) == 0, "changeset: NULL size");
+ ok(changeset_empty(NULL), "changeset: NULL empty");
+
+ // Test creation.
+ knot_dname_t *d = knot_dname_from_str_alloc("test.");
+ assert(d);
+ changeset_t *ch = changeset_new(d);
+ knot_dname_free(d, NULL);
+ ok(ch != NULL, "changeset: new");
+ if (!ch) {
+ return 1;
+ }
+ ok(changeset_empty(ch), "changeset: empty");
+ ch->soa_to = (knot_rrset_t *)0xdeadbeef;
+ ok(!changeset_empty(ch), "changseset: empty SOA");
+ ch->soa_to = NULL;
+ ok(changeset_size(ch) == 0, "changeset: empty size");
+
+ // Test additions.
+ d = knot_dname_from_str_alloc("non.terminals.test.");
+ assert(d);
+ knot_rrset_t *apex_txt_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600, NULL);
+ assert(apex_txt_rr);
+ uint8_t data[8] = "\7teststr";
+ knot_rrset_add_rdata(apex_txt_rr, data, sizeof(data), NULL);
+
+ int ret = changeset_add_addition(ch, apex_txt_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: add RRSet");
+ ok(changeset_size(ch) == 1, "changeset: size add");
+ ret = changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: rem RRSet");
+ ok(changeset_size(ch) == 1, "changeset: size remove");
+ ok(!changeset_empty(ch), "changeset: empty");
+ changeset_add_addition(ch, apex_txt_rr, CHANGESET_CHECK);
+
+ // Add another RR to node.
+ knot_rrset_t *apex_spf_rr = knot_rrset_new(d, KNOT_RRTYPE_SPF, KNOT_CLASS_IN, 3600, NULL);
+ assert(apex_spf_rr);
+ knot_rrset_add_rdata(apex_spf_rr, data, sizeof(data), NULL);
+ ret = changeset_add_addition(ch, apex_spf_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: add multiple");
+
+ // Add another node.
+ knot_dname_free(d, NULL);
+ d = knot_dname_from_str_alloc("here.come.more.non.terminals.test");
+ assert(d);
+ knot_rrset_t *other_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600, NULL);
+ assert(other_rr);
+ knot_rrset_add_rdata(other_rr, data, sizeof(data), NULL);
+ ret = changeset_add_addition(ch, other_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: remove multiple");
+
+ // Test add traversal.
+ changeset_iter_t it;
+ ret = changeset_iter_add(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: create iter add");
+ // Order: non.terminals.test. TXT, SPF, here.come.more.non.terminals.test. TXT.
+ knot_rrset_t iter = changeset_iter_next(&it);
+ bool trav_ok = knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, apex_spf_rr, KNOT_RRSET_COMPARE_WHOLE);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, other_rr, KNOT_RRSET_COMPARE_WHOLE);
+
+ ok(trav_ok, "changeset: add traversal");
+
+ iter = changeset_iter_next(&it);
+ changeset_iter_clear(&it);
+ ok(knot_rrset_empty(&iter), "changeset: traversal: skip non-terminals");
+
+ changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+ changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+
+ // Test remove traversal.
+ ret = changeset_iter_rem(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: create iter rem");
+ iter = changeset_iter_next(&it);
+ ok(knot_rrset_equal(&iter, apex_txt_rr, KNOT_RRSET_COMPARE_WHOLE),
+ "changeset: rem traversal");
+ changeset_iter_clear(&it);
+
+ // Test all traversal - just count.
+ ret = changeset_iter_all(&it, ch);
+ is_int(KNOT_EOK, ret, "changest: create iter all");
+ size_t size = 0;
+ iter = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&iter)) {
+ ++size;
+ iter = changeset_iter_next(&it);
+ }
+ changeset_iter_clear(&it);
+ ok(size == 3, "changeset: iter all");
+
+ // Create new changeset.
+ knot_dname_free(d, NULL);
+ d = knot_dname_from_str_alloc("test.");
+ assert(d);
+ changeset_t *ch2 = changeset_new(d);
+ knot_dname_free(d, NULL);
+ assert(ch2);
+ // Add something to add section.
+ knot_dname_free(apex_txt_rr->owner, NULL);
+ apex_txt_rr->owner = knot_dname_from_str_alloc("something.test.");
+ assert(apex_txt_rr->owner);
+ ret = changeset_add_addition(ch2, apex_txt_rr, CHANGESET_CHECK);
+ assert(ret == KNOT_EOK);
+
+ // Add something to remove section.
+ knot_dname_free(apex_txt_rr->owner, NULL);
+ apex_txt_rr->owner =
+ knot_dname_from_str_alloc("and.now.for.something.completely.different.test.");
+ assert(apex_txt_rr->owner);
+ ret = changeset_add_removal(ch2, apex_txt_rr, CHANGESET_CHECK);
+ assert(ret == KNOT_EOK);
+
+ // Test merge.
+ ret = changeset_merge(ch, ch2, 0);
+ ok(ret == KNOT_EOK && changeset_size(ch) == 5, "changeset: merge");
+
+ // Test preapply fix.
+ zone_contents_t *z = zone_contents_new((const knot_dname_t *)"\x04""test");
+ knot_dname_free(apex_txt_rr->owner, NULL);
+ apex_txt_rr->owner = knot_dname_from_str_alloc("something.test.");
+ assert(apex_txt_rr->owner);
+ zone_node_t *znode = NULL;
+ ret = zone_contents_add_rr(z, apex_txt_rr, &znode);
+ assert(ret == KNOT_EOK);
+ ret = changeset_preapply_fix(z, ch2);
+ ok(ret == KNOT_EOK, "changeset: preapply fix ok (%s)", knot_strerror(ret));
+ ok(changeset_empty(ch2), "changeset: preapply fix works");
+ zone_contents_deep_free(z);
+
+ // Test cancelout.
+ ret = changeset_add_removal(ch2, apex_txt_rr, 0);
+ assert(ret == KNOT_EOK);
+ ret = changeset_add_addition(ch2, apex_txt_rr, 0);
+ assert(ret == KNOT_EOK);
+ ret = changeset_cancelout(ch2);
+ ok(ret == KNOT_EOK, "changeset: cancelout ok (%s)", knot_strerror(ret));
+ ok(changeset_empty(ch2), "changeset: cancelout works");
+
+ // Test cleanup.
+ changeset_clear(ch);
+ ok(changeset_empty(ch), "changeset: clear");
+ free(ch);
+
+ list_t chgs;
+ init_list(&chgs);
+ add_head(&chgs, &ch2->n);
+ changesets_clear(&chgs);
+ ok(changeset_empty(ch2), "changeset: clear list");
+ free(ch2);
+
+ knot_rrset_free(apex_txt_rr, NULL);
+ knot_rrset_free(apex_spf_rr, NULL);
+ knot_rrset_free(other_rr, NULL);
+
+ return 0;
+}
diff --git a/tests/knot/test_conf.c b/tests/knot/test_conf.c
new file mode 100644
index 0000000..59ae4dd
--- /dev/null
+++ b/tests/knot/test_conf.c
@@ -0,0 +1,210 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "knot/conf/conf.c"
+#include "test_conf.h"
+
+#define ZONE_ARPA "0/25.2.0.192.in-addr.arpa."
+#define ZONE_ROOT "."
+#define ZONE_1LABEL "x."
+#define ZONE_3LABEL "abc.ab.a."
+#define ZONE_UNKNOWN "unknown."
+
+static void check_name(const char *zone, const char *name, const char *ref)
+{
+ knot_dname_t *z = knot_dname_from_str_alloc(zone);
+
+ char *file = get_filename(NULL, NULL, z, name);
+ ok(file != NULL, "Get zonefile path for %s", zone);
+ if (file != NULL) {
+ ok(strcmp(file, ref) == 0, "Zonefile path compare %s", name);
+ free(file);
+ }
+
+ knot_dname_free(z, NULL);
+}
+
+static void check_name_err(const char *zone, const char *name)
+{
+ knot_dname_t *z = knot_dname_from_str_alloc(zone);
+
+ ok(get_filename(NULL, NULL, z, name) == NULL, "Invalid name %s", name);
+
+ knot_dname_free(z, NULL);
+}
+
+static void test_get_filename(void)
+{
+ // Name formatter.
+ char *zone = "abc";
+ check_name(zone, "/%s", "/abc");
+
+ zone = ".";
+ check_name(zone, "/%s", "/");
+
+ // Char formatter.
+ zone = "abc.def.g";
+ check_name(zone, "/%c[0]", "/a");
+ check_name(zone, "/%c[3]", "/.");
+ check_name(zone, "/%c[8]", "/g");
+ check_name(zone, "/%c[9]", "/.");
+ check_name(zone, "/%c[10]", "/");
+ check_name(zone, "/%c[255]", "/");
+ check_name(zone, "/%c[0-1]", "/ab");
+ check_name(zone, "/%c[1-1]", "/b");
+ check_name(zone, "/%c[1-3]", "/bc.");
+ check_name(zone, "/%c[1-4]", "/bc.d");
+ check_name(zone, "/%c[254-255]", "/");
+ check_name_err(zone, "/%c");
+ check_name_err(zone, "/%cx");
+ check_name_err(zone, "/%c[a]");
+ check_name_err(zone, "/%c[:]");
+ check_name_err(zone, "/%c[/]");
+ check_name_err(zone, "/%c[-1]");
+ check_name_err(zone, "/%c[256]");
+ check_name_err(zone, "/%c[");
+ check_name_err(zone, "/%c[1");
+ check_name_err(zone, "/%c[1-");
+ check_name_err(zone, "/%c[1-2");
+ check_name_err(zone, "/%c[1-b]");
+ check_name_err(zone, "/%c[8-0]");
+
+ zone = "abcd";
+ check_name(zone, "/%c[2-9]", "/cd.");
+ check_name(zone, "/%c[3]", "/d");
+ check_name(zone, "/%c[4]", "/.");
+
+ zone = ".";
+ check_name(zone, "/%c[0]", "/.");
+ check_name(zone, "/%c[1]", "/");
+
+ // Label formatter.
+ zone = "abc.def.gh";
+ check_name(zone, "/%l[0]", "/gh");
+ check_name(zone, "/%l[1]", "/def");
+ check_name(zone, "/%l[2]", "/abc");
+ check_name(zone, "/%l[3]", "/");
+ check_name(zone, "/%l[255]", "/");
+ check_name(zone, "/%l[0]-%l[1]-%l[2]", "/gh-def-abc");
+ check_name_err(zone, "/%l[0-1]");
+ check_name_err(zone, "/%l[-1]");
+ check_name_err(zone, "/%l[256]");
+
+ zone = ".";
+ check_name(zone, "/%l[0]", "/");
+ check_name(zone, "/%l[1]", "/");
+}
+
+static void test_conf_zonefile(void)
+{
+ int ret;
+ char *file;
+
+ knot_dname_t *zone_arpa = knot_dname_from_str_alloc(ZONE_ARPA);
+ ok(zone_arpa != NULL, "create dname "ZONE_ARPA);
+ knot_dname_t *zone_root = knot_dname_from_str_alloc(ZONE_ROOT);
+ ok(zone_root != NULL, "create dname "ZONE_ROOT);
+ knot_dname_t *zone_1label = knot_dname_from_str_alloc(ZONE_1LABEL);
+ ok(zone_1label != NULL, "create dname "ZONE_1LABEL);
+ knot_dname_t *zone_3label = knot_dname_from_str_alloc(ZONE_3LABEL);
+ ok(zone_3label != NULL, "create dname "ZONE_3LABEL);
+ knot_dname_t *zone_unknown = knot_dname_from_str_alloc(ZONE_UNKNOWN);
+ ok(zone_unknown != NULL, "create dname "ZONE_UNKNOWN);
+
+ const char *conf_str =
+ "template:\n"
+ " - id: default\n"
+ " storage: /tmp\n"
+ "\n"
+ "zone:\n"
+ " - domain: "ZONE_ARPA"\n"
+ " file: dir/a%%b/%s.suffix/%a\n"
+ " - domain: "ZONE_ROOT"\n"
+ " file: /%s\n"
+ " - domain: "ZONE_1LABEL"\n"
+ " file: /%s\n"
+ " - domain: "ZONE_3LABEL"\n";
+
+ ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ // Relative path with formatters.
+ file = conf_zonefile(conf(), zone_arpa);
+ ok(file != NULL, "Get zonefile path for "ZONE_ARPA);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/dir/a%b/0_25.2.0.192.in-addr.arpa.suffix/") == 0,
+ "Zonefile path compare for "ZONE_ARPA);
+ free(file);
+ }
+
+ // Absolute path without formatters - root zone.
+ file = conf_zonefile(conf(), zone_root);
+ ok(file != NULL, "Get zonefile path for "ZONE_ROOT);
+ if (file != NULL) {
+ ok(strcmp(file, "/") == 0,
+ "Zonefile path compare for "ZONE_ROOT);
+ free(file);
+ }
+
+ // Absolute path without formatters - non-root zone.
+ file = conf_zonefile(conf(), zone_1label);
+ ok(file != NULL, "Get zonefile path for "ZONE_1LABEL);
+ if (file != NULL) {
+ ok(strcmp(file, "/x") == 0,
+ "Zonefile path compare for "ZONE_1LABEL);
+ free(file);
+ }
+
+ // Default zonefile path.
+ file = conf_zonefile(conf(), zone_3label);
+ ok(file != NULL, "Get zonefile path for "ZONE_3LABEL);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/abc.ab.a.zone") == 0,
+ "Zonefile path compare for "ZONE_3LABEL);
+ free(file);
+ }
+
+ // Unknown zone zonefile path.
+ file = conf_zonefile(conf(), zone_unknown);
+ ok(file != NULL, "Get zonefile path for "ZONE_UNKNOWN);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/unknown.zone") == 0,
+ "Zonefile path compare for "ZONE_UNKNOWN);
+ free(file);
+ }
+
+ conf_free(conf());
+ knot_dname_free(zone_arpa, NULL);
+ knot_dname_free(zone_root, NULL);
+ knot_dname_free(zone_1label, NULL);
+ knot_dname_free(zone_3label, NULL);
+ knot_dname_free(zone_unknown, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("get_filename");
+ test_get_filename();
+
+ diag("conf_zonefile");
+ test_conf_zonefile();
+
+ return 0;
+}
diff --git a/tests/knot/test_conf.h b/tests/knot/test_conf.h
new file mode 100644
index 0000000..708692a
--- /dev/null
+++ b/tests/knot/test_conf.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+*/
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "libknot/errcode.h"
+
+/* Prepare server configuration. */
+static inline int test_conf(const char *conf_str, const yp_item_t *schema)
+{
+ // Use default schema if not specified.
+ if (schema == NULL) {
+ schema = conf_schema;
+ }
+
+ conf_t *new_conf = NULL;
+ int ret = conf_new(&new_conf, schema, NULL, 2 * 1024 * 1024, CONF_FNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = conf_import(new_conf, conf_str, false);
+ if (ret != KNOT_EOK) {
+ conf_free(new_conf);
+ return ret;
+ }
+
+ conf_update(new_conf, CONF_UPD_FNONE);
+
+ return KNOT_EOK;
+}
diff --git a/tests/knot/test_conf_tools.c b/tests/knot/test_conf_tools.c
new file mode 100644
index 0000000..178b269
--- /dev/null
+++ b/tests/knot/test_conf_tools.c
@@ -0,0 +1,123 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "knot/conf/tools.h"
+#include "libknot/libknot.h"
+
+static void mod_id_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ mod_id_to_bin,
+ mod_id_to_txt } };
+
+ diag("module id \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(b, val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void mod_id_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ mod_id_to_bin,
+ mod_id_to_txt } };
+
+ diag("module id \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void edns_opt_test(const char *txt, uint16_t code, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ edns_opt_to_bin,
+ edns_opt_to_txt } };
+
+ diag("edns option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ uint64_t c = knot_wire_read_u64(b);
+ ok(c == code, "compare code");
+ ok(memcmp(yp_bin(b + sizeof(uint64_t)), val,
+ yp_bin_len(b + sizeof(uint64_t))) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void edns_opt_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ edns_opt_to_bin,
+ edns_opt_to_txt } };
+
+ diag("edns option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Module id tests. */
+ mod_id_test("module/id", "\x06moduleid");
+ mod_id_test("module", "\x06module");
+ mod_id_bad_test("module/", KNOT_EINVAL);
+ mod_id_bad_test("/", KNOT_EINVAL);
+ mod_id_bad_test("/id", KNOT_EINVAL);
+
+ /* EDNS option tests. */
+ edns_opt_test("0:", 0, "");
+ edns_opt_test("65535:", 65535, "");
+ edns_opt_test("1:abc", 1, "abc");
+ edns_opt_test("1:0x0102", 1, "\x01\x02");
+ edns_opt_bad_test("0", KNOT_EINVAL);
+ edns_opt_bad_test("-1:a", KNOT_ERANGE);
+ edns_opt_bad_test("65536:a", KNOT_ERANGE);
+ edns_opt_bad_test("0:0xa", KNOT_EINVAL);
+
+ return 0;
+}
diff --git a/tests/knot/test_confdb.c b/tests/knot/test_confdb.c
new file mode 100644
index 0000000..a650872
--- /dev/null
+++ b/tests/knot/test_confdb.c
@@ -0,0 +1,489 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "test_conf.h"
+#include "knot/conf/confdb.c"
+
+static void check_db_content(conf_t *conf, knot_db_txn_t *txn, int count)
+{
+ ok(db_check_version(conf, txn) == KNOT_EOK, "Version check");
+ if (count >= 0) {
+ ok(conf->api->count(txn) == 1 + count, "Check DB entries count");
+ }
+}
+
+static void check_code(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ uint8_t section_code,
+ const yp_name_t *name,
+ db_action_t action,
+ int ret,
+ uint8_t ref_code)
+{
+ uint8_t code = 0;
+ ok(db_code(conf, txn, section_code, name, action, &code) == ret,
+ "Compare DB code return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t k[64] = { section_code, 0 };
+ memcpy(k + 2, name + 1, name[0]);
+ knot_db_val_t key = { .data = k, .len = 2 + name[0] };
+ knot_db_val_t val;
+
+ ret = conf->api->find(txn, &key, &val, 0);
+ switch (action) {
+ case DB_GET:
+ case DB_SET:
+ ok(code == ref_code, "Compare DB code");
+ is_int(KNOT_EOK, ret, "Find DB code");
+ ok(val.len == 1, "Compare DB code length");
+ ok(((uint8_t *)val.data)[0] == code, "Compare DB code value");
+ break;
+ case DB_DEL:
+ is_int(KNOT_ENOENT, ret, "Find item code");
+ break;
+ }
+}
+
+static void test_db_code(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Add codes.
+ check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 1);
+ check_code(conf, txn, 0, C_LOG, DB_SET, KNOT_EOK, KEY1_FIRST + 1);
+ check_db_content(conf, txn, 2);
+ check_code(conf, txn, 2, C_IDENT, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 3);
+ check_code(conf, txn, 0, C_ZONE, DB_SET, KNOT_EOK, KEY1_FIRST + 2);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_VERSION, DB_SET, KNOT_EOK, KEY1_FIRST + 1);
+ check_db_content(conf, txn, 5);
+
+ // Add existing code (no change).
+ check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+
+ // Get codes.
+ check_code(conf, txn, 0, C_SERVER, DB_GET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+ check_code(conf, txn, 0, C_RMT, DB_GET, KNOT_ENOENT, 0);
+ check_db_content(conf, txn, 5);
+
+ // Delete not existing code.
+ check_code(conf, txn, 0, C_COMMENT, DB_DEL, KNOT_ENOENT, 0);
+ check_db_content(conf, txn, 5);
+
+ // Delete codes.
+ check_code(conf, txn, 0, C_SERVER, DB_DEL, KNOT_EOK, 0);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_IDENT, DB_DEL, KNOT_EOK, 0);
+ check_db_content(conf, txn, 3);
+
+ // Reuse deleted codes.
+ check_code(conf, txn, 0, C_ACL, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_NSID, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+}
+
+static void check_set(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *data,
+ size_t data_len,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ ok(conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len) == ret,
+ "Check set return");
+
+ if (ret != KNOT_EOK || (key1 == NULL && id == NULL)) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ section_code = 0, item_code = 0; // prevents Wuninitialized
+ ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code) == KNOT_EOK,
+ "Get DB section code");
+ if (key1 != NULL) {
+ ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK,
+ "Get DB item code");
+ } else {
+ item_code = KEY1_ID;
+ }
+
+ uint8_t k[64] = { section_code, item_code };
+ if (id != NULL) {
+ memcpy(k + 2, id, id_len);
+ }
+ knot_db_val_t key = { .data = k, .len = 2 + id_len };
+ knot_db_val_t val;
+
+ ok(conf->api->find(txn, &key, &val, 0) == KNOT_EOK, "Get inserted data");
+ ok(val.len == exp_data_len, "Compare data length");
+ ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data");
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_set(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Set section without item - noop.
+ check_set(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EOK, NULL, 0, NULL, 0);
+
+ // Set singlevalued item.
+ check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\0", 1);
+ check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4);
+
+ // Set multivalued item.
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3);
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7);
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // Set group id.
+ check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, (uint8_t *)"", 0);
+
+ // Set singlevalued item with id.
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\0", 1);
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4);
+
+ // Set multivalued item with id.
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3);
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7);
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // ERR set invalid section.
+ check_set(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR set invalid item.
+ check_set(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR redefine section id.
+ check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_CONF_EREDEFINE,
+ NULL, 0, NULL, 0);
+
+ // ERR set singlevalued item with non-existing id.
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0, NULL, 0);
+}
+
+static void check_get(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ conf_val_t val;
+ ok(conf_db_get(conf, txn, key0, key1, id, id_len, &val) == ret,
+ "Check get return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ ok(val.blob_len == exp_data_len, "Compare data length");
+ ok(val.blob != NULL && memcmp(val.blob, exp_data, exp_data_len) == 0,
+ "Compare data");
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_get(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Get singlevalued item.
+ check_get(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4);
+
+ // Get multivalued item.
+ check_get(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // Get group id.
+ check_get(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"", 0);
+
+ // Get singlevalued item with id.
+ check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a b\0", 4);
+
+ // Get multivalued item with id.
+ check_get(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // ERR get section without item.
+ check_get(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EINVAL, NULL, 0);
+
+ // ERR get invalid section.
+ check_get(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0);
+
+ // ERR get invalid item.
+ check_get(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0);
+
+ // ERR get singlevalued item with non-existing id.
+ check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0);
+}
+
+static void check_unset(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *data,
+ size_t data_len,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ ok(conf_db_unset(conf, txn, key0, key1, id, id_len, data, data_len, false) == ret,
+ "Check unset return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code) == KNOT_EOK,
+ "Get DB section code");
+ if (key1 != NULL) {
+ ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK,
+ "Get DB item code");
+ } else {
+ item_code = KEY1_ID;
+ }
+
+ uint8_t k[64] = { section_code, item_code };
+ if (id != NULL) {
+ memcpy(k + 2, id, id_len);
+ }
+ knot_db_val_t key = { .data = k, .len = 2 + id_len };
+ knot_db_val_t val;
+
+ ret = conf->api->find(txn, &key, &val, 0);
+ if (exp_data != NULL) {
+ is_int(KNOT_EOK, ret, "Get deleted data");
+ ok(val.len == exp_data_len, "Compare data length");
+ ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data");
+ } else {
+ is_int(KNOT_ENOENT, ret, "Get deleted data");
+ }
+
+ check_db_content(conf, txn, -1);
+}
+
+static void check_unset_key(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret)
+{
+ ok(conf_db_unset(conf, txn, key0, key1, id, id_len, NULL, 0, true) == ret,
+ "Check unset return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ ret = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code);
+ if (key1 == NULL && id_len == 0) {
+ is_int(KNOT_ENOENT, ret, "Get DB section code");
+ } else {
+ is_int(KNOT_EOK, ret, "Get DB section code");
+ ret = db_code(conf, txn, section_code, key1, DB_GET, &item_code);
+ is_int(KNOT_ENOENT, ret, "Get DB item code");
+ }
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_unset(conf_t *conf, knot_db_txn_t *txn)
+{
+ // ERR unset section without item.
+ check_unset(conf, txn, C_INCL, NULL, NULL, 0, KNOT_ENOENT,
+ NULL, 0, NULL, 0);
+
+ // ERR unset invalid section.
+ check_unset(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR unset invalid item.
+ check_unset(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR unset singlevalued item with non-existing id.
+ check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0, NULL, 0);
+
+ // ERR unset singlevalued item invalid value.
+ check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_ENOENT,
+ (uint8_t *)"x\0", 2, NULL, 0);
+
+ // Unset singlevalued item data.
+ check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, NULL, 0);
+ // Unset item.
+ check_unset_key(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK);
+
+ // Unset multivalued item.
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""b\0", 7);
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"", 1, (uint8_t *)"\x00\x02""b\0", 4);
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"b", 2, NULL, 0);
+ // Unset item.
+ check_unset_key(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK);
+ // Unset section.
+ check_unset_key(conf, txn, C_SERVER, NULL, NULL, 0, KNOT_EOK);
+
+ // Unset singlevalued item with id - all data at one step.
+ check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, NULL, 0);
+
+ // Unset multivalued item with id - all data at one step (non-null data!).
+ check_unset(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL + 1, 0, NULL, 0);
+
+ // Unset group id.
+ check_unset(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, NULL, 0);
+}
+
+static void test_conf_db_iter(conf_t *conf, knot_db_txn_t *txn)
+{
+ const size_t total = 4;
+ char names[][10] = { "alfa", "beta", "delta", "epsilon" };
+
+ // Prepare identifiers to iterate through.
+ for (size_t i = 0; i < total; i++) {
+ check_set(conf, txn, C_RMT, NULL, (uint8_t *)names[i],
+ strlen(names[i]), KNOT_EOK, NULL, 0, (uint8_t *)"", 0);
+ }
+
+ // Create section iterator.
+ conf_iter_t iter;
+ int ret = conf_db_iter_begin(conf, txn, C_RMT, &iter);
+ is_int(KNOT_EOK, ret, "Create iterator");
+
+ // Iterate through the section.
+ size_t count = 0;
+ while (ret == KNOT_EOK) {
+ const uint8_t *id;
+ size_t id_len;
+ id = NULL, id_len = 0; // prevents Wuinitialized
+ ret = conf_db_iter_id(conf, &iter, &id, &id_len);
+ is_int(KNOT_EOK, ret, "Get iteration id");
+ ok(id_len == strlen(names[count]), "Compare iteration id length");
+ ok(memcmp(id, names[count], id_len) == 0, "Compare iteration id");
+
+ ok(conf_db_iter_del(conf, &iter) == KNOT_EOK, "Delete iteration key");
+
+ count++;
+ ret = conf_db_iter_next(conf, &iter);
+ }
+ is_int(KNOT_EOF, ret, "Finished iteration");
+ ok(count == total, "Check iteration count");
+
+ // Check empty section.
+ ret = conf_db_iter_begin(conf, txn, C_RMT, &iter);
+ is_int(KNOT_ENOENT, ret, "Create iterator");
+
+ // ERR non-iterable section.
+ ok(conf_db_iter_begin(conf, txn, C_SERVER, &iter) == KNOT_ENOTSUP, "Create iterator");
+
+ // ERR empty section.
+ ok(conf_db_iter_begin(conf, txn, C_ZONE, &iter) == KNOT_ENOENT, "Create iterator");
+
+ // ERR section with no code.
+ ok(conf_db_iter_begin(conf, txn, C_LOG, &iter) == KNOT_ENOENT, "Create iterator");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ok(test_conf("", NULL) == KNOT_EOK, "Prepare configuration");
+ check_db_content(conf(), &conf()->read_txn, 0);
+
+ knot_db_txn_t txn;
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("db_code");
+ test_db_code(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("conf_db_set");
+ test_conf_db_set(conf(), &txn);
+
+ diag("conf_db_get");
+ test_conf_db_get(conf(), &txn);
+
+ diag("conf_db_unset");
+ test_conf_db_unset(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("conf_db_iter");
+ test_conf_db_iter(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c
new file mode 100644
index 0000000..6519d22
--- /dev/null
+++ b/tests/knot/test_confio.c
@@ -0,0 +1,1009 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "test_conf.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/tools.h"
+#include "libknot/yparser/yptrafo.h"
+#include "contrib/string.h"
+#include "contrib/openbsd/strlcat.h"
+
+#define SKIP_OPENBSD skip("Nested transactions are not supported on OpenBSD");
+#define OUT_LEN 1024
+#define ZONE1 "zone1"
+#define ZONE2 "zone2"
+#define ZONE3 "zone3"
+
+char *format_key(conf_io_t *io)
+{
+ char id[KNOT_DNAME_TXT_MAXLEN + 1] = "\0";
+ size_t id_len = sizeof(id);
+
+ // Get the textual item id.
+ if (io->id_len > 0 && !io->id_as_data) {
+ if (yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len, id,
+ &id_len, YP_SNOQUOTE) != KNOT_EOK) {
+ return NULL;
+ }
+ }
+
+ // Get the item prefix.
+ const char *prefix = "";
+ switch (io->type) {
+ case NEW: prefix = "+"; break;
+ case OLD: prefix = "-"; break;
+ default: break;
+ }
+
+ // Format the item key.
+ return sprintf_alloc(
+ "%s%.*s%s%.*s%s%s%.*s",
+ prefix, (int)io->key0->name[0], io->key0->name + 1,
+ (io->id_len > 0 && !io->id_as_data ? "[" : ""),
+ (io->id_len > 0 && !io->id_as_data ? (int)id_len : 0), id,
+ (io->id_len > 0 && !io->id_as_data ? "]" : ""),
+ (io->key1 != NULL ? "." : ""),
+ (io->key1 != NULL ? (int)io->key1->name[0] : 0),
+ (io->key1 != NULL ? io->key1->name + 1 : ""));
+}
+
+static int append_data(const yp_item_t *item, const uint8_t *bin, size_t bin_len,
+ char *out, size_t out_len)
+{
+ char buf[YP_MAX_TXT_DATA_LEN + 1] = "\0";
+ size_t buf_len = sizeof(buf);
+
+ int ret = yp_item_to_txt(item, bin, bin_len, buf, &buf_len, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (strlcat(out, buf, out_len) >= out_len) {
+ return KNOT_ESPACE;
+ }
+
+ return KNOT_EOK;
+}
+
+char *format_data(conf_io_t *io)
+{
+ char out[YP_MAX_TXT_DATA_LEN + 1] = "\0";
+
+ // Return the item identifier as the item data.
+ if (io->id_as_data) {
+ if (append_data(io->key0->var.g.id, io->id, io->id_len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+
+ return strdup(out);
+ }
+
+ // Check for no data.
+ if (io->data.val == NULL && io->data.bin == NULL) {
+ return NULL;
+ }
+
+ const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0;
+
+ // Format explicit binary data value.
+ if (io->data.bin != NULL) {
+ if (append_data(item, io->data.bin, io->data.bin_len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+ // Format multivalued item data.
+ } else if (item->flags & YP_FMULTI) {
+ size_t values = conf_val_count(io->data.val);
+ for (size_t i = 0; i < values; i++) {
+ // Skip other values if known index (counted from 1).
+ if (io->data.index > 0 &&
+ io->data.index != i + 1) {
+ conf_val_next(io->data.val);
+ continue;
+ }
+
+ if (i > 0) {
+ if (strlcat(out, " ", sizeof(out)) >= sizeof(out)) {
+ return NULL;
+ }
+ }
+
+ conf_val(io->data.val);
+ if (append_data(item, io->data.val->data, io->data.val->len,
+ out, sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+
+ conf_val_next(io->data.val);
+ }
+ // Format singlevalued item data.
+ } else {
+ conf_val(io->data.val);
+ if (append_data(item, io->data.val->data, io->data.val->len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+ }
+
+ return strdup(out);
+}
+
+static int format_item(conf_io_t *io)
+{
+ char *out = (char *)io->misc;
+
+ // Get the item key and data strings.
+ char *key = format_key(io);
+ char *data = format_data(io);
+
+ // Format the item.
+ char *item = sprintf_alloc(
+ "%s%s%s%s",
+ (*out != '\0' ? "\n" : ""),
+ (key != NULL ? key : ""),
+ (data != NULL ? " = " : ""),
+ (data != NULL ? data : ""));
+ free(key);
+ free(data);
+ if (item == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Append the item.
+ if (strlcat(out, item, OUT_LEN) >= OUT_LEN) {
+ free(item);
+ return KNOT_ESPACE;
+ }
+
+ free(item);
+
+ return KNOT_EOK;
+}
+
+static void test_conf_io_begin(void)
+{
+ ok(conf_io_begin(true) == KNOT_TXN_ENOTEXISTS, "begin child txn with no parent");
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
+
+ ok(conf_io_begin(false) == KNOT_TXN_EEXISTS, "begin another parent txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[i]), "check txn depth");
+ }
+ ok(conf_io_begin(true) == KNOT_TXN_EEXISTS, "begin another child txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[CONF_MAX_TXN_DEPTH - 1]),
+ "check txn depth");
+
+ conf_io_abort(false);
+ ok(conf()->io.txn == NULL, "check txn depth");
+#endif
+}
+
+static void test_conf_io_abort(void)
+{
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ // Test child persistence after subchild abort.
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ char idx[2] = { '0' };
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
+ char idx[2] = { '0' + i };
+ conf_io_abort(true);
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(*data == (idx[0] - 1), "compare txn data '%s'", data);
+ }
+
+ conf_io_abort(false);
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+ // Test child abort with committed subchild.
+ ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
+ ok(conf_io_begin(true) == KNOT_EOK, "begin subchild txn");
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_EOK, "set single value");
+ ok(conf_io_commit(true) == KNOT_EOK, "commit subchild txn");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(strcmp(data, "text") == 0, "compare subchild txn data '%s'", data);
+ conf_io_abort(true);
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ conf_io_abort(false);
+
+ // Test unchanged read_txn.
+ val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+#endif
+}
+
+static void test_conf_io_commit(void)
+{
+ ok(conf_io_commit(false) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
+ ok(conf_io_commit(true) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
+
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ // Test subchild persistence after commit.
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ char idx[2] = { '0' };
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_commit(true) == KNOT_EOK, "commit child txn %s", idx);
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(*data == ('0' + CONF_MAX_TXN_DEPTH - 1), "compare txn data '%s'", data);
+ }
+
+ ok(conf_io_commit(false) == KNOT_EOK, "commit parent txn");
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+ // Test child persistence after parent commit.
+ ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ idx[0] = '0' + CONF_MAX_TXN_DEPTH - 1;
+ const char *data = conf_str(&val);
+ ok(strcmp(data, idx) == 0, "compare final data '%s'", data);
+ conf_io_abort(false);
+
+ // Test unchanged read_txn.
+ val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+#endif
+}
+
+static void test_conf_io_check(void)
+{
+ conf_io_t io = { NULL };
+
+ // ERR no txn.
+ ok(conf_io_check(&io) ==
+ KNOT_TXN_ENOTEXISTS, "check without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // Section check.
+ ok(conf_io_set("remote", "id", NULL, "remote1") ==
+ KNOT_EOK, "set remote id");
+ ok(conf_io_check(&io) ==
+ KNOT_EINVAL, "check missing remote address");
+ ok(io.error.code == KNOT_EINVAL, "compare error code");
+
+ ok(conf_io_set("remote", "address", "remote1", "1.1.1.1") ==
+ KNOT_EOK, "set remote address");
+ ok(conf_io_check(&io) ==
+ KNOT_EOK, "check remote address");
+ ok(io.error.code == KNOT_EOK, "compare error code");
+
+ // Item check.
+ ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
+ KNOT_EOK, "set zone domain "ZONE1);
+ ok(conf_io_set("zone", "master", ZONE1, "remote1") ==
+ KNOT_EOK, "set zone master");
+
+ ok(conf_io_check(&io) ==
+ KNOT_EOK, "check all");
+
+ ok(conf_io_unset("remote", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset remotes");
+
+ ok(conf_io_check(&io) ==
+ KNOT_ENOENT, "check missing master remote");
+ ok(io.error.code == KNOT_ENOENT, "compare error code");
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_set(void)
+{
+ // ERR no txn.
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_TXN_ENOTEXISTS, "set without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_set(NULL, NULL, NULL, NULL) ==
+ KNOT_EINVAL, "set NULL key0");
+ ok(conf_io_set("", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set empty key0");
+ ok(conf_io_set("uknown", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set unknown key0");
+ ok(conf_io_set("server", "unknown", NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set unknown key1");
+ ok(conf_io_set("include", NULL, NULL, NULL) ==
+ KNOT_YP_ENODATA, "set non-group without data");
+ ok(conf_io_set("server", "background-workers", NULL, "x") ==
+ KNOT_EINVAL, "set invalid data");
+
+ // ERR callback
+ ok(conf_io_set("include", NULL, NULL, "invalid") ==
+ KNOT_EFILE, "set invalid callback value");
+
+ // Single group, no item, no value.
+ ok(conf_io_set("server", NULL, NULL, NULL) ==
+ KNOT_ENOTSUP, "set group no value");
+ // Single group, no item, value.
+ ok(conf_io_set("server", NULL, NULL, "text") ==
+ KNOT_YP_ENOTSUP_DATA, "set group value");
+ // Single group, item, no value.
+ ok(conf_io_set("server", "version", NULL, NULL) ==
+ KNOT_YP_ENODATA, "set group item no value");
+
+ // Single group, single value.
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_EOK, "set single value");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "text") == 0, "check entry value");
+
+ // Single group, multi value.
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "set multivalue 1");
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.2") ==
+ KNOT_EOK, "set multivalue 2");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(conf_val_count(&val) == 2, "check entry value count");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+ knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2 != NULL, "create dname "ZONE2);
+ knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
+ ok(zone3 != NULL, "create dname "ZONE3);
+
+ // Multi group no id.
+ ok(conf_io_set("zone", "domain", NULL, NULL) ==
+ KNOT_YP_ENOID, "set zone empty domain");
+
+ // Multi group ids.
+ ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
+ KNOT_EOK, "set zone domain "ZONE1);
+ ok(conf_io_set("zone", NULL, ZONE2, NULL) ==
+ KNOT_EOK, "set zone domain "ZONE2);
+
+ // Multi group, single value.
+ ok(conf_io_set("zone", "file", ZONE1, "name") ==
+ KNOT_EOK, "set zone file");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "name") == 0, "check entry value");
+
+ // Multi group, single value, bad id.
+ ok(conf_io_set("zone", "file", ZONE3, "name") ==
+ KNOT_YP_EINVAL_ID, "set zone file");
+
+ // Multi group, single value, all ids.
+ ok(conf_io_set("zone", "comment", NULL, "abc") ==
+ KNOT_EOK, "set zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
+
+ // Prepare different comment.
+ ok(conf_io_set("zone", "domain", NULL, ZONE3) ==
+ KNOT_EOK, "set zone domain "ZONE3);
+ ok(conf_io_set("zone", "comment", ZONE3, "xyz") ==
+ KNOT_EOK, "set zone comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "xyz") == 0, "check entry value");
+
+ knot_dname_free(zone1, NULL);
+ knot_dname_free(zone2, NULL);
+ knot_dname_free(zone3, NULL);
+
+ ok(conf_io_commit(false) == KNOT_EOK, "commit txn");
+
+ // Update read-only transaction.
+ ok(conf_refresh_txn(conf()) == KNOT_EOK, "update read-only txn");
+}
+
+static void test_conf_io_unset(void)
+{
+ // ERR no txn.
+ ok(conf_io_unset("server", "version", NULL, "text") ==
+ KNOT_TXN_ENOTEXISTS, "unset without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_unset("", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key0");
+ ok(conf_io_unset("uknown", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key0");
+ ok(conf_io_unset("server", "unknown", NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key1");
+ ok(conf_io_unset("include", NULL, NULL, "file") ==
+ KNOT_ENOTSUP, "unset non-group item");
+ ok(conf_io_unset("server", "background-workers", NULL, "x") ==
+ KNOT_EINVAL, "unset invalid data");
+
+ // Single group, single value.
+ ok(conf_io_unset("server", "version", NULL, "") ==
+ KNOT_ENOENT, "unset zero length text value");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "version", NULL, "bad text") ==
+ KNOT_ENOENT, "unset bad value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "version", NULL, "text") ==
+ KNOT_EOK, "unset explicit value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ ok(conf_io_unset("server", "version", NULL, NULL) ==
+ KNOT_EOK, "unset value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Single group, multi value.
+ ok(conf_io_unset("server", "listen", NULL, "9.9.9.9") ==
+ KNOT_ENOENT, "unset bad value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "unset explicit value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(conf_val_count(&val) == 1, "check entry value count");
+
+ ok(conf_io_unset("server", "listen", NULL, NULL) ==
+ KNOT_EOK, "unset value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Whole section items.
+ ok(conf_io_unset("server", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset section");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+ knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2 != NULL, "create dname "ZONE2);
+ knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
+ ok(zone3 != NULL, "create dname "ZONE3);
+
+ // Multi group, single value.
+ ok(conf_io_unset("zone", "file", ZONE1, "name") ==
+ KNOT_EOK, "unset zone file");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Multi group, single bad value, all ids.
+ ok(conf_io_unset("zone", "comment", NULL, "other") ==
+ KNOT_EOK, "unset zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, single value (not all match), all ids.
+ ok(conf_io_unset("zone", "comment", NULL, "abc") ==
+ KNOT_EOK, "unset some zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, single value (all match), all ids.
+ ok(conf_io_unset("zone", "comment", NULL, NULL) ==
+ KNOT_EOK, "unset all zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, all items, specific id.
+ ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
+ KNOT_EOK, "unset zone items");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, all items, all ids.
+ ok(conf_io_unset("zone", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset zone items");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // All groups.
+ ok(conf_io_unset(NULL, NULL, NULL, NULL) ==
+ KNOT_EOK, "unset all");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ knot_dname_free(zone1, NULL);
+ knot_dname_free(zone2, NULL);
+ knot_dname_free(zone3, NULL);
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_get(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR no txn.
+ ok(conf_io_get("server", "version", NULL, false, &io) ==
+ KNOT_TXN_ENOTEXISTS, "get without active txn");
+
+ // Get current, no active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, true, &io) ==
+ KNOT_EOK, "get current without active txn");
+ ref = "server.version = \"text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_get("", NULL, NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get empty key0");
+ ok(conf_io_get("uknown", NULL, NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get unknown key0");
+ ok(conf_io_get("server", "unknown", NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get unknown key1");
+ ok(conf_io_get("include", NULL, NULL, true, &io) ==
+ KNOT_ENOTSUP, "get non-group item");
+
+ // Update item in the active txn.
+ ok(conf_io_set("server", "version", NULL, "new text") ==
+ KNOT_EOK, "set single value");
+
+ // Get new, active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, false, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"new text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Get current, active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi value.
+ *out = '\0';
+ ok(conf_io_get("server", "listen", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Single group.
+ *out = '\0';
+ ok(conf_io_get("server", NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"\n"
+ "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+
+ // Multi group, all values, all ids.
+ *out = '\0';
+ ok(conf_io_get("zone", NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone[zone2.].comment = \"abc\"\n"
+ "zone.domain = \"zone3.\"\n"
+ "zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group ids.
+ *out = '\0';
+ ok(conf_io_get("zone", "domain", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone.domain = \"zone3.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group, all values, single id.
+ *out = '\0';
+ ok(conf_io_get("zone", NULL, ZONE1, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group, single value, single id.
+ *out = '\0';
+ ok(conf_io_get("zone", "file", ZONE1, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone[zone1.].file = \"name\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // All groups.
+ *out = '\0';
+ ok(conf_io_get(NULL, NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"\n"
+ "server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone[zone2.].comment = \"abc\"\n"
+ "zone.domain = \"zone3.\"\n"
+ "zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ knot_dname_free(zone1, NULL);
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_diff(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR no txn.
+ ok(conf_io_diff("server", "version", NULL, &io) ==
+ KNOT_TXN_ENOTEXISTS, "diff without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_diff("", NULL, NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff empty key0");
+ ok(conf_io_diff("uknown", NULL, NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff unknown key0");
+ ok(conf_io_diff("server", "unknown", NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff unknown key1");
+ ok(conf_io_diff("include", NULL, NULL, &io) ==
+ KNOT_ENOTSUP, "diff non-group item");
+
+ *out = '\0';
+ ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff no change");
+ ref = "";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Update singlevalued item.
+ ok(conf_io_set("server", "version", NULL, "new text") ==
+ KNOT_EOK, "set single value");
+
+ *out = '\0';
+ ok(conf_io_diff("server", "version", NULL, &io) == KNOT_EOK, "diff single item");
+ ref = "-server.version = \"text\"\n"
+ "+server.version = \"new text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Update multivalued item.
+ ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "unset multivalue");
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.3") ==
+ KNOT_EOK, "set multivalue");
+
+ *out = '\0';
+ ok(conf_io_diff("server", "listen", NULL, &io) == KNOT_EOK, "diff multi item");
+ ref = "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "+server.listen = \"1.1.1.2\" \"1.1.1.3\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Unset single item.
+ ok(conf_io_unset("zone", "comment", ZONE3, NULL) ==
+ KNOT_EOK, "unset multivalue");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", NULL, ZONE3, &io) == KNOT_EOK, "diff section");
+ ref = "-zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Unset id.
+ ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
+ KNOT_EOK, "unset id");
+ ok(conf_io_unset("zone", NULL, ZONE2, NULL) ==
+ KNOT_EOK, "unset id");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", NULL, ZONE2, &io) == KNOT_EOK, "diff id section");
+ ref = "-zone.domain = \"zone2.\"\n"
+ "-zone[zone2.].comment = \"abc\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", "domain", NULL, &io) == KNOT_EOK, "diff id");
+ ref = "-zone.domain = \"zone1.\"\n"
+ "-zone.domain = \"zone2.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ *out = '\0';
+ ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff whole change");
+ ref = "-server.version = \"text\"\n"
+ "+server.version = \"new text\"\n"
+ "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "+server.listen = \"1.1.1.2\" \"1.1.1.3\"\n"
+ "-zone.domain = \"zone1.\"\n"
+ "-zone[zone1.].file = \"name\"\n"
+ "-zone[zone1.].comment = \"abc\"\n"
+ "-zone.domain = \"zone2.\"\n"
+ "-zone[zone2.].comment = \"abc\"\n"
+ "-zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_list(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR.
+ ok(conf_io_list("", &io) ==
+ KNOT_YP_EINVAL_ITEM, "list empty key0");
+ ok(conf_io_list("uknown", &io) ==
+ KNOT_YP_EINVAL_ITEM, "list unknown key0");
+ ok(conf_io_list("include", &io) ==
+ KNOT_ENOTSUP, "list non-group item");
+
+ // Desc schema.
+ *out = '\0';
+ ok(conf_io_list(NULL, &io) ==
+ KNOT_EOK, "list schema");
+ ref = "server\n"
+ "control\n"
+ "remote\n"
+ "template\n"
+ "zone\n"
+ "include";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Desc group.
+ *out = '\0';
+ ok(conf_io_list("server", &io) ==
+ KNOT_EOK, "list group");
+ ref = "server.version\n"
+ "server.background-workers\n"
+ "server.listen\n"
+ "server.tcp-handshake-timeout\n"
+ "server.tcp-idle-timeout\n"
+ "server.tcp-reply-timeout\n"
+ "server.max-tcp-clients\n"
+ "server.max-udp-payload\n"
+ "server.max-ipv4-udp-payload\n"
+ "server.max-ipv6-udp-payload\n"
+ "server.edns-client-subnet\n"
+ "server.answer-rotation";
+ ok(strcmp(ref, out) == 0, "compare result");
+}
+
+static const yp_item_t desc_server[] = {
+ { C_VERSION, YP_TSTR, YP_VNONE },
+ { C_BG_WORKERS, YP_TINT, YP_VNONE },
+ { C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI },
+ // Required config cache items - assert fix.
+ { C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_MAX_TCP_CLIENTS, YP_TINT, YP_VNONE },
+ { C_MAX_UDP_PAYLOAD, YP_TINT, YP_VNONE },
+ { C_MAX_IPV4_UDP_PAYLOAD, YP_TINT, YP_VNONE },
+ { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VNONE },
+ { C_ECS, YP_TBOOL, YP_VNONE },
+ { C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_control[] = {
+ { C_TIMEOUT, YP_TINT, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_remote[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_ADDR, YP_TADDR, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+#define ZONE_ITEMS \
+ { C_FILE, YP_TSTR, YP_VNONE }, \
+ { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
+ { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE }, \
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+
+static const yp_item_t desc_template[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ ZONE_ITEMS
+ { NULL }
+};
+
+static const yp_item_t desc_zone[] = {
+ { C_DOMAIN, YP_TDNAME, YP_VNONE },
+ ZONE_ITEMS
+ { NULL }
+};
+
+const yp_item_t test_schema[] = {
+ { C_SRV, YP_TGRP, YP_VGRP = { desc_server } },
+ { C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
+ { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
+ { C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
+ { C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } },
+ { C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { include_file } },
+ { NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ok(test_conf("", test_schema) == KNOT_EOK, "Prepare configuration");
+
+ diag("conf_io_begin");
+ test_conf_io_begin();
+
+ diag("conf_io_abort");
+ test_conf_io_abort();
+
+ diag("conf_io_commit");
+ test_conf_io_commit();
+
+ diag("conf_io_check");
+ test_conf_io_check();
+
+ diag("conf_io_set");
+ test_conf_io_set();
+
+ diag("conf_io_unset");
+ test_conf_io_unset();
+
+ diag("conf_io_get");
+ test_conf_io_get();
+
+ diag("conf_io_diff");
+ test_conf_io_diff();
+
+ diag("conf_io_list");
+ test_conf_io_list();
+
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_dthreads.c b/tests/knot/test_dthreads.c
new file mode 100644
index 0000000..0637e7c
--- /dev/null
+++ b/tests/knot/test_dthreads.c
@@ -0,0 +1,145 @@
+/* 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 <signal.h>
+#include <stdlib.h>
+#include <tap/basic.h>
+
+#include "knot/server/dthreads.h"
+
+/* 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 *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;
+}
+
+/* Destructor data. */
+static volatile int _destructor_data = 0;
+static pthread_mutex_t _destructor_mx;
+
+/*! \brief Thread destructor. */
+int destruct(struct dthread *thread)
+{
+ pthread_mutex_lock(&_destructor_mx);
+ _destructor_data += 1;
+ pthread_mutex_unlock(&_destructor_mx);
+
+ return 0;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+int main(int argc, char *argv[])
+{
+ plan(8);
+
+ // 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));
+ pthread_mutex_init(&_runnable_mx, NULL);
+ pthread_mutex_init(&_destructor_mx, NULL);
+
+ /* Test 1: Create unit */
+ int size = 2;
+ dt_unit_t *unit = dt_create(size, &runnable, NULL, NULL);
+ ok(unit != NULL, "dthreads: create unit (size %d)", size);
+ if (unit == NULL) {
+ skip_block(7, "No dthreads unit");
+ goto skip_all;
+ }
+
+ /* Test 2: Start tasks. */
+ _runnable_i = 0;
+ ok(dt_start(unit) == 0, "dthreads: start single task");
+
+ /* Test 3: Wait for tasks. */
+ ok(dt_join(unit) == 0, "dthreads: join threads");
+
+ /* Test 4: Compare counter. */
+ int expected = _runnable_cycles * 2;
+ is_int(expected, _runnable_i, "dthreads: result ok");
+
+ /* Test 5: Deinitialize */
+ dt_delete(&unit);
+ ok(unit == NULL, "dthreads: delete unit");
+
+ /* Test 6: Wrong values. */
+ unit = dt_create(-1, NULL, NULL, NULL);
+ ok(unit == NULL, "dthreads: create with negative count");
+
+ /* Test 7: NULL operations crashing. */
+ int ret = 0;
+ ret += dt_activate(0);
+ ret += dt_cancel(0);
+ ret += dt_compact(0);
+ dt_delete(0);
+ ret += dt_is_cancelled(0);
+ ret += dt_join(0);
+ ret += dt_signalize(0, SIGALRM);
+ ret += dt_start(0);
+ ret += dt_stop(0);
+ ret += dt_unit_lock(0);
+ ret += dt_unit_unlock(0);
+ is_int(-198, ret, "dthreads: correct values when passed NULL context");
+
+ /* Test 8: Thread destructor. */
+ _destructor_data = 0;
+ unit = dt_create(2, 0, destruct, 0);
+ dt_start(unit);
+ dt_stop(unit);
+ dt_join(unit);
+ is_int(2, _destructor_data, "dthreads: destructor with dt_create_coherent()");
+ dt_delete(&unit);
+
+skip_all:
+
+ pthread_mutex_destroy(&_runnable_mx);
+ pthread_mutex_destroy(&_destructor_mx);
+ return 0;
+}
diff --git a/tests/knot/test_fdset.c b/tests/knot/test_fdset.c
new file mode 100644
index 0000000..181838c
--- /dev/null
+++ b/tests/knot/test_fdset.c
@@ -0,0 +1,152 @@
+/* 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 <fcntl.h>
+#include <unistd.h>
+#include <tap/basic.h>
+#include <time.h>
+
+#include "knot/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;
+}
+
+void* thr_action(void *arg)
+{
+ int *fd = (int *)arg;
+
+ /* Sleep for 100ms. */
+ struct timespec ts = { .tv_nsec = 1e8 };
+ nanosleep(&ts, NULL);
+
+ /* Write pattern. */
+ char pattern = WRITE_PATTERN;
+ if (write(*fd, &pattern, WRITE_PATTERN_LEN) == -1) {
+ // Error.
+ }
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan(12);
+
+ /* 1. Create fdset. */
+ fdset_t set;
+ int ret = fdset_init(&set, 32);
+ is_int(0, ret, "fdset: init");
+
+ /* 2. Create pipe. */
+ int fds[2], tmpfds[2];
+ ret = pipe(fds);
+ ok(ret >= 0, "fdset: pipe() works");
+ ret = pipe(tmpfds);
+ ok(ret >= 0, "fdset: 2nd pipe() works");
+
+ /* 3. Add fd to set. */
+ ret = fdset_add(&set, fds[0], POLLIN, NULL);
+ is_int(0, ret, "fdset: add to set works");
+ fdset_add(&set, tmpfds[0], POLLIN, NULL);
+
+ /* Schedule write. */
+ struct timeval ts, te;
+ gettimeofday(&ts, 0);
+ pthread_t t;
+ pthread_create(&t, 0, thr_action, &fds[1]);
+
+ /* 4. Watch fdset. */
+ int nfds = poll(set.pfd, set.n, 60 * 1000);
+ gettimeofday(&te, 0);
+ size_t diff = timeval_diff(&ts, &te);
+
+ ok(nfds > 0, "fdset: poll returned %d events in %zu ms", nfds, diff);
+
+ /* 5. Prepare event set. */
+ ok(set.pfd[0].revents & POLLIN, "fdset: pipe is active");
+
+ /* 6. Receive data. */
+ char buf = 0x00;
+ ret = read(set.pfd[0].fd, &buf, WRITE_PATTERN_LEN);
+ ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data");
+
+ /* 7-9. Remove from event set. */
+ ret = fdset_remove(&set, 0);
+ is_int(0, ret, "fdset: remove from fdset works");
+ close(fds[0]);
+ close(fds[1]);
+ ret = fdset_remove(&set, 0);
+ close(tmpfds[1]);
+ close(tmpfds[1]);
+ is_int(0, ret, "fdset: remove from fdset works (2)");
+ ret = fdset_remove(&set, 0);
+ ok(ret != 0, "fdset: removing nonexistent item");
+
+ /* 10. Crash test. */
+ fdset_init(0, 0);
+ fdset_add(0, 1, 1, 0);
+ fdset_add(0, 0, 1, 0);
+ fdset_remove(0, 1);
+ fdset_remove(0, 0);
+ ok(1, "fdset: crash test successful");
+
+ /* 11. Destroy fdset. */
+ ret = fdset_clear(&set);
+ is_int(0, ret, "fdset: destroyed");
+
+ /* Cleanup. */
+ pthread_join(t, 0);
+
+ return 0;
+}
diff --git a/tests/knot/test_journal.c b/tests/knot/test_journal.c
new file mode 100644
index 0000000..e12cc84
--- /dev/null
+++ b/tests/knot/test_journal.c
@@ -0,0 +1,746 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "libknot/libknot.h"
+#include "knot/journal/journal.c"
+#include "knot/zone/zone.h"
+#include "knot/zone/zone-diff.h"
+#include "libknot/rrtype/soa.h"
+#include "test_conf.h"
+
+#define RAND_RR_LABEL 16
+#define RAND_RR_PAYLOAD 64
+#define MIN_SOA_SIZE 22
+
+char *test_dir_name;
+journal_db_t *db;
+journal_t *j;
+const knot_dname_t *apex = (const uint8_t *)"\4test";
+
+static void set_conf(int zonefile_sync, size_t journal_usage)
+{
+ char conf_str[512];
+ snprintf(conf_str, sizeof(conf_str),
+ "zone:\n"
+ " - domain: %s\n"
+ " zonefile-sync: %d\n"
+ " max-journal-usage: %zu\n"
+ " max-journal-depth: 1000\n",
+ (const char *)(apex + 1), zonefile_sync, journal_usage);
+ int ret = test_conf(conf_str, NULL);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+}
+
+static void unset_conf(void)
+{
+ conf_update(NULL, CONF_UPD_FNONE);
+}
+
+/*! \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 Init RRSet with type SOA and given serial. */
+static void init_soa(knot_rrset_t *rr, const uint32_t serial, const knot_dname_t *apex)
+{
+ knot_rrset_init(rr, knot_dname_copy(apex, NULL), KNOT_RRTYPE_SOA, KNOT_CLASS_IN, 3600);
+
+ uint8_t soa_data[MIN_SOA_SIZE] = { 0 };
+ int ret = knot_rrset_add_rdata(rr, soa_data, sizeof(soa_data), NULL);
+ knot_soa_serial_set(rr->rrs.rdata, serial);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+}
+
+/*! \brief Init RRSet with type TXT, random owner and random payload. */
+static void init_random_rr(knot_rrset_t *rr , const knot_dname_t *apex)
+{
+ /* Create random label. */
+ char owner[RAND_RR_LABEL + knot_dname_size(apex)];
+ owner[0] = RAND_RR_LABEL - 1;
+ randstr(owner + 1, RAND_RR_LABEL);
+
+ /* Append zone apex. */
+ memcpy(owner + RAND_RR_LABEL, apex, knot_dname_size(apex));
+ knot_rrset_init(rr, knot_dname_copy((knot_dname_t *)owner, NULL),
+ KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600);
+
+ /* Create random RDATA. */
+ uint8_t txt[RAND_RR_PAYLOAD + 1];
+ txt[0] = RAND_RR_PAYLOAD - 1;
+ randstr((char *)(txt + 1), RAND_RR_PAYLOAD);
+
+ int ret = knot_rrset_add_rdata(rr, txt, RAND_RR_PAYLOAD, NULL);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+}
+
+/*! \brief Init changeset with random changes. */
+static void init_random_changeset(changeset_t *ch, const uint32_t from, const uint32_t to,
+ const size_t size, const knot_dname_t *apex, bool is_bootstrap)
+{
+ // Add SOAs
+ knot_rrset_t soa;
+
+ if (is_bootstrap) {
+ ch->soa_from = NULL;
+ } else {
+ init_soa(&soa, from, apex);
+ ch->soa_from = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_from);
+ knot_rrset_clear(&soa, NULL);
+ }
+
+ init_soa(&soa, to, apex);
+ ch->soa_to = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_to);
+ knot_rrset_clear(&soa, NULL);
+
+ // Add RRs to add section
+ for (size_t i = 0; i < size / 2; ++i) {
+ knot_rrset_t rr;
+ init_random_rr(&rr, apex);
+ int ret = changeset_add_addition(ch, &rr, 0);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+ knot_rrset_clear(&rr, NULL);
+ }
+
+ // Add RRs to remove section
+ for (size_t i = 0; i < size / 2 && !is_bootstrap; ++i) {
+ knot_rrset_t rr;
+ init_random_rr(&rr, apex);
+ int ret = changeset_add_removal(ch, &rr, 0);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+ knot_rrset_clear(&rr, NULL);
+ }
+}
+
+static void changeset_set_soa_serials(changeset_t *ch, uint32_t from, uint32_t to,
+ const knot_dname_t *apex)
+{
+ knot_rrset_t soa;
+
+ init_soa(&soa, from, apex);
+ knot_rrset_free(ch->soa_from, NULL);
+ ch->soa_from = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_from);
+ knot_rrset_clear(&soa, NULL);
+
+ init_soa(&soa, to, apex);
+ knot_rrset_free(ch->soa_to, NULL);
+ ch->soa_to = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_to);
+ knot_rrset_clear(&soa, NULL);
+}
+
+/*! \brief Compare two changesets for equality. */
+static bool changesets_eq(const changeset_t *ch1, changeset_t *ch2)
+{
+ if (changeset_size(ch1) != changeset_size(ch2)) {
+ return false;
+ }
+
+ changeset_iter_t it1;
+ changeset_iter_all(&it1, ch1);
+ changeset_iter_t it2;
+ changeset_iter_all(&it2, ch2);
+
+ knot_rrset_t rr1 = changeset_iter_next(&it1);
+ knot_rrset_t rr2 = changeset_iter_next(&it2);
+ bool ret = true;
+ while (!knot_rrset_empty(&rr1)) {
+ if (!knot_rrset_equal(&rr1, &rr2, KNOT_RRSET_COMPARE_WHOLE)) {
+ ret = false;
+ break;
+ }
+ rr1 = changeset_iter_next(&it1);
+ rr2 = changeset_iter_next(&it2);
+ }
+
+ changeset_iter_clear(&it1);
+ changeset_iter_clear(&it2);
+
+ return ret;
+}
+
+static bool changesets_list_eq(list_t *l1, list_t *l2)
+{
+ node_t *n = NULL;
+ node_t *k = HEAD(*l2);
+ WALK_LIST(n, *l1) {
+ if (k == NULL) {
+ return false;
+ }
+
+ changeset_t *ch1 = (changeset_t *) n;
+ changeset_t *ch2 = (changeset_t *) k;
+ if (!changesets_eq(ch1, ch2)) {
+ return false;
+ }
+
+ k = k->next;
+ }
+
+ if (k->next != NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \brief Test a list of changesets for continuity. */
+static bool test_continuity(list_t *l)
+{
+ node_t *n = NULL;
+ uint32_t key1, key2;
+ WALK_LIST(n, *l) {
+ if (n == TAIL(*l)) {
+ break;
+ }
+ changeset_t *ch1 = (changeset_t *) n;
+ changeset_t *ch2 = (changeset_t *) n->next;
+ key1 = knot_soa_serial(ch1->soa_to->rrs.rdata);
+ key2 = knot_soa_serial(ch2->soa_from->rrs.rdata);
+ if (key1 != key2) {
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static void test_journal_db(void)
+{
+ int ret, ret2 = KNOT_EOK;
+
+ ret = journal_db_init(&db, test_dir_name, 2 * 1024 * 1024, JOURNAL_MODE_ASYNC);
+ is_int(KNOT_EOK, ret, "journal: init db (%d)", ret);
+
+ ret = journal_open_db(&db);
+ is_int(KNOT_EOK, ret, "journal: open db (%d)", ret);
+
+ journal_db_close(&db);
+ ok(db == NULL, "journal: close and destroy db");
+
+ ret = journal_db_init(&db, test_dir_name, 4 * 1024 * 1024, JOURNAL_MODE_ASYNC);
+ if (ret == KNOT_EOK) ret2 = journal_open_db(&db);
+ ok(ret == KNOT_EOK && ret2 == KNOT_EOK, "journal: open with bigger mapsize (%d, %d)", ret, ret2);
+ journal_db_close(&db);
+
+ ret = journal_db_init(&db, test_dir_name, 1024 * 1024, JOURNAL_MODE_ASYNC);
+ if (ret == KNOT_EOK) ret2 = journal_open_db(&db);
+ ok(ret == KNOT_EOK && ret2 == KNOT_EOK, "journal: open with smaller mapsize (%d, %d)", ret, ret2);
+ journal_db_close(&db);
+}
+
+/*! \brief Test behavior with real changesets. */
+static void test_store_load(void)
+{
+ int ret, ret2 = KNOT_EOK;
+
+ set_conf(1000, 512 * 1024);
+
+ j = journal_new();
+ ok(j != NULL, "journal: new");
+
+ ret = journal_db_init(&db, test_dir_name, (512 + 1024) * 1024, JOURNAL_MODE_ASYNC);
+ if (ret == KNOT_EOK) ret2 = journal_open(j, &db, apex);
+ is_int(KNOT_EOK, ret, "journal: open (%d, %d)", ret, ret2);
+
+ /* Save and load changeset. */
+ changeset_t *m_ch = changeset_new(apex);
+ init_random_changeset(m_ch, 0, 1, 128, apex, false);
+ ret = journal_store_changeset(j, m_ch);
+ is_int(KNOT_EOK, ret, "journal: store changeset (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_INFO);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+ list_t l, k;
+ init_list(&l);
+ init_list(&k);
+ ret = journal_load_changesets(j, &l, 0);
+ add_tail(&k, &m_ch->n);
+ ok(ret == KNOT_EOK && changesets_list_eq(&l, &k), "journal: load changeset (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Load ctx's. */
+ chgset_ctx_list_t cl = { { 0 }, 0 };
+ ret = journal_load_chgset_ctx(j, &cl, 0);
+ ok(ret == KNOT_EOK, "journal: chgset_ctx: load (%s)", knot_strerror(ret));
+ chgset_ctx_list_close(&cl);
+
+ changesets_free(&l);
+ changesets_free(&k);
+
+ /* Flush the journal. */
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: first and simple flush (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+ init_list(&l);
+ init_list(&k);
+
+ /* Fill the journal. */
+ ret = KNOT_EOK;
+ uint32_t serial = 1;
+ for (; ret == KNOT_EOK && serial < 40000; ++serial) {
+ changeset_t *m_ch2 = changeset_new(apex);
+ init_random_changeset(m_ch2, serial, serial + 1, 128, apex, false);
+ ret = journal_store_changeset(j, m_ch2);
+ if (ret != KNOT_EOK) {
+ changeset_free(m_ch2);
+ break;
+ }
+ add_tail(&k, &m_ch2->n);
+ }
+ is_int(KNOT_EBUSY, ret, "journal: overfill with changesets (%d inserted) (%d should= %d)",
+ serial, ret, KNOT_EBUSY);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Load all changesets stored until now. */
+ ret = journal_load_changesets(j, &l, 1);
+ ok(ret == KNOT_EOK && changesets_list_eq(&l, &k), "journal: load changesets (%d)", ret);
+
+ changesets_free(&l);
+ init_list(&l);
+ ret = journal_load_changesets(j, &l, 1);
+ ok(ret == KNOT_EOK && changesets_list_eq(&l, &k), "journal: re-load changesets (%d)", ret);
+
+ ret = journal_load_chgset_ctx(j, &cl, 1);
+ ok(ret == KNOT_EOK, "journal: chgset_ctx: load 2 (%s)", knot_strerror(ret));
+ ok(list_size(&cl.l) == list_size(&l), "journal: chgset_ctx: load size %zu ?== %zu", list_size(&cl.l), list_size(&l));
+ chgset_ctx_list_close(&cl);
+
+ changesets_free(&l);
+ init_list(&l);
+
+ /* Flush the journal. */
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: second flush (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Test whether the journal kept changesets after flush. */
+ ret = journal_load_changesets(j, &l, 1);
+ ok(ret == KNOT_EOK && changesets_list_eq(&l, &k), "journal: load right after flush (%d)", ret);
+
+ changesets_free(&k);
+ changesets_free(&l);
+ init_list(&k);
+ init_list(&l);
+
+ /* Store next changeset. */
+ changeset_t ch;
+ ret = changeset_init(&ch, apex);
+ ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret);
+ init_random_changeset(&ch, serial, serial + 1, 128, apex, false);
+ ret = journal_store_changeset(j, &ch);
+ changeset_clear(&ch);
+ is_int(KNOT_EOK, ret, "journal: store after flush (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Load last changesets. */
+ init_list(&l);
+ ret = journal_load_changesets(j, &l, serial);
+ changesets_free(&l);
+ is_int(KNOT_EOK, ret, "journal: load changesets after flush (%d)", ret);
+
+ /* Flush the journal again. */
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: flush again (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Fill the journal using a list. */
+ uint32_t m_serial = 1;
+ for (; m_serial < serial / 2; ++m_serial) {
+ changeset_t *m_ch7 = changeset_new(apex);
+ init_random_changeset(m_ch7, m_serial, m_serial + 1, 128, apex, false);
+ add_tail(&l, &m_ch7->n);
+ }
+ ret = journal_store_changesets(j, &l);
+ is_int(KNOT_EOK, ret, "journal: fill with changesets using a list (%d inserted)", m_serial);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Cleanup. */
+ changesets_free(&l);
+ init_list(&l);
+
+ /* Load all previous changesets. */
+ ret = journal_load_changesets(j, &l, 1);
+ ok(ret == KNOT_EOK && knot_soa_serial(((changeset_t *)TAIL(l))->soa_to->rrs.rdata) == m_serial,
+ "journal: load all changesets");
+
+ /* Check for changeset ordering. */
+ ok(test_continuity(&l) == KNOT_EOK, "journal: changesets are in order");
+
+ /* Cleanup. */
+ changesets_free(&l);
+ init_list(&l);
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_flush 0");
+ ret = journal_drop_changesets(j); /* Clear the journal for the collision test */
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_drop_changesets");
+
+ /* Test for serial number collision handling. We insert changesets
+ * with valid serial sequence that overflows and then collides with itself.
+ * The sequence is 0 -> 1 -> 2 -> 2147483647 -> 4294967294 -> 1 which should
+ * remove changesets 0->1 and 1->2. */
+ ok(EMPTY_LIST(k), "journal: empty list k");
+ ok(EMPTY_LIST(l), "journal: empty list l");
+ changeset_t *m_ch3 = changeset_new(apex);
+ init_random_changeset(m_ch3, 0, 1, 128, apex, false);
+ ret = journal_store_changeset(j, m_ch3);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_store_changeset 1");
+ changeset_set_soa_serials(m_ch3, 1, 2, apex);
+ ret = journal_store_changeset(j, m_ch3);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_store_changeset 2");
+ changeset_set_soa_serials(m_ch3, 2, 2147483647, apex);
+ add_tail(&k, &m_ch3->n);
+ ret = journal_store_changeset(j, m_ch3);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_store_changeset 3");
+ changeset_t *m_ch4 = changeset_new(apex);
+ init_random_changeset(m_ch4, 2147483647, 4294967294, 128, apex, false);
+ add_tail(&k, &m_ch4->n);
+ ret = journal_store_changeset(j, m_ch4);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_store_changeset 4");
+ changeset_t *m_ch5 = changeset_new(apex);
+ init_random_changeset(m_ch5, 4294967294, 1, 128, apex, false);
+ add_tail(&k, &m_ch5->n);
+ ret = journal_store_changeset(j, m_ch5);
+ is_int(KNOT_EBUSY, ret, "journal: allways ok journal_store_changeset 5");
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_flush 1");
+ ret = journal_store_changeset(j, m_ch5);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_store_changeset 6");
+ ret = journal_flush(j);
+ is_int(KNOT_EOK, ret, "journal: allways ok journal_flush 2");
+ ret = journal_load_changesets(j, &l, 0);
+ ret2 = journal_load_changesets(j, &l, 1);
+ int ret3 = journal_load_changesets(j, &l, 2);
+ fprintf(stderr, "ret=%d ret2=%d ret3=%d\n", ret, ret2, ret3);
+ ok(ret == KNOT_ENOENT && ret2 == KNOT_ENOENT && ret3 == KNOT_EOK &&
+ changesets_list_eq(&l, &k), "journal: serial collision");
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ is_int(KNOT_EOK, ret, "journal check (%d)", ret);
+
+ /* Cleanup. */
+ changesets_free(&l);
+ changesets_free(&k);
+
+ init_list(&l);
+ init_list(&k);
+
+ /* Check bootstrap changeset */
+ ret = journal_drop_changesets(j);
+ ok(ret == KNOT_EOK, "journal: journal_drop_changesets must be ok");
+
+ changeset_t *m_ch6 = changeset_new(apex);
+ init_random_changeset(m_ch6, 0, 1, 128, apex, true);
+ ret = journal_store_changeset(j, m_ch6);
+ ok(ret == KNOT_EOK, "journal: store bootstrap (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ ok(ret == KNOT_EOK, "journal check (%d)", ret);
+ changeset_t *m_ch7 = changeset_new(apex);
+ init_random_changeset(m_ch7, 1, 2, 128, apex, false);
+ ret = journal_store_changeset(j, m_ch7);
+ ok(ret == KNOT_EOK, "journal: store after bootstrap (%d)", ret);
+ add_tail(&k, &m_ch6->n);
+ add_tail(&k, &m_ch7->n);
+ ret = journal_load_bootstrap(j, &l);
+ ok(ret == KNOT_EOK && changesets_list_eq(&l, &k), "journal: load boostrap (%d)", ret);
+ ret = journal_check(j, JOURNAL_CHECK_STDERR);
+ ok(ret == KNOT_EOK, "journal check (%d)", ret);
+
+ changesets_free(&l);
+ changesets_free(&k);
+
+ init_list(&l);
+ init_list(&k);
+
+ ret = journal_scrape(j);
+ ok(ret == KNOT_EOK, "journal: scrape must be ok");
+
+ unset_conf();
+}
+
+const uint8_t *rdA = (const uint8_t *) "\x01\x02\x03\x04";
+const uint8_t *rdB = (const uint8_t *) "\x01\x02\x03\x05";
+const uint8_t *rdC = (const uint8_t *) "\x01\x02\x03\x06";
+
+// frees owner
+static knot_rrset_t * tm_rrset(knot_dname_t * owner, const uint8_t * rdata)
+{
+ knot_rrset_t * rrs = knot_rrset_new(owner, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600, NULL);
+ knot_rrset_add_rdata(rrs, rdata, 4, NULL);
+ free(owner);
+ return rrs;
+}
+
+static knot_dname_t * tm_owner(const char * prefix, const knot_dname_t *apex)
+{
+ knot_dname_t * ret = malloc(strlen(prefix) + knot_dname_size(apex) + 2);
+ ret[0] = strlen(prefix);
+ strcpy((char *) (ret + 1), prefix);
+ memcpy(ret + ret[0] + 1, apex, knot_dname_size(apex));
+ return ret;
+}
+
+static knot_rrset_t * tm_rrs(const knot_dname_t * apex, int x)
+{
+ static knot_rrset_t * rrsA = NULL;
+ static knot_rrset_t * rrsB = NULL;
+ static knot_rrset_t * rrsC = NULL;
+
+ if (apex == NULL) {
+ knot_rrset_free(rrsA, NULL);
+ knot_rrset_free(rrsB, NULL);
+ knot_rrset_free(rrsC, NULL);
+ rrsA = rrsB = rrsC = NULL;
+ return NULL;
+ }
+
+ if (rrsA == NULL) rrsA = tm_rrset(tm_owner("aaaaaaaaaaaaaaaaa", apex), rdA);
+ if (rrsB == NULL) rrsB = tm_rrset(tm_owner("bbbbbbbbbbbbbbbbb", apex), rdB);
+ if (rrsC == NULL) rrsC = tm_rrset(tm_owner("ccccccccccccccccc", apex), rdC);
+ switch ((x % 3 + 3) % 3) {
+ case 0: return rrsA;
+ case 1: return rrsB;
+ case 2: return rrsC;
+ }
+ assert(0); return NULL;
+}
+
+int tm_rrcnt(const changeset_t * ch, int flg)
+{
+ changeset_iter_t it;
+ int i = 0;
+ if (flg >= 0) changeset_iter_add(&it, ch);
+ else changeset_iter_rem(&it, ch);
+
+ knot_rrset_t rri;
+ while (rri = changeset_iter_next(&it), !knot_rrset_empty(&rri)) i++;
+
+ changeset_iter_clear(&it);
+ return i;
+}
+
+static changeset_t * tm_chs(const knot_dname_t * apex, int x)
+{
+ static changeset_t * chsI = NULL, * chsX = NULL, * chsY = NULL;
+ static uint32_t serial = 0;
+
+ if (apex == NULL) {
+ changeset_free(chsI);
+ changeset_free(chsX);
+ changeset_free(chsY);
+ chsI = chsX = chsY = NULL;
+ return NULL;
+ }
+
+ if (chsI == NULL) {
+ chsI = changeset_new(apex);
+ assert(chsI != NULL);
+ changeset_add_addition(chsI, tm_rrs(apex, 0), 0);
+ changeset_add_addition(chsI, tm_rrs(apex, 1), 0);
+ }
+ if (chsX == NULL) {
+ chsX = changeset_new(apex);
+ assert(chsX != NULL);
+ changeset_add_removal(chsX, tm_rrs(apex, 1), 0);
+ changeset_add_addition(chsX, tm_rrs(apex, 2), 0);
+ }
+ if (chsY == NULL) {
+ chsY = changeset_new(apex);
+ assert(chsY != NULL);
+ changeset_add_removal(chsY, tm_rrs(apex, 2), 0);
+ changeset_add_addition(chsY, tm_rrs(apex, 1), 0);
+ }
+ assert(x >= 0);
+ changeset_t * ret;
+ if (x == 0) ret = chsI;
+ else if (x % 2 == 1) ret = chsX;
+ else ret = chsY;
+
+ changeset_set_soa_serials(ret, serial, serial + 1, apex);
+ serial++;
+
+ return ret;
+}
+
+static int merged_present(void)
+{
+ local_txn_t(txn, j);
+ txn_begin(txn, 0);
+ int res = md_flag(txn, MERGED_SERIAL_VALID);
+ txn_abort(txn);
+ return res;
+}
+
+static void test_merge(void)
+{
+ int i, ret;
+ list_t l;
+
+ // allow merge
+ set_conf(-1, 512 * 1024);
+ ok(journal_merge_allowed(j), "journal: merge allowed");
+
+ ret = journal_drop_changesets(j);
+ is_int(KNOT_EOK, ret, "journal: journal_drop_changesets must be ok");
+
+ // insert stuff and check the merge
+ for (i = 0; !merged_present() && i < 40000; i++) {
+ ret = journal_store_changeset(j, tm_chs(apex, i));
+ is_int(KNOT_EOK, ret, "journal: journal_store_changeset must be ok");
+ }
+ init_list(&l);
+ ret = journal_load_changesets(j, &l, 0);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets must be ok");
+ ok(list_size(&l) == 2, "journal: read the merged and one following");
+ changeset_t * mch = (changeset_t *)HEAD(l);
+ ok(list_size(&l) >= 1 && tm_rrcnt(mch, 1) == 2, "journal: merged additions # = 2");
+ ok(list_size(&l) >= 1 && tm_rrcnt(mch, -1) == 1, "journal: merged removals # = 1");
+ changesets_free(&l);
+
+ // insert one more and check the #s of results
+ journal_store_changeset(j, tm_chs(apex, i));
+ init_list(&l);
+ ret = journal_load_changesets(j, &l, 0);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets2 must be ok");
+ ok(list_size(&l) == 3, "journal: read merged together with new changeset");
+ changesets_free(&l);
+ init_list(&l);
+ ret = journal_load_changesets(j, &l, (uint32_t) (i - 3));
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets3 must be ok");
+ ok(list_size(&l) == 4, "journal: read short history of merged/unmerged changesets");
+ changesets_free(&l);
+
+ ret = journal_drop_changesets(j);
+ assert(ret == KNOT_EOK);
+
+ // disallow merge
+ unset_conf();
+ set_conf(1000, 512 * 1024);
+ ok(!journal_merge_allowed(j), "journal: merge disallowed");
+
+ tm_rrs(NULL, 0);
+ tm_chs(NULL, 0);
+ unset_conf();
+}
+
+static void test_stress_base(journal_t *j, size_t update_size, size_t file_size)
+{
+ int ret;
+ uint32_t serial = 0;
+
+ journal_close(j);
+ journal_db_close(&db);
+ db = NULL;
+ ret = journal_db_init(&db, test_dir_name, file_size, JOURNAL_MODE_ASYNC);
+ assert(ret == KNOT_EOK);
+ ret = journal_open_db(&db);
+ assert(ret == KNOT_EOK);
+ ret = journal_open(j, &db, apex);
+ assert(ret == KNOT_EOK);
+
+ set_conf(1000, file_size / 2);
+
+ changeset_t ch;
+ ret = changeset_init(&ch, apex);
+ ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret);
+ init_random_changeset(&ch, serial, serial + 1, update_size, apex, false);
+
+ for (int i = 1; i <= 6; ++i) {
+ serial = 0;
+ while (true) {
+ changeset_set_soa_serials(&ch, serial, serial + 1, apex);
+ ret = journal_store_changeset(j, &ch);
+ if (ret == KNOT_EOK) {
+ serial++;
+ } else {
+ break;
+ }
+ }
+
+ int ret = journal_flush(j);
+ ok(serial > 0 && ret == KNOT_EOK, "journal: pass #%d fillup run (%d inserts)", i, serial);
+ }
+
+ changeset_clear(&ch);
+
+ unset_conf();
+}
+
+/*! \brief Test behavior when writing to jurnal and flushing it. */
+static void test_stress(journal_t *j)
+{
+ diag("stress test: small data");
+ test_stress_base(j, 40, (1024 + 512) * 1024);
+
+ diag("stress test: medium data");
+ test_stress_base(j, 400, 3 * 1024 * 1024);
+
+ diag("stress test: large data");
+ test_stress_base(j, 4000, 10 * 1024 * 1024);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_dir_name = test_mkdtemp();
+
+ test_journal_db();
+
+ test_store_load();
+
+ test_merge();
+
+ test_stress(j);
+
+ journal_close(j);
+ journal_free(&j);
+ journal_db_close(&db);
+
+ test_rm_rf(test_dir_name);
+ free(test_dir_name);
+
+ return 0;
+}
diff --git a/tests/knot/test_kasp_db.c b/tests/knot/test_kasp_db.c
new file mode 100644
index 0000000..d775d0a
--- /dev/null
+++ b/tests/knot/test_kasp_db.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <limits.h>
+
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "libknot/libknot.h"
+#include "test_conf.h"
+#include "knot/dnssec/kasp/kasp_db.c"
+
+#define CHARS500_1 "kTZgFfrHPP2EOSK24zRjY9GlgCUEkZNBF5UwqsTWisCxGQT4ieinitjXWT1c" \
+ "pj+OR8UX/httSugee+MFsm5yOU/4/211BpLKwwOIAt4Yf8K7Bc+oXTdk15cH" \
+ "TRZtshM1AtfjRsX9rsLDsnaFCyMXzty9AQoRxSphjxnUUC6fszfrSsRx7htl" \
+ "/Xn1PAuwp9Bfn+FxAws98LYVuwiDqUgn4BR5lELdGd16zNOZnN7v023pmPDM" \
+ "nGyvIATuqTCPbFeXTfw7aIDyx2DF+y95/kSnPtY3c1b0Yf+oCv4t3Hx2jjWT" \
+ "9zuC6H+d+PL6HWqilJBs7ysn2FVpnE/Yo44VrQ8orw8QFZr1kR6z7AOVAcMk" \
+ "ac+44swsc8orGCyJx6OlUfN5oU3YahUfLqg9ewl13+P2chmeI6wUyttKsq/4" \
+ "Ud0YQAozBabiAKr1O/Eg7sZR6bV1YkCydQyYgmR/+VOu9D8Ld6uO4DcvhiE/" \
+ "2AmTkLsKLxtpMnQqsTnx"
+#define CHARS500_2 "pzqkMLvpxUYYg0KuMCcBsk9aMm4b5Ny+vJ5UnTq8DVC0jHJJyyGcljKqfpi3" \
+ "MkjfrWY0rbzXFZbZZ6i8bmhhRBcSxE+tK/3AU1LR7ZJsTuITuoJo5LKRH7Uu" \
+ "MU7RBAzFuk3o+Pcyk+9UdbiRn9p4QqPTvb+2xfYn1pJvGHofJcQsSsPEe9Hw" \
+ "ycVW+kdImvWiidn0/e1G6B2xibovnPKDUBFmTbdZIBKHb/eUUoUCNA9CWt5N" \
+ "g8MutK2ixlBJlOvA6CA1V/VW56EJpLqvMxLaoRks5VY5Ls7zWAy97GEFH0Pl" \
+ "uO/Rba1du5tsC0MAC08hljlmu9uoPhsvHdBYHUnQ7jDuYnu9GN3DN0Z6oVbV" \
+ "N01JQZYhKQK/Bl61oM5JubLydtAypryDoG3IH75LhoVC8iGxDoDkxt3zoi/Q" \
+ "PVfPZZsm5j5UOs3wrQL0KWylm2IDK42mrHK8F/XebnOYLNLQaan2a90C+fhH" \
+ "a6hvu0RorkZzGNAZkq/D"
+
+const knot_dname_t *zone1 = (const knot_dname_t *)"\x05""zonea";
+const knot_dname_t *zone2 = (const knot_dname_t *)"\x05""zoneb";
+
+kasp_db_t *db;
+
+const key_params_t params1 = { .id = "key id 1", .keytag = 1, .timing = { 1, 11, 111, 1111, 11111 },
+ .public_key = { 520, (uint8_t *)"pk1 plus 500 chars: " CHARS500_1 } };
+const key_params_t params2 = { .id = "key id 2", .keytag = 2, .timing = { 2, 22, 222, 2222, 22222 },
+ .public_key = { 520, (uint8_t *)"pk2 plus 500 chars: " CHARS500_2 } };
+
+bool params_eq(const key_params_t *a, const key_params_t *b)
+{
+ return ((a->keytag == b->keytag) && (a->public_key.size == b->public_key.size) &&
+ (a->timing.retire == b->timing.retire) && (strcmp(a->id, b->id) == 0) &&
+ (memcmp(a->public_key.data, b->public_key.data, b->public_key.size) == 0));
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ char *test_dir_name = test_mkdtemp();
+ bool ignore = false;
+
+ list_t l;
+ key_params_t *params;
+#define free_params free(params->id); free(params->public_key.data); params->id = NULL; params->public_key.data = NULL;
+
+ int ret = kasp_db_init(&db, test_dir_name, 500*1024*1024);
+ is_int(KNOT_EOK, ret, "kasp_db: init eok");
+ ret = kasp_db_open(db);
+ is_int(KNOT_EOK, ret, "kasp_db: open eok");
+ ok(db->keys_db != NULL, "kasp_db: keys db notnull");
+
+ ret = kasp_db_add_key(db, zone1, &params1);
+ is_int(KNOT_EOK, ret, "kasp_db: add key 1 eok");
+
+ ret = kasp_db_list_keys(db, zone1, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 1 eok");
+ is_int(1, list_size(&l), "kasp_db: list keys reports one key 1");
+ params = ((ptrnode_t *)HEAD(l))->d;
+ ok(params_eq(params, &params1), "kasp_db: key params equal 1");
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: list keys 1 enoent");
+ is_int(0, list_size(&l), "kasp_db: list keys reports no keys 1");
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_share_key(db, zone1, zone2, params1.id);
+ is_int(KNOT_EOK, ret, "kasp_db: share key eok");
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 3 eok");
+ is_int(1, list_size(&l), "kasp_db: list keys reports one key 2");
+ params = ((ptrnode_t *)HEAD(l))->d;
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_add_key(db, zone2, &params2);
+ is_int(KNOT_EOK, ret, "kasp_db: add key 2 eok");
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 4 eok");
+ is_int(2, list_size(&l), "kasp_db: list keys reports two keys 1");
+ params = ((ptrnode_t *)TAIL(l))->d;
+ ok(params_eq(params, &params2), "kasp_db: key params equal 2");
+ free_params
+ params = ((ptrnode_t *)HEAD(l))->d;
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_delete_key(db, zone1, params1.id, &ignore);
+ is_int(KNOT_EOK, ret, "kasp_db: delete key 1 eok");
+
+ ret = kasp_db_list_keys(db, zone1, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: list keys 2 enoent");
+ is_int(list_size(&l), 0, "kasp_db: list keys reports no keys 2");
+ ptrlist_deep_free(&l, NULL);
+
+ kasp_db_close(&db);
+
+ test_rm_rf(test_dir_name);
+ free(test_dir_name);
+
+ return 0;
+}
+
diff --git a/tests/knot/test_node.c b/tests/knot/test_node.c
new file mode 100644
index 0000000..ef2a01d
--- /dev/null
+++ b/tests/knot/test_node.c
@@ -0,0 +1,154 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "knot/zone/node.h"
+#include "libknot/libknot.h"
+
+static knot_rrset_t *create_dummy_rrset(const knot_dname_t *owner, uint16_t type)
+{
+ knot_rrset_t *r = knot_rrset_new(owner, type, KNOT_CLASS_IN, 3600, NULL);
+ assert(r);
+ uint8_t wire[16] = { 0 };
+ memcpy(wire, "testtest", strlen("testtest"));
+ int ret = knot_rrset_add_rdata(r, wire, strlen("testtest"), NULL);
+ assert(ret == KNOT_EOK);
+ (void)ret;
+ return r;
+}
+
+static knot_rrset_t *create_dummy_rrsig(const knot_dname_t *owner, uint16_t type)
+{
+ knot_rrset_t *r = knot_rrset_new(owner, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN,
+ 3600, NULL);
+ assert(r);
+ uint8_t wire[sizeof(uint16_t)];
+ knot_wire_write_u16(wire, type);
+ int ret = knot_rrset_add_rdata(r, wire, sizeof(uint16_t), NULL);
+ assert(ret == KNOT_EOK);
+ (void)ret;
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test.");
+ // Test new
+ zone_node_t *node = node_new(dummy_owner, NULL);
+ ok(node != NULL, "Node: new");
+ assert(node);
+ ok(knot_dname_is_equal(node->owner, dummy_owner), "Node: new - set fields");
+
+ // Test parent setting
+ zone_node_t *parent = node_new(dummy_owner, NULL);
+ assert(parent);
+ node_set_parent(node, parent);
+ ok(node->parent == parent && parent->children == 1, "Node: set parent.");
+
+ node_free(parent, NULL);
+
+ // Test RRSet addition
+ knot_rrset_t *dummy_rrset = create_dummy_rrset(dummy_owner, KNOT_RRTYPE_TXT);
+ int ret = node_add_rrset(node, dummy_rrset, NULL);
+ ok(ret == KNOT_EOK && node->rrset_count == 1 &&
+ knot_rdataset_eq(&dummy_rrset->rrs, &node->rrs[0].rrs), "Node: add RRSet.");
+
+ // Test shallow copy
+ node->flags |= NODE_FLAGS_DELEG;
+ zone_node_t *copy = node_shallow_copy(node, NULL);
+ ok(copy != NULL, "Node: shallow copy.");
+ assert(copy);
+ const bool copy_ok = knot_dname_is_equal(copy->owner, node->owner) &&
+ copy->rrset_count == node->rrset_count &&
+ memcmp(copy->rrs, node->rrs,
+ copy->rrset_count * sizeof(struct rr_data)) == 0 &&
+ copy->flags == node->flags;
+ ok(copy_ok, "Node: shallow copy - set fields.");
+
+ node_free(copy, NULL);
+
+ // Test RRSet getters
+ knot_rrset_t *n_rrset = node_create_rrset(node, KNOT_RRTYPE_TXT);
+ ok(n_rrset && knot_rrset_equal(n_rrset, dummy_rrset, KNOT_RRSET_COMPARE_WHOLE),
+ "Node: create existing RRSet.");
+
+ knot_rrset_free(n_rrset, NULL);
+
+ n_rrset = node_create_rrset(node, KNOT_RRTYPE_SOA);
+ ok(n_rrset == NULL, "Node: create non-existing RRSet.");
+
+ knot_rrset_t stack_rrset = node_rrset(node, KNOT_RRTYPE_TXT);
+ ok(knot_rrset_equal(&stack_rrset, dummy_rrset,
+ KNOT_RRSET_COMPARE_WHOLE), "Node: get existing RRSet.");
+ stack_rrset = node_rrset(node, KNOT_RRTYPE_SOA);
+ ok(knot_rrset_empty(&stack_rrset), "Node: get non-existent RRSet.");
+
+ knot_rdataset_t *n_rdataset = node_rdataset(node, KNOT_RRTYPE_TXT);
+ ok(n_rdataset && knot_rdataset_eq(n_rdataset, &dummy_rrset->rrs),
+ "Node: get existing rdataset.");
+ n_rdataset = node_rdataset(node, KNOT_RRTYPE_SOA);
+ ok(n_rdataset == NULL, "Node: get non-existing rdataset.");
+
+ stack_rrset = node_rrset_at(node, 0);
+ ok(knot_rrset_equal(&stack_rrset, dummy_rrset, KNOT_RRSET_COMPARE_WHOLE),
+ "Node: get existing position.");
+ stack_rrset = node_rrset_at(node, 1);
+ ok(knot_rrset_empty(&stack_rrset), "Node: get non-existent position.");
+
+ // Test TTL mismatch
+ dummy_rrset->ttl = 1800;
+ ret = node_add_rrset(node, dummy_rrset, NULL);
+ ok(ret == KNOT_ETTL && node->rrset_count == 1,
+ "Node: add RRSet, TTL mismatch.");
+
+ knot_rrset_free(dummy_rrset, NULL);
+
+ // Test bool functions
+ ok(node_rrtype_exists(node, KNOT_RRTYPE_TXT), "Node: type exists.");
+ ok(!node_rrtype_exists(node, KNOT_RRTYPE_AAAA), "Node: type does not exist.");
+ ok(!node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is not signed.");
+
+ dummy_rrset = create_dummy_rrsig(dummy_owner, KNOT_RRTYPE_TXT);
+ ret = node_add_rrset(node, dummy_rrset, NULL);
+ assert(ret == KNOT_EOK);
+
+ ok(node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is signed.");
+
+ knot_rrset_free(dummy_rrset, NULL);
+
+ // Test remove RRset
+ node_remove_rdataset(node, KNOT_RRTYPE_AAAA);
+ ok(node->rrset_count == 2, "Node: remove non-existent rdataset.");
+ void *to_free = node_rdataset(node, KNOT_RRTYPE_TXT)->rdata;
+ node_remove_rdataset(node, KNOT_RRTYPE_TXT);
+ ok(node->rrset_count == 1, "Node: remove existing rdataset.");
+
+ free(to_free);
+
+ // "Test" freeing
+ node_free_rrsets(node, NULL);
+ ok(node->rrset_count == 0, "Node: free RRSets.");
+
+ node_free(node, NULL);
+
+ knot_dname_free(dummy_owner, NULL);
+
+ return 0;
+}
diff --git a/tests/knot/test_process_query.c b/tests/knot/test_process_query.c
new file mode 100644
index 0000000..aa67877
--- /dev/null
+++ b/tests/knot/test_process_query.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libknot/descriptor.h"
+#include "libknot/packet/wire.h"
+#include "knot/nameserver/process_query.h"
+#include "test_server.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/mempool.h"
+
+/* Basic response check (4 TAP tests). */
+static void answer_sanity_check(const uint8_t *query,
+ const uint8_t *answer, uint16_t answer_len,
+ uint8_t expected_rcode, const char *name)
+{
+ ok(answer_len >= KNOT_WIRE_HEADER_SIZE, "ns: len(%s answer) >= DNS header", name);
+ if (answer_len >= KNOT_WIRE_HEADER_SIZE) {
+ ok(knot_wire_get_qr(answer), "ns: %s answer has QR=1", name);
+ is_int(expected_rcode, knot_wire_get_rcode(answer), "ns: %s answer RCODE=%d", name, expected_rcode);
+ is_int(knot_wire_get_id(query), knot_wire_get_id(answer), "ns: %s MSGID match", name);
+ } else {
+ skip_block(3, "ns: can't check DNS header");
+ }
+}
+
+/* Resolve query and check answer for sanity (2 TAP tests). */
+static void exec_query(knot_layer_t *layer, const char *name,
+ knot_pkt_t *query,
+ uint8_t expected_rcode)
+{
+ knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ assert(answer);
+
+ /* Input packet. */
+ knot_pkt_parse(query, 0);
+ knot_layer_consume(layer, query);
+
+ ok(layer->state == KNOT_STATE_PRODUCE ||
+ layer->state == KNOT_STATE_FAIL, "ns: process %s query", name);
+
+ /* Create answer. */
+ knot_layer_produce(layer, answer);
+ if (layer->state == KNOT_STATE_FAIL) {
+ /* Allow 1 generic error response. */
+ knot_layer_produce(layer, answer);
+ }
+
+ ok(layer->state == KNOT_STATE_DONE, "ns: answer %s query", name);
+
+ /* Check answer. */
+ answer_sanity_check(query->wire, answer->wire, answer->size, expected_rcode, name);
+
+ knot_pkt_free(answer);
+}
+
+/* \internal Helpers */
+#define WIRE_COPY(dst, dst_len, src, src_len) \
+ memcpy(dst, src, src_len); \
+ dst_len = src_len;
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Create processing context. */
+ knot_layer_t proc;
+ memset(&proc, 0, sizeof(knot_layer_t));
+ knot_layer_init(&proc, &mm, process_query_layer());
+
+ /* Create fake server environment. */
+ server_t server;
+ int ret = create_fake_server(&server, proc.mm);
+ is_int(KNOT_EOK, ret, "ns: fake server initialization");
+
+ zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);
+
+ /* Prepare. */
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm);
+
+ /* Create query processing parameter. */
+ struct sockaddr_storage ss;
+ memset(&ss, 0, sizeof(struct sockaddr_storage));
+ sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
+ knotd_qdata_params_t params = {
+ .remote = &ss,
+ .server = &server
+ };
+
+ /* Query processor (CH zone) */
+ knot_layer_begin(&proc, &params);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
+ exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR);
+
+ /* Query processor (valid input). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR);
+
+ /* Query processor (-1 bytes, not enough data). */
+ knot_layer_reset(&proc);
+ query->size -= 1;
+ exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR);
+ query->size += 1;
+
+ /* Query processor (+1 bytes trailing). */
+ knot_layer_reset(&proc);
+ query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
+ query->size += 1;
+ exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR);
+ query->size -= 1;
+
+ /* Forge NOTIFY query from SOA query. */
+ knot_layer_reset(&proc);
+ knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
+ exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH);
+
+ /* Forge AXFR query. */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
+ exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH);
+
+ /* Forge IXFR query (well formed). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
+ /* Append SOA RR. */
+ knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
+ knot_pkt_begin(query, KNOT_AUTHORITY);
+ knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0);
+ exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH);
+
+ /* \note Tests below are not possible without proper zone and zone data. */
+ /* #189 Process UPDATE query. */
+ /* #189 Process AXFR client. */
+ /* #189 Process IXFR client. */
+
+ /* Query processor (smaller than DNS header, ignore). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ size_t orig_query_size = query->size;
+ query->size = KNOT_WIRE_HEADER_SIZE - 1;
+ knot_layer_consume(&proc, query);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
+ query->size = orig_query_size;
+
+ /* Query processor (response, ignore). */
+ knot_layer_reset(&proc);
+ knot_wire_set_qr(query->wire);
+ knot_layer_consume(&proc, query);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
+
+ /* Finish. */
+ knot_layer_finish(&proc);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: processing end" );
+
+ /* Cleanup. */
+ mp_delete((struct mempool *)mm.ctx);
+ server_deinit(&server);
+ conf_free(conf());
+
+ return 0;
+}
+
+#undef WIRE_COPY
diff --git a/tests/knot/test_query_module.c b/tests/knot/test_query_module.c
new file mode 100644
index 0000000..cb4a606
--- /dev/null
+++ b/tests/knot/test_query_module.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libknot/libknot.h"
+#include "knot/nameserver/query_module.h"
+#include "libknot/packet/pkt.h"
+
+/* Universal processing stage. */
+unsigned state_visit(unsigned state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ knotd_mod_t *mod)
+{
+ /* Visit current state */
+ bool *state_map = (bool *)mod;
+ state_map[state] = true;
+
+ return state + 1;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create a map of expected steps. */
+ bool state_map[KNOTD_STAGES] = { false };
+
+ /* Prepare query plan. */
+ struct query_plan *plan = query_plan_create();
+ ok(plan != NULL, "query_plan: create");
+
+ /* Register all stage visits. */
+ int ret = KNOT_EOK;
+ for (unsigned stage = KNOTD_STAGE_BEGIN; stage < KNOTD_STAGES; ++stage) {
+ ret = query_plan_step(plan, stage, state_visit, state_map);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+ is_int(KNOT_EOK, ret, "query_plan: planned all steps");
+
+ /* Execute the plan. */
+ int state = 0, next_state = 0;
+ for (unsigned stage = KNOTD_STAGE_BEGIN; stage < KNOTD_STAGES; ++stage) {
+ struct query_step *step = NULL;
+ WALK_LIST(step, plan->stage[stage]) {
+ next_state = step->process(state, NULL, NULL, step->ctx);
+ if (next_state != state + 1) {
+ break;
+ }
+ state = next_state;
+ }
+ }
+ ok(state == KNOTD_STAGES, "query_plan: executed all steps");
+
+ /* Verify if all steps executed their callback. */
+ for (state = 0; state < KNOTD_STAGES; ++state) {
+ if (state_map[state] == false) {
+ break;
+ }
+ }
+ ok(state == KNOTD_STAGES, "query_plan: executed all callbacks");
+
+ /* Free the query plan. */
+ query_plan_free(plan);
+
+ return 0;
+}
diff --git a/tests/knot/test_requestor.c b/tests/knot/test_requestor.c
new file mode 100644
index 0000000..ea87fb7
--- /dev/null
+++ b/tests/knot/test_requestor.c
@@ -0,0 +1,164 @@
+/* Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <tap/basic.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+#include "knot/query/layer.h"
+#include "knot/query/requestor.h"
+#include "contrib/mempattern.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/mempool.h"
+
+/* @note Purpose of this test is not to verify process_answer functionality,
+ * but simply if the requesting/receiving works, so mirror is okay. */
+static int reset(knot_layer_t *ctx) { return KNOT_STATE_PRODUCE; }
+static int begin(knot_layer_t *ctx, void *module_param) { return reset(ctx); }
+static int finish(knot_layer_t *ctx) { return reset(ctx); }
+static int in(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_DONE; }
+static int out(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_CONSUME; }
+
+static const int TIMEOUT = 2000;
+
+/*! \brief Dummy answer processing module. */
+const knot_layer_api_t dummy_module = {
+ &begin, &reset, &finish, &in, &out
+};
+
+static void set_blocking_mode(int sock)
+{
+ int flags = fcntl(sock, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ fcntl(sock, F_SETFL, flags);
+}
+
+static void *responder_thread(void *arg)
+{
+ int fd = *(int *)arg;
+
+ set_blocking_mode(fd);
+ uint8_t buf[KNOT_WIRE_MAX_PKTSIZE] = { 0 };
+ while (true) {
+ int client = accept(fd, NULL, NULL);
+ if (client < 0) {
+ break;
+ }
+ int len = net_dns_tcp_recv(client, buf, sizeof(buf), -1);
+ if (len < KNOT_WIRE_HEADER_SIZE) {
+ close(client);
+ break;
+ }
+ knot_wire_set_qr(buf);
+ net_dns_tcp_send(client, buf, len, -1);
+ close(client);
+ }
+
+ return NULL;
+}
+
+/* Test implementations. */
+
+static struct knot_request *make_query(struct knot_requestor *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm);
+ assert(pkt);
+ static const knot_dname_t *root = (uint8_t *)"";
+ knot_pkt_put_question(pkt, root, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+
+ return knot_request_make(requestor->mm, (struct sockaddr *)dst,
+ (struct sockaddr *)src, pkt, NULL, 0);
+}
+
+static void test_disconnected(struct knot_requestor *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ struct knot_request *req = make_query(requestor, dst, src);
+ int ret = knot_requestor_exec(requestor, req, TIMEOUT);
+ is_int(KNOT_ECONN, ret, "requestor: disconnected/exec");
+ knot_request_free(req, requestor->mm);
+
+}
+
+static void test_connected(struct knot_requestor *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ /* Enqueue packet. */
+ struct knot_request *req = make_query(requestor, dst, src);
+ int ret = knot_requestor_exec(requestor, req, TIMEOUT);
+ is_int(KNOT_EOK, ret, "requestor: connected/exec");
+ knot_request_free(req, requestor->mm);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Initialize requestor. */
+ struct knot_requestor requestor;
+ knot_requestor_init(&requestor, &dummy_module, NULL, &mm);
+
+ /* Define endpoints. */
+ struct sockaddr_storage client = { 0 };
+ sockaddr_set(&client, AF_INET, "127.0.0.1", 0);
+ struct sockaddr_storage server = { 0 };
+ sockaddr_set(&server, AF_INET, "127.0.0.1", 0);
+
+ /* Bind to random port. */
+ int responder_fd = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&server, 0);
+ assert(responder_fd >= 0);
+ socklen_t addr_len = sockaddr_len((struct sockaddr *)&server);
+ getsockname(responder_fd, (struct sockaddr *)&server, &addr_len);
+
+ /* Test requestor in disconnected environment. */
+ test_disconnected(&requestor, &server, &client);
+
+ /* Start responder. */
+ int ret = listen(responder_fd, 10);
+ (void)ret;
+ assert(ret == 0);
+ pthread_t thread;
+ pthread_create(&thread, 0, responder_thread, &responder_fd);
+
+ /* Test requestor in connected environment. */
+ test_connected(&requestor, &server, &client);
+
+ /* Terminate responder. */
+ int conn = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&server, NULL);
+ assert(conn > 0);
+ conn = net_dns_tcp_send(conn, (uint8_t *)"", 1, TIMEOUT);
+ assert(conn > 0);
+ pthread_join(thread, NULL);
+ close(responder_fd);
+
+ /* Cleanup. */
+ mp_delete((struct mempool *)mm.ctx);
+
+ return 0;
+}
diff --git a/tests/knot/test_semantic_check.in b/tests/knot/test_semantic_check.in
new file mode 100644
index 0000000..ad65f78
--- /dev/null
+++ b/tests/knot/test_semantic_check.in
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+KZONECHECK="@top_builddir@/src/kzonecheck"
+DATA="@top_srcdir@/tests/knot/semantic_check_data"
+
+. "@top_srcdir@/tests/tap/libtap.sh"
+
+TMPDIR=$(test_tmpdir)
+LOG="$TMPDIR/log"
+
+# Params: zonefile fatal_error expected_erros_count semcheck_err_msg
+expect_error()
+{
+ if [ ! -r "$DATA/$1" ]; then
+ skip_block 4 "missing zone file for test"
+ return
+ fi
+
+ "$KZONECHECK" -o example.com "$DATA/$1" > "$LOG"
+ ok "$1 - check program return" test $? -eq 1
+
+ fatal=$(grep -E "^Serious semantic error detected" $LOG | wc -l)
+ ok "$1 - check fatal" test $fatal -eq $2
+
+ errors=$(grep -E "^\[.+\] $4" $LOG | wc -l)
+ ok "$1 - check errors" test $errors -eq $3
+ if [ $errors != $3 ]; then
+ diag "expected errors $3 but found $errors"
+ fi
+}
+
+#param zonefile
+test_correct()
+{
+ $KZONECHECK -o example.com "$DATA/$1" > /dev/null
+ ok "$1 - correct zone, without error" test $? -eq 0
+}
+
+if [ ! -x $KZONECHECK ]; then
+ skip_all "kzonecheck is missing or is not executable"
+fi
+
+# error messages exported from knot/src/zone/semantic-check.c
+CDNSKEY_MULTIPLE="multiple CDNSKEY records"
+CDNSKEY_NONE="missing CDNSKEY"
+CDS_MULTIPLE="multiple CDS records"
+CDS_NONE="missing CDS"
+CDS_NOT_MATCH="CDS not match CDNSKEY"
+CNAME_EXTRA_RECORDS="more records exist at CNAME"
+CNAME_MULTIPLE="multiple CNAME records"
+DNAME_CHILDREN="child record exists under DNAME"
+DNSKEY_PROTO="invalid protocol in DNSKEY"
+DS_ALG="invalid algorithm in DS"
+NSEC3PARAM_FLAGS="invalid flags in NSEC3PARAM"
+NSEC3_ALG="incorrect algorithm in NSEC3"
+NSEC3_INSECURE_DELEGATION_OPT="insecure delegation outside NSEC3 opt-out"
+NSEC3_ITERS="incorrect number of iterations in NSEC3"
+NSEC3_NONE="missing NSEC3"
+NSEC3_RDATA_BITMAP="incorrect type bitmap in NSEC3"
+NSEC3_RDATA_CHAIN="incoherent NSEC3 chain"
+NSEC_NONE="missing NSEC"
+NSEC_RDATA_BITMAP="incorrect type bitmap in NSEC"
+NSEC_RDATA_CHAIN="incoherent NSEC chain"
+NSEC_RDATA_MULTIPLE="multiple NSEC records"
+NS_APEX="missing NS at the zone apex"
+NS_GLUE="missing glue record"
+RRSIG_EXPIRED="expired RRSIG"
+RRSIG_NO_RRSIG="missing RRSIG"
+RRSIG_RDATA_DNSKEY_OWNER="wrong signer's name in RRSIG"
+RRSIG_RDATA_TTL="wrong original TTL in RRSIG"
+RRSIG_SIGNED="signed RRSIG"
+RRSIG_UNVERIFIABLE="unverifiable signature"
+
+plan_lazy
+
+expect_error "cname_extra_01.zone" 1 1 "$CNAME_EXTRA_RECORDS"
+expect_error "cname_extra_02.signed" 1 1 "$CNAME_EXTRA_RECORDS"
+expect_error "cname_multiple.zone" 1 1 "$CNAME_MULTIPLE"
+expect_error "dname_children.zone" 1 1 "$DNAME_CHILDREN"
+
+expect_error "missing_ns.zone" 0 1 "$NS_APEX"
+expect_error "missing_glue_01.zone" 0 2 "$NS_GLUE"
+expect_error "missing_glue_02.zone" 0 1 "$NS_GLUE"
+expect_error "missing_glue_03.zone" 0 1 "$NS_GLUE"
+expect_error "different_signer_name.signed" 0 1 "$RRSIG_RDATA_DNSKEY_OWNER \(record type NSEC\)"
+expect_error "different_signer_name.signed" 0 1 "$RRSIG_UNVERIFIABLE \(record type NSEC\)"
+expect_error "no_rrsig.signed" 0 1 "$RRSIG_NO_RRSIG \(record type A\)"
+expect_error "no_rrsig.signed" 0 1 "$RRSIG_NO_RRSIG \(record type NSEC\)"
+expect_error "no_rrsig_with_delegation.signed" 0 1 "$RRSIG_NO_RRSIG \(record type NSEC\)"
+expect_error "nsec_broken_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec_broken_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec_missing.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec_multiple.signed" 0 1 "$NSEC_RDATA_MULTIPLE"
+expect_error "nsec_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_missing.signed" 0 1 "$NSEC3_NONE"
+expect_error "nsec3_wrong_bitmap_01.signed" 0 1 "$NSEC3_RDATA_BITMAP"
+expect_error "nsec3_wrong_bitmap_02.signed" 0 1 "$NSEC3_RDATA_BITMAP"
+expect_error "nsec3_ds.signed" 0 1 "$NSEC3_NONE"
+expect_error "nsec3_optout.signed" 0 1 "$NSEC3_INSECURE_DELEGATION_OPT"
+expect_error "nsec3_chain_01.signed" 0 1 "$NSEC3_RDATA_CHAIN"
+expect_error "nsec3_chain_02.signed" 0 2 "$NSEC3_RDATA_CHAIN"
+expect_error "nsec3_chain_03.signed" 0 2 "$NSEC3_RDATA_CHAIN"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3_ALG"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3_ITERS"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3PARAM_FLAGS"
+expect_error "rrsig_signed.signed" 0 1 "$RRSIG_SIGNED"
+expect_error "rrsig_rdata_ttl.signed" 0 1 "$RRSIG_RDATA_TTL \(record type A\)"
+expect_error "duplicate.signature" 0 7 "$RRSIG_EXPIRED"
+expect_error "missing.signed" 0 1 "$NSEC_NONE"
+expect_error "dnskey_param_error.signed" 0 1 "$DNSKEY_PROTO"
+expect_error "invalid_ds.signed" 0 2 "$DS_ALG \(keytag 60485\)"
+expect_error "cdnskey.invalid" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.invalid.param" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.nocds" 0 1 "$CDS_NONE"
+expect_error "cdnskey.nocdnskey" 0 1 "$CDNSKEY_NONE"
+expect_error "cdnskey.nodnskey" 0 1 "$CDNSKEY_NOT_MATCH"
+expect_error "cdnskey.two" 0 1 "$CDS_MULTIPLE"
+expect_error "cdnskey.two" 0 1 "$CDNSKEY_MULTIPLE"
+
+test_correct "rrsig_ttl.signed"
+test_correct "no_error_delegaton_bitmap.signed"
+test_correct "no_error_nsec3_delegation.signed"
+test_correct "no_error_nsec3_optout.signed"
+test_correct "no_error_wildcard_glue.zone"
+test_correct "cdnskey.cds"
+test_correct "dname_apex_nsec3.signed"
+
+rm $LOG
diff --git a/tests/knot/test_server.c b/tests/knot/test_server.c
new file mode 100644
index 0000000..c1cc2ad
--- /dev/null
+++ b/tests/knot/test_server.c
@@ -0,0 +1,68 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include "knot/server/server.h"
+#include "test_conf.h"
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+int main(int argc, char *argv[])
+{
+ plan(2);
+
+ server_t server;
+ int ret = 0;
+
+ /* Some random configuration just to apply the default conf schema */
+ ret = test_conf("", NULL);
+ assert(ret == KNOT_EOK);
+
+ /* 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 */
+ ret = server_init(&server, 1);
+ is_int(KNOT_EOK, ret, "server: initialized");
+ if (ret != KNOT_EOK) {
+ return 1;
+ }
+
+ /* Test server startup */
+ ret = server_start(&server, false);
+ is_int(KNOT_EOK, ret, "server: started ok");
+ if (ret != KNOT_EOK) {
+ return 1;
+ }
+
+ server_stop(&server);
+
+ /* Wait for server to finish. */
+ server_wait(&server);
+
+ /* Wait for server to finish. */
+ server_deinit(&server);
+
+ return 0;
+}
diff --git a/tests/knot/test_server.h b/tests/knot/test_server.h
new file mode 100644
index 0000000..4a6e427
--- /dev/null
+++ b/tests/knot/test_server.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+ */
+
+#pragma once
+
+#include "test_conf.h"
+#include "knot/server/server.h"
+#include "contrib/mempattern.h"
+
+/* Some domain names. */
+#define ROOT_DNAME ((const uint8_t *)"")
+#define EXAMPLE_DNAME ((const uint8_t *)"\x7""example")
+#define IDSERVER_DNAME ((const uint8_t *)"\2""id""\6""server")
+
+/* Create fake root zone. */
+static inline void create_root_zone(server_t *server, knot_mm_t *mm)
+{
+ /* SOA RDATA. */
+ #define SOA_RDLEN 30
+ static const uint8_t SOA_RDATA[SOA_RDLEN] = {
+ 0x02, 'n', 's', 0x00, /* ns. */
+ 0x04, 'm', 'a', 'i', 'l', 0x00,/* mail. */
+ 0x77, 0xdf, 0x1e, 0x63, /* serial */
+ 0x00, 0x01, 0x51, 0x80, /* refresh */
+ 0x00, 0x00, 0x1c, 0x20, /* retry */
+ 0x00, 0x0a, 0x8c, 0x00, /* expire */
+ 0x00, 0x00, 0x0e, 0x10 /* min ttl */
+ };
+
+ /* Insert root zone. */
+ zone_t *root = zone_new(ROOT_DNAME);
+ root->journal_db = &server->journal_db;
+ root->contents = zone_contents_new(root->name);
+
+ knot_rrset_t *soa = knot_rrset_new(root->name, KNOT_RRTYPE_SOA, KNOT_CLASS_IN,
+ 7200, mm);
+ knot_rrset_add_rdata(soa, SOA_RDATA, SOA_RDLEN, mm);
+ node_add_rrset(root->contents->apex, soa, NULL);
+ knot_rrset_free(soa, mm);
+
+ /* Bake the zone. */
+ zone_contents_adjust_full(root->contents);
+
+ /* Switch zone db. */
+ knot_zonedb_free(&server->zone_db);
+ server->zone_db = knot_zonedb_new();
+ knot_zonedb_insert(server->zone_db, root);
+}
+
+/* Create fake server. */
+static inline int create_fake_server(server_t *server, knot_mm_t *mm)
+{
+ int ret;
+
+ /* Load test configuration. */
+ const char *conf_str = "server:\n identity: bogus.ns\n version: 0.11\n nsid: ""\n"
+ "zone:\n - domain: .\n zonefile-sync: -1\n";
+ ret = test_conf(conf_str, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Create name server. */
+ ret = server_init(server, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Insert root zone. */
+ create_root_zone(server, mm);
+
+ return KNOT_EOK;
+}
diff --git a/tests/knot/test_worker_pool.c b/tests/knot/test_worker_pool.c
new file mode 100644
index 0000000..e644aa6
--- /dev/null
+++ b/tests/knot/test_worker_pool.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <time.h>
+
+#include "knot/worker/pool.h"
+#include "knot/worker/queue.h"
+
+#define THREADS 4
+#define TASKS_BATCH 40
+
+/*!
+ * Task execution log.
+ */
+typedef struct task_log {
+ pthread_mutex_t mx;
+ unsigned executed;
+} task_log_t;
+
+/*!
+ * Get number of executed tasks and clear.
+ */
+static unsigned executed_reset(task_log_t *log)
+{
+ pthread_mutex_lock(&log->mx);
+ unsigned result = log->executed;
+ log->executed = 0;
+ pthread_mutex_unlock(&log->mx);
+
+ return result;
+}
+
+/*!
+ * Simple task, just increases the counter in the log.
+ */
+static void task_counting(task_t *task)
+{
+ task_log_t *log = task->ctx;
+
+ pthread_mutex_lock(&log->mx);
+ log->executed += 1;
+ pthread_mutex_unlock(&log->mx);
+}
+
+static void interrupt_handle(int s)
+{
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ // create pool
+
+ worker_pool_t *pool = worker_pool_create(THREADS);
+ ok(pool != NULL, "create worker pool");
+ if (!pool) {
+ return 1;
+ }
+
+ task_log_t log = {
+ .mx = PTHREAD_MUTEX_INITIALIZER,
+ };
+
+ // schedule jobs while pool is stopped
+
+ task_t task = { .run = task_counting, .ctx = &log };
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ sched_yield();
+ ok(executed_reset(&log) == 0, "executed count before start");
+
+ // start and wait for finish
+
+ worker_pool_start(pool);
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after start");
+
+ // add additional jobs while pool is running
+
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after add");
+
+ // temporary suspension
+
+ worker_pool_suspend(pool);
+
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ sched_yield();
+ ok(executed_reset(&log) == 0, "executed count after suspend");
+
+ worker_pool_resume(pool);
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after resume");
+
+ // try clean
+
+ pthread_mutex_lock(&log.mx);
+ for (int i = 0; i < THREADS + TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+ sched_yield();
+ worker_pool_clear(pool);
+ pthread_mutex_unlock(&log.mx);
+
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) <= THREADS, "executed count after clear");
+
+ // cleanup
+
+ worker_pool_stop(pool);
+ worker_pool_join(pool);
+ worker_pool_destroy(pool);
+
+ pthread_mutex_destroy(&log.mx);
+
+ return 0;
+}
diff --git a/tests/knot/test_worker_queue.c b/tests/knot/test_worker_queue.c
new file mode 100644
index 0000000..d35f0aa
--- /dev/null
+++ b/tests/knot/test_worker_queue.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "knot/worker/queue.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ task_t task_one = { 0 };
+ task_t task_two = { 0 };
+ task_t task_three = { 0 };
+
+ // init
+
+ worker_queue_t queue;
+ worker_queue_init(&queue);
+ ok(1, "queue init");
+
+ // enqueue
+
+ worker_queue_enqueue(&queue, &task_one);
+ ok(1, "enqueue first");
+ worker_queue_enqueue(&queue, &task_two);
+ ok(1, "enqueue second");
+
+ // dequeue
+
+ ok(worker_queue_dequeue(&queue) == &task_one, "dequeue first");
+ ok(worker_queue_dequeue(&queue) == &task_two, "dequeue second");
+ ok(worker_queue_dequeue(&queue) == NULL, "dequeue from empty");
+
+ // deinit
+
+ worker_queue_enqueue(&queue, &task_three);
+ ok(1, "enqueue third");
+
+ worker_queue_deinit(&queue);
+ ok(1, "queue deinit");
+
+ return 0;
+}
diff --git a/tests/knot/test_zone-tree.c b/tests/knot/test_zone-tree.c
new file mode 100644
index 0000000..a1fab68
--- /dev/null
+++ b/tests/knot/test_zone-tree.c
@@ -0,0 +1,118 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/errcode.h"
+#include "knot/zone/zone-tree.h"
+
+#define NCOUNT 4
+static knot_dname_t* NAME[NCOUNT];
+static zone_node_t NODE[NCOUNT];
+static knot_dname_t* ORDER[NCOUNT];
+static void ztree_init_data(void)
+{
+ NAME[0] = knot_dname_from_str_alloc(".");
+ NAME[1] = knot_dname_from_str_alloc("master.ac.");
+ NAME[2] = knot_dname_from_str_alloc("ac.");
+ NAME[3] = knot_dname_from_str_alloc("ns.");
+
+ knot_dname_t *order[NCOUNT] = {
+ NAME[0], NAME[2], NAME[1], NAME[3]
+ };
+ memcpy(ORDER, order, NCOUNT * sizeof(knot_dname_t*));
+
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ memset(NODE + i, 0, sizeof(zone_node_t));
+ NODE[i].owner = NAME[i];
+ NODE[i].prev = NODE + ((NCOUNT + i - 1) % NCOUNT);
+ NODE[i].rrset_count = 1; /* required for ordered search */
+ }
+}
+
+static void ztree_free_data(void)
+{
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ knot_dname_free(NAME[i], NULL);
+ }
+}
+
+static int ztree_iter_data(zone_node_t **node, void *data)
+{
+ unsigned *i = (unsigned *)data;
+ knot_dname_t *owner = (*node)->owner;
+ int result = KNOT_EOK;
+ if (owner != ORDER[*i]) {
+ result = KNOT_ERROR;
+ char *exp_s = knot_dname_to_str_alloc(ORDER[*i]);
+ char *owner_s = knot_dname_to_str_alloc(owner);
+ diag("ztree: at index: %u expected '%s' got '%s'\n", *i, exp_s, owner_s);
+ free(exp_s);
+ free(owner_s);
+ }
+ ++(*i);
+ return result;
+}
+
+int main(int argc, char *argv[])
+{
+ plan(5);
+
+ ztree_init_data();
+
+ /* 1. create test */
+ zone_tree_t* t = zone_tree_create();
+ ok(t != NULL, "ztree: created");
+
+ /* 2. insert test */
+ unsigned passed = 1;
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ if (zone_tree_insert(t, NODE + i) != KNOT_EOK) {
+ passed = 0;
+ break;
+ }
+ }
+ ok(passed, "ztree: insertion");
+
+ /* 3. check data test */
+ passed = 1;
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ zone_node_t *node = zone_tree_get(t, NAME[i]);
+ if (node == NULL || node != NODE + i) {
+ passed = 0;
+ break;
+ }
+ }
+ ok(passed, "ztree: lookup");
+
+ /* 4. ordered lookup */
+ zone_node_t *node = NULL;
+ zone_node_t *prev = NULL;
+ knot_dname_t *tmp_dn = knot_dname_from_str_alloc("z.ac.");
+ zone_tree_get_less_or_equal(t, tmp_dn, &node, &prev);
+ knot_dname_free(tmp_dn, NULL);
+ ok(prev == NODE + 1, "ztree: ordered lookup");
+
+ /* 5. ordered traversal */
+ unsigned i = 0;
+ int ret = zone_tree_apply(t, ztree_iter_data, &i);
+ ok (ret == KNOT_EOK, "ztree: ordered traversal");
+
+ zone_tree_free(&t);
+ ztree_free_data();
+ return 0;
+}
diff --git a/tests/knot/test_zone-update.c b/tests/knot/test_zone-update.c
new file mode 100644
index 0000000..5e528dd
--- /dev/null
+++ b/tests/knot/test_zone-update.c
@@ -0,0 +1,336 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <tap/files.h>
+
+#include "test_conf.h"
+#include "contrib/macros.h"
+#include "contrib/getline.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/node.h"
+#include "libzscanner/scanner.h"
+#include "knot/server/server.h"
+
+static const char *zone_str1 = "test. 600 IN SOA ns.test. m.test. 1 900 300 4800 900 \n";
+static const char *zone_str2 = "test. IN TXT \"test\"\n";
+static const char *add_str = "test. IN TXT \"test2\"\n";
+static const char *del_str = "test. IN TXT \"test\"\n";
+static const char *node_str1 = "node.test. IN TXT \"abc\"\n";
+static const char *node_str2 = "node.test. IN TXT \"def\"\n";
+
+knot_rrset_t rrset;
+
+/*!< \brief Returns true if node contains given RR in its RRSets. */
+static bool node_contains_rr(const zone_node_t *node,
+ const knot_rrset_t *rrset)
+{
+ const knot_rdataset_t *zone_rrs = node_rdataset(node, rrset->type);
+ if (zone_rrs != NULL) {
+ knot_rdata_t *rr = rrset->rrs.rdata;
+ for (size_t i = 0; i < rrset->rrs.count; ++i) {
+ if (!knot_rdataset_member(zone_rrs, rr)) {
+ return false;
+ }
+ rr = knot_rdataset_next(rr);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void process_rr(zs_scanner_t *scanner)
+{
+ knot_rrset_init(&rrset, scanner->r_owner, scanner->r_type, scanner->r_class,
+ scanner->r_ttl);
+
+ int ret = knot_rrset_add_rdata(&rrset, scanner->r_data,
+ scanner->r_data_length, NULL);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+}
+
+void test_full(zone_t *zone, zs_scanner_t *sc)
+{
+ zone_update_t update;
+ /* Init update */
+ int ret = zone_update_init(&update, zone, UPDATE_FULL);
+ is_int(KNOT_EOK, ret, "zone update: init full");
+
+ if (zs_set_input_string(sc, zone_str1, strlen(zone_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* First addition */
+ ret = zone_update_add(&update, &rrset);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ is_int(KNOT_EOK, ret, "full zone update: first addition");
+
+ if (zs_set_input_string(sc, zone_str2, strlen(zone_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* Second addition */
+ ret = zone_update_add(&update, &rrset);
+ zone_node_t *node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ bool rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: second addition");
+
+ /* Removal */
+ ret = zone_update_remove(&update, &rrset);
+ node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && !rrset_present, "full zone update: removal");
+
+ /* Last addition */
+ ret = zone_update_add(&update, &rrset);
+ node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: last addition");
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ /* Prepare node removal */
+ if (zs_set_input_string(sc, node_str1, strlen(node_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ if (zs_set_input_string(sc, node_str2, strlen(node_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ knot_dname_t *rem_node_name = knot_dname_from_str_alloc("node.test");
+ node = (zone_node_t *) zone_update_get_node(&update, rem_node_name);
+ assert(node && node_rdataset(node, KNOT_RRTYPE_TXT)->count == 2);
+ /* Node removal */
+ ret = zone_update_remove_node(&update, rem_node_name);
+ node = (zone_node_t *) zone_update_get_node(&update, rem_node_name);
+ ok(ret == KNOT_EOK && !node, "full zone update: node removal");
+ knot_dname_free(rem_node_name, NULL);
+
+ /* Test iteration */
+ zone_update_iter_t it;
+ ret = zone_update_iter(&it, &update);
+ is_int(KNOT_EOK, ret, "full zone update: init iter");
+
+ const zone_node_t *iter_node = zone_update_iter_val(&it);
+ assert(iter_node);
+ if (zs_set_input_string(sc, zone_str1, strlen(zone_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(rrset_present, "full zone update: first iter value check");
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ if (zs_set_input_string(sc, zone_str2, strlen(zone_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(rrset_present, "full zone update: second iter value check");
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ ret = zone_update_iter_next(&it);
+ is_int(KNOT_EOK, ret, "full zone update: iter next");
+
+ iter_node = zone_update_iter_val(&it);
+ ok(iter_node == NULL, "full zone update: iter val past end");
+
+ zone_update_iter_finish(&it);
+
+ /* Re-add a node for later incremental functionality test */
+ if (zs_set_input_string(sc, node_str1, strlen(node_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ /* Commit */
+ ret = zone_update_commit(conf(), &update);
+ node = zone_contents_find_node_for_rr(zone->contents, &rrset);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: commit");
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+}
+
+void test_incremental(zone_t *zone, zs_scanner_t *sc)
+{
+ int ret = KNOT_EOK;
+
+ /* Init update */
+ zone_update_t update;
+ zone_update_init(&update, zone, UPDATE_INCREMENTAL);
+ ok(update.zone == zone && changeset_empty(&update.change) && update.mm.alloc,
+ "incremental zone update: init");
+
+ if (zs_set_input_string(sc, add_str, strlen(add_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* Addition */
+ ret = zone_update_add(&update, &rrset);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ is_int(KNOT_EOK, ret, "incremental zone update: addition");
+
+ const zone_node_t *synth_node = zone_update_get_apex(&update);
+ ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 2,
+ "incremental zone update: add change");
+
+ if (zs_set_input_string(sc, del_str, strlen(del_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ /* Removal */
+ ret = zone_update_remove(&update, &rrset);
+ is_int(KNOT_EOK, ret, "incremental zone update: removal");
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ synth_node = zone_update_get_apex(&update);
+ ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 1,
+ "incremental zone update: del change");
+
+ /* Prepare node removal */
+ if (zs_set_input_string(sc, node_str2, strlen(node_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ knot_dname_t *rem_node_name = knot_dname_from_str_alloc("node.test");
+ synth_node = zone_update_get_node(&update, rem_node_name);
+ assert(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 2);
+ /* Node Removal */
+ ret = zone_update_remove_node(&update, rem_node_name);
+ synth_node = zone_update_get_node(&update, rem_node_name);
+ ok(ret == KNOT_EOK && !synth_node,
+ "incremental zone update: node removal");
+ knot_dname_free(rem_node_name, NULL);
+
+ /* Test iteration */
+ zone_update_iter_t it;
+ ret = zone_update_iter(&it, &update);
+ is_int(KNOT_EOK, ret, "incremental zone update: init iter");
+
+ if (zs_set_input_string(sc, del_str, strlen(del_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ const zone_node_t *iter_node = zone_update_iter_val(&it);
+ assert(iter_node);
+
+ bool rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(!rrset_present, "incremental zone update: first iter value check");
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ if (zs_set_input_string(sc, add_str, strlen(add_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(rrset_present, "incremental zone update: second iter value check");
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ ret = zone_update_iter_next(&it);
+ is_int(KNOT_EOK, ret, "incremental zone update: iter next");
+ ret = zone_update_iter_next(&it);
+ is_int(KNOT_EOK, ret, "incremental zone update: iter next");
+
+ iter_node = zone_update_iter_val(&it);
+ ok(iter_node == NULL, "incremental zone update: iter val past end");
+
+ zone_update_iter_finish(&it);
+
+ /* Commit */
+ ret = zone_update_commit(conf(), &update);
+ iter_node = zone_contents_find_node_for_rr(zone->contents, &rrset);
+ rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "incremental zone update: commit");
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ char *temp_dir = test_mkdtemp();
+ ok(temp_dir != NULL, "make temporary directory");
+
+ char conf_str[512];
+ snprintf(conf_str, sizeof(conf_str),
+ "zone:\n"
+ " - domain: test.\n"
+ "template:\n"
+ " - id: default\n"
+ " max-journal-db-size: 100M\n"
+ " storage: %s\n",
+ temp_dir);
+
+ /* Load test configuration. */
+ int ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "load configuration");
+
+ server_t server;
+ ret = server_init(&server, 1);
+ is_int(KNOT_EOK, ret, "server init");
+
+ /* Set up empty zone */
+ knot_dname_t *apex = knot_dname_from_str_alloc("test");
+ assert(apex);
+ zone_t *zone = zone_new(apex);
+ zone->journal_db = &server.journal_db;
+
+ /* Setup zscanner */
+ zs_scanner_t sc;
+ if (zs_init(&sc, "test.", KNOT_CLASS_IN, 3600) != 0 ||
+ zs_set_processing(&sc, process_rr, NULL, NULL) != 0) {
+ assert(0);
+ }
+
+ /* Test FULL update, commit it and use the result to test the INCREMENTAL update */
+ test_full(zone, &sc);
+ test_incremental(zone, &sc);
+
+ zs_deinit(&sc);
+ zone_free(&zone);
+ server_deinit(&server);
+ knot_dname_free(apex, NULL);
+ conf_free(conf());
+ test_rm_rf(temp_dir);
+ free(temp_dir);
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_events.c b/tests/knot/test_zone_events.c
new file mode 100644
index 0000000..801c2d9
--- /dev/null
+++ b/tests/knot/test_zone_events.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "knot/common/evsched.h"
+#include "knot/worker/pool.h"
+#include "knot/events/events.h"
+#include "knot/zone/zone.h"
+
+static void test_scheduling(zone_t *zone)
+{
+ const time_t now = time(NULL);
+ const unsigned offset = 1000;
+
+ time_t timestamp = 0;
+ zone_event_type_t event = 0;
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp < 0 && event == ZONE_EVENT_INVALID, "nothing planned");
+
+ // scheduling
+
+ zone_events_schedule_at(zone, ZONE_EVENT_EXPIRE, now + offset);
+ zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, now + (offset / 2));
+
+ for (int i = 0; i < ZONE_EVENT_COUNT; i++) {
+ time_t t = zone_events_get_time(zone, i);
+ bool scheduled = i == ZONE_EVENT_EXPIRE || i == ZONE_EVENT_FLUSH;
+ const char *name = zone_events_get_name(i);
+
+ ok((t > 0) == scheduled && name, "event %s (%s)", name,
+ scheduled ? "scheduled" : "not scheduled");
+ }
+
+ // queuing
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp >= now + (offset / 2) && event == ZONE_EVENT_FLUSH, "flush is next");
+
+ zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, 0);
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp >= now + offset && event == ZONE_EVENT_EXPIRE, "expire is next");
+
+ zone_events_schedule_at(zone, ZONE_EVENT_EXPIRE, 0);
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp < 0 && event == ZONE_EVENT_INVALID, "nothing planned");
+
+ // zone_events_enqueue
+
+ // zone_events_freeze
+ // zone_events_start
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ int r;
+
+ evsched_t sched = { 0 };
+ worker_pool_t *pool = NULL;
+ zone_t zone = { 0 };
+
+ r = evsched_init(&sched, NULL);
+ ok(r == KNOT_EOK, "create scheduler");
+
+ pool = worker_pool_create(1);
+ ok(pool != NULL, "create worker pool");
+
+ r = zone_events_init(&zone);
+ ok(r == KNOT_EOK, "zone events init");
+
+ r = zone_events_setup(&zone, pool, &sched, NULL);
+ ok(r == KNOT_EOK, "zone events setup");
+
+ test_scheduling(&zone);
+
+ zone_events_deinit(&zone);
+ worker_pool_destroy(pool);
+ evsched_deinit(&sched);
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_serial.c b/tests/knot/test_zone_serial.c
new file mode 100644
index 0000000..73b7f4c
--- /dev/null
+++ b/tests/knot/test_zone_serial.c
@@ -0,0 +1,134 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "knot/zone/serial.h"
+#include "knot/conf/schema.h"
+#include "contrib/strtonum.h"
+
+enum serials {
+ S_LOWEST = 0, // lowest value
+ S_2LOWEST = 1, // second lowest value
+ S_BELOW_MIDDLE = 0x7fffffff, // one below middle
+ S_ABOVE_MIDDLE = 0x80000000, // one above middle
+ S_2HIGHEST = 0xffffffff - 1, // second highest value
+ S_HIGHEST = 0xffffffff // highest value
+};
+
+static uint32_t random_serial(void)
+{
+ uint32_t s = rand() & 0xff;
+ s |= (rand() & 0xff) << 8;
+ s |= (rand() & 0xff) << 16;
+ s |= (rand() & 0xff) << 24;
+
+ return s;
+}
+
+static void check_dateserial(uint32_t current, uint32_t expected, const char *msg)
+{
+ uint32_t next = serial_next(current, SERIAL_POLICY_DATESERIAL);
+ ok(next == expected, "dateserial: %s", msg);
+}
+
+static void test_dateserial(void)
+{
+ time_t now = time(NULL);
+
+ struct tm *gm_ret = gmtime(&now);
+
+ char str[32];
+ int ret1 = strftime(str, sizeof(str), "%Y%m%d00", gm_ret);
+
+ uint32_t serial0 = 0;
+ int ret2 = str_to_u32(str, &serial0);
+
+ ok(gm_ret != NULL && ret1 > 0 && ret2 == KNOT_EOK,
+ "dateseril: prepare current value");
+
+ check_dateserial(2000010100, serial0, "from old date");
+ check_dateserial(serial0, serial0 + 1, "today's first increment");
+ check_dateserial(serial0 + 98, serial0 + 99, "today's last increment");
+ check_dateserial(2100010100, 2100010101, "from future date");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Serial compare test. */
+ ok(serial_compare(S_LOWEST, S_BELOW_MIDDLE) == SERIAL_LOWER,
+ "serial compare: lowest < below middle");
+ ok(serial_compare(S_BELOW_MIDDLE, S_LOWEST) == SERIAL_GREATER,
+ "serial compare: below middle > lowest");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ ok(serial_compare(S_LOWEST, S_ABOVE_MIDDLE) == SERIAL_INCOMPARABLE,
+ "serial compare: lowest < above_middle");
+ ok(serial_compare(S_ABOVE_MIDDLE, S_LOWEST) == SERIAL_INCOMPARABLE,
+ "serial compare: above_middle < lowest");
+
+ ok(serial_compare(S_LOWEST, S_HIGHEST) == SERIAL_GREATER,
+ "serial compare: lowest > highest");
+ ok(serial_compare(S_HIGHEST, S_LOWEST) == SERIAL_LOWER,
+ "serial compare: highest < lowest");
+
+ ok(serial_compare(S_2LOWEST, S_ABOVE_MIDDLE) == SERIAL_LOWER,
+ "serial compare: 2nd lowest < above middle");
+ ok(serial_compare(S_ABOVE_MIDDLE, S_2LOWEST) == SERIAL_GREATER,
+ "serial compare: above middle > 2nd lowest");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ ok(serial_compare(S_BELOW_MIDDLE, S_HIGHEST) == SERIAL_INCOMPARABLE,
+ "serial compare: below middle < highest");
+ ok(serial_compare(S_HIGHEST, S_BELOW_MIDDLE) == SERIAL_INCOMPARABLE,
+ "serial compare: highest < below middle");
+
+ ok(serial_compare(S_BELOW_MIDDLE, S_2HIGHEST) == SERIAL_LOWER,
+ "serial compare: below middle < 2nd highest");
+ ok(serial_compare(S_2HIGHEST, S_BELOW_MIDDLE) == SERIAL_GREATER,
+ "serial compare: 2nd highest > below middle");
+
+ ok(serial_compare(S_ABOVE_MIDDLE, S_HIGHEST) == SERIAL_LOWER,
+ "serial compare: above middle < highest");
+ ok(serial_compare(S_HIGHEST, S_ABOVE_MIDDLE) == SERIAL_GREATER,
+ "serial compare: highest > above middle");
+
+ ok(serial_compare(S_LOWEST, S_LOWEST) == SERIAL_EQUAL,
+ "serial compare: lowest == lowest");
+ ok(serial_compare(S_HIGHEST, S_HIGHEST) == SERIAL_EQUAL,
+ "serial compare: highest == highest");
+
+ ok(serial_compare(S_LOWEST - 1, S_HIGHEST) == SERIAL_EQUAL,
+ "serial compare: lowest - 1 == highest");
+ ok(serial_compare(S_LOWEST, S_HIGHEST + 1) == SERIAL_EQUAL,
+ "serial compare: lowest== highest + 1");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ uint32_t s1 = random_serial();
+ uint32_t s2 = s1 + S_ABOVE_MIDDLE; // exactly the 'opposite' number
+ ok(serial_compare(s1, s2) == SERIAL_INCOMPARABLE,
+ "serial compare: random opposites (s1 < s2)");
+ ok(serial_compare(s2, s1) == SERIAL_INCOMPARABLE,
+ "serial compare: random opposites (s2 < s1)");
+
+ test_dateserial();
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_timers.c b/tests/knot/test_zone_timers.c
new file mode 100644
index 0000000..d60aa8e
--- /dev/null
+++ b/tests/knot/test_zone_timers.c
@@ -0,0 +1,102 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "knot/zone/timers.h"
+#include "libknot/db/db_lmdb.h"
+#include "libknot/dname.h"
+#include "libknot/error.h"
+
+static const zone_timers_t MOCK_TIMERS = {
+ .soa_expire = 3600,
+ .last_refresh = 1474559950,
+ .next_refresh = 1474559960,
+ .last_flush = 1474559900,
+};
+
+static bool keep_all(const knot_dname_t *zone, void *data)
+{
+ return true;
+}
+
+static bool remove_all(const knot_dname_t *zone, void *data)
+{
+ return false;
+}
+
+static bool timers_eq(const zone_timers_t *a, const zone_timers_t *b)
+{
+ return a->soa_expire == b->soa_expire &&
+ a->last_refresh == b->last_refresh &&
+ a->next_refresh == b->next_refresh &&
+ a->last_flush == b->last_flush;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+ assert(knot_db_lmdb_api());
+
+ char *dbid = test_mkdtemp();
+ if (!dbid) {
+ return EXIT_FAILURE;
+ }
+
+ const knot_dname_t *zone = (uint8_t *)"\x7""example""\x3""com";
+ struct zone_timers timers = MOCK_TIMERS;
+
+ // Create database
+ knot_db_t *db = NULL;
+ int ret = zone_timers_open(dbid, &db, 1024 * 1024);
+ ok(ret == KNOT_EOK && db != NULL, "zone_timers_open()");
+
+ // Lookup nonexistent
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_ENOENT, ret, "zone_timer_read() nonexistent");
+
+ // Write timers
+ ret = zone_timers_write(db, zone, &timers, NULL);
+ is_int(KNOT_EOK, ret, "zone_timers_write()");
+
+ // Read timers
+ memset(&timers, 0, sizeof(timers));
+ ret = zone_timers_read(db, zone, &timers);
+ ok(ret == KNOT_EOK && timers_eq(&timers, &MOCK_TIMERS), "zone_timers_read()");
+
+ // Sweep none
+ ret = zone_timers_sweep(db, keep_all, NULL);
+ is_int(KNOT_EOK, ret, "zone_timers_sweep() none");
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_EOK, ret, "zone_timers_read()");
+
+ // Sweep all
+ ret = zone_timers_sweep(db, remove_all, NULL);
+ is_int(KNOT_EOK, ret, "zone_timers_sweep() all");
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_ENOENT, ret, "zone_timers_read() nonexistent");
+
+ // Clean up.
+ zone_timers_close(db);
+ test_rm_rf(dbid);
+ free(dbid);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/knot/test_zonedb.c b/tests/knot/test_zonedb.c
new file mode 100644
index 0000000..3ec2b93
--- /dev/null
+++ b/tests/knot/test_zonedb.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "knot/zone/zone.h"
+#include "knot/zone/zonedb.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define ZONE_COUNT 10
+static const char *zone_list[ZONE_COUNT] = {
+ ".",
+ "com",
+ "net",
+ "c.com",
+ "a.com",
+ "a.net",
+ "b.net",
+ "c.a.com",
+ "b.b.b.com",
+ "b.b.b.b.net",
+};
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create database. */
+ char buf[KNOT_DNAME_MAXLEN];
+ const char *prefix = "zzz.";
+ size_t nr_passed = 0;
+ knot_dname_t *dname = NULL;
+ zone_t *zones[ZONE_COUNT] = {0};
+ knot_zonedb_t *db = knot_zonedb_new();
+ ok(db != NULL, "zonedb: new");
+
+ /* Populate. */
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ knot_dname_t *zone_name = knot_dname_from_str_alloc(zone_list[i]);
+ zones[i] = zone_new(zone_name);
+ knot_dname_free(zone_name, NULL);
+
+ if (zones[i] == NULL) {
+ goto cleanup;
+ }
+ if (knot_zonedb_insert(db, zones[i]) == KNOT_EOK) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_add_zone(%s) failed", zone_list[i]);
+ }
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: add zones");
+
+ /* Lookup of exact names. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ dname = knot_dname_from_str_alloc(zone_list[i]);
+ if (knot_zonedb_find(db, dname) == zones[i]) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_find(%s) failed", zone_list[i]);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: find exact zones");
+
+ /* Lookup of sub-names. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ strlcpy(buf, prefix, sizeof(buf));
+ if (strcmp(zone_list[i], ".") != 0) {
+ strlcat(buf, zone_list[i], sizeof(buf));
+ }
+ dname = knot_dname_from_str_alloc(buf);
+ if (knot_zonedb_find_suffix(db, dname) == zones[i]) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_find_suffix(%s) failed", buf);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: find zones for subnames");
+
+ /* Remove all zones. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ dname = knot_dname_from_str_alloc(zone_list[i]);
+ if (knot_zonedb_del(db, dname) == KNOT_EOK) {
+ zone_free(&zones[i]);
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_remove_zone(%s) failed", zone_list[i]);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: removed all zones");
+
+cleanup:
+ knot_zonedb_deep_free(&db);
+ return 0;
+}
diff --git a/tests/libdnssec/sample_keys.h b/tests/libdnssec/sample_keys.h
new file mode 100644
index 0000000..c788ed5
--- /dev/null
+++ b/tests/libdnssec/sample_keys.h
@@ -0,0 +1,407 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+*/
+
+#pragma once
+
+#include <binary.h>
+
+typedef struct key_parameters {
+ // DNSSEC fields
+ uint8_t *name;
+ uint16_t flags;
+ uint8_t protocol;
+ uint8_t algorithm;
+ dnssec_binary_t public_key;
+
+ // DNSSEC wire format
+ dnssec_binary_t rdata;
+
+ // Hashes
+ const char *key_id;
+ uint16_t keytag;
+ dnssec_binary_t ds_sha1;
+ dnssec_binary_t ds_sha256;
+ dnssec_binary_t ds_sha384;
+
+ // Key information
+ unsigned bit_size;
+
+ // Private key in PEM
+ dnssec_binary_t pem;
+} key_parameters_t;
+
+/*
+
+RSA-SHA-256
+
+rsa. IN DNSKEY 256 3 8 AwEAAaqwL+O6GcCPkRZjoObfIJHcHPwQQY9mnAg6kROea2gsyRJOAwBNQPCfXoPtmrU0BiZ0aGBVTVPAvZh+HJRu9NEfTNDPK2HSyHdSucjY1qs6WAub6oWHJuLBxMesftpnUwoLnVZyN+pOblUZUMsvxP3PlS+mA+E6NyQX0F/fcfGL
+rsa. IN DS 37335 8 1 2ABEFAAB07A900F8CB5B266FC930EEBEF51766F6
+rsa. IN DS 37335 8 2 30226484F230814C08C6DD9E2DF6E7A3DB860C2552A418CBF70D0FEE94DFA15F
+rsa. IN DS 37335 8 4 978E0F7766096E131E3E90C50B63DBD825E7428E864BC5A3D32F3135A3786F0CDC6A070B6B8D760190F0F572B03CA4C0
+
+Modulus: qrAv47oZwI+RFmOg5t8gkdwc/BBBj2acCDqRE55raCzJEk4DAE1A8J9eg+2atTQGJnRoYFVNU8C9mH4clG700R9M0M8rYdLId1K5yNjWqzpYC5vqhYcm4sHEx6x+2mdTCgudVnI36k5uVRlQyy/E/c+VL6YD4To3JBfQX99x8Ys=
+PublicExponent: AQAB
+PrivateExponent: NGDSoVBHfMbRoAw8oPxRk1D3eAZJCAdV1FSclmej0BkGLt7PnvUV+4D8UQHF2ts3E+/e48jpbM0VoUj53jbaWx1ULVmQ1cpJY0XLsRUmaQdOwEnSgXjtQy2htlth8RinB+LnVG8eUS9jWnEEikfvCLH0ptkOa/u6GKFUMj+Q95k=
+Prime1: 4ZZj/YD5xvjxEuE0uR0KedsZeGT6iHqwtmJuLNuhFaeXIw5vXXZmg88U/lIo2t0DESYTbfXglw0eu62MwWb+5w==
+Prime2: wbMU0wM6MYaDs4FfEeuTXT14P3cXZOFGikJPWiIUGoMGvDgYzxdiFoHzGdLkapsPizTqBKMtYQ9CYQa8g1cXvQ==
+Exponent1: ywKuZVqGbdtmB9mHuvc5kEPuffxRwjS3hsq538CfDH/PcYryCagdxYy8lcqWXa/7rJkZbyGQxh7Wg4tBWmM4DQ==
+Exponent2: L8MYv29sSgoBL6IW7zRHghZGMGANRLLH0g/HwVHl4yOr5X1voKEDbslcSGHYMPFLQ+goTDxwVB6PH52pnjk7gQ==
+Coefficient: USHiV/UQkTz3BlxZc1IAiUQv9/Ba8wtHWSVp+YqPhxt1sfdiyUMXtlA4f6WAKAGMraoRw4wIcYr+N6Wx+wwXZw==
+
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKqwL+O6GcCPkRZj
+oObfIJHcHPwQQY9mnAg6kROea2gsyRJOAwBNQPCfXoPtmrU0BiZ0aGBVTVPAvZh+
+HJRu9NEfTNDPK2HSyHdSucjY1qs6WAub6oWHJuLBxMesftpnUwoLnVZyN+pOblUZ
+UMsvxP3PlS+mA+E6NyQX0F/fcfGLAgMBAAECgYA0YNKhUEd8xtGgDDyg/FGTUPd4
+BkkIB1XUVJyWZ6PQGQYu3s+e9RX7gPxRAcXa2zcT797jyOlszRWhSPneNtpbHVQt
+WZDVykljRcuxFSZpB07ASdKBeO1DLaG2W2HxGKcH4udUbx5RL2NacQSKR+8IsfSm
+2Q5r+7oYoVQyP5D3mQJBAOGWY/2A+cb48RLhNLkdCnnbGXhk+oh6sLZibizboRWn
+lyMOb112ZoPPFP5SKNrdAxEmE2314JcNHrutjMFm/ucCQQDBsxTTAzoxhoOzgV8R
+65NdPXg/dxdk4UaKQk9aIhQagwa8OBjPF2IWgfMZ0uRqmw+LNOoEoy1hD0JhBryD
+Vxe9AkEAywKuZVqGbdtmB9mHuvc5kEPuffxRwjS3hsq538CfDH/PcYryCagdxYy8
+lcqWXa/7rJkZbyGQxh7Wg4tBWmM4DQJAL8MYv29sSgoBL6IW7zRHghZGMGANRLLH
+0g/HwVHl4yOr5X1voKEDbslcSGHYMPFLQ+goTDxwVB6PH52pnjk7gQJAUSHiV/UQ
+kTz3BlxZc1IAiUQv9/Ba8wtHWSVp+YqPhxt1sfdiyUMXtlA4f6WAKAGMraoRw4wI
+cYr+N6Wx+wwXZw==
+-----END PRIVATE KEY-----
+
+*/
+
+static const key_parameters_t SAMPLE_RSA_KEY = {
+ .name = (uint8_t *)"\x03""rsa",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 8,
+ .public_key = { .size = 132, .data = (uint8_t []) {
+ 0x03, 0x01, 0x00, 0x01, 0xaa, 0xb0, 0x2f, 0xe3, 0xba, 0x19,
+ 0xc0, 0x8f, 0x91, 0x16, 0x63, 0xa0, 0xe6, 0xdf, 0x20, 0x91,
+ 0xdc, 0x1c, 0xfc, 0x10, 0x41, 0x8f, 0x66, 0x9c, 0x08, 0x3a,
+ 0x91, 0x13, 0x9e, 0x6b, 0x68, 0x2c, 0xc9, 0x12, 0x4e, 0x03,
+ 0x00, 0x4d, 0x40, 0xf0, 0x9f, 0x5e, 0x83, 0xed, 0x9a, 0xb5,
+ 0x34, 0x06, 0x26, 0x74, 0x68, 0x60, 0x55, 0x4d, 0x53, 0xc0,
+ 0xbd, 0x98, 0x7e, 0x1c, 0x94, 0x6e, 0xf4, 0xd1, 0x1f, 0x4c,
+ 0xd0, 0xcf, 0x2b, 0x61, 0xd2, 0xc8, 0x77, 0x52, 0xb9, 0xc8,
+ 0xd8, 0xd6, 0xab, 0x3a, 0x58, 0x0b, 0x9b, 0xea, 0x85, 0x87,
+ 0x26, 0xe2, 0xc1, 0xc4, 0xc7, 0xac, 0x7e, 0xda, 0x67, 0x53,
+ 0x0a, 0x0b, 0x9d, 0x56, 0x72, 0x37, 0xea, 0x4e, 0x6e, 0x55,
+ 0x19, 0x50, 0xcb, 0x2f, 0xc4, 0xfd, 0xcf, 0x95, 0x2f, 0xa6,
+ 0x03, 0xe1, 0x3a, 0x37, 0x24, 0x17, 0xd0, 0x5f, 0xdf, 0x71,
+ 0xf1, 0x8b,
+ }},
+ .rdata = { .size = 136, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x08,
+ 0x03, 0x01, 0x00, 0x01, 0xaa, 0xb0, 0x2f, 0xe3, 0xba, 0x19,
+ 0xc0, 0x8f, 0x91, 0x16, 0x63, 0xa0, 0xe6, 0xdf, 0x20, 0x91,
+ 0xdc, 0x1c, 0xfc, 0x10, 0x41, 0x8f, 0x66, 0x9c, 0x08, 0x3a,
+ 0x91, 0x13, 0x9e, 0x6b, 0x68, 0x2c, 0xc9, 0x12, 0x4e, 0x03,
+ 0x00, 0x4d, 0x40, 0xf0, 0x9f, 0x5e, 0x83, 0xed, 0x9a, 0xb5,
+ 0x34, 0x06, 0x26, 0x74, 0x68, 0x60, 0x55, 0x4d, 0x53, 0xc0,
+ 0xbd, 0x98, 0x7e, 0x1c, 0x94, 0x6e, 0xf4, 0xd1, 0x1f, 0x4c,
+ 0xd0, 0xcf, 0x2b, 0x61, 0xd2, 0xc8, 0x77, 0x52, 0xb9, 0xc8,
+ 0xd8, 0xd6, 0xab, 0x3a, 0x58, 0x0b, 0x9b, 0xea, 0x85, 0x87,
+ 0x26, 0xe2, 0xc1, 0xc4, 0xc7, 0xac, 0x7e, 0xda, 0x67, 0x53,
+ 0x0a, 0x0b, 0x9d, 0x56, 0x72, 0x37, 0xea, 0x4e, 0x6e, 0x55,
+ 0x19, 0x50, 0xcb, 0x2f, 0xc4, 0xfd, 0xcf, 0x95, 0x2f, 0xa6,
+ 0x03, 0xe1, 0x3a, 0x37, 0x24, 0x17, 0xd0, 0x5f, 0xdf, 0x71,
+ 0xf1, 0x8b,
+ }},
+ .key_id = "76f0d6c093d8328bc7f0e25bd8cde5575bad9b44",
+ .keytag = 37335,
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x01,
+ 0x2a, 0xbe, 0xfa, 0xab, 0x07, 0xa9, 0x00, 0xf8, 0xcb, 0x5b,
+ 0x26, 0x6f, 0xc9, 0x30, 0xee, 0xbe, 0xf5, 0x17, 0x66, 0xf6,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x02,
+ 0x30, 0x22, 0x64, 0x84, 0xf2, 0x30, 0x81, 0x4c, 0x08, 0xc6,
+ 0xdd, 0x9e, 0x2d, 0xf6, 0xe7, 0xa3, 0xdb, 0x86, 0x0c, 0x25,
+ 0x52, 0xa4, 0x18, 0xcb, 0xf7, 0x0d, 0x0f, 0xee, 0x94, 0xdf,
+ 0xa1, 0x5f,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x04,
+ 0x97, 0x8e, 0x0f, 0x77, 0x66, 0x09, 0x6e, 0x13, 0x1e, 0x3e,
+ 0x90, 0xc5, 0x0b, 0x63, 0xdb, 0xd8, 0x25, 0xe7, 0x42, 0x8e,
+ 0x86, 0x4b, 0xc5, 0xa3, 0xd3, 0x2f, 0x31, 0x35, 0xa3, 0x78,
+ 0x6f, 0x0c, 0xdc, 0x6a, 0x07, 0x0b, 0x6b, 0x8d, 0x76, 0x01,
+ 0x90, 0xf0, 0xf5, 0x72, 0xb0, 0x3c, 0xa4, 0xc0,
+ }},
+ .bit_size = 1024,
+ .pem = { .size = 916, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e,
+ 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49,
+ 0x49, 0x43, 0x64, 0x67, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4e,
+ 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77,
+ 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
+ 0x41, 0x6d, 0x41, 0x77, 0x67, 0x67, 0x4a, 0x63, 0x41, 0x67,
+ 0x45, 0x41, 0x41, 0x6f, 0x47, 0x42, 0x41, 0x4b, 0x71, 0x77,
+ 0x4c, 0x2b, 0x4f, 0x36, 0x47, 0x63, 0x43, 0x50, 0x6b, 0x52,
+ 0x5a, 0x6a, 0x0a, 0x6f, 0x4f, 0x62, 0x66, 0x49, 0x4a, 0x48,
+ 0x63, 0x48, 0x50, 0x77, 0x51, 0x51, 0x59, 0x39, 0x6d, 0x6e,
+ 0x41, 0x67, 0x36, 0x6b, 0x52, 0x4f, 0x65, 0x61, 0x32, 0x67,
+ 0x73, 0x79, 0x52, 0x4a, 0x4f, 0x41, 0x77, 0x42, 0x4e, 0x51,
+ 0x50, 0x43, 0x66, 0x58, 0x6f, 0x50, 0x74, 0x6d, 0x72, 0x55,
+ 0x30, 0x42, 0x69, 0x5a, 0x30, 0x61, 0x47, 0x42, 0x56, 0x54,
+ 0x56, 0x50, 0x41, 0x76, 0x5a, 0x68, 0x2b, 0x0a, 0x48, 0x4a,
+ 0x52, 0x75, 0x39, 0x4e, 0x45, 0x66, 0x54, 0x4e, 0x44, 0x50,
+ 0x4b, 0x32, 0x48, 0x53, 0x79, 0x48, 0x64, 0x53, 0x75, 0x63,
+ 0x6a, 0x59, 0x31, 0x71, 0x73, 0x36, 0x57, 0x41, 0x75, 0x62,
+ 0x36, 0x6f, 0x57, 0x48, 0x4a, 0x75, 0x4c, 0x42, 0x78, 0x4d,
+ 0x65, 0x73, 0x66, 0x74, 0x70, 0x6e, 0x55, 0x77, 0x6f, 0x4c,
+ 0x6e, 0x56, 0x5a, 0x79, 0x4e, 0x2b, 0x70, 0x4f, 0x62, 0x6c,
+ 0x55, 0x5a, 0x0a, 0x55, 0x4d, 0x73, 0x76, 0x78, 0x50, 0x33,
+ 0x50, 0x6c, 0x53, 0x2b, 0x6d, 0x41, 0x2b, 0x45, 0x36, 0x4e,
+ 0x79, 0x51, 0x58, 0x30, 0x46, 0x2f, 0x66, 0x63, 0x66, 0x47,
+ 0x4c, 0x41, 0x67, 0x4d, 0x42, 0x41, 0x41, 0x45, 0x43, 0x67,
+ 0x59, 0x41, 0x30, 0x59, 0x4e, 0x4b, 0x68, 0x55, 0x45, 0x64,
+ 0x38, 0x78, 0x74, 0x47, 0x67, 0x44, 0x44, 0x79, 0x67, 0x2f,
+ 0x46, 0x47, 0x54, 0x55, 0x50, 0x64, 0x34, 0x0a, 0x42, 0x6b,
+ 0x6b, 0x49, 0x42, 0x31, 0x58, 0x55, 0x56, 0x4a, 0x79, 0x57,
+ 0x5a, 0x36, 0x50, 0x51, 0x47, 0x51, 0x59, 0x75, 0x33, 0x73,
+ 0x2b, 0x65, 0x39, 0x52, 0x58, 0x37, 0x67, 0x50, 0x78, 0x52,
+ 0x41, 0x63, 0x58, 0x61, 0x32, 0x7a, 0x63, 0x54, 0x37, 0x39,
+ 0x37, 0x6a, 0x79, 0x4f, 0x6c, 0x73, 0x7a, 0x52, 0x57, 0x68,
+ 0x53, 0x50, 0x6e, 0x65, 0x4e, 0x74, 0x70, 0x62, 0x48, 0x56,
+ 0x51, 0x74, 0x0a, 0x57, 0x5a, 0x44, 0x56, 0x79, 0x6b, 0x6c,
+ 0x6a, 0x52, 0x63, 0x75, 0x78, 0x46, 0x53, 0x5a, 0x70, 0x42,
+ 0x30, 0x37, 0x41, 0x53, 0x64, 0x4b, 0x42, 0x65, 0x4f, 0x31,
+ 0x44, 0x4c, 0x61, 0x47, 0x32, 0x57, 0x32, 0x48, 0x78, 0x47,
+ 0x4b, 0x63, 0x48, 0x34, 0x75, 0x64, 0x55, 0x62, 0x78, 0x35,
+ 0x52, 0x4c, 0x32, 0x4e, 0x61, 0x63, 0x51, 0x53, 0x4b, 0x52,
+ 0x2b, 0x38, 0x49, 0x73, 0x66, 0x53, 0x6d, 0x0a, 0x32, 0x51,
+ 0x35, 0x72, 0x2b, 0x37, 0x6f, 0x59, 0x6f, 0x56, 0x51, 0x79,
+ 0x50, 0x35, 0x44, 0x33, 0x6d, 0x51, 0x4a, 0x42, 0x41, 0x4f,
+ 0x47, 0x57, 0x59, 0x2f, 0x32, 0x41, 0x2b, 0x63, 0x62, 0x34,
+ 0x38, 0x52, 0x4c, 0x68, 0x4e, 0x4c, 0x6b, 0x64, 0x43, 0x6e,
+ 0x6e, 0x62, 0x47, 0x58, 0x68, 0x6b, 0x2b, 0x6f, 0x68, 0x36,
+ 0x73, 0x4c, 0x5a, 0x69, 0x62, 0x69, 0x7a, 0x62, 0x6f, 0x52,
+ 0x57, 0x6e, 0x0a, 0x6c, 0x79, 0x4d, 0x4f, 0x62, 0x31, 0x31,
+ 0x32, 0x5a, 0x6f, 0x50, 0x50, 0x46, 0x50, 0x35, 0x53, 0x4b,
+ 0x4e, 0x72, 0x64, 0x41, 0x78, 0x45, 0x6d, 0x45, 0x32, 0x33,
+ 0x31, 0x34, 0x4a, 0x63, 0x4e, 0x48, 0x72, 0x75, 0x74, 0x6a,
+ 0x4d, 0x46, 0x6d, 0x2f, 0x75, 0x63, 0x43, 0x51, 0x51, 0x44,
+ 0x42, 0x73, 0x78, 0x54, 0x54, 0x41, 0x7a, 0x6f, 0x78, 0x68,
+ 0x6f, 0x4f, 0x7a, 0x67, 0x56, 0x38, 0x52, 0x0a, 0x36, 0x35,
+ 0x4e, 0x64, 0x50, 0x58, 0x67, 0x2f, 0x64, 0x78, 0x64, 0x6b,
+ 0x34, 0x55, 0x61, 0x4b, 0x51, 0x6b, 0x39, 0x61, 0x49, 0x68,
+ 0x51, 0x61, 0x67, 0x77, 0x61, 0x38, 0x4f, 0x42, 0x6a, 0x50,
+ 0x46, 0x32, 0x49, 0x57, 0x67, 0x66, 0x4d, 0x5a, 0x30, 0x75,
+ 0x52, 0x71, 0x6d, 0x77, 0x2b, 0x4c, 0x4e, 0x4f, 0x6f, 0x45,
+ 0x6f, 0x79, 0x31, 0x68, 0x44, 0x30, 0x4a, 0x68, 0x42, 0x72,
+ 0x79, 0x44, 0x0a, 0x56, 0x78, 0x65, 0x39, 0x41, 0x6b, 0x45,
+ 0x41, 0x79, 0x77, 0x4b, 0x75, 0x5a, 0x56, 0x71, 0x47, 0x62,
+ 0x64, 0x74, 0x6d, 0x42, 0x39, 0x6d, 0x48, 0x75, 0x76, 0x63,
+ 0x35, 0x6b, 0x45, 0x50, 0x75, 0x66, 0x66, 0x78, 0x52, 0x77,
+ 0x6a, 0x53, 0x33, 0x68, 0x73, 0x71, 0x35, 0x33, 0x38, 0x43,
+ 0x66, 0x44, 0x48, 0x2f, 0x50, 0x63, 0x59, 0x72, 0x79, 0x43,
+ 0x61, 0x67, 0x64, 0x78, 0x59, 0x79, 0x38, 0x0a, 0x6c, 0x63,
+ 0x71, 0x57, 0x58, 0x61, 0x2f, 0x37, 0x72, 0x4a, 0x6b, 0x5a,
+ 0x62, 0x79, 0x47, 0x51, 0x78, 0x68, 0x37, 0x57, 0x67, 0x34,
+ 0x74, 0x42, 0x57, 0x6d, 0x4d, 0x34, 0x44, 0x51, 0x4a, 0x41,
+ 0x4c, 0x38, 0x4d, 0x59, 0x76, 0x32, 0x39, 0x73, 0x53, 0x67,
+ 0x6f, 0x42, 0x4c, 0x36, 0x49, 0x57, 0x37, 0x7a, 0x52, 0x48,
+ 0x67, 0x68, 0x5a, 0x47, 0x4d, 0x47, 0x41, 0x4e, 0x52, 0x4c,
+ 0x4c, 0x48, 0x0a, 0x30, 0x67, 0x2f, 0x48, 0x77, 0x56, 0x48,
+ 0x6c, 0x34, 0x79, 0x4f, 0x72, 0x35, 0x58, 0x31, 0x76, 0x6f,
+ 0x4b, 0x45, 0x44, 0x62, 0x73, 0x6c, 0x63, 0x53, 0x47, 0x48,
+ 0x59, 0x4d, 0x50, 0x46, 0x4c, 0x51, 0x2b, 0x67, 0x6f, 0x54,
+ 0x44, 0x78, 0x77, 0x56, 0x42, 0x36, 0x50, 0x48, 0x35, 0x32,
+ 0x70, 0x6e, 0x6a, 0x6b, 0x37, 0x67, 0x51, 0x4a, 0x41, 0x55,
+ 0x53, 0x48, 0x69, 0x56, 0x2f, 0x55, 0x51, 0x0a, 0x6b, 0x54,
+ 0x7a, 0x33, 0x42, 0x6c, 0x78, 0x5a, 0x63, 0x31, 0x49, 0x41,
+ 0x69, 0x55, 0x51, 0x76, 0x39, 0x2f, 0x42, 0x61, 0x38, 0x77,
+ 0x74, 0x48, 0x57, 0x53, 0x56, 0x70, 0x2b, 0x59, 0x71, 0x50,
+ 0x68, 0x78, 0x74, 0x31, 0x73, 0x66, 0x64, 0x69, 0x79, 0x55,
+ 0x4d, 0x58, 0x74, 0x6c, 0x41, 0x34, 0x66, 0x36, 0x57, 0x41,
+ 0x4b, 0x41, 0x47, 0x4d, 0x72, 0x61, 0x6f, 0x52, 0x77, 0x34,
+ 0x77, 0x49, 0x0a, 0x63, 0x59, 0x72, 0x2b, 0x4e, 0x36, 0x57,
+ 0x78, 0x2b, 0x77, 0x77, 0x58, 0x5a, 0x77, 0x3d, 0x3d, 0x0a,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a,
+ }},
+};
+
+/*
+
+ECDSA-P256-SHA256
+
+ecdsa. IN DNSKEY 256 3 13 8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7g bvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==
+ecdsa. IN DS 5345 13 1 954103ac7c43810ce9f414e80f30ab1cbe49b236
+ecdsa. IN DS 5345 13 2 bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca
+ecdsa. IN DS 5345 13 4 a0ac6790483872be72a258314200a88ab75cdd70f66a18a09f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1
+
+PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=
+
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhAIsiyD3ZNwziMVZp
+kmJZk7LW7VD4lZRMg1+OqbMgsDVUoAoGCCqGSM49AwEHoUQDQgAE8uD7C4THTM/w
+7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNm
+YA/gZsuXzA==
+-----END PRIVATE KEY-----
+
+*/
+
+static const key_parameters_t SAMPLE_ECDSA_KEY = {
+ .name = (uint8_t *)"\x05""ecdsa",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 13,
+ .public_key = { .size = 64, .data = (uint8_t []) {
+ 0xf2, 0xe0, 0xfb, 0x0b, 0x84, 0xc7, 0x4c, 0xcf, 0xf0, 0xee,
+ 0xe8, 0x6b, 0xc9, 0x14, 0x93, 0xa1, 0xe1, 0x3f, 0x8c, 0xa4,
+ 0xfb, 0xf3, 0xfa, 0x7c, 0xe7, 0x74, 0x57, 0xd0, 0xbe, 0x44,
+ 0xc2, 0xb6, 0x6b, 0x48, 0xb0, 0x6e, 0x6c, 0xb3, 0xe2, 0x07,
+ 0x0e, 0xe0, 0x6e, 0xf5, 0x0f, 0xe9, 0x2b, 0x08, 0x81, 0xae,
+ 0x59, 0x43, 0x80, 0x92, 0x03, 0x13, 0x66, 0x60, 0x0f, 0xe0,
+ 0x66, 0xcb, 0x97, 0xcc,
+ }},
+ .rdata = { .size = 68, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0d,
+ 0xf2, 0xe0, 0xfb, 0x0b, 0x84, 0xc7, 0x4c, 0xcf, 0xf0, 0xee,
+ 0xe8, 0x6b, 0xc9, 0x14, 0x93, 0xa1, 0xe1, 0x3f, 0x8c, 0xa4,
+ 0xfb, 0xf3, 0xfa, 0x7c, 0xe7, 0x74, 0x57, 0xd0, 0xbe, 0x44,
+ 0xc2, 0xb6, 0x6b, 0x48, 0xb0, 0x6e, 0x6c, 0xb3, 0xe2, 0x07,
+ 0x0e, 0xe0, 0x6e, 0xf5, 0x0f, 0xe9, 0x2b, 0x08, 0x81, 0xae,
+ 0x59, 0x43, 0x80, 0x92, 0x03, 0x13, 0x66, 0x60, 0x0f, 0xe0,
+ 0x66, 0xcb, 0x97, 0xcc,
+ }},
+ .keytag = 5345,
+ .key_id = "47fd10011e76cc6741af586041eae5519465fc8d",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x01,
+ 0x95, 0x41, 0x03, 0xac, 0x7c, 0x43, 0x81, 0x0c, 0xe9, 0xf4,
+ 0x14, 0xe8, 0x0f, 0x30, 0xab, 0x1c, 0xbe, 0x49, 0xb2, 0x36,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x02,
+ 0xba, 0xc2, 0x10, 0x70, 0x36, 0xe7, 0x35, 0xb5, 0x0f, 0x85,
+ 0x00, 0x6c, 0xe4, 0x09, 0xa1, 0x9a, 0x34, 0x38, 0xca, 0xb2,
+ 0x72, 0xe7, 0x07, 0x69, 0xeb, 0xda, 0x03, 0x22, 0x39, 0xa3,
+ 0xd0, 0xca,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x04,
+ 0xa0, 0xac, 0x67, 0x90, 0x48, 0x38, 0x72, 0xbe, 0x72, 0xa2,
+ 0x58, 0x31, 0x42, 0x00, 0xa8, 0x8a, 0xb7, 0x5c, 0xdd, 0x70,
+ 0xf6, 0x6a, 0x18, 0xa0, 0x9f, 0x0f, 0x41, 0x4c, 0x07, 0x4d,
+ 0xf0, 0x98, 0x9f, 0xdb, 0x1d, 0xf0, 0xe6, 0x7d, 0x82, 0xd4,
+ 0x31, 0x2c, 0xda, 0x67, 0xb9, 0x3a, 0x76, 0xc1,
+ }},
+ .bit_size = 256,
+ .pem = { .size = 262, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e,
+ 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49,
+ 0x47, 0x55, 0x41, 0x67, 0x45, 0x41, 0x4d, 0x42, 0x4d, 0x47,
+ 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67,
+ 0x45, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39,
+ 0x41, 0x77, 0x45, 0x48, 0x42, 0x48, 0x6f, 0x77, 0x65, 0x41,
+ 0x49, 0x42, 0x41, 0x51, 0x51, 0x68, 0x41, 0x49, 0x73, 0x69,
+ 0x79, 0x44, 0x33, 0x5a, 0x4e, 0x77, 0x7a, 0x69, 0x4d, 0x56,
+ 0x5a, 0x70, 0x0a, 0x6b, 0x6d, 0x4a, 0x5a, 0x6b, 0x37, 0x4c,
+ 0x57, 0x37, 0x56, 0x44, 0x34, 0x6c, 0x5a, 0x52, 0x4d, 0x67,
+ 0x31, 0x2b, 0x4f, 0x71, 0x62, 0x4d, 0x67, 0x73, 0x44, 0x56,
+ 0x55, 0x6f, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53,
+ 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, 0x48, 0x6f, 0x55, 0x51,
+ 0x44, 0x51, 0x67, 0x41, 0x45, 0x38, 0x75, 0x44, 0x37, 0x43,
+ 0x34, 0x54, 0x48, 0x54, 0x4d, 0x2f, 0x77, 0x0a, 0x37, 0x75,
+ 0x68, 0x72, 0x79, 0x52, 0x53, 0x54, 0x6f, 0x65, 0x45, 0x2f,
+ 0x6a, 0x4b, 0x54, 0x37, 0x38, 0x2f, 0x70, 0x38, 0x35, 0x33,
+ 0x52, 0x58, 0x30, 0x4c, 0x35, 0x45, 0x77, 0x72, 0x5a, 0x72,
+ 0x53, 0x4c, 0x42, 0x75, 0x62, 0x4c, 0x50, 0x69, 0x42, 0x77,
+ 0x37, 0x67, 0x62, 0x76, 0x55, 0x50, 0x36, 0x53, 0x73, 0x49,
+ 0x67, 0x61, 0x35, 0x5a, 0x51, 0x34, 0x43, 0x53, 0x41, 0x78,
+ 0x4e, 0x6d, 0x0a, 0x59, 0x41, 0x2f, 0x67, 0x5a, 0x73, 0x75,
+ 0x58, 0x7a, 0x41, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
+ 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x0a,
+ }},
+};
+
+/*
+ * Private-key-format: v1.2
+ * Algorithm: 15 (ED25519)
+ * PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
+ *
+ * example.com. 3600 IN DNSKEY 256 3 15 (
+ * l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )
+ *
+ * example.com. 3600 IN DS 3612 15 2 (
+ * 3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79
+ * a304b )
+ *
+ * example.com. 3600 IN MX 10 mail.example.com.
+ *
+ * example.com. 3600 IN RRSIG MX 15 2 3600 (
+ * 1440021600 1438207200 3613 example.com. (
+ * oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f
+ * x8A4M3e23mRZ9VrbpMngwcrqNAg== )
+ */
+
+static const key_parameters_t SAMPLE_ED25519_KEY = {
+ .name = (uint8_t *)"\x07""ed25519",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 15,
+ .public_key = { .size = 32, .data = (uint8_t []) {
+ 0x97, 0x4d, 0x96, 0xa2, 0x2d, 0x22, 0x4b, 0xc0, 0x1a, 0xdb, 0x91, 0x50,
+ 0x91, 0x47, 0x7d, 0x44, 0xcc, 0xd9, 0x1c, 0x9a, 0x41, 0xa1, 0x14, 0x30,
+ 0x01, 0x01, 0x17, 0xd5, 0x2c, 0x59, 0x24, 0x0e,
+ }},
+ .rdata = { .size = 36, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0f,
+ 0x97, 0x4d, 0x96, 0xa2, 0x2d, 0x22, 0x4b, 0xc0, 0x1a, 0xdb, 0x91, 0x50,
+ 0x91, 0x47, 0x7d, 0x44, 0xcc, 0xd9, 0x1c, 0x9a, 0x41, 0xa1, 0x14, 0x30,
+ 0x01, 0x01, 0x17, 0xd5, 0x2c, 0x59, 0x24, 0x0e,
+ }},
+ .keytag = 3612,
+ .key_id = "bea75b99fb22ee1a68106ad6399e4acc43eb9929",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x01,
+ 0x50, 0x12, 0x49, 0x72, 0x1e, 0x1f, 0x09, 0xa7, 0x9d, 0x30, 0xd5, 0xc6,
+ 0xc4, 0xdc, 0xa1, 0xdc, 0x1d, 0xa4, 0xed, 0x5d,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x02,
+ 0x1b, 0x1c, 0x87, 0x66, 0xb2, 0xa9, 0x65, 0x66, 0xff, 0x19, 0x6f, 0x77,
+ 0xc0, 0xc4, 0x19, 0x4a, 0xf8, 0x6a, 0xaa, 0x10, 0x9c, 0x53, 0x46, 0xff,
+ 0x60, 0x23, 0x1a, 0x27, 0xd2, 0xb0, 0x7a, 0xc0,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x04,
+ 0xd1, 0x18, 0x31, 0x15, 0x3a, 0xf4, 0x98, 0x5e, 0xfb, 0xd0, 0xae, 0x79,
+ 0x2c, 0x96, 0x7e, 0xb4, 0xaf, 0xf3, 0xc3, 0x54, 0x88, 0xdb, 0x95, 0xf7,
+ 0xe2, 0xf8, 0x5d, 0xce, 0xc7, 0x4a, 0xe8, 0xf5, 0x9f, 0x9a, 0x72, 0x64,
+ 0x17, 0x98, 0xc9, 0x1c, 0x67, 0xc6, 0x75, 0xdb, 0x1d, 0x71, 0x0c, 0x18,
+ }},
+ .bit_size = 256,
+ .pem = { .size = 119, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x43, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77,
+ 0x42, 0x51, 0x59, 0x44, 0x4b, 0x32, 0x56, 0x77, 0x42, 0x43, 0x49, 0x45,
+ 0x49, 0x44, 0x67, 0x79, 0x4d, 0x6a, 0x59, 0x77, 0x4d, 0x7a, 0x67, 0x30,
+ 0x4e, 0x6a, 0x49, 0x34, 0x4d, 0x44, 0x67, 0x77, 0x4d, 0x54, 0x49, 0x79,
+ 0x4e, 0x6a, 0x51, 0x31, 0x4d, 0x54, 0x6b, 0x77, 0x4d, 0x6a, 0x41, 0x30,
+ 0x4d, 0x54, 0x51, 0x79, 0x4d, 0x6a, 0x59, 0x79, 0x0a, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54,
+ 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a
+ }},
+};
diff --git a/tests/libdnssec/test_binary.c b/tests/libdnssec/test_binary.c
new file mode 100644
index 0000000..cab3185
--- /dev/null
+++ b/tests/libdnssec/test_binary.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "binary.h"
+#include "error.h"
+
+typedef struct test_string {
+ const char *encoded;
+ size_t encoded_size;
+ const char *decoded;
+ size_t decoded_size;
+} test_string_t;
+
+static void test_base64(void)
+{
+ test_string_t data[] = {
+ { "", 0, "", 0 },
+ { "YQ==", 4, "a", 1 },
+ { "YWI=", 4, "ab", 2 },
+ { "YWJj", 4, "abc", 3 },
+ { "YWJjZA==", 8, "abcd", 4 },
+ { "YWJjZGU=", 8, "abcde", 5 },
+ { "YWJjZGVm", 8, "abcdef", 6 },
+ { NULL }
+ };
+
+ for (int i = 0; data[i].encoded != NULL; i++) {
+ test_string_t *ts = &data[i];
+
+ const dnssec_binary_t base64 = {
+ .size = ts->encoded_size,
+ .data = (uint8_t *) ts->encoded
+ };
+
+ dnssec_binary_t binary = { 0 };
+
+ int r = dnssec_binary_from_base64(&base64, &binary);
+ ok(r == DNSSEC_EOK &&
+ binary.size == ts->decoded_size &&
+ (binary.size == 0 || memcmp(binary.data, ts->decoded, binary.size) == 0),
+ "dnssec_binary_from_base64() for '%s'", ts->encoded);
+
+ dnssec_binary_t encoded = { 0 };
+ r = dnssec_binary_to_base64(&binary, &encoded);
+ ok(r == DNSSEC_EOK &&
+ encoded.size == ts->encoded_size &&
+ memcmp(encoded.data, ts->encoded, encoded.size) == 0,
+ "dnssec_binary_to_base64() for '%s'", ts->encoded);
+
+ dnssec_binary_free(&binary);
+ dnssec_binary_free(&encoded);
+ }
+}
+
+static void test_dup(void)
+{
+ dnssec_binary_t src = { .size = 5, .data = (uint8_t *) "ahoj" };
+ dnssec_binary_t dst = { 0 };
+
+ int r = dnssec_binary_dup(&src, &dst);
+ ok(r == DNSSEC_EOK &&
+ src.size == dst.size && memcmp(src.data, dst.data, src.size) == 0,
+ "dnssec_binary_dup()");
+
+ dnssec_binary_free(&dst);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_base64();
+ test_dup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_crypto.c b/tests/libdnssec/test_crypto.c
new file mode 100644
index 0000000..617637b
--- /dev/null
+++ b/tests/libdnssec/test_crypto.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "crypto.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ // not much we can test
+
+ dnssec_crypto_init();
+ ok(1, "dnssec_crypto_init() didn't crash");
+
+ dnssec_crypto_reinit();
+ ok(1, "dnssec_crypto_reinit() didn't crash");
+
+ dnssec_crypto_cleanup();
+ ok(1, "dnssec_crypto_cleanup() didn't crash");
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key.c b/tests/libdnssec/test_key.c
new file mode 100644
index 0000000..0a73739
--- /dev/null
+++ b/tests/libdnssec/test_key.c
@@ -0,0 +1,212 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+
+#include "binary.h"
+#include "crypto.h"
+#include "error.h"
+#include "key.h"
+
+#include "sample_keys.h"
+
+#define check_attr_scalar(key, type, name, def_val, set_val) { \
+ type value = dnssec_key_get_##name(key); \
+ ok(value == def_val, #name " default"); \
+ int r = dnssec_key_set_##name(key, set_val); \
+ ok(r == DNSSEC_EOK, #name " set"); \
+ value = dnssec_key_get_##name(key); \
+ ok(value == set_val, #name " get"); \
+}
+
+static void check_key_tag(dnssec_key_t *key, const key_parameters_t *params)
+{
+ uint16_t keytag = dnssec_key_get_keytag(key);
+ ok(keytag == params->keytag, "get keytag");
+}
+
+static void check_key_size(dnssec_key_t *key, const key_parameters_t *params)
+{
+ ok(dnssec_key_get_size(key) == params->bit_size,
+ "key size %u bits", params->bit_size);
+}
+
+static void check_usage(dnssec_key_t *key, bool ok_verify, bool ok_sign)
+{
+ ok(dnssec_key_can_verify(key) == ok_verify,
+ "%s verify", ok_verify ? "can" : "cannot");
+ ok(dnssec_key_can_sign(key) == ok_sign,
+ "%s sign", ok_sign ? "can" : "cannot");
+}
+
+static void test_public_key(const key_parameters_t *params)
+{
+ dnssec_key_t *key = NULL;
+ int r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+
+ // create from parameters
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_INVALID_KEY_ALGORITHM,
+ "set public key (fails, no algorithm set)");
+
+ check_attr_scalar(key, uint16_t, flags, 256, params->flags);
+ check_attr_scalar(key, uint8_t, protocol, 3, params->protocol);
+ check_attr_scalar(key, uint8_t, algorithm, 0, params->algorithm);
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_EOK, "set public key (succeeds)");
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_KEY_ALREADY_PRESENT,
+ "set public key (fails, already present)");
+
+ dnssec_binary_t rdata = { 0 };
+ r = dnssec_key_get_rdata(key, &rdata);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&rdata, &params->rdata) == 0,
+ "get RDATA");
+
+ check_key_tag(key, params);
+
+ // create from RDATA
+
+ dnssec_key_clear(key);
+ r = dnssec_key_set_rdata(key, &params->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ check_key_tag(key, params);
+ check_key_size(key, params);
+ check_usage(key, true, false);
+
+ // create copy
+
+ dnssec_key_t *copy = dnssec_key_dup(key);
+ ok(copy != NULL, "duplicate key");
+
+ check_key_tag(copy, params);
+ check_key_size(copy, params);
+ check_usage(copy, true, false);
+
+ dnssec_key_free(copy);
+ dnssec_key_free(key);
+}
+
+static void test_private_key(const key_parameters_t *params)
+{
+ dnssec_key_t *key = NULL;
+ int r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+
+ // import to public
+
+ r = dnssec_key_set_rdata(key, &params->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ r = dnssec_key_load_pkcs8(key, &params->pem);
+ ok(r == DNSSEC_EOK, "load private key (1)");
+
+ ok(dnssec_key_can_verify(key), "can verify");
+ ok(dnssec_key_can_sign(key), "can sign");
+
+ // purely from parameters
+
+ dnssec_key_clear(key);
+
+ dnssec_key_set_algorithm(key, params->algorithm);
+ dnssec_key_set_flags(key, params->flags);
+ r = dnssec_key_load_pkcs8(key, &params->pem);
+ ok(r == DNSSEC_EOK, "load private key (2)");
+
+ dnssec_binary_t rdata = { 0 };
+ r = dnssec_key_get_rdata(key, &rdata);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&rdata, &params->rdata) == 0,
+ "get RDATA");
+
+ check_key_tag(key, params);
+ check_key_size(key, params);
+ check_usage(key, true, true);
+
+ // create copy
+
+ dnssec_key_t *copy = dnssec_key_dup(key);
+ ok(copy != NULL, "duplicate key");
+
+ check_key_tag(copy, params);
+ check_key_size(copy, params);
+ check_usage(copy, true, false);
+
+ dnssec_key_free(copy);
+ dnssec_key_free(key);
+}
+
+static void test_naming(void)
+{
+ dnssec_key_t *key = NULL;
+ dnssec_key_new(&key);
+
+ const uint8_t *input = (uint8_t *)"\x07""eXample""\x03""COM";
+ const uint8_t *expected = (uint8_t *)"\x07""example""\x03""com";
+ size_t expected_size = 13;
+
+ ok(dnssec_key_get_dname(key) == NULL, "implicit key name");
+
+ dnssec_key_set_dname(key, input);
+ const uint8_t *output = dnssec_key_get_dname(key);
+
+ ok(strlen((char *)output) + 1 == 13 &&
+ memcmp(output, expected, expected_size) == 0,
+ "set key name");
+
+ dnssec_key_set_dname(key, NULL);
+ ok(dnssec_key_get_dname(key) == NULL, "clear key name");
+
+ dnssec_key_free(key);
+}
+
+typedef struct keyinfo {
+ const char *name;
+ const key_parameters_t *parameters;
+} keyinfo_t;
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ static const keyinfo_t keys[] = {
+ { "RSA", &SAMPLE_RSA_KEY },
+ { "ECDSA", &SAMPLE_ECDSA_KEY },
+#ifdef HAVE_ED25519
+ { "ED25519", &SAMPLE_ED25519_KEY },
+#endif
+ { NULL }
+ };
+
+ for (const keyinfo_t *k = keys; k->name != NULL; k += 1) {
+ diag("%s key", k->name);
+ test_public_key(k->parameters);
+ test_private_key(k->parameters);
+ }
+
+ test_naming();
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key_algorithm.c b/tests/libdnssec/test_key_algorithm.c
new file mode 100644
index 0000000..edffbaa
--- /dev/null
+++ b/tests/libdnssec/test_key_algorithm.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "error.h"
+#include "key.h"
+
+static void ok_range(dnssec_key_algorithm_t algo,
+ unsigned exp_min, unsigned exp_max,
+ const char *name)
+{
+ unsigned min = 0, max = 0;
+ int r = dnssec_algorithm_key_size_range(algo, &min, &max);
+ ok(r == DNSSEC_EOK && min == exp_min && max == exp_max,
+ "dnssec_algorithm_key_size_range() for %s", name);
+}
+
+static void null_range(void)
+{
+ dnssec_key_algorithm_t algo = DNSSEC_KEY_ALGORITHM_RSA_SHA256;
+ unsigned val = 0;
+ int r;
+
+ r = dnssec_algorithm_key_size_range(algo, NULL, NULL);
+ ok(r == DNSSEC_EINVAL, "dnssec_algorithm_key_size_range() all null");
+ r = dnssec_algorithm_key_size_range(algo, &val, NULL);
+ ok(r == DNSSEC_EOK && val == 1024, "dnssec_algorithm_key_size_range() min only");
+ r = dnssec_algorithm_key_size_range(algo, NULL, &val);
+ ok(r == DNSSEC_EOK && val == 4096, "dnssec_algorithm_key_size_range() max only");
+}
+
+static void check_borders(void)
+{
+ dnssec_key_algorithm_t rsa = DNSSEC_KEY_ALGORITHM_RSA_SHA1;
+
+ ok(dnssec_algorithm_key_size_check(rsa, 1023) == false, "rsa 1023");
+ ok(dnssec_algorithm_key_size_check(rsa, 1024) == true, "rsa 1024");
+ ok(dnssec_algorithm_key_size_check(rsa, 1025) == true, "rsa 1025");
+ ok(dnssec_algorithm_key_size_check(rsa, 4095) == true, "rsa 4095");
+ ok(dnssec_algorithm_key_size_check(rsa, 4096) == true, "rsa 4096");
+ ok(dnssec_algorithm_key_size_check(rsa, 4097) == false, "rsa 4097");
+}
+
+static void check_defaults(void)
+{
+ is_int(2048, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3), "rsa default");
+ is_int(256, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256), "ecc default");
+#ifdef HAVE_ED25519
+ is_int(256, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ED25519), "ed25519 default");
+#endif
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ // ranges
+ ok_range(DNSSEC_KEY_ALGORITHM_RSA_SHA512, 1024, 4096, "RSA/SHA256");
+ ok_range(DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, 384, 384, "ECDSA/SHA384");
+#ifdef HAVE_ED25519
+ ok_range(DNSSEC_KEY_ALGORITHM_ED25519, 256, 256, "ED25519");
+#endif
+ null_range();
+
+ check_borders();
+
+ check_defaults();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key_ds.c b/tests/libdnssec/test_key_ds.c
new file mode 100644
index 0000000..25d7c9d
--- /dev/null
+++ b/tests/libdnssec/test_key_ds.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <stddef.h>
+#include <string.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "sample_keys.h"
+
+static void test_key(const char *name, const struct key_parameters *params)
+{
+ dnssec_key_t *key = NULL;
+
+ dnssec_key_new(&key);
+ dnssec_key_set_dname(key, params->name);
+ dnssec_key_set_rdata(key, &params->rdata);
+
+ struct ds_type {
+ const char *name;
+ dnssec_key_digest_t digest;
+ size_t params_offset;
+ };
+
+ static const struct ds_type DS_TYPES[] = {
+ { "SHA-1", DNSSEC_KEY_DIGEST_SHA1, offsetof(typeof(*params), ds_sha1) },
+ { "SHA-256", DNSSEC_KEY_DIGEST_SHA256, offsetof(typeof(*params), ds_sha256) },
+ { "SHA-384", DNSSEC_KEY_DIGEST_SHA384, offsetof(typeof(*params), ds_sha384) },
+ { NULL }
+ };
+
+ for (const struct ds_type *dt = DS_TYPES; dt->name != NULL; dt++) {
+ dnssec_binary_t ds = { 0 };
+ int r = dnssec_key_create_ds(key, dt->digest, &ds);
+
+ const dnssec_binary_t *expect = (void *)params + dt->params_offset;
+
+ ok(r == DNSSEC_EOK &&
+ ds.size == expect->size &&
+ memcmp(ds.data, expect->data, ds.size) == 0,
+ "dnssec_key_create_ds() for %s/%s", name, dt->name);
+
+ dnssec_binary_free(&ds);
+ }
+
+ dnssec_key_free(key);
+}
+
+static void test_errors(const struct key_parameters *params)
+{
+ dnssec_key_t *key = NULL;
+ dnssec_binary_t ds = { 0 };
+
+ int r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_EINVAL, r, "dnssec_key_create_ds() no key");
+ dnssec_binary_free(&ds);
+
+ dnssec_key_new(&key);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_INVALID_KEY_NAME, r, "dnssec_key_create_ds() no key name");
+
+ dnssec_key_set_dname(key, params->name);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_INVALID_PUBLIC_KEY, r, "dnssec_key_create_ds() no public key");
+
+ dnssec_key_set_rdata(key, &params->rdata);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, NULL);
+ is_int(DNSSEC_EINVAL, r, "dnssec_key_create_ds() no RDATA buffer");
+
+ r = dnssec_key_create_ds(key, 3, &ds);
+ is_int(DNSSEC_INVALID_DS_ALGORITHM, r, "dnssec_key_create_ds() unsupported algorithm");
+
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_EOK, r, "dnssec_key_create_ds() valid parameters");
+
+ dnssec_binary_free(&ds);
+ dnssec_key_free(key);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ test_key("RSA", &SAMPLE_RSA_KEY);
+ test_key("ECDSA", &SAMPLE_ECDSA_KEY);
+#ifdef HAVE_ED25519
+ test_key("ED25519", &SAMPLE_ED25519_KEY);
+#endif
+
+ test_errors(&SAMPLE_ECDSA_KEY);
+#ifdef HAVE_ED25519
+ test_errors(&SAMPLE_ED25519_KEY);
+#endif
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keyid.c b/tests/libdnssec/test_keyid.c
new file mode 100644
index 0000000..15bfe98
--- /dev/null
+++ b/tests/libdnssec/test_keyid.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "keyid.h"
+
+static void test_keyid_is_valid_run(const char *param, bool should_ok)
+{
+ ok(dnssec_keyid_is_valid(param) == should_ok,
+ "dnssec_keyid_is_valid(\"%s\")", param);
+}
+
+static void test_keyid_is_valid(void)
+{
+ test_keyid_is_valid_run(NULL, false);
+ test_keyid_is_valid_run("3e90c5cb1fad5f8512da2028fda3808e749d3bf", false);
+ test_keyid_is_valid_run("9aa6dAAC706fb6fe4aceb327452a7b5FEA457544", true);
+ test_keyid_is_valid_run("eac45c184b7f476472c16d5b0c4f0c52389848001", false);
+ test_keyid_is_valid_run("9aa6daac706fb6fe4aceb32g452a7b5fea457544", false);
+}
+
+static void test_keyid_normalize(void)
+{
+ char id[] = "3711927404f64CE7df88253d763e442CE39f9B5c";
+ const char *id_norm = "3711927404f64ce7df88253d763e442ce39f9b5c";
+
+ dnssec_keyid_normalize(id);
+ ok(strcmp(id, id_norm) == 0, "dnssec_keyid_normalize()");
+}
+
+static void test_keyid_copy(void)
+{
+ const char *id = "21669f1eca6418f9aBBBf0007e6f73463d467424";
+ const char *expected = "21669f1eca6418f9abbbf0007e6f73463d467424";
+
+ char *copy = dnssec_keyid_copy(id);
+ ok(copy && strcmp(copy, expected) == 0, "dnssec_keyid_copy()");
+
+ free(copy);
+}
+
+static void test_keyid_equal(void)
+{
+ const char *id = "dd63237d4a07867de715499690c9ad12990519f0";
+ const char *id_case = "dd63237d4a07867de715499690C9AD12990519F0";
+ const char *id_diff = "dd63237d4a07867de715499690c9ad12990519f1";
+
+ ok(dnssec_keyid_equal(id, NULL) == false, "dnssec_keyid_equal(id, NULL)");
+ ok(dnssec_keyid_equal(id, id) == true, "dnssec_keyid_equal(id, id)");
+ ok(dnssec_keyid_equal(id, id_case) == true, "dnssec_keyid_equal(id, ID)");
+ ok(dnssec_keyid_equal(id, id_diff) == false, "dnssec_keyid_equal(ida, idb)");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_keyid_is_valid();
+ test_keyid_normalize();
+ test_keyid_copy();
+ test_keyid_equal();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keystore_pkcs11.c b/tests/libdnssec/test_keystore_pkcs11.c
new file mode 100644
index 0000000..c83eb82
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs11.c
@@ -0,0 +1,484 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <tap/files.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/sign.h"
+
+#include "sample_keys.h"
+
+#define ENV_SOFTHSM_DSO "KNOT_SOFTHSM2_DSO"
+#define ENV_SOFTHSM_UTIL "KNOT_SOFTHSM2_UTIL"
+
+#define SOFTHSM_DSO "libsofthsm2.so"
+#define SOFTHSM_CONF "softhsm2.conf"
+#define SOFTHSM_CONF_ENV "SOFTHSM2_CONF"
+#define SOFTHSM_UTIL "softhsm2-util"
+
+#define TOKEN_LABEL "libdnssec-test"
+#define TOKEN_PIN "1234"
+#define TOKEN_SOPIN "123456"
+
+#define EXIT_EXEC_FAILED 127
+
+#ifndef LIBDIR
+# include <bits/wordsize.h>
+# if __WORDSIZE == 32
+# define LIBDIR "/usr/lib32"
+# elif __WORDSIZE == 64
+# define LIBDIR "/usr/lib64"
+# endif
+#endif
+
+#define MSG_SOFTWARE "soft -"
+#define MSG_PKCS11 "p11 -"
+
+/*!
+ * Get SoftHSM DSO path.
+ */
+static char *libsofthsm_dso(void)
+{
+ // prefer environment variable
+
+ const char *env = getenv(ENV_SOFTHSM_DSO);
+ if (env) {
+ return (env[0] != '\0' ? strdup(env) : NULL);
+ }
+
+ // autodetection
+
+ static const char *paths[] = {
+ LIBDIR "/pkcs11/" SOFTHSM_DSO,
+ LIBDIR "/softhsm/" SOFTHSM_DSO,
+ LIBDIR "/" SOFTHSM_DSO,
+ NULL
+ };
+
+ for (const char **path_ptr = paths; *path_ptr; path_ptr += 1) {
+ const char *path = *path_ptr;
+ if (access(path, R_OK|X_OK) == 0) {
+ return strdup(path);
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * Get SoftHSM utility path.
+ */
+static char *libsofthsm_util(void)
+{
+ // prefer environment variable
+
+ const char *env = getenv(ENV_SOFTHSM_UTIL);
+ if (env && env[0] != '\0') {
+ return strdup(env);
+ }
+
+ // fallback, will rely on PATH
+
+ return strdup(SOFTHSM_UTIL);
+}
+
+/*!
+ * Path to temporary token data.
+ */
+static char *token_path = NULL;
+
+/*!
+ * Cleanup token test data.
+ */
+static void token_cleanup(void)
+{
+ test_rm_rf(token_path);
+ free(token_path);
+}
+
+/*!
+ * Initialize token using the support tool.
+ */
+static bool token_init_exec(const char *util)
+{
+ pid_t child = fork();
+ if (child == -1) {
+ return false;
+ }
+
+ // child
+
+ if (child == 0) {
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ const char *basename = strrchr(util, '/');
+ if (basename) {
+ basename += 1;
+ } else {
+ basename = util;
+ }
+
+ execlp(util, basename,
+ "--init-token", "--slot=0", "--label=" TOKEN_LABEL,
+ "--pin=" TOKEN_PIN, "--so-pin=" TOKEN_SOPIN,
+ NULL);
+
+ exit(EXIT_EXEC_FAILED);
+ }
+
+ // parent
+
+ int status = 0;
+ if (waitpid(child, &status, 0) == -1) {
+ return false;
+ }
+
+ int exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ if (exit_code != 0) {
+ diag("%s exit status %d", util, exit_code);
+ if (exit_code == EXIT_EXEC_FAILED) {
+ diag("set %s environment variable to adjust the path",
+ ENV_SOFTHSM_UTIL);
+ }
+ }
+
+ return exit_code == 0;
+}
+
+/*!
+ * Initialize environment and token for testing.
+ */
+static bool token_init(void)
+{
+ token_path = test_mkdtemp();
+ if (!token_path) {
+ return false;
+ }
+
+ // generate configuration file for unit test
+
+ char config[4096] = { 0 };
+ int r = snprintf(config, sizeof(config), "%s/%s", token_path, SOFTHSM_CONF);
+ if (r <= 0 || r >= sizeof(config)) {
+ return false;
+ }
+
+ FILE *file = fopen(config, "w");
+ if (!file) {
+ return false;
+ }
+
+ fprintf(file, "directories.tokendir = %s\n", token_path);
+ fprintf(file, "objectstore.backend = file\n");
+ fprintf(file, "log.debug = INFO\n");
+
+ fclose(file);
+
+ // update environment to use the config
+
+ if (setenv(SOFTHSM_CONF_ENV, config, 1) != 0) {
+ return false;
+ }
+
+ // initialize token
+
+ char *util = libsofthsm_util();
+ if (!util) {
+ return false;
+ }
+
+ bool inited = token_init_exec(util);
+ free(util);
+
+ return inited;
+}
+
+static void create_dnskeys(dnssec_keystore_t *keystore,
+ dnssec_key_algorithm_t algorithm, const char *id,
+ dnssec_key_t **p11_key_ptr, dnssec_key_t **soft_key_ptr)
+{
+ int r;
+
+ // construct PKCS #11 privkey-pubkey key pair
+
+ dnssec_key_t *p11_key = NULL;
+ r = dnssec_key_new(&p11_key);
+ ok(r == DNSSEC_EOK && p11_key != NULL, MSG_PKCS11 " dnssec_key_new()");
+
+ r = dnssec_key_set_algorithm(p11_key, algorithm);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_set_key_algorithm()");
+
+ r = dnssec_key_import_keystore(p11_key, keystore, id);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_key_import_keystore()");
+
+ // construct software public key
+
+ dnssec_key_t *soft_key = NULL;
+ r = dnssec_key_new(&soft_key);
+ ok(r == DNSSEC_EOK && soft_key != NULL, MSG_SOFTWARE " dnssec_key_new()");
+
+ dnssec_binary_t rdata = { 0 };
+ dnssec_key_get_rdata(p11_key, &rdata);
+ r = dnssec_key_set_rdata(soft_key, &rdata);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_key_set_rdata()");
+
+ *p11_key_ptr = p11_key;
+ *soft_key_ptr = soft_key;
+}
+
+static void test_sign(dnssec_key_t *p11_key, dnssec_key_t *soft_key)
+{
+ int r;
+
+ static const dnssec_binary_t input = {
+ .data = (uint8_t *)"So Long, and Thanks for All the Fish.",
+ .size = 37
+ };
+
+ dnssec_binary_t sign = { 0 };
+
+ // usage constraints
+
+ ok(dnssec_key_can_sign(p11_key), MSG_PKCS11 " dnssec_key_can_sign()");
+ ok(dnssec_key_can_verify(p11_key), MSG_PKCS11 " dnssec_key_can_verify()");
+
+ ok(!dnssec_key_can_sign(soft_key), MSG_SOFTWARE " dnssec_key_can_sign()");
+ ok(dnssec_key_can_verify(soft_key), MSG_SOFTWARE " dnssec_key_can_verify()");
+
+ // PKCS #11 key signature
+
+ dnssec_sign_ctx_t *ctx = NULL;
+ r = dnssec_sign_new(&ctx, p11_key);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_init() ");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_add()");
+
+ r = dnssec_sign_write(ctx, &sign);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_write()");
+
+ // PKCS #11 key verification
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_init()");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_add()");
+
+ r = dnssec_sign_verify(ctx, &sign);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_verify()");
+
+ // software verification
+
+ dnssec_sign_free(ctx);
+ ctx = NULL;
+
+ r = dnssec_sign_new(&ctx, soft_key);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_init()");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_add()");
+
+ r = dnssec_sign_verify(ctx, &sign);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_verify()");
+
+ dnssec_binary_free(&sign);
+ dnssec_sign_free(ctx);
+}
+
+static void test_key_use(dnssec_keystore_t *store,
+ dnssec_key_algorithm_t algorithm,
+ const char *keyid)
+{
+ dnssec_key_t *p11_key = NULL;
+ dnssec_key_t *soft_key = NULL;
+
+ create_dnskeys(store, algorithm, keyid, &p11_key, &soft_key);
+ test_sign(p11_key, soft_key);
+
+ dnssec_key_free(p11_key);
+ dnssec_key_free(soft_key);
+}
+
+static void test_algorithm(dnssec_keystore_t *store,
+ const key_parameters_t *params)
+{
+ char *id_generate = NULL;
+ char *id_import = NULL;
+
+ int r;
+
+ diag("algorithm %d, generated key", params->algorithm);
+
+ r = dnssec_keystore_generate_key(store, params->algorithm, params->bit_size, &id_generate);
+ ok(r == DNSSEC_EOK && id_generate != NULL, "dnssec_keystore_generate_key()");
+ test_key_use(store, params->algorithm, id_generate);
+
+ diag("algorithm %d, imported key", params->algorithm);
+
+ r = dnssec_keystore_import(store, &params->pem, &id_import);
+ ok(r == DNSSEC_EOK && id_import != NULL, "dnssec_keystore_import()");
+ test_key_use(store, params->algorithm, id_import);
+
+ free(id_generate);
+ free(id_import);
+}
+
+static void test_key_listing(dnssec_keystore_t *store,
+ const key_parameters_t **key_params, int count)
+{
+ dnssec_list_t *keys = NULL;
+ int r = dnssec_keystore_list_keys(store, &keys);
+ ok(r == DNSSEC_EOK && dnssec_list_size(keys) == count * 2,
+ "dnssec_keystore_list_keys(), two keys per algorithm");
+
+ bool imported_found[count];
+ for (int i = 0; i < count; i++) {
+ imported_found[i] = false;
+ }
+
+ dnssec_list_foreach(item, keys) {
+ const char *id = dnssec_item_get(item);
+ for (int i = 0; i < count; i++) {
+ if (strcmp(id, key_params[i]->key_id) == 0) {
+ imported_found[i] = true;
+ break;
+ }
+ }
+ }
+
+ int imported_count = 0;
+ for (int i = 0; i < count; i++) {
+ imported_count += imported_found[i] ? 1 : 0;
+ }
+
+ ok(imported_count == count, "list contains key for all algorithms");
+ dnssec_list_free_full(keys, NULL, NULL);
+
+ // key removal
+
+ assert(count > 0);
+ const key_parameters_t *key_remove = key_params[0];
+
+ r = dnssec_keystore_remove_key(store, key_remove->key_id);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_remove_key");
+
+ keys = NULL;
+ r = dnssec_keystore_list_keys(store, &keys);
+ ok(r == DNSSEC_EOK && dnssec_list_size(keys) == count * 2 - 1,
+ "dnssec_keystore_list_keys(), one less key than before");
+
+ bool removed_found = false;
+ dnssec_list_foreach(item, keys) {
+ const char *id = dnssec_item_get(item);
+ if (strcmp(id, key_remove->key_id) == 0) {
+ removed_found = true;
+ break;
+ }
+ }
+
+ ok(!removed_found, "dnssec_keystore_list_keys(), removed key not found");
+
+ dnssec_list_free_full(keys, NULL, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ // PKCS #11 initialization
+
+ dnssec_keystore_t *store = NULL;
+ int r = dnssec_keystore_init_pkcs11(&store);
+ if (r == DNSSEC_NOT_IMPLEMENTED_ERROR) {
+ skip_all("not supported");
+ goto done;
+ }
+ ok(r == DNSSEC_EOK && store, "dnssec_keystore_init_pkcs11()");
+
+ char *dso_name = libsofthsm_dso();
+ if (!dso_name) {
+ skip_all("%s not found, set %s environment variable",
+ SOFTHSM_DSO, ENV_SOFTHSM_DSO);
+ goto done;
+ }
+ ok(dso_name != NULL, "find token DSO");
+
+ bool success = token_init();
+ if (!success) {
+ skip_all("failed to configure and initialize the token");
+ goto done;
+ }
+ ok(success, "initialize the token");
+
+ char config[4096] = { 0 };
+ r = snprintf(config, sizeof(config), "pkcs11:token=%s;pin-value=%s %s",
+ TOKEN_LABEL, TOKEN_PIN, dso_name);
+ free(dso_name);
+ ok(r > 0 && r < sizeof(config), "build configuration");
+
+ // key store access
+
+ r = dnssec_keystore_init(store, config);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init()");
+
+ r = dnssec_keystore_open(store, config);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_open()");
+
+ dnssec_list_t *keys = NULL;
+ r = dnssec_keystore_list_keys(store, &keys);
+ ok(r == DNSSEC_EOK && dnssec_list_size(keys) == 0, "dnssec_keystore_list_keys(), empty");
+ dnssec_list_free_full(keys, NULL, NULL);
+
+ // key manipulation
+
+ static const int KEYS_COUNT = 2;
+ static const key_parameters_t *KEYS[] = {
+ &SAMPLE_RSA_KEY,
+ &SAMPLE_ECDSA_KEY,
+ };
+ assert(KEYS_COUNT == sizeof(KEYS) / sizeof(*KEYS));
+
+ for (int i = 0; i < KEYS_COUNT; i++) {
+ test_algorithm(store, KEYS[i]);
+ }
+
+ test_key_listing(store, KEYS, KEYS_COUNT);
+
+ r = dnssec_keystore_close(store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_close()");
+done:
+ dnssec_keystore_deinit(store);
+ dnssec_crypto_cleanup();
+ token_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keystore_pkcs8.c b/tests/libdnssec/test_keystore_pkcs8.c
new file mode 100644
index 0000000..b7c74e7
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs8.c
@@ -0,0 +1,219 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <stdbool.h>
+
+#include "binary.h"
+#include "crypto.h"
+#include "error.h"
+#include "key.h"
+#include "keyid.h"
+#include "keystore.h"
+
+/* -- mock key store ------------------------------------------------------- */
+
+static void *test_handle = (void *)0x42;
+
+static bool test_handle_new_ok = false;
+static int test_handle_new(void **handle_ptr)
+{
+ if (handle_ptr) {
+ *handle_ptr = test_handle;
+ test_handle_new_ok = true;
+ }
+
+ return DNSSEC_EOK;
+}
+
+static bool test_handle_free_ok = false;
+static int test_handle_free(void *handle)
+{
+ test_handle_free_ok = (handle == test_handle);
+
+ return DNSSEC_EOK;
+}
+
+static bool test_init_ok = false;
+static int test_init(void *handle, const char *config)
+{
+ test_init_ok = (handle == test_handle && config && strcmp(config, "init config") == 0);
+
+ return DNSSEC_EOK;
+}
+
+static bool test_open_ok = false;
+static int test_open(void *handle, const char *config)
+{
+ test_open_ok = (handle == test_handle && config && strcmp(config, "open config") == 0);
+
+ return DNSSEC_EOK;
+}
+
+static bool test_close_ok = false;
+static int test_close(void *handle)
+{
+ test_close_ok = (handle == test_handle);
+
+ return DNSSEC_EOK;
+}
+
+static bool test_write_ok = false;
+static char *test_write_id = NULL;
+static dnssec_binary_t test_write_pem = { 0 };
+static int test_write(void *handle, const char *id, const dnssec_binary_t *pem)
+{
+ if (handle == test_handle && id && pem) {
+ test_write_ok = true;
+ test_write_id = dnssec_keyid_copy(id);
+ dnssec_binary_dup(pem, &test_write_pem);
+ }
+
+ return DNSSEC_EOK;
+}
+
+static bool test_read_ok = false;
+static char *test_read_id = NULL;
+static int test_read(void *handle, const char *id, dnssec_binary_t *pem)
+{
+ if (handle == test_handle && id && pem) {
+ test_read_ok = true;
+ test_read_id = dnssec_keyid_copy(id);
+ dnssec_binary_dup(&test_write_pem, pem);
+ }
+
+ return DNSSEC_EOK;
+}
+
+static bool test_list_ok = false;
+static int test_list(void *handle, dnssec_list_t **list_ptr)
+{
+ if (handle == test_handle && list_ptr) {
+ test_list_ok = true;
+ }
+
+ if (list_ptr) {
+ *list_ptr = dnssec_list_new();
+ }
+
+ return DNSSEC_EOK;
+}
+
+static bool test_remove_ok = false;
+static char *test_remove_id = NULL;
+static int test_remove(void *handle, const char *id)
+{
+ test_remove_ok = (handle == test_handle && id);
+ test_remove_id = dnssec_keyid_copy(id);
+
+ return DNSSEC_EOK;
+}
+
+static const dnssec_keystore_pkcs8_functions_t test_store = {
+ .handle_new = test_handle_new,
+ .handle_free = test_handle_free,
+ .init = test_init,
+ .open = test_open,
+ .close = test_close,
+ .read = test_read,
+ .write = test_write,
+ .list = test_list,
+ .remove = test_remove,
+};
+
+/* -- test plan ------------------------------------------------------------ */
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ int r = 0;
+
+ // create/init/open
+
+ dnssec_keystore_t *store = NULL;
+ r = dnssec_keystore_init_pkcs8_custom(&store, &test_store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init_pkcs8_custom()");
+ ok(test_handle_new_ok, "test_handle_new_ok() called");
+
+ r = dnssec_keystore_init(store, "init config");
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init()");
+ ok(test_init_ok, "test_init() called");
+
+ r = dnssec_keystore_open(store, "open config");
+ ok(r == DNSSEC_EOK && test_open_ok, "dnssec_keystore_open()");
+ ok(test_open_ok, "test_open() called");
+
+ // write
+
+ char *gen_id = NULL;
+ r = dnssec_keystore_generate_key(store, DNSSEC_KEY_ALGORITHM_RSA_SHA256,
+ 1024, &gen_id);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_generate_key()");
+ ok(test_write_ok, "test_write() called");
+ is_string(gen_id, test_write_id, "test_write() correct key ID");
+
+ // read
+
+ dnssec_key_t *key = NULL;
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_key_import_keystore(key, store, gen_id);
+ ok(r == DNSSEC_EOK, "dnssec_key_import_keystore()");
+ ok(test_read_ok, "test_read() called");
+ is_string(gen_id, test_read_id, "test_read() correct key ID");
+ dnssec_key_free(key);
+
+ // remove
+
+ r = dnssec_keystore_remove_key(store, gen_id);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_remove_key()");
+ ok(test_remove_ok, "test_remove() called");
+ is_string(gen_id, test_remove_id, "test_remove() correct key ID");
+
+ // close
+
+ r = dnssec_keystore_close(store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_clse()");
+ ok(test_close_ok, "test_close() called");
+
+ // list
+
+ dnssec_list_t *list = NULL;
+ r = dnssec_keystore_list_keys(store, &list);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_list_keys()");
+ ok(test_list_ok, "test_list() called");
+ ok(list && dnssec_list_size(list) == 0, "dnssec_list() correct output");
+ dnssec_list_free(list);
+
+ // cleanup
+
+ dnssec_keystore_deinit(store);
+ ok(test_handle_free_ok, "test_handle_free() called");
+
+ dnssec_crypto_cleanup();
+
+ free(gen_id);
+ free(test_write_id);
+ dnssec_binary_free(&test_write_pem);
+ free(test_read_id);
+ free(test_remove_id);
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keystore_pkcs8_dir.c b/tests/libdnssec/test_keystore_pkcs8_dir.c
new file mode 100644
index 0000000..124b8c3
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs8_dir.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+#include <unistd.h>
+
+#include "binary.h"
+#include "error.h"
+#include "key.h"
+#include "keystore.h"
+#include "keystore/pkcs8_dir.c"
+
+typedef struct test_pem {
+ const char *id;
+ dnssec_binary_t data;
+} test_pem_t;
+
+extern const dnssec_keystore_pkcs8_functions_t PKCS8_DIR_FUNCTIONS;
+
+const test_pem_t TEST_PEM_A = {
+ "7b0c9f6a59b1c76b26ed93ea8684f300821eee41",
+ { .size = 5, .data = (uint8_t *)"hello" }
+};
+
+const test_pem_t TEST_PEM_B = {
+ "f4f3e73cf4ee605993c2ef2d790571ade827244c",
+ { .size = 4, .data = (uint8_t *)"knot" }
+};
+
+static void rm(const char *dir, const char *file)
+{
+ char buffer[4096] = { 0 };
+ int r = snprintf(buffer, 4096, "%s/%s", dir, file);
+ if (r < 0) {
+ ok(0, "rm");
+ return;
+ }
+
+ r = unlink(buffer);
+ ok(r == 0, "rm %s", buffer);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ int r = 0;
+ void *handle = NULL;
+ dnssec_binary_t bin = { 0 };
+
+ char *dir = test_tmpdir();
+ if (!dir) {
+ return 1;
+ }
+
+ // create context
+
+ const dnssec_keystore_pkcs8_functions_t *func = &PKCS8_DIR_FUNCTIONS;
+
+ r = func->handle_new(&handle);
+ ok(r == DNSSEC_EOK && handle != NULL, "new handle");
+
+ r = func->init(handle, dir);
+ ok(r == DNSSEC_EOK, "init");
+
+ r = func->open(handle, dir);
+ ok(r == DNSSEC_EOK, "open");
+
+ // non-existent reads
+
+ r = func->read(handle, TEST_PEM_A.id, &bin);
+ ok(r == DNSSEC_ENOENT && bin.size == 0, "read non-existent");
+
+ // writing new content
+
+ r = func->write(handle, TEST_PEM_A.id, &TEST_PEM_A.data);
+ ok(r == DNSSEC_EOK, "write A");
+
+ r = func->write(handle, TEST_PEM_B.id, &TEST_PEM_B.data);
+ ok(r == DNSSEC_EOK, "write B");
+
+ r = func->write(handle, TEST_PEM_A.id, &TEST_PEM_A.data);
+ ok(r == DNSSEC_EOK, "write A (duplicate)");
+
+ // content listing
+
+ dnssec_list_t *list = NULL;
+ r = func->list(handle, &list);
+ ok(r == DNSSEC_EOK, "get list");
+ is_int(2, dnssec_list_size(list), "list size");
+
+ bool found_a = false;
+ bool found_b = false;
+ dnssec_list_foreach(item, list) {
+ char *id = dnssec_item_get(item);
+ if (id && strcmp(TEST_PEM_A.id, id) == 0) { found_a = true; }
+ if (id && strcmp(TEST_PEM_B.id, id) == 0) { found_b = true; }
+ }
+ ok(found_a && found_b, "list content");
+
+ dnssec_list_free_full(list, NULL, NULL);
+
+ // reading existing content
+
+ r = func->read(handle, TEST_PEM_A.id, &bin);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&TEST_PEM_A.data, &bin) == 0,
+ "read A");
+ dnssec_binary_free(&bin);
+
+ r = func->read(handle, TEST_PEM_B.id, &bin);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&TEST_PEM_B.data, &bin) == 0,
+ "read B");
+ dnssec_binary_free(&bin);
+
+ // content removal
+
+ r = func->remove(handle, TEST_PEM_A.id);
+ ok(r == DNSSEC_EOK, "remove A");
+
+ r = func->read(handle, TEST_PEM_A.id, &bin);
+ ok(r == DNSSEC_ENOENT && bin.size == 0, "read removed");
+
+ // cleanup
+
+ r = func->close(handle);
+ ok(r == DNSSEC_EOK, "close");
+
+ r = func->handle_free(handle);
+ ok(r == DNSSEC_EOK, "free handle");
+
+ rm(dir, "f4f3e73cf4ee605993c2ef2d790571ade827244c.pem");
+
+ test_tmpdir_free(dir);
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keytag.c b/tests/libdnssec/test_keytag.c
new file mode 100644
index 0000000..f71426e
--- /dev/null
+++ b/tests/libdnssec/test_keytag.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <stdint.h>
+
+#include "binary.h"
+#include "error.h"
+#include "keytag.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ const dnssec_binary_t rsa_md5_rdata = { .size = 72, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x01,
+ 0x03, 0x01, 0x00, 0x01, 0xa6, 0x83, 0x41, 0x42, 0x58, 0x1b,
+ 0xc2, 0xa7, 0xc7, 0xd5, 0xef, 0xb5, 0x6c, 0xec, 0x34, 0xc5,
+ 0xc9, 0x5e, 0x84, 0xa8, 0x35, 0x4a, 0xe0, 0xc2, 0x70, 0xf5,
+ 0x40, 0xe9, 0x92, 0x06, 0x70, 0x45, 0x88, 0xd3, 0x86, 0xcf,
+ 0xf5, 0xff, 0x83, 0x08, 0x06, 0x98, 0xe7, 0x8a, 0xa9, 0x2c,
+ 0xe7, 0xe1, 0x4d, 0xa6, 0x46, 0xef, 0x3a, 0x96, 0x93, 0x8c,
+ 0xc1, 0x02, 0x00, 0x6f, 0x31, 0x9f, 0xa2, 0x1d
+ }};
+
+ uint16_t tag = 0;
+ ok(dnssec_keytag(&rsa_md5_rdata, &tag) == DNSSEC_EOK && tag == 40866,
+ "keytag for RSA/MD5");
+
+ const dnssec_binary_t ecdsa_rdata = { .size = 100, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0e,
+ 0xbe, 0x8f, 0x42, 0x92, 0x34, 0xa0, 0x06, 0x5f, 0x18, 0xa1,
+ 0x15, 0x01, 0x84, 0x50, 0x33, 0x1f, 0x44, 0xa2, 0xbb, 0x61,
+ 0x2a, 0xc8, 0x86, 0x9c, 0xf3, 0x4b, 0x2e, 0xf9, 0x63, 0xd1,
+ 0x81, 0x72, 0x56, 0x96, 0xc7, 0x67, 0x34, 0xa1, 0x55, 0xc2,
+ 0xf3, 0x1d, 0x03, 0xbe, 0x1b, 0x39, 0xeb, 0xa7, 0xb8, 0x2c,
+ 0x72, 0x2e, 0x58, 0x75, 0x56, 0x42, 0x0b, 0x6f, 0x21, 0xa2,
+ 0x33, 0xf4, 0x21, 0x00, 0xb7, 0x0f, 0x5a, 0xf7, 0x1a, 0xf0,
+ 0xe9, 0x94, 0xfa, 0x43, 0xb0, 0x4a, 0x48, 0xb8, 0x64, 0x89,
+ 0x7b, 0xc9, 0xe0, 0xf7, 0x97, 0x52, 0xf4, 0x85, 0x0f, 0xb4,
+ 0xf4, 0xfc, 0xe2, 0x10, 0xd4, 0x26
+ }};
+
+ ok(dnssec_keytag(&ecdsa_rdata, &tag) == DNSSEC_EOK && tag == 61768,
+ "keytag for ECDSA/SHA384");
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_list.c b/tests/libdnssec/test_list.c
new file mode 100644
index 0000000..cd0a060
--- /dev/null
+++ b/tests/libdnssec/test_list.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "list.h"
+
+static void * const free_context = (void *)0xcafe;
+static int counter = 0;
+
+static void item_free(void *pointer, void *data)
+{
+ if (pointer && data == free_context) {
+ counter += 1;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* new list */
+
+ dnssec_list_t *list = dnssec_list_new();
+ ok(list != NULL, "create new list");
+
+ ok(dnssec_list_size(list) == 0, "new list has zero size");
+ ok(dnssec_list_is_empty(list), "new list is empty");
+ ok(dnssec_list_head(list) == NULL, "new list has no head");
+ ok(dnssec_list_tail(list) == NULL, "new list has no tail");
+
+ /* populate the list */
+
+ dnssec_list_append(list, (void *)7);
+ dnssec_list_append(list, (void *)9);
+ // 7, 9
+
+ dnssec_list_prepend(list, (void *)5);
+ dnssec_list_prepend(list, (void *)2);
+ // 2, 5, 7, 9
+
+ dnssec_item_t *head = dnssec_list_head(list);
+ dnssec_list_insert_before(head, (void *)1);
+ dnssec_list_insert_after(head, (void *)3);
+ // 1, 2, 3, 5, 7, 9
+
+ dnssec_item_t *tail = dnssec_list_tail(list);
+ dnssec_list_insert_before(tail, (void *)8);
+ dnssec_list_insert_after(tail, (void *)10);
+ // 1, 2, 3, 5, 7, 8, 9, 10
+
+ dnssec_item_t *item_5 = dnssec_list_nth(list, 3);
+ dnssec_list_insert_before(item_5, (void *)4);
+ dnssec_list_insert_after(item_5, (void *)6);
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+
+ ok(dnssec_list_size(list) == 10, "populated list has expected size");
+ ok(!dnssec_list_is_empty(list), "populated list is non-empty");
+
+ // content iteration
+
+ int sum = 0;
+ int previous = 0;
+ bool increasing = true;
+
+ for (dnssec_item_t *i = dnssec_list_head(list); i; i = dnssec_list_next(list, i)) {
+ int number = (int)(intptr_t)dnssec_item_get(i);
+ sum += number;
+
+ if (previous + 1 != number) {
+ increasing = false;
+ }
+
+ previous = number;
+ }
+
+ ok(sum == 55, "all items are in the list");
+ ok(increasing, "append and prepend work");
+
+ // content lookup
+
+ ok(dnssec_list_contains(list, (void *)7), "contains: positive");
+ ok(!dnssec_list_contains(list, (void *)17), "contains: negative");
+
+ ok(dnssec_list_search(list, (void *)3) == dnssec_list_nth(list, 2), "search: positive");
+ ok(dnssec_list_search(list, (void *)12) == NULL, "search: negative");
+
+ // item removal
+
+ dnssec_list_remove(dnssec_list_head(list));
+ dnssec_list_remove(dnssec_list_tail(list));
+ dnssec_list_remove(dnssec_list_nth(list, 5));
+
+ ok(dnssec_list_size(list) == 7, "three items removed");
+
+ // full free
+
+ counter = 0;
+ dnssec_list_free_full(list, item_free, free_context);
+ ok(counter == 7, "list full free");
+
+ // non-full free
+
+ list = dnssec_list_new();
+ dnssec_list_append(list, NULL);
+ ok(!dnssec_list_is_empty(list), "new list with one item");
+
+ counter = 0;
+ dnssec_list_free(list);
+ ok(counter == 0, "list non-full free");
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_nsec_bitmap.c b/tests/libdnssec/test_nsec_bitmap.c
new file mode 100644
index 0000000..37676fe
--- /dev/null
+++ b/tests/libdnssec/test_nsec_bitmap.c
@@ -0,0 +1,102 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "nsec.h"
+#include "libknot/descriptor.h"
+
+#define TEST_BITMAP_SIZE 18
+
+int main(void)
+{
+ plan_lazy();
+
+ // Which rrtypes will be contained in the bitmap.
+ int test_contains_count = 8;
+ enum knot_rr_type test_contains[] = {
+ KNOT_RRTYPE_A,
+ KNOT_RRTYPE_NS,
+ KNOT_RRTYPE_SOA,
+ KNOT_RRTYPE_RRSIG,
+ KNOT_RRTYPE_NSEC,
+ KNOT_RRTYPE_DNSKEY,
+ KNOT_RRTYPE_SPF,
+ KNOT_RRTYPE_CAA
+ };
+
+ // Which rrtypes will not be contained in the bitmap.
+ int test_not_contains_count = 4;
+ enum knot_rr_type test_not_contains[] = {
+ KNOT_RRTYPE_AAAA,
+ KNOT_RRTYPE_MX,
+ KNOT_RRTYPE_AXFR,
+ KNOT_RRTYPE_CNAME
+ };
+
+ // Allocate new bitmap.
+ dnssec_nsec_bitmap_t *bitmap = dnssec_nsec_bitmap_new();
+ ok(bitmap != NULL, "allocate bitmap");
+ if (!bitmap) {
+ return 1;
+ }
+
+ // Add the desired RR types to bitmap.
+ for (int i = 0; i < test_contains_count; i++) {
+ dnssec_nsec_bitmap_add(bitmap, test_contains[i]);
+ }
+
+ size_t size = dnssec_nsec_bitmap_size(bitmap);
+ ok(size == TEST_BITMAP_SIZE, "valid bitmap size");
+ if (size != TEST_BITMAP_SIZE) {
+ dnssec_nsec_bitmap_free(bitmap);
+ return 1;
+ }
+
+ const uint8_t expected[TEST_BITMAP_SIZE] = {
+ 0x00, 0x0D, 0x62, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x01, 0x01, 0x40
+ };
+ uint8_t encoded[TEST_BITMAP_SIZE] = { 0 };
+ dnssec_nsec_bitmap_write(bitmap, encoded);
+
+ ok(memcmp(encoded, expected, TEST_BITMAP_SIZE) == 0, "valid bitmap");
+
+ // Test contained types.
+ char rrtype_str[50];
+ for (int i = 0; i < test_contains_count; i++) {
+ bool contains = dnssec_nsec_bitmap_contains(encoded, size, test_contains[i]);
+ (void)knot_rrtype_to_string(test_contains[i], rrtype_str, 50);
+ ok(contains, "bitmap contains %s", rrtype_str);
+ }
+
+ // Test not contained types.
+ for (int i = 0; i < test_not_contains_count; i++) {
+ bool contains = dnssec_nsec_bitmap_contains(encoded, size, test_not_contains[i]);
+ (void)knot_rrtype_to_string(test_not_contains[i], rrtype_str, 50);
+ ok(!contains, "bitmap does not contain %s", rrtype_str);
+ }
+
+ dnssec_nsec_bitmap_clear(bitmap);
+ ok(dnssec_nsec_bitmap_size(bitmap) == 0, "bitmap clear");
+
+ dnssec_nsec_bitmap_free(bitmap);
+ return 0;
+}
diff --git a/tests/libdnssec/test_nsec_hash.c b/tests/libdnssec/test_nsec_hash.c
new file mode 100644
index 0000000..47e4501
--- /dev/null
+++ b/tests/libdnssec/test_nsec_hash.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "crypto.h"
+#include "error.h"
+#include "nsec.h"
+
+static const dnssec_binary_t RDATA = { .size = 9, .data = (uint8_t []) {
+ 0x01, // algorithm
+ 0x00, // flags
+ 0x00, 0x0a, // iterations
+ 0x04, // salt length
+ 'a', 'b', 'c', 'd' // salt
+}};
+
+static void test_length(void)
+{
+ ok(dnssec_nsec3_hash_length(DNSSEC_NSEC3_ALGORITHM_SHA1) == 20,
+ "dnssec_nsec3_hash_length() for SHA1");
+}
+
+static void test_parsing(void)
+{
+
+ dnssec_nsec3_params_t params = { 0 };
+ int result = dnssec_nsec3_params_from_rdata(&params, &RDATA);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_params_from_rdata()");
+
+ ok(params.algorithm == 1, "algorithm");
+ ok(params.flags == 0, "flags");
+ ok(params.iterations == 10, "iterations");
+ ok(params.salt.size == 4, "salt length");
+ ok(params.salt.data != NULL && memcmp(params.salt.data, "abcd", 4) == 0,
+ "salt content");
+
+ dnssec_nsec3_params_free(&params);
+ ok(params.salt.data == NULL, "dnssec_nsec3_params_free()");
+}
+
+static void test_hashing(void)
+{
+ const dnssec_binary_t dname = {
+ .size = 13,
+ .data = (uint8_t *) "\x08""knot-dns""\x02""cz"
+ };
+
+ const dnssec_nsec3_params_t params = {
+ .algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1,
+ .flags = 0,
+ .iterations = 7,
+ .salt = { .size = 14, .data = (uint8_t *) "happywithnsec3" }
+ };
+
+ const dnssec_binary_t expected = { .size = 20, .data = (uint8_t []) {
+ 0x72, 0x40, 0x55, 0x83, 0x92, 0x93, 0x95, 0x28, 0xee, 0xa2,
+ 0xcc, 0xe1, 0x13, 0xbe, 0xcd, 0x41, 0xee, 0x8a, 0x71, 0xfd
+ }};
+
+ dnssec_binary_t hash = { 0 };
+
+ int result = dnssec_nsec3_hash(&dname, &params, &hash);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_hash()");
+
+ ok(hash.size == expected.size && hash.data != NULL &&
+ memcmp(hash.data, expected.data, expected.size) == 0,
+ "valid hash");
+
+ dnssec_binary_free(&hash);
+}
+
+static void test_clear(void)
+{
+ const dnssec_nsec3_params_t empty = { 0 };
+ dnssec_nsec3_params_t params = { 0 };
+
+ int result = dnssec_nsec3_params_from_rdata(&params, &RDATA);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_params_from_rdata()");
+
+ ok(memcmp(&params, &empty, sizeof(dnssec_nsec3_params_t)) != 0,
+ "non-empty after dnssec_nsec3_params_from_rdata()");
+
+ dnssec_nsec3_params_free(&params);
+
+ ok(memcmp(&params, &empty, sizeof(dnssec_nsec3_params_t)) == 0,
+ "cleared after dnssec_nsec3_params_free()");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_length();
+ test_parsing();
+ test_hashing();
+ test_clear();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_random.c b/tests/libdnssec/test_random.c
new file mode 100644
index 0000000..7c86aa6
--- /dev/null
+++ b/tests/libdnssec/test_random.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "crypto.h"
+#include "error.h"
+#include "random.h"
+
+int check_buffer(void)
+{
+ const size_t buffer_size = 128;
+ uint8_t buffer_prev[buffer_size];
+ memset(buffer_prev, 0, buffer_size);
+ uint8_t buffer[buffer_size];
+ memset(buffer, 0, buffer_size);
+
+ for (int i = 0; i < 10; i++) {
+ int result = dnssec_random_buffer(buffer, buffer_size);
+ if (result != DNSSEC_EOK) {
+ return 1;
+ }
+
+ if (memcmp(buffer, buffer_prev, buffer_size) == 0) {
+ return 1;
+ }
+
+ memmove(buffer_prev, buffer, buffer_size);
+ }
+
+ return 0;
+}
+
+int check_random_type(void)
+{
+ uint16_t numbers[1000] = { 0 };
+ int conflicts = 0;
+
+ for (int i = 0; i < 1000; i++) {
+ numbers[i] = dnssec_random_uint16_t();
+ // check all previous
+ for (int j = 0; j < i; j++) {
+ if (numbers[i] == numbers[j]) {
+ conflicts += 1;
+ }
+ }
+ }
+
+ // allow 5 % of conflicts
+ return conflicts <= 50 ? 0 : 1;
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ // quite stupid, just check if it does something
+
+ ok(check_buffer() == 0, "dnssec_random_buffer()");
+ ok(check_random_type() == 0, "dnssec_random_uint16_t()");
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_shared_bignum.c b/tests/libdnssec/test_shared_bignum.c
new file mode 100644
index 0000000..0e8f8d5
--- /dev/null
+++ b/tests/libdnssec/test_shared_bignum.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+
+#include "bignum.h"
+#include "binary.h"
+
+#define bin_init(array) { .data = array, .size = sizeof(array) }
+
+#define test_size(value, usize, ssize, msg) \
+ dnssec_binary_t __bin = bin_init(value); \
+ is_int(usize, bignum_size_u(&__bin), "bignum_size_u, " msg); \
+ is_int(ssize, bignum_size_s(&__bin), "bignum_size_s, " msg)
+
+#define test_write(num, expect, msg) \
+ uint8_t __buffer[sizeof(expect)]; \
+ memset(__buffer, 0xaa, sizeof(__buffer)); \
+ wire_ctx_t __ctx = wire_ctx_init(__buffer, sizeof(expect)); \
+ dnssec_binary_t __num = bin_init(num); \
+ dnssec_binary_t __exp = bin_init(expect); \
+ bignum_write(&__ctx, sizeof(expect), &__num); \
+ dnssec_binary_t __dst = bin_init(__buffer); \
+ ok(dnssec_binary_cmp(&__dst, &__exp) == 0, "bignum_write, " msg)
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ {
+ uint8_t num[] = { };
+ test_size(num, 1, 1, "empty string");
+ }
+
+ {
+ uint8_t num[] = { 0x00 };
+ test_size(num, 1, 1, "zero");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00 };
+ test_size(num, 1, 1, "long zero");
+ }
+
+ {
+ uint8_t num[] = { 0x01, 0x02, 0x03 };
+ test_size(num, 3, 3, "no MSB");
+ }
+
+ {
+ uint8_t num[] = { 0x7f, 0xff, 0x00, 0x00, 0x00 };
+ test_size(num, 5, 5, "no MSB but all other bits");
+ }
+
+ {
+ uint8_t num[] = { 0x84, 0x42 };
+ test_size(num, 2, 3, "MSB");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x84, 0x42 };
+ test_size(num, 2, 3, "MSB and leading zero");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00, 0x00, 0xfc, 0xe1, 0xda };
+ test_size(num, 3, 4, "MSB, many leading zeroes");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00, 0x01 };
+ test_size(num, 1, 1, "no MSB, many leading zeroes");
+ }
+
+ // test writing
+
+ {
+ uint8_t num[] = { };
+ uint8_t exp[] = { 0x00 };
+ test_write(num, exp, "empty string");
+ }
+
+ {
+ uint8_t num[] = { 0x00 };
+ uint8_t exp[] = { 0x00 };
+ test_write(num, exp, "zero");
+ }
+
+ {
+ uint8_t num[] = { 0x11, 0x22, 0x33 };
+ uint8_t exp[] = { 0x00, 0x00, 0x00, 0x11, 0x22, 0x33 };
+ test_write(num, exp, "no MSB, right-aligned");
+ }
+
+ {
+ uint8_t num[] = { 0xff, 0xee, 0xdd };
+ uint8_t exp[] = { 0x00, 0x00, 0x00, 0xff, 0xee, 0xdd };
+ test_write(num, exp, "MSB, right-aligned");
+ }
+
+ {
+ uint8_t num[] = { 0x11, 0x22, 0x33 };
+ uint8_t exp[] = { 0x11, 0x22, 0x33 };
+ test_write(num, exp, "no MSB, fitting exactly");
+ }
+
+ {
+ uint8_t num[] = { 0xff, 0xee, 0xdd };
+ uint8_t exp[] = { 0xff, 0xee, 0xdd };
+ test_write(num, exp, "MSB, fitting exactly");
+ }
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_shared_dname.c b/tests/libdnssec/test_shared_dname.c
new file mode 100644
index 0000000..8eb93cf
--- /dev/null
+++ b/tests/libdnssec/test_shared_dname.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <stdbool.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "dname.h"
+
+static void ok_length(const char *dname, size_t length, const char *info)
+{
+ ok(dname_length((uint8_t *)dname) == length,
+ "dname_length() for %s", info);
+}
+
+static void test_length(void)
+{
+ ok_length(NULL, 0, "NULL");
+ ok_length("", 1, ".");
+ ok_length("\x2""cz", 4, "cz.");
+ ok_length("\x7""example""\x3""com", 13, "example.com.");
+}
+
+static bool dname_binary_equal(const uint8_t *one, const uint8_t *two)
+{
+ return one && two && strcmp((char *)one, (char *)two) == 0;
+}
+
+static void test_copy(void)
+{
+ const uint8_t *dname = (uint8_t *)"\x3""www""\x8""KNOT-DNS""\x2""cz";
+ uint8_t *copy = dname_copy(dname);
+ ok(dname_binary_equal(dname, copy), "dname_copy()");
+ free(copy);
+}
+
+static void test_equal(void)
+{
+ #define eq(a, b) dname_equal((uint8_t *)a, (uint8_t *)b)
+
+ ok(eq("\x4""kiwi""\x4""limo", "\x4""kiwi""\x4""limo") == true,
+ "dname_equal() same");
+ ok(eq("\x6""orange", "\x6""ORANGE") == true,
+ "dname_equal() case single label");
+ ok(eq("\x6""Banana""\03""Tea", "\x6""bANAna""\x3""tea") == true,
+ "dname_equal() case two labels");
+ ok(eq("\x4""Coco""\x4""MILK", "\x3""cow""\x4""milk") == false,
+ "dname_equal() different first");
+ ok(eq("\x4""LIME""\x5""syrup", "\x4""LIme""\x4""beer") == false,
+ "dname_equal() different last");
+ ok(eq("\x5""apple", "\x5""apple""\x5""shake") == false,
+ "dname_equal() a prefix of b");
+ ok(eq("\x5""apple""\x5""juice", "\x5""apple") == false,
+ "dname_equal() b prefix of a");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_length();
+ test_copy();
+ test_equal();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_sign.c b/tests/libdnssec/test_sign.c
new file mode 100644
index 0000000..1f61d96
--- /dev/null
+++ b/tests/libdnssec/test_sign.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "sample_keys.h"
+
+#include "binary.h"
+#include "crypto.h"
+#include "error.h"
+#include "key.h"
+#include "sign.h"
+
+static const dnssec_binary_t input_data = {
+ .size = 25,
+ .data = (uint8_t *)"Very good, young padawan."
+};
+
+static const dnssec_binary_t signed_rsa = { .size = 128, .data = (uint8_t []) {
+ 0x21, 0xba, 0xff, 0x0c, 0x15, 0x10, 0x73, 0x25, 0xa7, 0x8e,
+ 0xf4, 0x71, 0x4b, 0xd3, 0x97, 0x6d, 0x95, 0x52, 0xc2, 0x0b,
+ 0x43, 0xb3, 0x7d, 0x82, 0xe4, 0x3e, 0x2a, 0xc3, 0xb7, 0x17,
+ 0x5b, 0x05, 0xe9, 0x1e, 0x13, 0xac, 0x27, 0x6f, 0x20, 0x93,
+ 0x1a, 0xeb, 0xe2, 0x2c, 0x72, 0x70, 0x14, 0xe6, 0x49, 0xa7,
+ 0x62, 0xdd, 0x4c, 0x72, 0x1e, 0x1d, 0xd8, 0xf9, 0xba, 0xbc,
+ 0x96, 0x0e, 0xc3, 0xd4, 0xc1, 0x8f, 0x95, 0xdb, 0x01, 0x18,
+ 0x24, 0x43, 0xbd, 0x2b, 0x52, 0x9b, 0x10, 0x1f, 0xba, 0x0a,
+ 0x76, 0xbe, 0x0e, 0xaa, 0x91, 0x27, 0x7b, 0x9f, 0xed, 0x5a,
+ 0xad, 0x96, 0x1a, 0x02, 0x97, 0x42, 0x91, 0x30, 0x03, 0x2b,
+ 0x5c, 0xb8, 0xcc, 0x6b, 0xcf, 0x39, 0x62, 0x5e, 0x47, 0xae,
+ 0x6d, 0x5b, 0x43, 0xd2, 0xc2, 0xd8, 0x22, 0x5d, 0xf5, 0x5e,
+ 0x0a, 0x97, 0x65, 0x41, 0xc7, 0xaa, 0x28, 0x67,
+}};
+
+static const dnssec_binary_t signed_ecdsa = { .size = 64, .data = (uint8_t []) {
+ 0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62,
+ 0xa2, 0xc3, 0xa4, 0x8d, 0xd4, 0x53, 0x5c, 0xba, 0x29, 0x71,
+ 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3, 0x1e, 0x4e, 0x58, 0xe2,
+ 0x36, 0x7e,
+ 0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b, 0xd3, 0x8c, 0x88,
+ 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70, 0x55, 0x99, 0xec, 0xd5,
+ 0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5,
+ 0xad, 0x2f,
+}};
+
+#ifdef HAVE_ED25519
+static const dnssec_binary_t signed_ed25519 = { .size = 64, .data = (uint8_t []) {
+ 0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27,
+ 0x0e, 0x98, 0x34, 0xd3, 0x48, 0xef, 0x5a, 0x6e,
+ 0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4,
+ 0x2c, 0x68, 0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c,
+ 0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28, 0x1f,
+ 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b,
+ 0x5a, 0x48, 0xd6, 0xe5, 0x1c, 0xf9, 0xe3, 0xe0,
+ 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05,
+ }};
+#endif
+
+static dnssec_binary_t binary_set_string(char *str)
+{
+ dnssec_binary_t result = { .data = (uint8_t *)str, .size = strlen(str) };
+ return result;
+}
+
+static void check_key(const key_parameters_t *key_data, const dnssec_binary_t *data,
+ const dnssec_binary_t *signature, bool signature_match)
+{
+ int r;
+
+ // initialize key from public parameters
+
+ dnssec_key_t *key = NULL;
+ r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+ r = dnssec_key_set_rdata(key, &key_data->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ // check validation on static signature
+
+ dnssec_sign_ctx_t *ctx = NULL;
+ r = dnssec_sign_new(&ctx, key);
+ ok(r == DNSSEC_EOK, "create signing context");
+ r = dnssec_sign_add(ctx, data);
+ ok(r == DNSSEC_EOK, "add data to be signed");
+ r = dnssec_sign_verify(ctx, signature);
+ ok(r == DNSSEC_EOK, "signature verified");
+
+ // create new signature and self-validate
+
+ r = dnssec_key_load_pkcs8(key, &key_data->pem);
+ ok(r == DNSSEC_EOK, "load private key");
+
+ if (signature_match) {
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+ r = dnssec_sign_add(ctx, data);
+ ok(r == DNSSEC_EOK, "add data to be signed");
+ dnssec_binary_t new_signature = { 0 };
+ r = dnssec_sign_write(ctx, &new_signature);
+ ok(r == DNSSEC_EOK, "write the signature");
+ ok(dnssec_binary_cmp(signature, &new_signature) == 0,
+ "signature exact match");
+ r = dnssec_sign_verify(ctx, &new_signature);
+ ok(r == DNSSEC_EOK, "reverify the new signature");
+ dnssec_binary_free(&new_signature);
+ }
+
+ // context reinitialization
+
+ dnssec_binary_t tmp = { 0 };
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("bind");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (1)");
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("knot");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (2)");
+
+ tmp = binary_set_string(" is the best");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (3)");
+
+ dnssec_binary_t new_signature = { 0 };
+ r = dnssec_sign_write(ctx, &new_signature);
+ ok(r == DNSSEC_EOK, "write signature");
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("knot is the best");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (4)");
+
+ r = dnssec_sign_verify(ctx, &new_signature);
+ ok(r == DNSSEC_EOK, "verify signature");
+
+ dnssec_binary_free(&new_signature);
+
+ // cleanup
+
+ dnssec_sign_free(ctx);
+ dnssec_key_free(key);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ diag("RSA signing");
+ check_key(&SAMPLE_RSA_KEY, &input_data, &signed_rsa, true);
+ diag("ECDSA signing");
+ check_key(&SAMPLE_ECDSA_KEY, &input_data, &signed_ecdsa, false);
+#ifdef HAVE_ED25519
+ diag("ED25519 signing");
+ check_key(&SAMPLE_ED25519_KEY, &input_data, &signed_ed25519, true);
+#endif
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_sign_der.c b/tests/libdnssec/test_sign_der.c
new file mode 100644
index 0000000..04bfeac
--- /dev/null
+++ b/tests/libdnssec/test_sign_der.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "binary.h"
+#include "error.h"
+#include "sign/der.c"
+
+static int binary_eq(const dnssec_binary_t *a, const dnssec_binary_t *b)
+{
+ return a && b &&
+ a->size == b->size &&
+ memcmp(a->data, b->data, a->size) == 0;
+}
+
+#define DECODE_OK(der, r, s, message) \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __out_s = { 0 }; \
+ dnssec_binary_t __out_r = { 0 }; \
+ int _result = dss_sig_value_decode(&__der, &__out_r, &__out_s); \
+ ok(_result == DNSSEC_EOK && \
+ binary_eq(&__r, &__out_r) && \
+ binary_eq(&__s, &__out_s), \
+ "decode ok, " message)
+
+#define DECODE_FAIL(der, message) \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __out_r = { 0 }; \
+ dnssec_binary_t __out_s = { 0 }; \
+ int _result = dss_sig_value_decode(&__der, &__out_r, &__out_s); \
+ ok(_result != DNSSEC_EOK, \
+ "decode fail, " message)
+
+#define ENCODE_OK(r, s, der, message) \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __out_der = { 0 }; \
+ int _result = dss_sig_value_encode(&__r, &__s, &__out_der); \
+ ok(_result == DNSSEC_EOK && \
+ binary_eq(&__der, &__out_der), \
+ "encode ok, " message); \
+ dnssec_binary_free(&__out_der)
+
+#define ENCODE_FAIL(r, s, message) \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __out_der = { 0 }; \
+ int _result = dss_sig_value_encode(&__r, &__s, &__out_der); \
+ ok(_result != DNSSEC_EOK, \
+ "encode fail, " message); \
+ dnssec_binary_free(&__out_der)
+
+#define ONE_64_TIMES \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01
+
+#define ONE_128_TIMES \
+ ONE_64_TIMES, ONE_64_TIMES
+
+int main(void)
+{
+ plan_lazy();
+
+ {
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0x1a,0x2b, 0x02,0x02,0x3c,0x4d };
+ uint8_t r[] = { 0x1a, 0x2b };
+ uint8_t s[] = { 0x3c, 0x4d };
+ DECODE_OK(der, r, s, "simple without MSB");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0xff,0xff, 0x02,0x02,0x80,0x00 };
+ uint8_t r[] = { 0xff, 0xff };
+ uint8_t s[] = { 0x80, 0x00 };
+ DECODE_OK(der, r, s, "simple with MSB");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x09, 0x02,0x04,0x00,0x00,0x00,0xfa, 0x02,0x01,0x07 };
+ uint8_t r[] = { 0xfa };
+ uint8_t s[] = { 0x07 };
+ DECODE_OK(der, r, s, "leading zeros");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x00, 0x02,0x02,0x00,0x00 };
+ uint8_t r[] = { 0x00 };
+ uint8_t s[] = { 0x00 };
+ DECODE_OK(der, r, s, "zero values" );
+ }
+
+ {
+ uint8_t der[] = { };
+ DECODE_FAIL(der, "empty input");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x04, 0x02,0x01,0x01 };
+ DECODE_FAIL(der, "partial sequence");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x01, 0x02,0x02,0x01 };
+ DECODE_FAIL(der, "partial integer");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x00 };
+ DECODE_FAIL(der, "zero-length sequence");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x05, 0x02,0x01,0xff, 0x02,0x00 };
+ DECODE_FAIL(der, "zero-length integer");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x84, 0x02,0x40,ONE_64_TIMES, 0x02,0x40,ONE_64_TIMES };
+ DECODE_FAIL(der, "unsupported size");
+ }
+
+ {
+ uint8_t r[] = { 0x01, };
+ uint8_t s[] = { 0x02,0x03 };
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x01, 0x02,0x02,0x02,0x03 };
+ ENCODE_OK(r, s, der, "simple");
+ }
+
+ {
+ uint8_t r[] = { 0x00,0x01, };
+ uint8_t s[] = { 0x00,0x00,0x02,0x03 };
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x01, 0x02,0x02,0x02,0x03 };
+ ENCODE_OK(r, s, der, "unnecessary leading zeros");
+ }
+
+ {
+ uint8_t r[] = { 0x00,0x8f };
+ uint8_t s[] = { 0x00,0x00,0xff };
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0x00,0x8f, 0x02,0x02,0x00,0xff };
+ ENCODE_OK(r, s, der, "required zero not removed");
+ }
+
+ {
+ uint8_t r[] = { 0x8c };
+ uint8_t s[] = { 0xff,0xee };
+ uint8_t der[] = { 0x30,0x09, 0x02,0x02,0x00,0x8c, 0x02,0x03,0x00,0xff,0xee };
+ ENCODE_OK(r, s, der, "implicitly added zero");
+ }
+
+ {
+ uint8_t r[] = { 0x00 };
+ uint8_t s[] = { 0x00,0x00 };
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x00, 0x02,0x01,0x00 };
+ ENCODE_OK(r, s, der, "zero");
+ }
+
+ {
+ uint8_t r[] = { 0x01 };
+ uint8_t s[] = { };
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x01, 0x02,0x01,0x00 };
+ ENCODE_OK(r, s, der, "zero-length input");
+ }
+
+ {
+ uint8_t r[] = { ONE_128_TIMES };
+ uint8_t s[] = { 0x01 };
+ ENCODE_FAIL(r, s, "input too long");
+ }
+
+ {
+ uint8_t r[] = { ONE_64_TIMES };
+ uint8_t s[] = { ONE_64_TIMES };
+ ENCODE_FAIL(r, s, "result too long");
+ }
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_tsig.c b/tests/libdnssec/test_tsig.c
new file mode 100644
index 0000000..91674be
--- /dev/null
+++ b/tests/libdnssec/test_tsig.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+
+#include "binary.h"
+#include "dname.h"
+#include "tsig.h"
+
+static const dnssec_binary_t payload = {
+ .size = 40,
+ .data = (uint8_t []){
+ 0xfd, 0x07, 0xca, 0x30, 0xf9, 0xff, 0x38, 0xb1, 0x32, 0x54,
+ 0xd1, 0x16, 0x24, 0xaa, 0x81, 0x2c, 0x97, 0xa0, 0x7a, 0xac,
+ 0x68, 0x7a, 0x3a, 0x60, 0xde, 0xc9, 0xf7, 0x7a, 0x5a, 0x58,
+ 0xff, 0xc9, 0x0c, 0xef, 0x31, 0xc7, 0x45, 0x2c, 0xee, 0x9d,
+ }
+};
+
+static const dnssec_binary_t key = {
+ .size = 16,
+ .data = (uint8_t []){
+ 0xa8, 0x05, 0x9c, 0x5c, 0x20, 0xc5, 0x00, 0x22, 0x6f, 0xad,
+ 0xf2, 0x55, 0xdf, 0x89, 0x8a, 0x68
+ }
+};
+
+typedef struct hmac {
+ int algorithm;
+ const char *name;
+ const dnssec_binary_t hmac;
+} hmac_t;
+
+static const hmac_t HMACS[] = {
+ { DNSSEC_TSIG_HMAC_MD5, "md5", { .size = 16, .data = (uint8_t []) {
+ 0x12, 0x38, 0x17, 0x4f, 0xa9, 0xc7, 0x5b, 0xcf, 0xd7, 0x08,
+ 0x19, 0x97, 0xf9, 0x3d, 0x5e, 0xe7
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA1, "sha1", { .size = 20, .data = (uint8_t []) {
+ 0xb8, 0x18, 0x2a, 0x5d, 0xf8, 0x2e, 0xa0, 0xb7, 0xcc, 0xcc,
+ 0xed, 0xc1, 0xaa, 0x34, 0xeb, 0x92, 0x48, 0xf9, 0x65, 0x7b
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA224, "sha224", { .size = 28, .data = (uint8_t []) {
+ 0xb7, 0x43, 0xcd, 0x0d, 0x9d, 0x51, 0x8c, 0x61, 0xc6, 0x43,
+ 0x98, 0x73, 0x5c, 0x16, 0x01, 0x1b, 0xfc, 0x82, 0xe9, 0x99,
+ 0xc2, 0x21, 0xde, 0x16, 0xb1, 0x94, 0x2d, 0xd5
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA256, "sha256", { .size = 32, .data = (uint8_t []) {
+ 0x16, 0x5e, 0xf6, 0xed, 0x9b, 0x1a, 0xe5, 0x67, 0x58, 0x7b,
+ 0xf1, 0x35, 0x9e, 0x59, 0xbd, 0x50, 0x6d, 0x72, 0xf8, 0x87,
+ 0x0e, 0x22, 0xda, 0x65, 0x00, 0xd6, 0x76, 0x91, 0xde, 0x5f,
+ 0xec, 0xd8
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA384, "sha384", { .size = 48, .data = (uint8_t []) {
+ 0x8a, 0xcf, 0xf3, 0xb7, 0x1c, 0xbe, 0x5c, 0x3e, 0x05, 0x74,
+ 0x97, 0x46, 0x04, 0x79, 0x3a, 0xe7, 0x8a, 0x5b, 0x7b, 0x12,
+ 0xca, 0xcd, 0xf2, 0xe2, 0xdf, 0xa9, 0x17, 0xfc, 0x8e, 0x61,
+ 0xc5, 0x86, 0x3e, 0xdc, 0xad, 0x84, 0x9e, 0x13, 0x0d, 0xa0,
+ 0x04, 0xb6, 0x6f, 0x7c, 0x85, 0x1b, 0x5c, 0xdf
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA512, "sha512", { .size = 64, .data = (uint8_t []) {
+ 0xc3, 0x41, 0xd0, 0x96, 0x50, 0xd7, 0xf7, 0xfd, 0x59, 0x73,
+ 0xde, 0xd6, 0xc7, 0x4c, 0xda, 0xf1, 0x5d, 0xe1, 0x59, 0x34,
+ 0x79, 0xdc, 0x93, 0x23, 0xcb, 0xf2, 0x1f, 0x25, 0x4e, 0x35,
+ 0xb0, 0xd0, 0x9f, 0xfc, 0x22, 0xf1, 0xea, 0xbf, 0x9c, 0x18,
+ 0xd8, 0xcc, 0xcd, 0xb6, 0xb1, 0x4a, 0x06, 0x09, 0xc4, 0x3f,
+ 0x28, 0x93, 0x71, 0xd6, 0xca, 0xce, 0xf3, 0xa6, 0x08, 0x38,
+ 0xe3, 0x99, 0xc1, 0xb2
+ }}},
+ { 0 }
+};
+
+static void test_lookup_dname(const uint8_t *dname, int algorithm)
+{
+ dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(dname);
+ const char *name = dnssec_tsig_algorithm_to_name(algorithm);
+ if (name == NULL) name = "invalid";
+ ok(alg == algorithm, "dnssec_tsig_algorithm_from_dname(%s)", name);
+
+ const uint8_t *reverse = dnssec_tsig_algorithm_to_dname(algorithm);
+ ok((algorithm == DNSSEC_TSIG_UNKNOWN && reverse == NULL) ||
+ (algorithm != DNSSEC_TSIG_UNKNOWN && dname_equal(reverse, dname)),
+ "dnssec_tsig_algorithm_to_dname(%d)", algorithm);
+}
+
+static void test_lookup_name(const char *name, int algorithm)
+{
+ ok(dnssec_tsig_algorithm_from_name(name) == algorithm,
+ "dnssec_tsig_algorithm_from_name(%s)", name);
+
+ const char *reverse = dnssec_tsig_algorithm_to_name(algorithm);
+ ok((algorithm == DNSSEC_TSIG_UNKNOWN && reverse == NULL) ||
+ (algorithm != DNSSEC_TSIG_UNKNOWN && strcasecmp(reverse, name) == 0),
+ "dnssec_tsig_algorithm_to_name(%d)", algorithm);
+}
+
+static void test_tsig_hmac(const hmac_t *params)
+{
+ dnssec_tsig_ctx_t *ctx = NULL;
+ dnssec_tsig_new(&ctx, params->algorithm, &key);
+ dnssec_tsig_add(ctx, &payload);
+
+ size_t size = dnssec_tsig_size(ctx);
+ uint8_t hmac[size];
+ dnssec_tsig_write(ctx, hmac);
+ dnssec_tsig_free(ctx);
+
+ ok(size == params->hmac.size && memcmp(hmac, params->hmac.data, size) == 0,
+ "dnssec_tsig_write(%s)", params->name);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_lookup_dname((uint8_t *)"\x08""HMAC-MD5""\x07""SIG-ALG""\x03""REG""\x03""INT",
+ DNSSEC_TSIG_HMAC_MD5);
+ test_lookup_dname((uint8_t *)"\x0B""hmac-sha224", DNSSEC_TSIG_HMAC_SHA224);
+ test_lookup_dname((uint8_t *)"\x06""foobar", DNSSEC_TSIG_UNKNOWN);
+
+ test_lookup_name("hmac-md5", DNSSEC_TSIG_HMAC_MD5);
+ test_lookup_name("hmac-sha512", DNSSEC_TSIG_HMAC_SHA512);
+ test_lookup_name("barfoo", DNSSEC_TSIG_UNKNOWN);
+ test_lookup_name("hmac-foo", DNSSEC_TSIG_UNKNOWN);
+
+ for (const hmac_t *h = HMACS; h->algorithm != 0; h++) {
+ test_tsig_hmac(h);
+ }
+
+ return 0;
+}
diff --git a/tests/libknot/test_control.c b/tests/libknot/test_control.c
new file mode 100644
index 0000000..3ec4daf
--- /dev/null
+++ b/tests/libknot/test_control.c
@@ -0,0 +1,221 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/wait.h>
+#include <unistd.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#define CTL_BUFF_SIZE 18
+#include "libknot/control/control.c"
+
+#define fake_ok(condition, msg, ...) \
+ if (!(condition)) { \
+ if (msg != NULL) { \
+ printf("error: " msg "\n", ##__VA_ARGS__); \
+ } \
+ exit(-1); \
+ }
+
+static void ctl_client(const char *socket, size_t argc, knot_ctl_data_t *argv)
+{
+ knot_ctl_t *ctl = knot_ctl_alloc();
+ fake_ok(ctl != NULL, "Allocate control");
+
+ int ret;
+ for (int i = 0; i < 20; i++) {
+ ret = knot_ctl_connect(ctl, socket);
+ if (ret == KNOT_EOK) {
+ break;
+ }
+ usleep(100000);
+ }
+ fake_ok(ret == KNOT_EOK, "Connect to socket");
+
+ diag("BEGIN: Client -> Server");
+
+ if (argc > 0) {
+ for (size_t i = 0; i < argc; i++) {
+ if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
+ fake_ok(ret == KNOT_EOK, "Client send data block end type");
+ } else {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
+ fake_ok(ret == KNOT_EOK, "Client send data %zu", i);
+ }
+ }
+ }
+
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ fake_ok(ret == KNOT_EOK, "Client send final data");
+
+ diag("END: Client -> Server");
+ diag("BEGIN: Client <- Server");
+
+ size_t count = 0;
+ knot_ctl_data_t data;
+ knot_ctl_type_t type;
+ while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
+ if (type == KNOT_CTL_TYPE_END) {
+ break;
+ }
+ if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
+ fake_ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
+ } else {
+ fake_ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
+ for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
+ fake_ok((data[i] == NULL && argv[count][i] == NULL) ||
+ (data[i] != NULL && argv[count][i] != NULL),
+ "Client compare input item occupation %zu", i);
+ if (data[i] == NULL) {
+ continue;
+ }
+
+ fake_ok(strcmp(data[i], argv[count][i]) == 0,
+ "Client compare input item '%s", argv[count][i]);
+ }
+ }
+ count++;
+ }
+ fake_ok(ret == KNOT_EOK, "Receive OK check");
+ fake_ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
+ fake_ok(count == argc, "Client compare input count '%zu'", argc);
+
+ diag("END: Client <- Server");
+
+ knot_ctl_close(ctl);
+ knot_ctl_free(ctl);
+}
+
+static void ctl_server(const char *socket, size_t argc, knot_ctl_data_t *argv)
+{
+ knot_ctl_t *ctl = knot_ctl_alloc();
+ ok(ctl != NULL, "Allocate control");
+
+ int ret = knot_ctl_bind(ctl, socket);
+ is_int(KNOT_EOK, ret, "Bind control socket");
+
+ ret = knot_ctl_accept(ctl);
+ is_int(KNOT_EOK, ret, "Accept a connection");
+
+ diag("BEGIN: Server <- Client");
+
+ size_t count = 0;
+ knot_ctl_data_t data;
+ knot_ctl_type_t type;
+ while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
+ if (type == KNOT_CTL_TYPE_END) {
+ break;
+ }
+ if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
+ } else {
+ ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
+ for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
+ ok((data[i] == NULL && argv[count][i] == NULL) ||
+ (data[i] != NULL && argv[count][i] != NULL),
+ "Server compare input item occupation %zu", i);
+ if (data[i] == NULL) {
+ continue;
+ }
+
+ ok(strcmp(data[i], argv[count][i]) == 0,
+ "Server compare input item '%s", argv[count][i]);
+ }
+ }
+ count++;
+ }
+ is_int(KNOT_EOK, ret, "Receive OK check");
+ ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
+ ok(count == argc, "Server compare input count '%zu'", argc);
+
+ diag("END: Server <- Client");
+ diag("BEGIN: Server -> Client");
+
+ if (argc > 0) {
+ for (size_t i = 0; i < argc; i++) {
+ if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
+ is_int(KNOT_EOK, ret, "Client send data block end type");
+ } else {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
+ is_int(KNOT_EOK, ret, "Server send data %zu", i);
+ }
+ }
+ }
+
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ is_int(KNOT_EOK, ret, "Server send final data");
+
+ diag("END: Server -> Client");
+
+ knot_ctl_close(ctl);
+ knot_ctl_unbind(ctl);
+ knot_ctl_free(ctl);
+}
+
+static void test_client_server_client(void)
+{
+ char *socket = test_mktemp();
+ ok(socket != NULL, "Make a temporary socket file '%s'", socket);
+
+ size_t data_len = 5;
+ knot_ctl_data_t data[] = {
+ { "command", "error", "section", "item", "identifier",
+ "zone", "owner", "ttl", "type", "data" },
+ { [KNOT_CTL_IDX_DATA] = "\x01\x02" },
+ { [KNOT_CTL_IDX_CMD] = "\0" }, // This means block end in this test!
+ { NULL },
+ { [KNOT_CTL_IDX_ERROR] = "Ultra long message" }
+ };
+
+ // Fork a client process.
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ ok(child_pid >= 0, "Process fork");
+ return;
+ }
+ if (child_pid == 0) {
+ ctl_client(socket, data_len, data);
+ free(socket);
+ return;
+ } else {
+ ctl_server(socket, data_len, data);
+ }
+
+ int status = 0;
+ wait(&status);
+ ok(WIFEXITED(status), "Wait for client");
+
+ test_rm_rf(socket);
+ free(socket);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Client -> Server -> Client");
+ test_client_server_client();
+
+ return 0;
+}
diff --git a/tests/libknot/test_cookies.c b/tests/libknot/test_cookies.c
new file mode 100644
index 0000000..b50b940
--- /dev/null
+++ b/tests/libknot/test_cookies.c
@@ -0,0 +1,177 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/cookies.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "contrib/sockaddr.h"
+
+static void client_generate(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr,
+ const uint8_t *secret, const char *msg, int code, uint64_t le_cc)
+{
+ knot_edns_cookie_params_t params = {
+ .client_addr = (struct sockaddr *)c_addr,
+ .server_addr = (struct sockaddr *)s_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ knot_edns_cookie_t cc;
+ int ret = knot_edns_cookie_client_generate(&cc, &params);
+ is_int(ret, code, "client_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ uint64_t ref = le64toh(le_cc);
+ ok(cc.len == sizeof(ref) && memcmp(cc.data, &ref, cc.len) == 0,
+ "client_generate value: %s", msg);
+ }
+}
+
+static void server_generate(struct sockaddr_storage *c_addr, const uint8_t *secret,
+ const knot_edns_cookie_t *cc, const char *msg, int code,
+ uint64_t le_sc)
+{
+ knot_edns_cookie_params_t params = {
+ .client_addr = (struct sockaddr *)c_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ knot_edns_cookie_t sc;
+ int ret = knot_edns_cookie_server_generate(&sc, cc, &params);
+ is_int(ret, code, "server_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ uint64_t ref = le64toh(le_sc);
+ ok(sc.len == sizeof(ref) && memcmp(sc.data, &ref, sc.len) == 0,
+ "server_generate value: %s", msg);
+ }
+}
+
+static void client_check(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr,
+ const uint8_t *secret, const char *msg, uint16_t le_cc_len,
+ uint64_t le_cc, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .client_addr = (struct sockaddr *)c_addr,
+ .server_addr = (struct sockaddr *)s_addr,
+ };
+ if (secret != NULL) {
+ memcpy(params.secret, secret, sizeof(params.secret));
+ }
+
+ uint64_t ref = le64toh(le_cc);
+ knot_edns_cookie_t cc = {
+ .len = le_cc_len
+ };
+ memcpy(cc.data, &ref, le_cc_len);
+
+ int ret = knot_edns_cookie_client_check(&cc, &params);
+ is_int(ret, code, "client_check ret: %s", msg);
+}
+
+static void server_check(struct sockaddr_storage *c_addr, const uint8_t *secret,
+ const char *msg, uint16_t le_cc_len, uint64_t le_cc,
+ uint16_t le_sc_len, uint64_t le_sc, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .client_addr = (struct sockaddr *)c_addr,
+ };
+ if (secret != NULL) {
+ memcpy(params.secret, secret, sizeof(params.secret));
+ }
+
+ uint64_t ref = le64toh(le_cc);
+ knot_edns_cookie_t cc = {
+ .len = le_cc_len
+ };
+ memcpy(cc.data, &ref, le_cc_len);
+
+ ref = le64toh(le_sc);
+ knot_edns_cookie_t sc = {
+ .len = le_sc_len
+ };
+ memcpy(sc.data, &ref, le_sc_len);
+
+ int ret = knot_edns_cookie_server_check(&sc, &cc, &params);
+ is_int(ret, code, "server_check ret: %s", msg);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ const uint8_t secret[] = "\xCE\xFA\xEF\xBE\xAD\xDE\x00\x00\xEF\xBE\xAD\xDE\xCE\xFA\x00\x00";
+
+ struct sockaddr_storage unspec_sa = { 0 };
+
+ struct sockaddr_storage c4_sa = { 0 };
+ struct sockaddr_storage s4_sa = { 0 };
+ sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0);
+ sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0);
+
+ struct sockaddr_storage c6_sa = { 0 };
+ struct sockaddr_storage s6_sa = { 0 };
+ sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0);
+ sockaddr_set(&s6_sa, AF_INET6, "::1", 0);
+
+ /* Client cookie generate. */
+ client_generate(NULL, &s4_sa, secret, "NULL, IPv4", KNOT_EINVAL, 0);
+ client_generate(&c4_sa, NULL, secret, "IPv4, NULL", KNOT_EINVAL, 0);
+ client_generate(&c4_sa, &s4_sa, secret, "IPv4, IPv4", KNOT_EOK, 0xde3832f4f59bf5ab);
+ client_generate(&unspec_sa, &s4_sa, secret, "unspec, IPv4", KNOT_EOK, 0x6b636ff225a1b340);
+ client_generate(&c4_sa, &unspec_sa, secret, "IPv4, unspec", KNOT_EOK, 0xd713ab1a81179bb3);
+
+ /* Client cookie check. */
+ client_check(NULL, &s6_sa, secret, "no client addr", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+ client_check(&c6_sa, NULL, secret, "no server addr", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+ client_check(&c6_sa, &s6_sa, NULL, "no secret", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+ client_check(&c6_sa, &s6_sa, secret, "no cookie", 0, 0, KNOT_EINVAL);
+ client_check(&c6_sa, &s6_sa, secret, "bad cookie length", 7, 0xf99dbd02b69ab3c2, KNOT_EINVAL);
+ client_check(&c6_sa, &s6_sa, secret, "invalid cookie", 8, 0, KNOT_EINVAL);
+ client_check(&c6_sa, &s6_sa, secret, "good cookie", 8, 0xf99dbd02b69ab3c2, KNOT_EOK);
+
+ const knot_edns_cookie_t cc = {
+ .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+ .len = 8
+ };
+
+ /* Server cookie generate. */
+ knot_edns_cookie_t cc_part = cc; cc_part.len--;
+ server_generate(NULL, secret, &cc, "NULL", KNOT_EINVAL, 0);
+ server_generate(&c4_sa, secret, &cc_part, "cookie part", KNOT_EINVAL, 0);
+ server_generate(&c4_sa, secret, &cc, "IPv4", KNOT_EOK, 0x52f86bfcc98ded6);
+ server_generate(&c6_sa, secret, &cc, "IPv6", KNOT_EOK, 0x33ac6c9005acf469);
+ server_generate(&unspec_sa, secret, &cc, "unspec", KNOT_EOK, 0x96df9dbf28f0f59e);
+
+ /* Server cookie check. */
+ server_check(NULL, secret, "no addr", 8, 0x0706050403020100,
+ 8, 0x33ac6c9005acf469, KNOT_EINVAL);
+ server_check(&c6_sa, NULL, "no secret", 8, 0x0706050403020100,
+ 8, 0x33ac6c9005acf469, KNOT_EINVAL);
+ server_check(&c6_sa, secret, "no client cookie", 0, 0,
+ 8, 0x33ac6c9005acf469, KNOT_EINVAL);
+ server_check(&c6_sa, secret, "no server cookie", 8, 0x0706050403020100,
+ 0, 0, KNOT_EINVAL);
+ server_check(&c6_sa, secret, "bad client cookie", 8, 0,
+ 8, 0x33ac6c9005acf469, KNOT_EINVAL);
+ server_check(&c6_sa, secret, "bad server cookie", 8, 0x0706050403020100,
+ 8, 0, KNOT_EINVAL);
+ server_check(&c6_sa, secret, "good cookie 1", 8, 0x0706050403020100,
+ 8, 0x33ac6c9005acf469, KNOT_EOK);
+
+ return 0;
+}
diff --git a/tests/libknot/test_db.c b/tests/libknot/test_db.c
new file mode 100644
index 0000000..49bd6e4
--- /dev/null
+++ b/tests/libknot/test_db.c
@@ -0,0 +1,287 @@
+/* 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "contrib/string.h"
+#include "libknot/libknot.h"
+#include "contrib/mempattern.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/ucw/mempool.h"
+
+/* UCW array sorting defines. */
+#define ASORT_PREFIX(X) str_key_##X
+#define ASORT_KEY_TYPE char*
+#define ASORT_LT(x, y) (strcmp((x), (y)) < 0)
+#include "contrib/ucw/array-sort.h"
+
+/* Constants. */
+#define KEY_MAXLEN 64
+#define KEY_SET(key, str) key.data = (str); key.len = strlen(str) + 1
+
+/*! \brief Generate random key. */
+static const char *alphabet = "abcdefghijklmn0123456789";
+static char *str_key_rand(size_t len, knot_mm_t *pool)
+{
+ char *s = mm_alloc(pool, len);
+ memset(s, 0, len);
+ for (unsigned i = 0; i < len - 1; ++i) {
+ s[i] = alphabet[rand() % strlen(alphabet)];
+ }
+ return s;
+}
+
+static void knot_db_test_set(unsigned nkeys, char **keys, void *opts,
+ const knot_db_api_t *api, knot_mm_t *pool)
+{
+ if (api == NULL) {
+ skip("API not compiled in");
+ return;
+ }
+
+ /* Create database */
+ knot_db_t *db = NULL;
+ int ret = api->init(&db, pool, opts);
+ ok(ret == KNOT_EOK && db != NULL, "%s: create", api->name);
+
+ /* Start WR transaction. */
+ knot_db_txn_t txn;
+ ret = api->txn_begin(db, &txn, 0);
+ is_int(KNOT_EOK, ret, "%s: txn_begin(WR)", api->name);
+
+ /* Insert keys */
+ knot_db_val_t key, val;
+ bool passed = true;
+ for (unsigned i = 0; i < nkeys; ++i) {
+ KEY_SET(key, keys[i]);
+ val.len = sizeof(void*);
+ val.data = &key.data;
+
+ ret = api->insert(&txn, &key, &val, 0);
+ if (ret != KNOT_EOK && ret != KNOT_EEXIST) {
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "%s: insert", api->name);
+
+ /* Commit WR transaction. */
+ ret = api->txn_commit(&txn);
+ is_int(KNOT_EOK, ret, "%s: txn_commit(WR)", api->name);
+
+ /* Start RD transaction. */
+ ret = api->txn_begin(db, &txn, KNOT_DB_RDONLY);
+ is_int(KNOT_EOK, ret, "%s: txn_begin(RD)", api->name);
+
+ /* Lookup all keys */
+ passed = true;
+ for (unsigned i = 0; i < nkeys; ++i) {
+ KEY_SET(key, keys[i]);
+
+ ret = api->find(&txn, &key, &val, 0);
+ if (ret != KNOT_EOK) {
+ passed = false;
+ break;
+ }
+
+ const char **stored_key = val.data;
+ if (strcmp(*stored_key, keys[i]) != 0) {
+ diag("%s: mismatch on element '%u'", api->name, i);
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "%s: lookup all keys", api->name);
+
+ /* Fetch dataset size. */
+ int db_size = api->count(&txn);
+ ok(db_size > 0 && db_size <= nkeys, "%s: count %d", api->name, db_size);
+
+ /* Unsorted iteration */
+ int iterated = 0;
+ knot_db_iter_t *it = api->iter_begin(&txn, 0);
+ while (it != NULL) {
+ ++iterated;
+ it = api->iter_next(it);
+ }
+ api->iter_finish(it);
+ is_int(db_size, iterated, "%s: unsorted iteration", api->name);
+
+ /* Sorted iteration. */
+ char first_key[KEY_MAXLEN] = { '\0' };
+ char second_key[KEY_MAXLEN] = { '\0' };
+ char last_key[KEY_MAXLEN] = { '\0' };
+ char key_buf[KEY_MAXLEN] = {'\0'};
+ iterated = 0;
+ memset(&key, 0, sizeof(key));
+ it = api->iter_begin(&txn, KNOT_DB_SORTED);
+ while (it != NULL) {
+ api->iter_key(it, &key);
+ if (iterated > 0) { /* Only if previous exists. */
+ if (strcmp(key_buf, key.data) > 0) {
+ diag("%s: iter_sort '%s' <= '%s' FAIL\n",
+ api->name, key_buf, (const char *)key.data);
+ break;
+ }
+ if (iterated == 1) {
+ memcpy(second_key, key.data, key.len);
+ }
+ } else {
+ memcpy(first_key, key.data, key.len);
+ }
+ ++iterated;
+ memcpy(key_buf, key.data, key.len);
+ it = api->iter_next(it);
+ }
+ strlcpy(last_key, key_buf, sizeof(last_key));
+ is_int(db_size, iterated, "%s: sorted iteration", api->name);
+ api->iter_finish(it);
+
+ /* Interactive iteration. */
+ it = api->iter_begin(&txn, KNOT_DB_NOOP);
+ if (it != NULL) { /* If supported. */
+ ret = 0;
+ /* Check if first and last keys are reachable */
+ it = api->iter_seek(it, NULL, KNOT_DB_FIRST);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(FIRST)", api->name);
+ /* Check left/right iteration. */
+ it = api->iter_seek(it, &key, KNOT_DB_NEXT);
+ ret += api->iter_key(it, &key);
+ is_string(second_key, key.data, "%s: iter_set(NEXT)", api->name);
+ it = api->iter_seek(it, &key, KNOT_DB_PREV);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(PREV)", api->name);
+ it = api->iter_seek(it, &key, KNOT_DB_LAST);
+ ret += api->iter_key(it, &key);
+ is_string(last_key, key.data, "%s: iter_set(LAST)", api->name);
+ /* Check if prev(last_key + 1) is the last_key */
+ strlcpy(key_buf, last_key, sizeof(key_buf));
+ key_buf[0] += 1;
+ KEY_SET(key, key_buf);
+ it = api->iter_seek(it, &key, KNOT_DB_LEQ);
+ ret += api->iter_key(it, &key);
+ is_string(last_key, key.data, "%s: iter_set(LEQ)", api->name);
+ /* Check if next(first_key - 1) is the first_key */
+ strlcpy(key_buf, first_key, sizeof(key_buf));
+ key_buf[0] -= 1;
+ KEY_SET(key, key_buf);
+ it = api->iter_seek(it, &key, KNOT_DB_GEQ);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(GEQ)", api->name);
+ api->iter_finish(it);
+ is_int(ret, 0, "%s: iter_* error codes", api->name);
+ }
+ api->txn_abort(&txn);
+
+ /* Deleting during iteration. */
+ const uint8_t DEL_MAX_CNT = 3;
+ api->txn_begin(db, &txn, 0);
+ api->clear(&txn);
+ for (uint8_t i = 0; i < DEL_MAX_CNT; ++i) {
+ key.data = &i;
+ key.len = sizeof(i);
+ val.data = NULL;
+ val.len = 0;
+
+ ret = api->insert(&txn, &key, &val, 0);
+ is_int(KNOT_EOK, ret, "%s: add key '%u'", api->name, i);
+ }
+ it = api->iter_begin(&txn, KNOT_DB_NOOP);
+ if (it != NULL) { /* If supported. */
+ is_int(DEL_MAX_CNT, api->count(&txn), "%s: key count before", api->name);
+ it = api->iter_seek(it, NULL, KNOT_DB_FIRST);
+ uint8_t pos = 0;
+ while (it != NULL) {
+ ret = api->iter_key(it, &key);
+ is_int(KNOT_EOK, ret, "%s: iter key before del", api->name);
+ is_int(pos, ((uint8_t *)(key.data))[0], "%s: iter compare key '%u'",
+ api->name, pos);
+
+ ret = knot_db_lmdb_iter_del(it);
+ is_int(KNOT_EOK, ret, "%s: iter del", api->name);
+
+ it = api->iter_next(it);
+
+ ret = api->iter_key(it, &key);
+ if (++pos < DEL_MAX_CNT) {
+ is_int(KNOT_EOK, ret, "%s: iter key after del", api->name);
+ is_int(pos, ((uint8_t *)key.data)[0], "%s: iter compare key '%u",
+ api->name, pos);
+ } else {
+ is_int(KNOT_EINVAL, ret, "%s: iter key after del", api->name);
+ }
+ }
+ api->iter_finish(it);
+ is_int(0, api->count(&txn), "%s: key count after", api->name);
+ }
+ api->txn_abort(&txn);
+
+ /* Clear database and recheck. */
+ ret = api->txn_begin(db, &txn, 0);
+ ret += api->clear(&txn);
+ ret += api->txn_commit(&txn);
+ is_int(0, ret, "%s: clear()", api->name);
+
+ /* Check if the database is empty. */
+ api->txn_begin(db, &txn, KNOT_DB_RDONLY);
+ db_size = api->count(&txn);
+ is_int(0, db_size, "%s: count after clear = %d", api->name, db_size);
+ api->txn_abort(&txn);
+
+ api->deinit(db);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_mm_t pool;
+ mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
+
+ char *dbid = test_mkdtemp();
+ ok(dbid != NULL, "make temporary directory");
+
+ /* Random keys. */
+ unsigned nkeys = 10000;
+ char **keys = mm_alloc(&pool, sizeof(char*) * nkeys);
+ for (unsigned i = 0; i < nkeys; ++i) {
+ keys[i] = str_key_rand(KEY_MAXLEN, &pool);
+ }
+
+ /* Sort random keys. */
+ str_key_sort(keys, nkeys);
+
+ /* Execute test set for all backends. */
+ struct knot_db_lmdb_opts lmdb_opts = KNOT_DB_LMDB_OPTS_INITIALIZER;
+ lmdb_opts.path = dbid;
+ struct knot_db_trie_opts trie_opts = KNOT_DB_TRIE_OPTS_INITIALIZER;
+ knot_db_test_set(nkeys, keys, &lmdb_opts, knot_db_lmdb_api(), &pool);
+ knot_db_test_set(nkeys, keys, &trie_opts, knot_db_trie_api(), &pool);
+
+ /* Cleanup. */
+ mp_delete(pool.ctx);
+ test_rm_rf(dbid);
+ free(dbid);
+
+ return 0;
+}
diff --git a/tests/libknot/test_descriptor.c b/tests/libknot/test_descriptor.c
new file mode 100644
index 0000000..ecb6797
--- /dev/null
+++ b/tests/libknot/test_descriptor.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/descriptor.h"
+
+#define BUF_LEN 256
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ const knot_rdata_descriptor_t *descr;
+ char name[BUF_LEN] = { 0 };
+ int ret;
+ uint16_t num;
+
+ // Get descriptor, type num to string:
+ // 1. TYPE0
+ descr = knot_get_rdata_descriptor(0);
+ ok(descr->type_name == 0, "get TYPE0 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE0 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE0 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(0, name, BUF_LEN);
+ ok(ret != -1, "get TYPE0 ret");
+ ok(strcmp(name, "TYPE0") == 0, "get TYPE0 name");
+
+ // 2. A
+ descr = knot_get_rdata_descriptor(1);
+ ok(strcmp(descr->type_name, "A") == 0, "get A descriptor name");
+ ok(descr->block_types[0] == 4,
+ "get A descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get A descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(1, name, BUF_LEN);
+ ok(ret != -1, "get A ret");
+ ok(strcmp(name, "A") == 0, "get A name");
+
+ // 3. CNAME
+ descr = knot_get_rdata_descriptor(5);
+ ok(strcmp(descr->type_name, "CNAME") == 0, "get CNAME descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ "get CNAME descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get CNAME descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(5, name, BUF_LEN);
+ ok(ret != -1, "get CNAME ret");
+ ok(strcmp(name, "CNAME") == 0, "get CNAME name");
+
+ // 4. TYPE38 (A6)
+ descr = knot_get_rdata_descriptor(38);
+ ok(descr->type_name == 0, "get TYPE38 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE38 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE38 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(38, name, BUF_LEN);
+ ok(ret != -1, "get TYPE38 ret");
+ ok(strcmp(name, "TYPE38") == 0, "get TYPE38 name");
+
+ // 5. ANY
+ descr = knot_get_rdata_descriptor(255);
+ ok(strcmp(descr->type_name, "ANY") == 0, "get ANY descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get ANY descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get ANY descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(255, name, BUF_LEN);
+ ok(ret != -1, "get ANY ret");
+ ok(strcmp(name, "ANY") == 0, "get ANY name");
+
+ // 6. TYPE65535
+ descr = knot_get_rdata_descriptor(65535);
+ ok(descr->type_name == 0, "get TYPE65535 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE65535 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE65535 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(65535, name, BUF_LEN);
+ ok(ret != -1, "get TYPE65535 ret");
+ ok(strcmp(name, "TYPE65535") == 0, "get TYPE65535 name");
+
+ // Class num to string:
+ // 7. CLASS0
+ ret = knot_rrclass_to_string(0, name, BUF_LEN);
+ ok(ret != -1, "get CLASS0 ret");
+ ok(strcmp(name, "CLASS0") == 0, "get CLASS0 name");
+
+ // 8. IN
+ ret = knot_rrclass_to_string(1, name, BUF_LEN);
+ ok(ret != -1, "get IN ret");
+ ok(strcmp(name, "IN") == 0, "get IN name");
+
+ // 9. ANY
+ ret = knot_rrclass_to_string(255, name, BUF_LEN);
+ ok(ret != -1, "get ANY ret");
+ ok(strcmp(name, "ANY") == 0, "get ANY name");
+
+ // 10. CLASS65535
+ ret = knot_rrclass_to_string(65535, name, BUF_LEN);
+ ok(ret != -1, "get CLASS65535 ret");
+ ok(strcmp(name, "CLASS65535") == 0, "get CLASS65535 name");
+
+ // String to type num:
+ // 11. A
+ ret = knot_rrtype_from_string("A", &num);
+ ok(ret != -1, "get A num ret");
+ ok(num == 1, "get A num");
+
+ // 12. a
+ ret = knot_rrtype_from_string("a", &num);
+ ok(ret != -1, "get a num ret");
+ ok(num == 1, "get a num");
+
+ // 13. AaAa
+ ret = knot_rrtype_from_string("AaAa", &num);
+ ok(ret != -1, "get AaAa num ret");
+ ok(num == 28, "get AaAa num");
+
+ // 14. ""
+ ret = knot_rrtype_from_string("", &num);
+ ok(ret == -1, "get "" num ret");
+
+ // 15. DUMMY
+ ret = knot_rrtype_from_string("DUMMY", &num);
+ ok(ret == -1, "get DUMMY num ret");
+
+ // 16. TypE33
+ ret = knot_rrtype_from_string("TypE33", &num);
+ ok(ret != -1, "get TypE33 num ret");
+ ok(num == 33, "get TypE33 num");
+
+ // 17. TYPE
+ ret = knot_rrtype_from_string("TYPE", &num);
+ ok(ret == -1, "get TYPE num ret");
+
+ // 18. TYPE0
+ ret = knot_rrtype_from_string("TYPE0", &num);
+ ok(ret != -1, "get TYPE0 num ret");
+ ok(num == 0, "get TYPE0 num");
+
+ // 19. TYPE65535
+ ret = knot_rrtype_from_string("TYPE65535", &num);
+ ok(ret != -1, "get TYPE65535 num ret");
+ ok(num == 65535, "get TYPE65535 num");
+
+ // 20. TYPE65536
+ ret = knot_rrtype_from_string("TYPE65536", &num);
+ ok(ret == -1, "get TYPE65536 num ret");
+
+ // String to class num:
+ // 21. In
+ ret = knot_rrclass_from_string("In", &num);
+ ok(ret != -1, "get In num ret");
+ ok(num == 1, "get In num");
+
+ // 22. ANY
+ ret = knot_rrclass_from_string("ANY", &num);
+ ok(ret != -1, "get ANY num ret");
+ ok(num == 255, "get ANY num");
+
+ // 23. ""
+ ret = knot_rrclass_from_string("", &num);
+ ok(ret == -1, "get "" num ret");
+
+ // 24. DUMMY
+ ret = knot_rrclass_from_string("DUMMY", &num);
+ ok(ret == -1, "get DUMMY num ret");
+
+ // 25. CLass33
+ ret = knot_rrclass_from_string("CLass33", &num);
+ ok(ret != -1, "get CLass33 num ret");
+ ok(num == 33, "get CLass33 num");
+
+ // 26. CLASS
+ ret = knot_rrclass_from_string("CLASS", &num);
+ ok(ret == -1, "get CLASS num ret");
+
+ // 27. CLASS0
+ ret = knot_rrclass_from_string("CLASS0", &num);
+ ok(ret != -1, "get CLASS0 num ret");
+ ok(num == 0, "get CLASS0 num");
+
+ // 28. CLASS65535
+ ret = knot_rrclass_from_string("CLASS65535", &num);
+ ok(ret != -1, "get CLASS65535 num ret");
+ ok(num == 65535, "get CLASS65535 num");
+
+ // 29. CLASS65536
+ ret = knot_rrclass_from_string("CLASS65536", &num);
+ ok(ret == -1, "get CLASS65536 num ret");
+
+ // Get obsolete descriptor:
+ // 30. TYPE0
+ descr = knot_get_obsolete_rdata_descriptor(0);
+ ok(descr->type_name == 0, "get TYPE0 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE0 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE0 descriptor 2. item type");
+
+ // 31. MD
+ descr = knot_get_obsolete_rdata_descriptor(3);
+ ok(strcmp(descr->type_name, "MD") == 0, "get MD descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ "get A descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get A descriptor 2. item type");
+
+ // 32. NXT
+ descr = knot_get_obsolete_rdata_descriptor(30);
+ ok(strcmp(descr->type_name, "NXT") == 0, "get NXT descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ "get CNAME descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_REMAINDER,
+ "get CNAME descriptor 2. item type");
+ ok(descr->block_types[2] == KNOT_RDATA_WF_END,
+ "get CNAME descriptor 3. item type");
+
+ // 33. TYPE38 (A6)
+ descr = knot_get_obsolete_rdata_descriptor(38);
+ ok(descr->type_name == 0, "get TYPE38 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE38 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE38 descriptor 2. item type");
+
+ // knot_rrtype_to_string invalid output buffer size
+ ret = knot_rrtype_to_string(1, NULL, 0);
+ ok(ret == -1, "knot_rrtype_to_string: invalid output buffer size");
+
+ // knot_rrclass_to_string invalid output buffer size
+ ret = knot_rrclass_to_string(1, NULL, 0);
+ ok(ret == -1, "knot_rrclass_to_string: invalid output buffer size");
+
+ // knot_rrtype_is_metatype
+ ok(knot_rrtype_is_metatype(0) == 0,
+ "rrtype is not metatype");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_SIG) != 0,
+ "rrtype is SIG");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_OPT) != 0,
+ "rrtype is OPT");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_TKEY) != 0,
+ "rrtype is TKEY");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_TSIG) != 0,
+ "rrtype is TSIG");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_IXFR) != 0,
+ "rrtype is IXFR");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_AXFR) != 0,
+ "rrtype is AXFR");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_ANY) != 0,
+ "rrtype is ANY");
+
+ // knot_rrtype_is_dnssec
+ ok(knot_rrtype_is_dnssec(0) == 0,
+ "rrtype is not DNSSEC");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_DNSKEY) != 0,
+ "rrtype is DNSKEY");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_RRSIG) != 0,
+ "rrtype is RRSIG");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC) != 0,
+ "rrtype is NSEC");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC3) != 0,
+ "rrtype is NSEC3");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC3PARAM) != 0,
+ "rrtype is NSEC3PARAM");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_CDNSKEY) != 0,
+ "rrtype is CDNSKEY");
+
+ // knot_rrtype_additional_needed
+ ok(knot_rrtype_additional_needed(0) == 0,
+ "rrtype is not additional needed");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_NS) != 0,
+ "rrtype is NS");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_MX) != 0,
+ "rrtype is MX");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_SRV) != 0,
+ "rrtype is SRV");
+
+ // knot_rrtype_should_be_lowercased
+ ok(knot_rrtype_should_be_lowercased(0) == 0,
+ "rrtype should not be lowercased");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NS) != 0,
+ "rrtype is NS");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MD) != 0,
+ "rrtype is MD");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MF) != 0,
+ "rrtype is MF");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_CNAME) != 0,
+ "rrtype is CNAME");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SOA) != 0,
+ "rrtype is SOA");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MB) != 0,
+ "rrtype is MB");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MG) != 0,
+ "rrtype is MG");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MR) != 0,
+ "rrtype is MR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_PTR) != 0,
+ "rrtype is PTR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MINFO) != 0,
+ "rrtype is MINFO");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MX) != 0,
+ "rrtype is MX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RP) != 0,
+ "rrtype is RP");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_AFSDB) != 0,
+ "rrtype is AFSDB");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RT) != 0,
+ "rrtype is RT");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SIG) != 0,
+ "rrtype is SIG");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_PX) != 0,
+ "rrtype is PX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NXT) != 0,
+ "rrtype is NXT");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NAPTR) != 0,
+ "rrtype is NAPTR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_KX) != 0,
+ "rrtype is KX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SRV) != 0,
+ "rrtype is SRV");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_DNAME) != 0,
+ "rrtype is DNAME");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RRSIG) != 0,
+ "rrtype is RRSIG");
+
+ ret = knot_opt_code_to_string(0, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "CODE0") == 0, "opt to str, code 0");
+ ret = knot_opt_code_to_string(10, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "COOKIE") == 0, "opt to str, code 10");
+ ret = knot_opt_code_to_string(65535, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "CODE65535") == 0, "opt to str, code 65535");
+
+ return 0;
+}
diff --git a/tests/libknot/test_dname.c b/tests/libknot/test_dname.c
new file mode 100644
index 0000000..1201d46
--- /dev/null
+++ b/tests/libknot/test_dname.c
@@ -0,0 +1,584 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/dname.h"
+
+/* Test dname_parse_from_wire */
+static int test_fw(size_t l, const char *w) {
+ const uint8_t *np = (const uint8_t *)w + l;
+ return knot_dname_wire_check((const uint8_t *)w, np, NULL) > 0;
+}
+
+/* Test dname to/from string operations */
+static void test_str(const char *in_str, const char *in_bin, size_t bin_len) {
+ uint8_t d1[KNOT_DNAME_MAXLEN] = "";
+ char s1[4 * KNOT_DNAME_MAXLEN] = "";
+ knot_dname_t *d2 = NULL, *aux_d = NULL;
+ char *s2 = NULL, *aux_s = NULL;
+ int ret = 0;
+
+ /* dname_from_str */
+ aux_d = knot_dname_from_str(d1, in_str, sizeof(d1));
+ ok(aux_d != NULL, "dname_from_str: %s", in_str);
+ if (aux_d == NULL) {
+ skip_block(10, "dname_from_str: %s", in_str);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d1, d1 + sizeof(d1), NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", in_str);
+
+ /* dname compare */
+ ok(memcmp(d1, in_bin, bin_len) == 0, "dname compare: %s", in_str);
+
+ /* dname_to_str */
+ aux_s = knot_dname_to_str(s1, d1, sizeof(s1));
+ ok(aux_s != NULL, "dname_to_str: %s", in_str);
+ if (aux_s == NULL) {
+ skip_block(7, "dname_to_str: %s", in_str);
+ return;
+ }
+
+ /* dname_from_str_alloc */
+ d2 = knot_dname_from_str_alloc(s1);
+ ok(d2 != NULL, "dname_from_str_alloc: %s", s1);
+ if (d2 == NULL) {
+ skip_block(6, "dname_from_str_alloc: %s", s1);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d2, d2 + bin_len, NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", s1);
+
+ /* dname compare */
+ ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s1);
+
+ /* dname_to_str_alloc */
+ s2 = knot_dname_to_str_alloc(d2);
+ knot_dname_free(d2, NULL);
+ ok(s2 != NULL, "dname_to_str_alloc: %s", s1);
+ if (s2 == NULL) {
+ skip_block(3, "dname_to_str_alloc: %s", s1);
+ return;
+ }
+
+ /* As the string representation is ambiguous, the following steps
+ * are just for comparison in wire form.
+ */
+ d2 = knot_dname_from_str_alloc(s2);
+ ok(d2 != NULL, "dname_from_str_alloc: %s", s2);
+ if (aux_d == NULL) {
+ skip_block(2, "dname_from_str_alloc: %s", s2);
+ free(s2);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d2, d2 + bin_len, NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", s2);
+
+ /* dname compare */
+ ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s2);
+
+ knot_dname_free(d2, NULL);
+ free(s2);
+}
+
+static void test_dname_lf(void)
+{
+ knot_dname_storage_t storage;
+
+ /* Maximal DNAME length */
+ const knot_dname_t *in = (uint8_t *)
+ "\x3f""iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
+ "\x3f""hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+ "\x3f""ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "\x1f""fffffffffffffffffffffffffffffff"
+ "\x0f""eeeeeeeeeeeeeee"
+ "\x07""ddddddd"
+ "\x03""ccc"
+ "\x01""b"
+ "\x00";
+ const uint8_t *ref = (uint8_t *)
+ "\xFE"
+ "b""\x00"
+ "ccc""\00"
+ "ddddddd""\x00"
+ "eeeeeeeeeeeeeee""\x00"
+ "fffffffffffffffffffffffffffffff""\x00"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg""\x00"
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh""\x00"
+ "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii""\x00";
+ uint8_t *out = knot_dname_lf(in, storage);
+ ok(out != NULL && memcmp(ref, out, KNOT_DNAME_MAXLEN) == 0,
+ "knot_dname_lf: max-length DNAME converted");
+
+ /* Zero label DNAME*/
+ in = (uint8_t *) "\x00";
+ out = knot_dname_lf(in, storage);
+ ok(out != NULL && out[0] == '\x00', "knot_dname_lf: zero-label DNAME converted");
+}
+
+static void test_dname_storage(void)
+{
+ const knot_dname_t *dname = (uint8_t *)"\x04""test";
+ size_t dname_len = knot_dname_size(dname);
+
+ knot_dname_storage_t storage;
+ size_t store_len = knot_dname_store(storage, dname);
+ size_t storage_len = knot_dname_size(storage);
+
+ ok(store_len == dname_len && storage_len == dname_len &&
+ memcmp(storage, dname, dname_len) == 0,
+ "knot_dname_storage: valid name");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_dname_t *d = NULL, *d2 = NULL;
+ const char *w = NULL, *t = NULL;
+ char *s = NULL;
+
+ /* DNAME WIRE CHECKS */
+
+ /* NULL wire */
+ ok(!test_fw(0, NULL), "parsing NULL dname");
+
+ /* empty label */
+ ok(test_fw(1, ""), "parsing empty dname");
+
+ /* incomplete dname */
+ ok(!test_fw(5, "\x08" "dddd"), "parsing incomplete wire");
+
+ /* non-fqdn */
+ ok(!test_fw(3, "\x02" "ab"), "parsing non-fqdn name");
+
+ /* label length == 63 */
+ w = "\x3f" "123456789012345678901234567890123456789012345678901234567890123";
+ ok(test_fw(1 + 63 + 1, w), "parsing label length == 63");
+
+ /* label length > 63 */
+ w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234";
+ ok(!test_fw(1 + 64 + 1, w), "parsing label length > 63");
+
+ /* label count == 127 (also maximal dname length) */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ ok(test_fw(127 * 2 + 1, w), "parsing label count == 127");
+
+ /* label count > 127 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ ok(!test_fw(128 * 2 + 1, w), "parsing label count > 127");
+
+ /* dname length > 255 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64";
+ ok(!test_fw(126 * 2 + 3 + 1, w), "parsing dname len > 255");
+
+ /* DNAME STRING CHECKS */
+
+ /* root dname */
+ test_str(".", "\x00", 1);
+
+ /* 1-char dname */
+ test_str("a.", "\x01""a", 2 + 1);
+
+ /* 1-char dname - non-fqdn */
+ test_str("a", "\x01""a", 2 + 1);
+
+ /* wildcard and asterisks */
+ test_str("*.*a.a*a.**.",
+ "\x01" "*" "\x02" "*a" "\x03" "a*a" "\x02" "**",
+ 2 + 3 + 4 + 3 + 1);
+
+ /* special label */
+ test_str("\\000\\0320\\ \\\\\\\"\\.\\@\\*.",
+ "\x09" "\x00\x20\x30\x20\x5c\x22.@*",
+ 10 + 1);
+
+ /* unescaped special characters */
+ test_str("_a.b-c./d.",
+ "\x02" "_a" "\x03" "b-c" "\x02" "/d",
+ 3 + 4 + 3 + 1);
+
+ /* all possible characters */
+ test_str("\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019",
+ "\x14" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13",
+ 22);
+ test_str("\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029\\030\\031\\032\\033\\034\\035\\036\\037\\038\\039",
+ "\x14" "\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27",
+ 22);
+ test_str("\\040\\041\\042\\043\\044\\045\\046\\047\\048\\049\\050\\051\\052\\053\\054\\055\\056\\057\\058\\059",
+ "\x14" "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b",
+ 22);
+ test_str("\\060\\061\\062\\063\\064\\065\\066\\067\\068\\069\\070\\071\\072\\073\\074\\075\\076\\077\\078\\079",
+ "\x14" "\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+ 22);
+ test_str("\\080\\081\\082\\083\\084\\085\\086\\087\\088\\089\\090\\091\\092\\093\\094\\095\\096\\097\\098\\099",
+ "\x14" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63",
+ 22);
+ test_str("\\100\\101\\102\\103\\104\\105\\106\\107\\108\\109\\110\\111\\112\\113\\114\\115\\116\\117\\118\\119",
+ "\x14" "\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77",
+ 22);
+ test_str("\\120\\121\\122\\123\\124\\125\\126\\127\\128\\129\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139",
+ "\x14" "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b",
+ 22);
+ test_str("\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149\\150\\151\\152\\153\\154\\155\\156\\157\\158\\159",
+ "\x14" "\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+ 22);
+ test_str("\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179",
+ "\x14" "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3",
+ 22);
+ test_str("\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199",
+ "\x14" "\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+ 22);
+ test_str("\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219",
+ "\x14" "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb",
+ 22);
+ test_str("\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239",
+ "\x14" "\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
+ 22);
+ test_str("\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249\\250\\251\\252\\253\\254\\255",
+ "\x10" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ 18);
+
+ /* maximal dname label length */
+ test_str("12345678901234567890123456789012345678901234567890123456789012\\063",
+ "\x3f" "12345678901234567890123456789012345678901234567890123456789012?",
+ 65);
+
+ /* maximal dname length */
+ test_str("1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "\\#234567890123456789012345678901234567890123456789012\\063",
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x35" "#234567890123456789012345678901234567890123456789012?",
+ 255);
+
+ /* NULL output, positive maxlen */
+ w = "\x02" "aa";
+ s = knot_dname_to_str(NULL, (const uint8_t *)w, 1);
+ ok(s != NULL, "dname_to_str: null dname");
+ if (s != NULL) {
+ ok(memcmp(s, "aa.", 4) == 0, "dname_to_str: null dname compare");
+ free(s);
+ } else {
+ skip("dname_to_str: null dname");
+ }
+
+ /* non-NULL output, zero maxlen */
+ char s_small[2];
+ s = knot_dname_to_str(s_small, (const uint8_t *)w, 0);
+ ok(s == NULL, "dname_to_str: non-NULL output, zero maxlen");
+
+ /* small buffer */
+ s = knot_dname_to_str(s_small, (const uint8_t *)w, 1);
+ ok(s == NULL, "dname_to_str: small buffer");
+
+ /* NULL dname */
+ s = knot_dname_to_str_alloc(NULL);
+ ok(s == NULL, "dname_to_str: null dname");
+
+ /* empty dname is considered as a root dname */
+ w = "";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: empty dname");
+ if (s != NULL) {
+ ok(memcmp(s, ".", 1) == 0, "dname_to_str: empty dname is root dname");
+ free(s);
+ } else {
+ skip("dname_to_str: empty dname");
+ }
+
+ /* incomplete dname */
+ /* ASAN: global-buffer-overflow
+ w = "\x08" "dddd";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: incomplete dname");
+ free(s);
+ */
+
+ /* non-fqdn */
+ w = "\x02" "ab";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: non-fqdn");
+ free(s);
+
+ /* label length > 63 */
+ w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: label length > 63");
+ free(s);
+
+ /* label count > 127 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: label count > 127");
+ free(s);
+
+ /* dname length > 255 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: dname length > 255");
+ free(s);
+
+ /* output overflow sanity check */
+ uint8_t in[4] = "\x02""\x00\x00""\x00";
+ for (uint16_t i = 0; i < UINT16_MAX; i++) {
+ memcpy(in + 1, &i, sizeof(i));
+ for (int j = 3; j < 8; j++) {
+ char tmp[j];
+ char *out_static = knot_dname_to_str(tmp, in, sizeof(tmp));
+ char *out_dynamic = knot_dname_to_str_alloc(in);
+ if (out_dynamic == NULL) {
+ ok(out_dynamic != NULL, "dname_to_str_alloc: invalid input");
+ } else if (strlen(out_dynamic) < sizeof(tmp) - 1 &&
+ out_static == NULL) {
+ ok(out_static != NULL, "dname_to_str: invalid input");
+ }
+ free(out_dynamic);
+ }
+ }
+
+ /* NULL output, positive maxlen */
+ s = "aa.";
+ d = knot_dname_from_str(NULL, s, 1);
+ ok(s != NULL, "dname_from_str: null name");
+ if (s != NULL) {
+ ok(memcmp(d, "\x02" "aa", 4) == 0, "dname_from_str: null name compare");
+ free(d);
+ } else {
+ skip("dname_from_str: null name");
+ }
+
+ /* non-NULL output, zero maxlen */
+ uint8_t d_small[2];
+ d = knot_dname_from_str(d_small, s, 0);
+ ok(d == NULL, "dname_from_str: non-NULL output, zero maxlen");
+
+ /* small buffer */
+ d = knot_dname_from_str(d_small, s, 1);
+ ok(d == NULL, "dname_from_str: small buffer");
+
+ /* NULL string */
+ d = knot_dname_from_str_alloc(NULL);
+ ok(d == NULL, "dname_from_str: null string");
+
+ /* empty string */
+ t = "";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: empty string");
+
+ /* empty label */
+ t = "..";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: empty label");
+
+ /* leading dot */
+ t = ".a";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: leading dot");
+
+ /* incomplete decimal notation I */
+ t = "\\1";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: incomplete decimal I");
+
+ /* incomplete decimal notation II */
+ t = "\\12";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: incomplete decimal II");
+
+ /* invalid decimal notation I */
+ t = "\\256";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid decimal I");
+
+ /* invalid decimal notation II */
+ t = "\\2x6";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid decimal II");
+
+ /* invalid escape notation */
+ t = "\\2";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid escape");
+
+ /* label length > 63 I */
+ t = "1234567890123456789012345678901234567890123456789012345678901234";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 I");
+
+ /* label length > 63 II */
+ t = "123456789012345678901234567890123456789012345678901234567890123\\?";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 II");
+
+ /* label length > 63 III */
+ t = "123456789012345678901234567890123456789012345678901234567890123\\063";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 III");
+
+ /* dname length > 255 */
+ t = "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "123456789012345678901234567890123456789012345678901234.",
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: dname length > 255");
+
+ /* DNAME SUBDOMAIN CHECKS */
+
+ /* equal name is subdomain */
+ t = "ab.cd.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ t = "ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) == 0, "dname_subdomain: equal name");
+ knot_dname_free(d, NULL);
+
+ /* true subdomain */
+ t = "0.ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) == 1, "dname_subdomain: true subdomain");
+ knot_dname_free(d, NULL);
+
+ /* not subdomain */
+ t = "cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) < 0, "dname_subdomain: not subdomain");
+ knot_dname_free(d, NULL);
+
+ /* root subdomain */
+ t = ".";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d2, d) == 3, "dname_subdomain: root subdomain");
+ knot_dname_free(d, NULL);
+ knot_dname_free(d2, NULL);
+
+ /* DNAME EQUALITY CHECKS */
+
+ t = "ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_equal(d, d), "dname_is_equal: equal names");
+
+ t = "ab.cd.fe";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: same label count");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) < len(d2)");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.ef.gh";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) > len(d2)");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.efe";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label longer");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.e";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label shorter");
+ knot_dname_free(d2, NULL);
+
+ knot_dname_free(d, NULL);
+
+ /* OTHER CHECKS */
+
+ test_dname_lf();
+
+ test_dname_storage();
+
+ return 0;
+}
diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c
new file mode 100644
index 0000000..0da882c
--- /dev/null
+++ b/tests/libknot/test_edns.c
@@ -0,0 +1,484 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <assert.h>
+#include "libknot/libknot.h"
+#include "libknot/rrtype/opt.h"
+#include "libknot/descriptor.h"
+#include "libknot/wire.h"
+#include "contrib/sockaddr.h"
+
+static const uint16_t E_MAX_PLD = 10000;
+static const uint16_t E_MAX_PLD2 = 20000;
+static const uint8_t E_VERSION = 1;
+static const uint8_t E_VERSION2 = 2;
+static const uint8_t E_RCODE = 0;
+static const uint8_t E_RCODE2 = 200;
+
+static const char *E_NSID_STR = "FooBar";
+static const uint16_t E_NSID_LEN = 6;
+
+#define E_NSID_SIZE (4 + E_NSID_LEN)
+
+static const uint16_t E_OPT3_CODE = 15;
+static const char *E_OPT3_FAKE_DATA = "Not used";
+static const char *E_OPT3_DATA = NULL;
+static const uint16_t E_OPT3_LEN = 0;
+static const uint16_t E_OPT3_FAKE_LEN = 8;
+
+#define E_OPT3_SIZE (4 + E_OPT3_LEN)
+
+static const uint16_t E_OPT4_CODE = 30;
+static const char *E_OPT4_DATA = NULL;
+static const uint16_t E_OPT4_LEN = 0;
+
+#define E_OPT4_SIZE (4 + E_OPT4_LEN)
+
+enum offsets {
+ /*! \brief Offset of Extended RCODE in wire order of TTL. */
+ OFFSET_ERCODE = 0,
+ /*! \brief Offset of Version in wire order of TTL. */
+ OFFSET_VER = 1,
+ /*! \brief Offset of Flags in wire order of TTL. */
+ OFFSET_FLAGS = 2,
+ /*! \brief Offset of OPTION code in one OPTION in RDATA. */
+ OFFSET_OPT_CODE = 0,
+ /*! \brief Offset of OPTION size in one OPTION in RDATA. */
+ OFFSET_OPT_SIZE = 2,
+ /*! \brief Offset of OPTION data in one OPTION in RDATA. */
+ OFFSET_OPT_DATA = 4
+};
+
+static const uint16_t DO_FLAG = (uint16_t)1 << 15;
+
+static void check_ttl(knot_rrset_t *rrset, uint8_t ext_rcode, uint8_t ver,
+ uint16_t flags, char *msg)
+{
+ if (rrset == NULL) {
+ return;
+ }
+
+ /* TTL should be stored in machine byte order.
+ We need network byte order to compare its parts. */
+ uint8_t ttl_wire[4] = { 0, 0, 0, 0 };
+ knot_wire_write_u32(ttl_wire, rrset->ttl);
+
+ /* Convert Flags from EDNS parameters to wire format for comparison. */
+ uint8_t flags_wire[2] = { 0, 0 };
+ knot_wire_write_u16(flags_wire, flags);
+
+ /* TTL = Ext RCODE + Version + Flags */
+ bool check = (ttl_wire[OFFSET_ERCODE] == ext_rcode);
+ ok(check, "%s: extended RCODE", msg);
+
+ check = (ttl_wire[OFFSET_VER] == ver);
+ ok(check, "%s: version", msg);
+
+ check = (memcmp(flags_wire, ttl_wire + OFFSET_FLAGS, 2) == 0);
+ ok(check, "%s: flags", msg);
+}
+
+static void check_option(knot_rdata_t *rdata, uint16_t opt_code,
+ uint16_t opt_len, uint8_t *opt_data, char *msg)
+{
+ assert(rdata != NULL);
+
+ uint8_t *data = rdata->data;
+ uint16_t data_len = rdata->len;
+
+ /* Check RDLENGTH according to given data length. */
+ bool check = (data_len >= 4 + opt_len);
+ ok(check, "%s: RDLENGTH (%u)", msg, data_len);
+
+ /* Find the desired option. */
+ bool found = false;
+ int pos = 0;
+ while (pos <= data_len - 4) {
+ uint16_t code = knot_wire_read_u16(data + pos + OFFSET_OPT_CODE);
+ if (code == opt_code) {
+ found = true;
+ break;
+ }
+ uint16_t len = knot_wire_read_u16(data + pos + OFFSET_OPT_SIZE);
+ pos += 4 + len;
+ }
+
+ /* Check that the option is present. */
+ ok(found, "%s: find OPTION %u in OPT RR", msg, opt_code);
+
+ /* Check that the first OPTION's size si the size of the option data. */
+ uint16_t opt_size = knot_wire_read_u16(data + pos + OFFSET_OPT_SIZE);
+ check = (opt_size == opt_len);
+ ok(check, "%s: OPTION data size", msg);
+
+ /* Check the actual NSID data. */
+ check = (opt_data == 0 || memcmp(data + pos + OFFSET_OPT_DATA, opt_data, opt_len) == 0);
+ ok(check, "%s: OPTION data", msg);
+}
+
+static void check_header(knot_rrset_t *opt_rr, uint16_t payload, uint8_t ver,
+ uint16_t flags, uint8_t ext_rcode, char *msg)
+{
+ assert(opt_rr != NULL);
+ bool check;
+
+ /* Check values in OPT RR by hand. */
+ /* CLASS == Max UDP payload */
+ check = (opt_rr->rclass == payload);
+ ok(check, "%s: max payload", msg);
+
+ /* The OPT RR should have exactly one RDATA. */
+ check = (opt_rr->rrs.count == 1);
+ ok(check, "%s: RR count == 1", msg);
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ check = (rdata != NULL);
+ ok(check, "%s: RDATA exists", msg);
+
+ check_ttl(opt_rr, ext_rcode, ver, flags, msg);
+}
+
+static void test_getters(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+
+ /* These values should be set from the setters test:
+ * Max UDP payload: E_MAX_PLD2
+ * Version: E_VERSION2
+ * RCODE: E_RCODE2
+ * Flags: E_FLAGS | KNOT_EDNS_FLAG_DO
+ * OPTIONs: 1) KNOT_EDNS_OPTION_NSID, E_NSID_LEN, E_NSID_STR
+ * 2) E_OPT3_CODE, 0, 0
+ * 3) E_OPT4_CODE, 0, 0
+ */
+
+ /* Payload */
+ bool check = (knot_edns_get_payload(opt_rr) == E_MAX_PLD2);
+ ok(check, "OPT RR getters: payload");
+
+ /* Extended RCODE */
+ check = (knot_edns_get_ext_rcode(opt_rr) == E_RCODE2);
+ ok(check, "OPT RR getters: extended RCODE");
+
+ /* Extended RCODE */
+ check = (knot_edns_get_version(opt_rr) == E_VERSION2);
+ ok(check, "OPT RR getters: version");
+
+ /* DO bit */
+ check = knot_edns_do(opt_rr);
+ ok(check, "OPT RR getters: DO bit check");
+
+ /* Wire size */
+ size_t total_size = KNOT_EDNS_MIN_SIZE
+ + E_NSID_SIZE + E_OPT3_SIZE + E_OPT4_SIZE;
+ size_t actual_size = knot_edns_wire_size(opt_rr);
+ check = actual_size == total_size;
+ ok(check, "OPT RR getters: wire size (expected: %zu, actual: %zu)",
+ total_size, actual_size);
+
+ /* NSID */
+ check = knot_edns_get_option(opt_rr, KNOT_EDNS_OPTION_NSID) != NULL;
+ ok(check, "OPT RR getters: NSID check");
+
+ /* Other OPTIONs */
+ check = knot_edns_get_option(opt_rr, E_OPT3_CODE) != NULL;
+ ok(check, "OPT RR getters: empty option 1");
+
+ check = knot_edns_get_option(opt_rr, E_OPT4_CODE) != NULL;
+ ok(check, "OPT RR getters: empty option 2");
+
+ uint16_t code = knot_edns_opt_get_code((const uint8_t *)"\x00\x0a" "\x00\x00");
+ ok(code == KNOT_EDNS_OPTION_COOKIE, "OPT RR getters: EDNS OPT code");
+}
+
+static void test_setters(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+
+ /* Header-related setters. */
+ knot_edns_set_payload(opt_rr, E_MAX_PLD2);
+ knot_edns_set_ext_rcode(opt_rr, E_RCODE2);
+ knot_edns_set_version(opt_rr, E_VERSION2);
+ knot_edns_set_do(opt_rr);
+
+ check_header(opt_rr, E_MAX_PLD2, E_VERSION2, DO_FLAG, E_RCODE2,
+ "OPT RR setters");
+
+ /* OPTION(RDATA)-related setters. */
+
+ /* Proper option. */
+ int ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
+ E_NSID_LEN, (uint8_t *)E_NSID_STR, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add option with data (ret = %s)",
+ knot_strerror(ret));
+
+ /* Wrong argument: no OPT RR. */
+ ret = knot_edns_add_option(NULL, E_OPT3_CODE, E_OPT3_FAKE_LEN,
+ (uint8_t *)E_OPT3_FAKE_DATA, NULL);
+ is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (rr == NULL) "
+ "(ret = %s)", knot_strerror(ret));
+
+ /* Wrong argument: option length != 0 && data == NULL. */
+ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_FAKE_LEN, NULL,
+ NULL);
+ is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (data == NULL, "
+ "len != 0) (ret = %s)", knot_strerror(ret));
+
+ /* Empty OPTION (length 0, data != NULL). */
+ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_LEN,
+ (uint8_t *)E_OPT3_FAKE_DATA, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 1 (ret = %s)",
+ knot_strerror(ret));
+
+ /* Empty OPTION (length 0, data == NULL). */
+ ret = knot_edns_add_option(opt_rr, E_OPT4_CODE, E_OPT4_LEN,
+ (uint8_t *)E_OPT4_DATA, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 2 (ret = %s)",
+ knot_strerror(ret));
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ ok(rdata != NULL, "OPT RR setters: non-empty RDATA");
+
+ /* Check proper option */
+ check_option(rdata, KNOT_EDNS_OPTION_NSID, E_NSID_LEN,
+ (uint8_t *)E_NSID_STR, "OPT RR setters (proper option)");
+
+ /* Check empty option 1 */
+ check_option(rdata, E_OPT3_CODE, E_OPT3_LEN,
+ (uint8_t *)E_OPT3_DATA, "OPT RR setters (empty option 1)");
+
+ /* Check empty option 2 */
+ check_option(rdata, E_OPT4_CODE, E_OPT4_LEN,
+ (uint8_t *)E_OPT4_DATA, "OPT RR setters (empty option 2)");
+}
+
+static void test_alignment(void)
+{
+ int ret;
+
+ ret = knot_edns_alignment_size(1, 1, 1);
+ ok(ret == -1, "no alignment");
+
+ ret = knot_edns_alignment_size(1, 1, 2);
+ ok(ret == -1, "no alignment");
+
+ ret = knot_edns_alignment_size(1, 1, 3);
+ ok(ret == (6 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+
+ ret = knot_edns_alignment_size(1, 1, 4);
+ ok(ret == (8 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+
+ ret = knot_edns_alignment_size(1, 1, 512);
+ ok(ret == (512 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+}
+
+static void test_keepalive(void)
+{
+ typedef struct {
+ char *msg;
+ uint16_t opt_len;
+ char *opt;
+ uint16_t val;
+ } test_t;
+
+ // OK tests.
+
+ static const test_t TESTS[] = {
+ { "ok 0", 0, "", 0 },
+ { "ok 1", 2, "\x00\x01", 1 },
+ { "ok 258", 2, "\x01\x02", 258 },
+ { "ok 65535", 2, "\xFF\xFF", 65535 },
+ { NULL }
+ };
+
+ for (const test_t *t = TESTS; t->msg != NULL; t++) {
+ uint16_t len = knot_edns_keepalive_size(t->val);
+ ok(len == t->opt_len, "%s: %s, size", __func__, t->msg);
+
+ uint8_t wire[8] = { 0 };
+ int ret = knot_edns_keepalive_write(wire, sizeof(wire), t->val);
+ is_int(KNOT_EOK, ret, "%s: %s, write, return", __func__, t->msg);
+ ok(memcmp(wire, t->opt, t->opt_len) == 0, "%s: %s, write, value",
+ __func__, t->msg);
+
+ uint16_t timeout = 0;
+ ret = knot_edns_keepalive_parse(&timeout, (uint8_t *)t->opt, t->opt_len);
+ is_int(KNOT_EOK, ret, "%s: %s, parse, return", __func__, t->msg);
+ ok(timeout == t->val, "%s: %s, parse, value", __func__, t->msg);
+ }
+
+ // Error tests.
+
+ uint8_t wire[8] = { 0 };
+ ok(knot_edns_keepalive_write(NULL, 0, 0) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_keepalive_write(wire, 1, 1) == KNOT_ESPACE,
+ "%s: write, no room", __func__);
+
+ uint16_t timeout = 0;
+ ok(knot_edns_keepalive_parse(NULL, (const uint8_t *)"", 0) == KNOT_EINVAL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_keepalive_parse(&timeout, NULL, 0) == KNOT_EINVAL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_keepalive_parse(&timeout, (const uint8_t *)"\x01", 1) == KNOT_EMALF,
+ "%s: parse, malformed", __func__);
+}
+
+static void test_chain(void)
+{
+ typedef struct {
+ char *msg;
+ uint16_t opt_len;
+ knot_dname_t *dname;
+ } test_t;
+
+ // OK tests.
+
+ static const test_t TESTS[] = {
+ { ".", 1, (knot_dname_t *)"" },
+ { "a.", 3, (knot_dname_t *)"\x01" "a" },
+ { NULL }
+ };
+
+ for (const test_t *t = TESTS; t->msg != NULL; t++) {
+ uint16_t len = knot_edns_chain_size(t->dname);
+ ok(len == t->opt_len, "%s: dname %s, size", __func__, t->msg);
+
+ uint8_t wire[8] = { 0 };
+ int ret = knot_edns_chain_write(wire, sizeof(wire), t->dname);
+ is_int(KNOT_EOK, ret, "%s: dname %s, write, return", __func__, t->msg);
+ ok(memcmp(wire, t->dname, t->opt_len) == 0, "%s: dname %s, write, value",
+ __func__, t->msg);
+
+ knot_dname_t *dname = NULL;
+ ret = knot_edns_chain_parse(&dname, (uint8_t *)t->dname, t->opt_len, NULL);
+ is_int(KNOT_EOK, ret, "%s: dname %s, parse, return", __func__, t->msg);
+ ok(knot_dname_is_equal(dname, t->dname), "%s: dname %s, parse, value",
+ __func__, t->msg);
+ knot_dname_free(dname, NULL);
+ }
+
+ // Error tests.
+
+ ok(knot_edns_chain_size(NULL) == 0, "%s: size, NULL", __func__);
+
+ uint8_t wire[8] = { 0 };
+ ok(knot_edns_chain_write(NULL, 0, wire) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_chain_write(wire, 0, NULL) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_chain_write(wire, 0, (const knot_dname_t *)"") == KNOT_ESPACE,
+ "%s: write, no room", __func__);
+
+ knot_dname_t *dname = NULL;
+ ok(knot_edns_chain_parse(NULL, wire, 0, NULL) == KNOT_EINVAL && dname == NULL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_chain_parse(&dname, NULL, 0, NULL) == KNOT_EINVAL && dname == NULL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_chain_parse(&dname, (const uint8_t *)"\x01", 1, NULL) == KNOT_EMALF &&
+ dname == NULL, "%s: parse, malformed", __func__);
+}
+
+static void check_cookie_parse(const char *opt, knot_edns_cookie_t *cc,
+ knot_edns_cookie_t *sc, int code, const char *msg)
+{
+ const uint8_t *data = NULL;
+ uint16_t data_len = 0;
+ if (opt != NULL) {
+ data = knot_edns_opt_get_data((uint8_t *)opt);
+ data_len = knot_edns_opt_get_length((uint8_t *)opt);
+ }
+
+ int ret = knot_edns_cookie_parse(cc, sc, data, data_len);
+ is_int(code, ret, "cookie parse ret: %s", msg);
+}
+
+static void ok_cookie_check(const char *opt, knot_edns_cookie_t *cc,
+ knot_edns_cookie_t *sc, uint16_t cc_len, uint16_t sc_len,
+ const char *msg)
+{
+ check_cookie_parse(opt, cc, sc, KNOT_EOK, msg);
+
+ is_int(cc->len, cc_len, "cookie parse cc len: %s", msg);
+ is_int(sc->len, sc_len, "cookie parse cc len: %s", msg);
+
+ uint16_t size = knot_edns_cookie_size(cc, sc);
+ is_int(size, cc_len + sc_len, "cookie len: %s", msg);
+
+ uint8_t buf[64];
+ int ret = knot_edns_cookie_write(buf, sizeof(buf), cc, sc);
+ is_int(KNOT_EOK, ret, "cookie write ret: %s", msg);
+}
+
+static void test_cookie(void)
+{
+ const char *good[] = {
+ "\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */
+ "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */
+ "\x00\x0a" "\x00\x28" "\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" /* 32 octets long server cookie. */
+ };
+
+ const char *bad[] = {
+ "\x00\x0a" "\x00\x00", /* Zero length cookie. */
+ "\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */
+ "\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */
+ "\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */
+ "\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */
+ "\x00\x0a" "\x00\x29" "\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", /* Long server cookie. */
+ };
+
+ knot_edns_cookie_t cc, sc;
+
+ ok_cookie_check(good[0], &cc, &sc, 8, 0, "good cookie 0");
+ ok_cookie_check(good[1], &cc, &sc, 8, 8, "good cookie 1");
+ ok_cookie_check(good[2], &cc, &sc, 8, 32, "good cookie 2");
+
+ check_cookie_parse(NULL, &cc, &sc, KNOT_EINVAL, "no data");
+ check_cookie_parse(good[0], NULL, &sc, KNOT_EINVAL, "no client cookie");
+ check_cookie_parse(good[1], &cc, NULL, KNOT_EINVAL, "no server cookie");
+
+ check_cookie_parse(bad[0], &cc, &sc, KNOT_EMALF, "bad cookie 0");
+ check_cookie_parse(bad[1], &cc, &sc, KNOT_EMALF, "bad cookie 1");
+ check_cookie_parse(bad[2], &cc, &sc, KNOT_EMALF, "bad cookie 2");
+ check_cookie_parse(bad[3], &cc, &sc, KNOT_EMALF, "bad cookie 3");
+ check_cookie_parse(bad[4], &cc, &sc, KNOT_EMALF, "bad cookie 4");
+ check_cookie_parse(bad[5], &cc, &sc, KNOT_EMALF, "bad cookie 5");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_rrset_t opt_rr;
+ int ret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR: init");
+
+ /* Check initialized values (no NSID yet). */
+ check_header(&opt_rr, E_MAX_PLD, E_VERSION, 0, E_RCODE, "OPT RR: check header");
+
+ test_setters(&opt_rr);
+ test_getters(&opt_rr);
+ test_alignment();
+ test_keepalive();
+ test_chain();
+ test_cookie();
+
+ knot_rrset_clear(&opt_rr, NULL);
+
+ return 0;
+}
diff --git a/tests/libknot/test_edns_ecs.c b/tests/libknot/test_edns_ecs.c
new file mode 100644
index 0000000..6733339
--- /dev/null
+++ b/tests/libknot/test_edns_ecs.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+#include "libknot/rrtype/opt.h"
+
+#define GARBAGE_BYTE 0xdb
+
+static void test_size(void)
+{
+ struct test {
+ const char *msg;
+ size_t expected;
+ knot_edns_client_subnet_t ecs;
+ };
+
+ static struct test const TESTS[] = {
+ // invalid
+ { "zero family", 0, { 0 } },
+ { "zero family & source", 0, { 0, 1 } },
+ { "unknown family", 0, { 42, 0 } },
+ { "unknown family & source", 0, { 42, 1 } },
+ // IPv4 bit ops
+ { "IPv4, zero source", 4, { 1 } },
+ { "IPv4, 7 bits in last byte", 7, { 1, 23 } },
+ { "IPv4, 8 bits in last byte", 7, { 1, 24 } },
+ { "IPv4, 1 bit in last byte", 8, { 1, 25 } },
+ // IPv6 bit ops
+ { "IPv6, zero source", 4, { 2 } },
+ { "IPv6, 7 bits in last byte", 19, { 2, 113 } },
+ { "IPv6, 8 bits in last byte", 19, { 2, 120 } },
+ { "IPv6, 1 bit in last byte", 20, { 2, 121 } },
+ // sources
+ { "IPv4, source < max", 8, { 1, 31 } },
+ { "IPv4, source = max", 8, { 1, 32 } },
+ { "IPv4, source > max", 0, { 1, 33 } },
+ // scopes
+ { "IPv6, scope < source", 12, { 2, 64, 48 } },
+ { "IPv6, scope = source", 20, { 2, 128, 128 } },
+ { "IPv6, scope > max", 0, { 2, 128, 129 } },
+ { NULL }
+ };
+
+ is_int(0, knot_edns_client_subnet_size(NULL), "%s: null", __func__);
+
+ for (struct test const *t = TESTS; t->msg != NULL; t++) {
+ int r = knot_edns_client_subnet_size(&t->ecs);
+ is_int(t->expected, r, "%s: %s", __func__, t->msg);
+ }
+}
+
+struct test_io {
+ const char *msg;
+ int expected;
+ size_t option_len;
+ const char *option;
+ knot_edns_client_subnet_t ecs;
+};
+
+static void test_write(void)
+{
+ static struct test_io const TESTS[] = {
+ // invalid
+ { "unset family", KNOT_EINVAL, 0, NULL, { 0 } },
+ { "invalid family", KNOT_EINVAL, 0, NULL, { 3 } },
+ { "small buffer", KNOT_ESPACE, 4, NULL, { 1, 1 } },
+ // IPv4 prefix
+ { "IPv4, zero source", KNOT_EOK, 4, "\x00\x01\x00\x00", { 1 } },
+ { "IPv4, 7 bits in LSB", KNOT_EOK, 6, "\x00\x01\x0f\x00\xff\xfe", { 1, 15, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, 8 bits in LSB", KNOT_EOK, 6, "\x00\x01\x10\x00\xff\xff", { 1, 16, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, 7, "\x00\x01\x11\x00\xff\xff\x80", { 1, 17, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, source = max", KNOT_EOK, 8, "\x00\x01\x20\x00\xaa\xbb\xcc\xdd", { 1, 32, 0, "\xaa\xbb\xcc\xdd" } },
+ { "IPv6, source > max", KNOT_EINVAL, 0, NULL, { 2, 129 } },
+ // IPv6 scope
+ { "IPv6, scope < source", KNOT_EOK, 6, "\x00\x02\x10\x0e\xff\xff", { 2, 16, 14, "\xff\xff\xff\xff" } },
+ { "IPv6, scope = source", KNOT_EOK, 6, "\x00\x02\x08\x08\xff", { 2, 8, 8, "\xff\xff\xff\xff" } },
+ { "IPv6, scope > max", KNOT_EINVAL, 0, NULL, { 2, 128, 129 } },
+ // other
+ { "larger buffer", KNOT_EOK, 7, "\x00\x01\x10\x0e\xff\xff\x00", { 1, 16, 14, "\xff\xff\xff\xff" } },
+ { NULL }
+ };
+
+ for (struct test_io const *t = TESTS; t->msg != NULL; t++) {
+ uint8_t option[64];
+ assert(sizeof(option) >= t->option_len);
+ memset(option, GARBAGE_BYTE, sizeof(option));
+
+ int r = knot_edns_client_subnet_write(option, t->option_len, &t->ecs);
+ ok(r == t->expected &&
+ (t->expected != KNOT_EOK || memcmp(option, t->option, t->option_len) == 0),
+ "%s: %s", __func__, t->msg);
+ }
+}
+
+static void test_parse(void)
+{
+ static struct test_io const TESTS[] = {
+ // invalid
+ { "null", KNOT_EINVAL, 0, NULL },
+ { "empty buffer", KNOT_EMALF, 0, "" },
+ { "incomplete header", KNOT_EMALF, 3, "\x00\x01\x00" },
+ { "incomplete source", KNOT_EMALF, 5, "\x00\x0a\x00\x00\xff\xff" },
+ { "zero family", KNOT_EMALF, 4, "\x00\x00\x00\x00" },
+ { "unknown family", KNOT_EMALF, 4, "\x00\x03\x00\x00" },
+ // IPv4 prefix
+ { "IPv4, zero source", KNOT_EOK, 4, "\x00\x01\x00\x00", { 1 } },
+ { "IPv4, 7 bits in LSB", KNOT_EOK, 6, "\x00\x01\x0f\x00\xff\xfe", { 1, 15, 0, "\xff\xfe" } },
+ { "IPv4, 9 bits in LSB", KNOT_EOK, 6, "\x00\x01\x10\x00\xff\xff", { 1, 16, 0, "\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, 7, "\x00\x01\x11\x00\xff\xff\x80", { 1, 17, 0, "\xff\xff\x80" } },
+ { "IPv4, source = max", KNOT_EOK, 8, "\x00\x01\x20\x00\xaa\xbb\xcc\xdd", { 1, 32, 0, "\xaa\xbb\xcc\xdd" } },
+ { "IPv4, dirty source", KNOT_EOK, 8, "\x00\x01\x0b\x00\xff\xff\xff\xff", { 1, 11, 0, "\xff\xe0" } },
+ { "IPv4, source > max", KNOT_EMALF, 9, "\x00\x01\x21\x00\xaa\xbb\xcc\xdd\xee" },
+ // IPv6 scope
+ { "IPv6 scope < source", KNOT_EOK, 5, "\x00\x02\x07\x05\xff", { 2, 7, 5, "\xfe" } },
+ { "IPv6 scope = source", KNOT_EOK, 5, "\x00\x02\x06\x06\xff", { 2, 6, 6, "\xfc" } },
+ { "IPv6 scope > max", KNOT_EMALF, 5, "\x00\x02\x06\x81\xff" },
+ // extra buffer size
+ { "extra space", KNOT_EOK, 6, "\x00\x01\x00\x00\xff\x00", { 1 } },
+ { "extra space", KNOT_EOK, 6, "\x00\x01\x01\x00\xff\x00", { 1, 1, 0, "\x80" } },
+ { NULL }
+ };
+
+ for (struct test_io const *t = TESTS; t->msg != NULL; t++) {
+ knot_edns_client_subnet_t ecs = { 0 };
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+
+ int r = knot_edns_client_subnet_parse(&ecs, (uint8_t *)t->option, t->option_len);
+ ok(r == t->expected &&
+ (t->expected != KNOT_EOK || memcmp(&ecs, &t->ecs, sizeof(ecs)) == 0),
+ "%s: %s", __func__, t->msg);
+ }
+}
+
+static struct sockaddr_storage addr_init(const char *addr)
+{
+ struct sockaddr_storage sa = { 0 };
+
+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST };
+ struct addrinfo *info = NULL;
+ int r = getaddrinfo(addr, NULL, &hints, &info);
+ (void)r;
+ assert(r == 0);
+ memcpy(&sa, info->ai_addr, info->ai_addrlen);
+ freeaddrinfo(info);
+
+ return sa;
+}
+
+static void test_set_address(void)
+{
+ int r;
+ knot_edns_client_subnet_t ecs = { 0 };
+ struct sockaddr_storage ss = { 0 };
+
+ r = knot_edns_client_subnet_set_addr(NULL, &ss);
+ is_int(KNOT_EINVAL, r, "%s: missing ECS", __func__);
+
+ r = knot_edns_client_subnet_set_addr(&ecs, NULL);
+ is_int(KNOT_EINVAL, r, "%s: missing address", __func__);
+
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+ ss = addr_init("198.51.100.42");
+ assert(ss.ss_family == AF_INET);
+ const uint8_t raw4[4] = { 198, 51, 100, 42 };
+
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss);
+ ok(r == KNOT_EOK &&
+ ecs.family == 1 && ecs.source_len == 32 && ecs.scope_len == 0 &&
+ memcmp(ecs.address, raw4, sizeof(raw4)) == 0,
+ "%s: IPv4", __func__);
+
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+ ss = addr_init("2001:db8::dead:beef");
+ assert(ss.ss_family == AF_INET6);
+ const uint8_t raw6[16] = "\x20\x01\x0d\xb8\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\xde\xad\xbe\xef";
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss);
+ ok(r == KNOT_EOK &&
+ ecs.family == 2 && ecs.source_len == 128 && ecs.scope_len == 0 &&
+ memcmp(ecs.address, raw6, sizeof(raw6)) == 0,
+ "%s: IPv6", __func__);
+
+ const struct sockaddr_storage ss_unix = { .ss_family = AF_UNIX };
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss_unix);
+ is_int(KNOT_ENOTSUP, r, "%s: UNIX not supported", __func__);
+}
+
+static bool sockaddr_eq(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
+{
+ return sockaddr_cmp((struct sockaddr *)a, (struct sockaddr *)b) == 0;
+}
+
+static void test_get_address(void)
+{
+ struct test {
+ const char *msg;
+ int expected;
+ const char *addr_str;
+ knot_edns_client_subnet_t ecs;
+ };
+
+ static struct test const TESTS[] = {
+ // invalid
+ { "unset family", KNOT_ENOTSUP, NULL, { 0 } },
+ { "unknown family", KNOT_ENOTSUP, NULL, { 3 } },
+ // zero source
+ { "IPv4, any", KNOT_EOK, "0.0.0.0", { 1 } },
+ { "IPv6, any", KNOT_EOK, "::0" , { 2 } },
+ // IPv4
+ { "IPv4, 7 bits in LSB", KNOT_EOK, "198.50.0.0", { 1, 15, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, 8 bits in LSB", KNOT_EOK, "198.51.0.0", { 1, 16, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, "198.51.128.0", { 1, 17, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, source = max", KNOT_EOK, "198.51.128.1", { 1, 32, 0, "\xc6\x33\x80\x01" } },
+ // IPv6
+ { "IPv6, 7 bits in LSB", KNOT_EOK, "2001:db8:200::", { 2, 39, 0, "\x20\x01\x0d\xb8\x03\xff" } },
+ { "IPv6, 8 bits in LSB", KNOT_EOK, "2001:db8:100::", { 2, 40, 0, "\x20\x01\x0d\xb8\x01\xff" } },
+ { "IPv6, 1 bit in LSB", KNOT_EOK, "2001:db8:180::", { 2, 41, 0, "\x20\x01\x0d\xb8\x01\xff" } },
+ { "IPv6, source = max", KNOT_EOK, "2001:db8::1", { 2, 128, 0, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" } },
+ { NULL }
+ };
+
+ for (struct test const *t = TESTS; t->msg != NULL; t++) {
+ struct sockaddr_storage result = { 0 };
+ int r = knot_edns_client_subnet_get_addr(&result, &t->ecs);
+ bool valid = false;
+
+ if (t->expected == KNOT_EOK) {
+ struct sockaddr_storage addr = addr_init(t->addr_str);
+ assert(addr.ss_family != AF_UNSPEC);
+ valid = (r == t->expected && sockaddr_eq(&result, &addr));
+ } else {
+ valid = (r == t->expected);
+ }
+
+ ok(valid, "%s: %s", __func__, t->msg);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_size();
+ test_write();
+ test_parse();
+ test_set_address();
+ test_get_address();
+
+ return 0;
+}
diff --git a/tests/libknot/test_endian.c b/tests/libknot/test_endian.c
new file mode 100644
index 0000000..ef3fb1e
--- /dev/null
+++ b/tests/libknot/test_endian.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/endian.h"
+
+int main(int argc, char *argv[])
+{
+ plan(12);
+
+ typedef union {
+ uint16_t value;
+ uint8_t array[2];
+ } trafo16_t;
+
+ const uint16_t host16 = 0x0102;
+ const trafo16_t be16 = { .array = { 0x01, 0x02 } };
+ const trafo16_t le16 = { .array = { 0x02, 0x01 } };
+ ok(htobe16(host16) == be16.value, "htobe16");
+ ok(htole16(host16) == le16.value, "htole16");
+ ok(be16toh(be16.value) == host16, "be16toh");
+ ok(le16toh(le16.value) == host16, "le16toh");
+
+ typedef union {
+ uint32_t value;
+ uint8_t array[4];
+ } trafo32_t;
+
+ const uint32_t host32 = 0x01020304;
+ const trafo32_t be32 = { .array = { 0x01, 0x02, 0x03, 0x04 } };
+ const trafo32_t le32 = { .array = { 0x04, 0x03, 0x02, 0x01 } };
+ ok(htobe32(host32) == be32.value, "htobe32");
+ ok(htole32(host32) == le32.value, "htole32");
+ ok(be32toh(be32.value) == host32, "be32toh");
+ ok(le32toh(le32.value) == host32, "le32toh");
+
+ typedef union {
+ uint64_t value;
+ uint8_t array[8];
+ } trafo64_t;
+
+ const uint64_t host64 = 0x0102030405060708;
+ const trafo64_t be64 = { .array = { 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08 } };
+ const trafo64_t le64 = { .array = { 0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01 } };
+ ok(htobe64(host64) == be64.value, "htobe64");
+ ok(htole64(host64) == le64.value, "htole64");
+ ok(be64toh(be64.value) == host64, "be64toh");
+ ok(le64toh(le64.value) == host64, "le64toh");
+
+ return 0;
+}
diff --git a/tests/libknot/test_lookup.c b/tests/libknot/test_lookup.c
new file mode 100644
index 0000000..acb612f
--- /dev/null
+++ b/tests/libknot/test_lookup.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/lookup.h"
+
+const knot_lookup_t test_table[] = {
+ { 0, "test item 0" },
+ { 10, "" },
+ { 2, "test item 2" },
+ { -1, "test item -1" },
+ { 0, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ plan(9);
+
+ /* Lookup by ID. */
+ const knot_lookup_t *found = knot_lookup_by_id(test_table, 3);
+ ok(found == NULL, "lookup table: find by id - non-existent ID");
+
+ found = knot_lookup_by_id(test_table, 2);
+ ok(found && found->id == 2 && strcmp(found->name, "test item 2") == 0,
+ "lookup table: find by id - ID 2 (unordered IDs)");
+
+ found = knot_lookup_by_id(NULL, 2);
+ ok(found == NULL, "lookup table: find by id - table == NULL");
+
+ /* Lookup by name. */
+ found = knot_lookup_by_name(test_table, "test item 2");
+ ok(found && found->id == 2 && strcmp(found->name, "test item 2") == 0,
+ "lookup table: find by name - existent");
+
+ found = knot_lookup_by_name(test_table, "");
+ ok(found && found->id == 10 && strcmp(found->name, "") == 0,
+ "lookup table: find by name - empty string");
+
+ found = knot_lookup_by_name(test_table, NULL);
+ ok(found == NULL, "lookup table: find by name - NULL name");
+
+ found = knot_lookup_by_name(NULL, "test item 2");
+ ok(found == NULL, "lookup table: find by name - NULL table");
+
+ found = knot_lookup_by_name(NULL, NULL);
+ ok(found == NULL, "lookup table: find by name - NULL table & NULL name");
+
+ found = knot_lookup_by_name(test_table, "non existent name");
+ ok(found == NULL, "lookup table: find by name - non-existent name");
+
+ return 0;
+}
diff --git a/tests/libknot/test_pkt.c b/tests/libknot/test_pkt.c
new file mode 100644
index 0000000..65458cd
--- /dev/null
+++ b/tests/libknot/test_pkt.c
@@ -0,0 +1,199 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "libknot/packet/pkt.c"
+#include "contrib/ucw/mempool.h"
+
+#define TTL 7200
+#define NAMECOUNT 3
+#define DATACOUNT 3
+const char *g_names[NAMECOUNT] = {
+ "example.com",
+ "ns1.example.com",
+ "ns2.example.com"
+};
+
+const char *g_rdata[DATACOUNT] = {
+ "\x04" "\xc2\x0c\x00\x01", /* 4B, 194.0.12.1" */
+ "\x11" "\x03""ns1""\x07""example""\x03""com""\x00", /* domain name */
+ "\x11" "\x03""ns2""\x07""example""\x03""com""\x00", /* domain name */
+};
+
+#define RDVAL(i) ((const uint8_t*)(g_rdata[(i)] + 1))
+#define RDLEN(i) ((uint16_t)(g_rdata[(i)][0]))
+
+/* @note Packet equivalence test, 5 checks. */
+static void packet_match(knot_pkt_t *in, knot_pkt_t *out)
+{
+ assert(in);
+ assert(out);
+
+ /* Check counts */
+ is_int(knot_wire_get_qdcount(out->wire),
+ knot_wire_get_qdcount(in->wire), "pkt: QD match");
+ is_int(knot_wire_get_ancount(out->wire),
+ knot_wire_get_ancount(in->wire), "pkt: AN match");
+ is_int(knot_wire_get_nscount(out->wire),
+ knot_wire_get_nscount(in->wire), "pkt: NS match");
+ is_int(knot_wire_get_arcount(out->wire),
+ knot_wire_get_arcount(in->wire), "pkt: AR match");
+
+ /* Check RRs */
+ int rr_matched = 0;
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ if (knot_rrset_equal(&out->rr[i], &in->rr[i], KNOT_RRSET_COMPARE_WHOLE) > 0) {
+ ++rr_matched;
+ }
+ }
+ is_int(NAMECOUNT, rr_matched, "pkt: RR content match");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create memory pool context. */
+ int ret = 0;
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Create names and data. */
+ knot_dname_t* dnames[NAMECOUNT] = {0};
+ knot_rrset_t* rrsets[NAMECOUNT] = {0};
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ dnames[i] = knot_dname_from_str_alloc(g_names[i]);
+ }
+
+ uint8_t *edns_str = (uint8_t *)"ab";
+ /* Create OPT RR. */
+ knot_rrset_t opt_rr = { 0 };
+ ret = knot_edns_init(&opt_rr, 1024, 0, 0, &mm);
+ is_int(KNOT_EOK, ret, "initialize OPT RR");
+
+ /* Add NSID */
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID,
+ strlen((char *)edns_str), edns_str, &mm);
+ is_int(KNOT_EOK, ret, "initialize NSID in OPT RR");
+
+ /*
+ * Packet writer tests.
+ */
+
+ /* Create packet. */
+ knot_pkt_t *out = knot_pkt_new(NULL, MM_DEFAULT_BLKSIZE, &mm);
+ ok(out != NULL, "pkt: new");
+ assert(out);
+
+ /* Mark as response (not part of the test). */
+ knot_wire_set_qr(out->wire);
+
+ /* Secure packet. */
+ const char *tsig_secret = "abcd";
+ knot_tsig_key_t tsig_key;
+ tsig_key.algorithm = DNSSEC_TSIG_HMAC_MD5;
+ tsig_key.name = dnames[0];
+ tsig_key.secret.data = (uint8_t *)strdup(tsig_secret);
+ tsig_key.secret.size = strlen(tsig_secret);
+ ret = knot_pkt_reserve(out, knot_tsig_wire_size(&tsig_key));
+ is_int(KNOT_EOK, ret, "pkt: set TSIG key");
+
+ /* Write question. */
+ ret = knot_pkt_put_question(out, dnames[0], KNOT_CLASS_IN, KNOT_RRTYPE_A);
+ is_int(KNOT_EOK, ret, "pkt: put question");
+
+ /* Add OPT to packet (empty NSID). */
+ ret = knot_pkt_reserve(out, knot_edns_wire_size(&opt_rr));
+ is_int(KNOT_EOK, ret, "pkt: reserve OPT RR");
+
+ /* Begin ANSWER section. */
+ ret = knot_pkt_begin(out, KNOT_ANSWER);
+ is_int(KNOT_EOK, ret, "pkt: begin ANSWER");
+
+ /* Write ANSWER section. */
+ rrsets[0] = knot_rrset_new(dnames[0], KNOT_RRTYPE_A, KNOT_CLASS_IN, TTL, NULL);
+ knot_dname_free(dnames[0], NULL);
+ knot_rrset_add_rdata(rrsets[0], RDVAL(0), RDLEN(0), NULL);
+ ret = knot_pkt_put(out, KNOT_COMPR_HINT_QNAME, rrsets[0], 0);
+ is_int(KNOT_EOK, ret, "pkt: write ANSWER");
+
+ /* Begin AUTHORITY. */
+ ret = knot_pkt_begin(out, KNOT_AUTHORITY);
+ is_int(KNOT_EOK, ret, "pkt: begin AUTHORITY");
+
+ /* Write rest to AUTHORITY. */
+ ret = KNOT_EOK;
+ for (unsigned i = 1; i < NAMECOUNT; ++i) {
+ rrsets[i] = knot_rrset_new(dnames[i], KNOT_RRTYPE_NS, KNOT_CLASS_IN, TTL, NULL);
+ knot_dname_free(dnames[i], NULL);
+ knot_rrset_add_rdata(rrsets[i], RDVAL(i), RDLEN(i), NULL);
+ ret |= knot_pkt_put(out, KNOT_COMPR_HINT_NONE, rrsets[i], 0);
+ }
+ is_int(KNOT_EOK, ret, "pkt: write AUTHORITY(%u)", NAMECOUNT - 1);
+
+ /* Begin ADDITIONALS */
+ ret = knot_pkt_begin(out, KNOT_ADDITIONAL);
+ is_int(KNOT_EOK, ret, "pkt: begin ADDITIONALS");
+
+ /* Encode OPT RR. */
+ ret = knot_pkt_put(out, KNOT_COMPR_HINT_NONE, &opt_rr, 0);
+ is_int(KNOT_EOK, ret, "pkt: write OPT RR");
+
+ /*
+ * Packet reader tests.
+ */
+
+ /* Create new packet from query packet. */
+ knot_pkt_t *in = knot_pkt_new(out->wire, out->size, &out->mm);
+ ok(in != NULL, "pkt: create packet for parsing");
+
+ /* Read packet header. */
+ ret = knot_pkt_parse_question(in);
+ is_int(KNOT_EOK, ret, "pkt: read header");
+
+ /* Read packet payload. */
+ ret = parse_payload(in, 0);
+ is_int(KNOT_EOK, ret, "pkt: read payload");
+
+ /* Compare parsed packet to written packet. */
+ packet_match(in, out);
+
+ /*
+ * Copied packet tests.
+ */
+ knot_pkt_t *copy = knot_pkt_new(NULL, in->max_size, &in->mm);
+ ret = knot_pkt_copy(copy, in);
+ is_int(KNOT_EOK, ret, "pkt: create packet copy");
+
+ /* Compare copied packet to original. */
+ packet_match(in, copy);
+
+ /* Free packets. */
+ knot_pkt_free(copy);
+ knot_pkt_free(out);
+ knot_pkt_free(in);
+
+ /* Free extra data. */
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ knot_rrset_free(rrsets[i], NULL);
+ }
+ free(tsig_key.secret.data);
+ mp_delete((struct mempool *)mm.ctx);
+
+ return 0;
+}
diff --git a/tests/libknot/test_rdata.c b/tests/libknot/test_rdata.c
new file mode 100644
index 0000000..094cc4d
--- /dev/null
+++ b/tests/libknot/test_rdata.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <stdbool.h>
+#include <tap/basic.h>
+
+#include "libknot/rdata.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test array size
+ ok(knot_rdata_size(1) == 2 + 1 + 1, "rdata: array size odd.");
+ ok(knot_rdata_size(2) == 2 + 2, "rdata: array size even.");
+
+ // Test init
+ const size_t data_size = 16;
+ uint8_t buf1[knot_rdata_size(data_size)];
+ knot_rdata_t *rdata = (knot_rdata_t *)buf1;
+ uint8_t payload[] = "abcdefghijklmnop";
+ knot_rdata_init(rdata, data_size, payload);
+ const bool set_ok = rdata->len == data_size &&
+ memcmp(rdata->data, payload, data_size) == 0;
+ ok(set_ok, "rdata: init.");
+
+ // Test compare
+ rdata->len = data_size;
+ ok(knot_rdata_cmp(rdata, rdata) == 0, "rdata: cmp eq.");
+
+ knot_rdata_t *lower = rdata;
+ uint8_t buf2[knot_rdata_size(data_size)];
+ knot_rdata_t *greater = (knot_rdata_t *)buf2;
+ knot_rdata_init(greater, data_size, (uint8_t *)"qrstuvwxyz123456");
+ ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower.");
+ ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater.");
+
+ // Payloads will be the same.
+ memcpy(greater->data, lower->data, data_size);
+ assert(knot_rdata_cmp(lower, greater) == 0);
+
+ lower->len = data_size - 1;
+ ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower size.");
+ ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater size.");
+
+ return 0;
+}
diff --git a/tests/libknot/test_rdataset.c b/tests/libknot/test_rdataset.c
new file mode 100644
index 0000000..eb0dcbf
--- /dev/null
+++ b/tests/libknot/test_rdataset.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <string.h>
+
+#include "libknot/rdataset.h"
+#include "libknot/libknot.h"
+
+// Inits rdataset with given rdata.
+#define RDATASET_INIT_WITH(set, rdata) \
+ knot_rdataset_clear(&set, NULL); \
+ ret = knot_rdataset_add(&set, rdata, NULL); \
+ assert(ret == KNOT_EOK);
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test init
+ knot_rdataset_t rdataset;
+ knot_rdataset_init(&rdataset);
+ ok(rdataset.rdata == NULL && rdataset.count == 0, "rdataset: init.");
+
+ // Test rdata addition
+ uint8_t buf_gt[knot_rdata_size(4)];
+ knot_rdata_t *rdata_gt = (knot_rdata_t *)buf_gt;
+ knot_rdata_init(rdata_gt, 4, (uint8_t *)"wxyz");
+
+ int ret = knot_rdataset_add(NULL, NULL, NULL);
+ is_int(KNOT_EINVAL, ret, "rdataset: add NULL.");
+ ret = knot_rdataset_add(&rdataset, rdata_gt, NULL);
+ bool add_ok = ret == KNOT_EOK && rdataset.count == 1 &&
+ knot_rdata_cmp(rdata_gt, rdataset.rdata) == 0;
+ ok(add_ok, "rdataset: add.");
+
+ uint8_t buf_lo[knot_rdata_size(4)];
+ knot_rdata_t *rdata_lo = (knot_rdata_t *)buf_lo;
+ knot_rdata_init(rdata_lo, 4, (uint8_t *)"abcd");
+ ret = knot_rdataset_add(&rdataset, rdata_lo, NULL);
+ add_ok = ret == KNOT_EOK && rdataset.count == 2 &&
+ knot_rdata_cmp(rdata_lo, rdataset.rdata) == 0;
+ ok(add_ok, "rdataset: add lower.");
+
+ // Test getters
+ ok(knot_rdata_cmp(knot_rdataset_at(&rdataset, 0), rdata_lo) == 0 &&
+ knot_rdata_cmp(knot_rdataset_at(&rdataset, 1), rdata_gt) == 0,
+ "rdataset: at.");
+
+ ok(knot_rdataset_size(&rdataset) == knot_rdata_size(4) * 2,
+ "rdataset: size.");
+
+ // Test copy
+ ok(knot_rdataset_copy(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: copy NULL.");
+ knot_rdataset_t copy;
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ const bool copy_ok = ret == KNOT_EOK && copy.count == rdataset.count &&
+ knot_rdataset_size(&copy) == knot_rdataset_size(&rdataset) &&
+ memcmp(rdataset.rdata, copy.rdata,
+ knot_rdataset_size(&rdataset)) == 0;
+ ok(copy_ok, "rdataset: copy");
+
+ // Test eq
+ ok(knot_rdataset_eq(&rdataset, &copy), "rdataset: equal");
+
+ // Test clear
+ knot_rdataset_clear(&copy, NULL);
+ ok(copy.count == 0 && copy.rdata == NULL, "rdataset: clear.");
+
+ // Test not equal (different count)
+ ok(!knot_rdataset_eq(&rdataset, &copy), "rdataset: not equal - count");
+
+ // Test member
+ uint8_t buf_not[knot_rdata_size(1)];
+ knot_rdata_t *not_a_member = (knot_rdata_t *)buf_not;
+ knot_rdata_init(not_a_member, 1, (uint8_t *)"?");
+ ok(knot_rdataset_member(&rdataset, rdata_gt), "rdataset: is member.");
+ ok(!knot_rdataset_member(&rdataset, not_a_member), "rdataset: is not member.");
+
+ // Test merge
+ ok(knot_rdataset_merge(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: merge NULL.");
+ knot_rdataset_t empty;
+ knot_rdataset_init(&empty);
+ ret = knot_rdataset_merge(&empty, &rdataset, NULL);
+ bool merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&empty, &rdataset);
+ ok(merge_ok, "rdataset: merge empty.");
+ knot_rdata_t *data_before = rdataset.rdata;
+ ret = knot_rdataset_merge(&rdataset, &rdataset, NULL);
+ merge_ok = ret == KNOT_EOK && rdataset.count == 2 &&
+ data_before == rdataset.rdata;
+ ok(merge_ok, "rdataset: merge self.");
+
+ knot_rdataset_clear(&empty, NULL);
+
+ // Init structs for merge sort testing
+ knot_rdataset_t rdataset_lo; // "Lower" rdataset
+ knot_rdataset_init(&rdataset_lo);
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ knot_rdataset_t rdataset_gt; // "Greater" rdataset
+ knot_rdataset_init(&rdataset_gt);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+
+ // Test not equal - different data
+ ok(!knot_rdataset_eq(&rdataset_gt, &rdataset_lo), "rdataset: data not equal.");
+
+ // Test that merge keeps the sorted order
+ ret = knot_rdataset_merge(&rdataset_lo, &rdataset_gt, NULL);
+ merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_lo, &rdataset);
+ ok(merge_ok, "rdataset: merge into lower.");
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_merge(&rdataset_gt, &rdataset_lo, NULL);
+ merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_gt, &rdataset);
+ ok(merge_ok, "rdataset: merge into greater.");
+
+ // Test intersect
+ ok(knot_rdataset_intersect(NULL, NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: intersect NULL.");
+
+ knot_rdataset_t intersection;
+ ret = knot_rdataset_intersect(&rdataset, &rdataset, &intersection, NULL);
+ bool intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset, &intersection);
+ ok(intersect_ok, "rdataset: intersect self.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_intersect(&rdataset_lo, &rdataset_gt, &intersection, NULL);
+ intersect_ok = ret == KNOT_EOK && intersection.count == 0;
+ ok(intersect_ok, "rdataset: intersect no common.");
+
+ ret = knot_rdataset_intersect(&rdataset, &rdataset_lo, &intersection, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&intersection, &rdataset_lo);
+ ok(intersect_ok, "rdataset: intersect normal.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ // Test subtract
+ ok(knot_rdataset_subtract(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: subtract NULL.");
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ok(knot_rdataset_subtract(&copy, &copy, NULL) == KNOT_EOK &&
+ copy.count == 0, "rdataset: subtract self.");
+
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ret = knot_rdataset_subtract(&copy, &rdataset, NULL);
+ bool subtract_ok = ret == KNOT_EOK && copy.count == 0;
+ ok(subtract_ok, "rdataset: subtract identical.");
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ data_before = rdataset_lo.rdata;
+ ret = knot_rdataset_subtract(&rdataset_lo, &rdataset_gt, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset_lo.count == 1 &&
+ rdataset_lo.rdata == data_before;
+ ok(subtract_ok, "rdataset: subtract no common.");
+
+ ret = knot_rdataset_subtract(&rdataset, &rdataset_gt, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset.count == 1;
+ ok(subtract_ok, "rdataset: subtract the second.");
+
+ ret = knot_rdataset_subtract(&rdataset, &rdataset_lo, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset.count == 0 &&
+ rdataset.rdata == NULL;
+ ok(subtract_ok, "rdataset: subtract last.");
+
+ RDATASET_INIT_WITH(rdataset, rdata_gt);
+
+ size_t old_rrs_size = knot_rdataset_size(&rdataset);
+ size_t rr_size = rdata_lo->len;
+ ret = knot_rdataset_reserve(&rdataset, rr_size, NULL);
+ size_t new_rrs_size = knot_rdataset_size(&rdataset);
+ bool reserve_ok = ret == KNOT_EOK && new_rrs_size == (old_rrs_size + knot_rdata_size(rr_size));
+ ok(reserve_ok, "rdataset: reserve normal");
+
+ RDATASET_INIT_WITH(copy, rdata_lo);
+ knot_rdataset_add(&copy, rdata_gt, NULL);
+
+ knot_rdata_init(knot_rdataset_at(&rdataset, 1), 4, (uint8_t *)"abcd");
+
+ knot_rdataset_clear(&copy, NULL);
+ knot_rdataset_clear(&rdataset, NULL);
+ knot_rdataset_clear(&rdataset_lo, NULL);
+ knot_rdataset_clear(&rdataset_gt, NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/libknot/test_rrset-wire.c b/tests/libknot/test_rrset-wire.c
new file mode 100644
index 0000000..ea1d3c2
--- /dev/null
+++ b/tests/libknot/test_rrset-wire.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+
+// Wire initializers
+
+#define MESSAGE_HEADER(AN, AUTH, ADD) 0xd4, 0xec, 0x81, 0xa0, 0x00, 0x01, \
+ 0x00, AN, 0x00, AUTH, 0x00, ADD
+
+#define QUERY(qname, type) qname, 0x00, type, 0x00, 0x01
+
+#define RR_HEADER(owner, type, rdlength0, rdlength1) owner, 0x00, type, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, rdlength0, rdlength1
+
+#define QNAME_POINTER 0xc0, 0x0c
+
+// Initializers' sizes
+
+#define QUERY_SIZE 12 + 4
+#define RR_HEADER_SIZE 10
+
+// Sample domain names
+
+#define QNAME 0x03, 0x6e, 0x69, 0x63, 0x02, 0x63, 0x7a, 0x00
+#define QNAME_SIZE 8
+#define QNAME_LONG \
+0x3f,'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+'m', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', \
+'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+'m', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'w', 'y', \
+'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,\
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
+'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', \
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
+'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', \
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
+'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
+'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'i', 'k', 0x3d,'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
+'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
+'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 0x00
+#define QNAME_LONG_SIZE 255
+#define POINTER_SIZE 2
+
+struct wire_data {
+ uint8_t wire[65535];
+ size_t size;
+ size_t pos;
+ int code;
+ const char *msg;
+};
+
+#define FROM_CASE_COUNT 17
+
+static const struct wire_data FROM_CASES[FROM_CASE_COUNT] = {
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A)},
+ .size = QUERY_SIZE + QNAME_SIZE,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "No header" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A), 0x00, 0x00, 0x01},
+ .size = QUERY_SIZE + QNAME_SIZE + 3,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Partial header" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04) },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "No RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Partial RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01, 0x02, 0x03, 0x04 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 4,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "OK RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x05), 0x01, 0x02, 0x03, 0x04, 0x05 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 5,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Trailing RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SOA),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SOA, 0x00, 0x18), QNAME_POINTER, QNAME_POINTER,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_LONG_SIZE + 6 + 20,
+ .pos = QUERY_SIZE + QNAME_LONG_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Max DNAME" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_SIG),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xdb),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME },
+ .size = 65535,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Max RDLENGTH" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SIG),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xff),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME_POINTER },
+ .size = 65535 + QNAME_LONG_SIZE + QUERY_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_LONG_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Max RDLENGTH + compression"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NSEC),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NSEC, 0x00, 0x03),
+ QNAME_POINTER, 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "DNAME wrong compression"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x01),
+ 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR missing header"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, QNAME_POINTER},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 9,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR bad offset"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 7,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR no DNAME"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x0c),
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, QNAME_POINTER},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 10 + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "NAPTR valid"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_APL),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_APL, 0x00, 0x00) },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Valid 0 RDATA"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_TXT),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_TXT, 0x00, 0x00) },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Invalid 0 RDATA"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_PX),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_PX, 0x00, 0x06),
+ 0x00, 0x00, QNAME_POINTER, QNAME_POINTER },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 6,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Obsolete RR type"},
+};
+
+#define TEST_CASE_FROM(rrset, i) size_t _pos##i = FROM_CASES[i].pos; \
+ ok(knot_rrset_rr_from_wire(FROM_CASES[i].wire, &_pos##i, FROM_CASES[i].size, \
+ rrset, NULL, true) == FROM_CASES[i].code, "rrset wire: %s", FROM_CASES[i].msg)
+
+static void test_inputs(void)
+{
+ for (size_t i = 0; i < FROM_CASE_COUNT; ++i) {
+ knot_rrset_t rrset;
+ knot_rrset_init_empty(&rrset);
+ TEST_CASE_FROM(&rrset, i);
+ knot_rrset_clear(&rrset, NULL);
+ }
+}
+
+static void check_canon(uint8_t *wire, size_t size, size_t pos, bool canon,
+ knot_dname_t *qname, knot_dname_t *dname)
+{
+ knot_rrset_t rrset;
+ knot_rrset_init_empty(&rrset);
+
+ int ret = knot_rrset_rr_from_wire(wire, &pos, size, &rrset, NULL, canon);
+ is_int(KNOT_EOK, ret, "OK %s canonization", canon ? "with" : "without");
+ ok(memcmp(rrset.owner, qname, knot_dname_size(qname)) == 0, "compare owner");
+
+ uint8_t *rdata = rrset.rrs.rdata->data;
+ ok(memcmp(rdata, dname, knot_dname_size(dname)) == 0, "compare rdata dname");
+
+ knot_rrset_clear(&rrset, NULL);
+}
+
+static void test_canonization(void)
+{
+ #define UPP_QNAME_SIZE 5
+ #define UPP_QNAME 0x01, 0x41, 0x01, 0x5a, 0x00 // A.Z.
+ #define LOW_QNAME 0x01, 0x61, 0x01, 0x7a, 0x00 // a.z.
+
+ #define UPP_DNAME_SIZE 3
+ #define UPP_DNAME 0x01, 0x58, 0x00 // X.
+ #define LOW_DNAME 0x01, 0x78, 0x00 // x.
+
+ uint8_t wire[] = {
+ MESSAGE_HEADER(1, 0, 0), QUERY(UPP_QNAME, KNOT_RRTYPE_NS),
+ RR_HEADER(UPP_QNAME, KNOT_RRTYPE_NS, 0x00, UPP_DNAME_SIZE), UPP_DNAME
+ };
+ size_t size = QUERY_SIZE + RR_HEADER_SIZE + UPP_QNAME_SIZE * 2 + UPP_DNAME_SIZE;
+ size_t pos = QUERY_SIZE + UPP_QNAME_SIZE;
+
+ knot_dname_t upp_qname[] = { UPP_QNAME };
+ knot_dname_t upp_dname[] = { UPP_DNAME };
+ check_canon(wire, size, pos, false, upp_qname, upp_dname);
+
+ knot_dname_t low_qname[] = { LOW_QNAME };
+ knot_dname_t low_dname[] = { LOW_DNAME };
+ check_canon(wire, size, pos, true, low_qname, low_dname);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Test NULL parameters");
+ int ret = knot_rrset_rr_from_wire(NULL, NULL, 0, NULL, NULL, true);
+ is_int(KNOT_EINVAL, ret, "rr wire: Invalid params");
+
+ diag("Test various inputs");
+ test_inputs();
+
+ diag("Test canonization");
+ test_canonization();
+
+ return 0;
+}
diff --git a/tests/libknot/test_rrset.c b/tests/libknot/test_rrset.c
new file mode 100644
index 0000000..a0f1d53
--- /dev/null
+++ b/tests/libknot/test_rrset.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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>
+#include <tap/basic.h>
+
+#include "libknot/rrset.h"
+#include "libknot/descriptor.h"
+
+static bool check_rrset(const knot_rrset_t *rrset, const knot_dname_t *owner,
+ uint16_t type, uint16_t rclass, uint32_t ttl)
+{
+ if (!rrset) {
+ return false;
+ }
+
+ const bool dname_cmp = owner == NULL ? rrset->owner == NULL :
+ knot_dname_is_equal(rrset->owner, owner);
+ return rrset->type == type && rrset->rclass == rclass && dname_cmp &&
+ rrset->ttl == ttl && rrset->rrs.count == 0; // We do not test rdataset here
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test new
+ knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test.");
+ assert(dummy_owner);
+
+ knot_rrset_t *rrset = knot_rrset_new(dummy_owner, KNOT_RRTYPE_TXT,
+ KNOT_CLASS_IN, 3600, NULL);
+ ok(rrset != NULL, "rrset: create.");
+ assert(rrset);
+
+ ok(check_rrset(rrset, dummy_owner, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600),
+ "rrset: set fields during create.");
+
+ // Test init
+ knot_dname_free(dummy_owner, NULL);
+ dummy_owner = knot_dname_from_str_alloc("test2.");
+ assert(dummy_owner);
+
+ knot_dname_free(rrset->owner, NULL);
+ knot_rrset_init(rrset, dummy_owner, KNOT_RRTYPE_A, KNOT_CLASS_CH, 7200);
+ ok(check_rrset(rrset, dummy_owner, KNOT_RRTYPE_A, KNOT_CLASS_CH, 7200),
+ "rrset: init.");
+
+ // Test copy
+ knot_rrset_t *copy = knot_rrset_copy(rrset, NULL);
+ ok(copy != NULL, "rrset: copy.");
+ ok(check_rrset(copy, rrset->owner, rrset->type, rrset->rclass, 7200),
+ "rrset: set fields during copy.");
+ ok(knot_rrset_copy(NULL, NULL) == NULL, "rrset: copy NULL.");
+ assert(copy);
+
+ // Test equal - pointers
+ ok(knot_rrset_equal((knot_rrset_t *)0xdeadbeef, (knot_rrset_t *)0xdeadbeef,
+ KNOT_RRSET_COMPARE_PTR), "rrset: cmp equal pointers");
+ ok(!knot_rrset_equal((knot_rrset_t *)0xcafebabe, (knot_rrset_t *)0xdeadbeef,
+ KNOT_RRSET_COMPARE_PTR), "rrset: cmp different pointers");
+
+ // Test equal - header
+ ok(knot_rrset_equal(rrset, copy, KNOT_RRSET_COMPARE_HEADER),
+ "rrset: cmp equal headers");
+
+ copy->type = KNOT_RRTYPE_AAAA;
+ ok(!knot_rrset_equal(rrset, copy, KNOT_RRSET_COMPARE_HEADER),
+ "rrset: cmp headers - different type");
+
+ // Test equal - full, rdata empty
+ copy->type = rrset->type;
+ ok(knot_rrset_equal(rrset, copy, KNOT_RRSET_COMPARE_WHOLE),
+ "rrset: cmp headers - rdata");
+
+ knot_dname_free(rrset->owner, NULL);
+ rrset->owner = NULL;
+ ok(!knot_rrset_equal(rrset, copy, KNOT_RRSET_COMPARE_HEADER),
+ "rrset: cmp NULL owner");
+
+ ok(knot_rrset_equal(rrset, rrset, KNOT_RRSET_COMPARE_HEADER),
+ "rrset: cmp NULL owners");
+
+ // Test clear
+ knot_rrset_clear(rrset, NULL);
+ ok(rrset->owner == NULL, "rrset: clear.");
+
+ // Test empty
+ ok(knot_rrset_empty(rrset), "rrset: empty.");
+ ok(knot_rrset_empty(NULL), "rrset: empty NULL.");
+ copy->rrs.count = 1;
+ ok(!knot_rrset_empty(copy), "rrset: not empty.");
+
+ // Test init empty
+ knot_rrset_init_empty(rrset);
+ ok(check_rrset(rrset, NULL, 0, KNOT_CLASS_IN, 0), "rrset: init empty.");
+
+ // "Test" freeing
+ knot_rrset_free(rrset, NULL);
+ knot_rrset_free(copy, NULL);
+
+ return 0;
+}
diff --git a/tests/libknot/test_tsig.c b/tests/libknot/test_tsig.c
new file mode 100644
index 0000000..1a861c6
--- /dev/null
+++ b/tests/libknot/test_tsig.c
@@ -0,0 +1,204 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libknot/errcode.h"
+#include "libknot/tsig.h"
+
+static bool key_is_eq(const knot_tsig_key_t *a, const knot_tsig_key_t *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ return a->algorithm == b->algorithm &&
+ knot_dname_is_equal(a->name, b->name) &&
+ dnssec_binary_cmp(&a->secret, &b->secret) == 0;
+}
+
+#define test_function(function, msg, expected, ...) \
+ knot_tsig_key_t key = { 0 }; \
+ int r = function(&key, __VA_ARGS__); \
+ ok((r != KNOT_EOK && expected == NULL) || \
+ (r == KNOT_EOK && key_is_eq(&key, expected)), \
+ "%s: %s", #function, msg); \
+ knot_tsig_key_deinit(&key);
+
+static void test_init(const char *msg, const knot_tsig_key_t *expected,
+ const char *algo, const char *name, const char *secret)
+{
+ test_function(knot_tsig_key_init, msg, expected, algo, name, secret);
+}
+
+static void test_init_str(const char *msg, const knot_tsig_key_t *expected,
+ const char *params)
+{
+ test_function(knot_tsig_key_init_str, msg, expected, params);
+}
+
+static void test_init_file(const char *msg, const knot_tsig_key_t *expected,
+ const char *filename)
+{
+ test_function(knot_tsig_key_init_file, msg, expected, filename);
+}
+
+static void test_init_file_content(const char *msg,
+ const knot_tsig_key_t *expected,
+ const char *content)
+{
+ char filename[] = "testkey.XXXXXX";
+
+ int fd = mkstemp(filename);
+ if (fd == -1) {
+ bail("failed to create temporary file");
+ return;
+ }
+
+ ok(write(fd, content, strlen(content)) != -1, "file write");
+ close(fd);
+
+ test_init_file(msg, expected, filename);
+
+ unlink(filename);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // initialization from parameters
+
+ test_init("missing name", NULL, "hmac-md5", NULL, "Wg==");
+ test_init("missing secret", NULL, "hmac-md5", "name", NULL);
+ test_init("invalid HMAC", NULL, "hmac-sha11", "name", "Wg==");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA256,
+ .name = (uint8_t *)"\x3""key""\x4""name",
+ .secret.size = 1,
+ .secret.data = (uint8_t *)"\x5a"
+ };
+ test_init("default algorithm", &key, NULL, "key.name", "Wg==");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""knot""\x3""dns",
+ .secret.size = 6,
+ .secret.data = (uint8_t *)"secret"
+ };
+ test_init("sha1", &key, "hmac-sha1", "knot.dns.", "c2VjcmV0");
+ }
+
+ // initialization from string
+
+ test_init_str("missing value", NULL, NULL);
+ test_init_str("malformed", NULL, "this is malformed");
+ test_init_str("invalid HMAC", NULL, "hmac-sha51299:key:Wg==");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA256,
+ .name = (uint8_t *)"\x4""tsig""\x3""key",
+ .secret.size = 9,
+ .secret.data = (uint8_t *)"bananakey"
+ };
+ test_init_str("default algorithm", &key, "tsig.key:YmFuYW5ha2V5");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA384,
+ .name = (uint8_t *)"\x6""strong""\x3""key",
+ .secret.size = 8,
+ .secret.data = (uint8_t *)"applekey"
+ };
+ test_init_str("sha384", &key, "hmac-sha384:strong.KEY:YXBwbGVrZXk=");
+ }
+
+ // initialization from a file
+
+ test_init_file("no filename", NULL, NULL);
+ test_init_file("not-existing", NULL, "/this-really-should-not-exist");
+ test_init_file_content("malformed content", NULL, "malformed\n");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA512,
+ .name = (uint8_t *)"\x6""django""\x3""one",
+ .secret.size = 40,
+ .secret.data = (uint8_t *)"Who's that stumbling around in the dark?"
+ };
+ test_init_file_content("sha512", &key,
+ "hmac-sha512:django.one:V2hvJ3MgdGhhdCB"
+ "zdHVtYmxpbmcgYXJvdW5kIGluIHRoZSBkYXJrP"
+ "w==\n\n\n");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA512,
+ .name = (uint8_t *)"\x6""django""\x3""two",
+ .secret.size = 22,
+ .secret.data = (uint8_t *)"Prepare to get winged!"
+ };
+ test_init_file_content("sha512 without newline", &key,
+ "hmac-sha512:django.two:UHJlcGFyZSB0byB"
+ "nZXQgd2luZ2VkIQ==");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""test",
+ .secret.size = 1,
+ .secret.data = (uint8_t *)"\x5a"
+ };
+ test_init_file_content("leading and trailing white spaces", &key,
+ "\thmac-sha1:test:Wg== \n");
+ }
+
+ // tsig key duplication
+
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""copy""\x2""me",
+ .secret.size = 6,
+ .secret.data = (uint8_t *)"orange"
+ };
+
+ knot_tsig_key_t copy = { 0 };
+ int r;
+
+ r = knot_tsig_key_copy(NULL, &key);
+ ok(r != KNOT_EOK, "knot_tsig_key_copy: no destination");
+ r = knot_tsig_key_copy(&copy, NULL);
+ ok(r != KNOT_EOK, "knot_tsig_key_copy: no source");
+ r = knot_tsig_key_copy(&copy, &key);
+ ok(r == KNOT_EOK && key_is_eq(&copy, &key) &&
+ copy.secret.data != key.secret.data && copy.name != key.name,
+ "knot_tsig_key_copy: simple copy");
+
+ knot_tsig_key_deinit(&copy);
+ }
+
+ return 0;
+}
diff --git a/tests/libknot/test_wire.c b/tests/libknot/test_wire.c
new file mode 100644
index 0000000..70617f9
--- /dev/null
+++ b/tests/libknot/test_wire.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/wire.h"
+
+#define write_test(size, value, ...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t wdata[sizeof(expect)] = { 0x00 }; \
+ knot_wire_write_u ## size(wdata, value); \
+ ok(memcmp(wdata, expect, sizeof(expect)) == 0, "%d-bit write", size); \
+}
+
+int main(int argc, char *argv[])
+{
+ plan(8);
+
+ const uint8_t rdata[] = { 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+
+ is_hex( 0x8899, knot_wire_read_u16(rdata), "16-bit read");
+ is_hex( 0x8899aabb, knot_wire_read_u32(rdata), "32-bit read");
+ is_hex( 0x8899aabbccdd, knot_wire_read_u48(rdata), "48-bit read");
+ is_hex(0x8899aabbccddeeff, knot_wire_read_u64(rdata), "64-bit read");
+
+ write_test(16, 0x1122, 0x11, 0x22);
+ write_test(32, 0x66778899, 0x66, 0x77, 0x88, 0x99);
+ write_test(48, 0xbbccdd778899, 0xbb, 0xcc, 0xdd, 0x77, 0x88, 0x99);
+ write_test(64, 0xbbccddee66778899, 0xbb, 0xcc, 0xdd, 0xee,
+ 0x66, 0x77, 0x88, 0x99);
+
+ return 0;
+}
diff --git a/tests/libknot/test_yparser.c b/tests/libknot/test_yparser.c
new file mode 100644
index 0000000..7ee0332
--- /dev/null
+++ b/tests/libknot/test_yparser.c
@@ -0,0 +1,280 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/libknot.h"
+
+const char *syntax_ok =
+ "#comment\n"
+ " # comment\n"
+ "a:\n"
+ "a :\n"
+ "a : #comment\n"
+ "\n"
+ "b: \"b\"\n"
+ "b: b #comment\n"
+ "b : b\n"
+ "b: [ b] # comment\n"
+ "b: [b ]\n"
+ "b: [ b ]\n"
+ "\n"
+ " f: \"f\"\n"
+ " f: f #comment\n"
+ " f : f\n"
+ " f: [ f] # comment\n"
+ " f: [f ]\n"
+ " f: [ f ]\n"
+ " f: [ \"f\" ]\n"
+ "\n"
+ "c: [a,b]\n"
+ "c: [a, b]\n"
+ "c: [a ,b]\n"
+ "c: [a , b]\n"
+ "c: [ a , b ]\n"
+ "c: [ \"a\" , \"b\" ]\n"
+ "\n"
+ "- d: d\n"
+ "- d : d # comment\n"
+ "\n"
+ "e: \"a#b' c[d,]\"\n"
+ "\n"
+ "zone:\n"
+ "#comment\n"
+ " # comment\n"
+ " - domain: example. # comment\n"
+ " master: bind\n"
+ " - domain: example.\n"
+ " master: bind\n"
+ "zone2:\n"
+ " - a: b # different indentation";
+
+const char *syntax_error1 =
+ "f:\n"
+ " - a: b\n"
+ " - b: c\n";
+
+const char *syntax_error2 =
+ "f:\n"
+ " - a: b\n"
+ " c: d\n";
+
+const char *syntax_error3 =
+ "f:\n"
+ " a: b\n"
+ " c: d\n";
+
+const char *dname_ok =
+ ".:\n"
+ "dom-ain:\n"
+ "\\070-\\071.\\072.:";
+
+const char *quotes_ok =
+ "g: \"\"\n"
+ "g: a\\ b\n"
+ "g: \"\\# 1 00\"\n"
+ "g: \"\\\"\\\"\"\n"
+ "g: \" a \\\" b \\\" \\\"c\\\" \"\n"
+ "g: \"\\@ \\[ \\# \\, \\]\"\n";
+
+static void test_syntax_ok(yp_parser_t *yp)
+{
+ // OK input.
+ int ret = yp_set_input_string(yp, syntax_ok, strlen(syntax_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 3;
+ for (int i = 0; i < 3; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'a' &&
+ yp->data_len == 0 && yp->event == YP_EKEY0 &&
+ yp->line_count == line + i, "compare %i. key0", i);
+ }
+
+ line += 4;
+ for (int i = 0; i < 6; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'b' &&
+ yp->data_len == 1 && yp->data[0] == 'b' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with value", i);
+ }
+
+ line += 7;
+ for (int i = 0; i < 7; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key1 with value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'f' &&
+ yp->data_len == 1 && yp->data[0] == 'f' &&
+ yp->event == YP_EKEY1 && yp->line_count == line + i,
+ "compare %i. key1 with value", i);
+ }
+
+ line += 8;
+ for (int i = 0; i < 6; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with first value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'c' &&
+ yp->data_len == 1 && yp->data[0] == 'a' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with first value", i);
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with second value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'c' &&
+ yp->data_len == 1 && yp->data[0] == 'b' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with second value", i);
+ }
+
+ line += 7;
+ for (int i = 0; i < 2; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. id", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'd' &&
+ yp->data_len == 1 && yp->data[0] == 'd' &&
+ yp->event == YP_EID && yp->line_count == line + i,
+ "compare %i. id", i);
+ }
+
+ line += 3;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0 with quoted value");
+ ok(yp->key_len == 1 && yp->key[0] == 'e' && yp->data_len == 10 &&
+ memcmp(yp->data, "a#b' c[d,]", yp->data_len) == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 with quoted value");
+
+ line += 2;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ok(yp->key_len == 4 && strcmp(yp->key, "zone") == 0 &&
+ yp->data_len == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 value");
+
+ line += 3;
+ for (int i = 0; i < 2; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. id", i);
+ ok(yp->key_len == 6 && strcmp(yp->key, "domain") == 0 &&
+ yp->data_len == 8 && strcmp(yp->data, "example.") == 0 &&
+ yp->event == YP_EID && yp->line_count == line + 2 * i,
+ "compare id");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key1", i);
+ ok(yp->key_len == 6 && strcmp(yp->key, "master") == 0 &&
+ yp->data_len == 4 && strcmp(yp->data, "bind") == 0 &&
+ yp->event == YP_EKEY1 && yp->line_count == line + 2 * i + 1,
+ "compare key1");
+ }
+
+ line += 4;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ok(yp->key_len == 5 && strcmp(yp->key, "zone2") == 0 &&
+ yp->data_len == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 value");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key1");
+ ok(yp->key_len == 1 && strcmp(yp->key, "a") == 0 &&
+ yp->data_len == 1 && strcmp(yp->data, "b") == 0 &&
+ yp->event == YP_EID && yp->line_count == line + 1,
+ "compare key1 value");
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOF, ret, "parse EOF");
+}
+
+static void test_syntax_error(yp_parser_t *yp, const char *input)
+{
+ static int count = 1;
+
+ int ret = yp_set_input_string(yp, input, strlen(input));
+ is_int(KNOT_EOK, ret, "set error input string %i", count++);
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key1");
+ ret = yp_parse(yp);
+ is_int(KNOT_YP_EINVAL_INDENT, ret, "parse key1 - invalid indentation");
+}
+
+static void test_dname(yp_parser_t *yp)
+{
+#define CHECK_DNAME(str) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse dname " str); \
+ ok(yp->key_len == strlen(str) && strcmp(yp->key, str) == 0 && yp->data_len == 0 && \
+ yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
+
+ // Dname key value.
+ int ret = yp_set_input_string(yp, dname_ok, strlen(dname_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 1;
+ CHECK_DNAME(".");
+ CHECK_DNAME("dom-ain");
+ CHECK_DNAME("\\070-\\071.\\072.");
+
+}
+
+static void test_quotes(yp_parser_t *yp)
+{
+#define CHECK_QUOTE(str) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse quoted " str); \
+ ok(yp->key_len == 1 && yp->key[0] == 'g' && \
+ yp->data_len == strlen(str) && strcmp(yp->data, str) == 0 && \
+ yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
+
+ int ret = yp_set_input_string(yp, quotes_ok, strlen(quotes_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 1;
+ CHECK_QUOTE("");
+ CHECK_QUOTE("a\\ b");
+ CHECK_QUOTE("\\# 1 00");
+ CHECK_QUOTE("\"\"");
+ CHECK_QUOTE(" a \" b \" \"c\" ");
+ CHECK_QUOTE("\\@ \\[ \\# \\, \\]");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ yp_parser_t yp;
+ yp_init(&yp);
+
+ test_syntax_ok(&yp);
+ test_syntax_error(&yp, syntax_error1);
+ test_syntax_error(&yp, syntax_error2);
+ test_syntax_error(&yp, syntax_error3);
+ test_dname(&yp);
+ test_quotes(&yp);
+
+ yp_deinit(&yp);
+
+ return 0;
+}
diff --git a/tests/libknot/test_ypschema.c b/tests/libknot/test_ypschema.c
new file mode 100644
index 0000000..50d56f6
--- /dev/null
+++ b/tests/libknot/test_ypschema.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/libknot.h"
+
+#define C_ID "\x02""id"
+#define C_INT "\x07""integer"
+#define C_BOOL "\x04""bool"
+#define C_OPT "\x06""option"
+#define C_STR "\x06""string"
+#define C_ADDR "\x07""address"
+#define C_DNAME "\x05""dname"
+#define C_HEX "\x03""hex"
+#define C_BASE64 "\x06""base64"
+#define C_DATA "\x04""data"
+#define C_REF "\x09""reference"
+#define C_GRP "\x05""group"
+#define C_MULTIGRP "\x0B""multi-group"
+
+static const yp_item_t group[] = {
+ { C_INT, YP_TINT, YP_VINT = { 0, 100, YP_NIL } },
+ { C_STR, YP_TSTR, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+static const yp_item_t multi_group[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_HEX, YP_THEX, YP_VNONE },
+ { C_BASE64, YP_TB64, YP_VNONE },
+ { NULL }
+};
+
+static const knot_lookup_t opts[] = {
+ { 1, "one" },
+ { 10, "ten" },
+ { 0, NULL }
+ };
+
+static const yp_item_t static_schema[] = {
+ { C_OPT, YP_TOPT, YP_VOPT = { opts } },
+ { C_BOOL, YP_TBOOL, YP_VNONE },
+ { C_DNAME, YP_TDNAME, YP_VNONE },
+ { C_GRP, YP_TGRP, YP_VGRP = { group } },
+ { C_MULTIGRP, YP_TGRP, YP_VGRP = { multi_group }, YP_FMULTI },
+ { C_REF, YP_TREF, YP_VREF = { C_MULTIGRP } },
+ { C_DATA, YP_TDATA, YP_VNONE },
+ { NULL }
+};
+
+static void schema_find_test(void)
+{
+ yp_item_t *schema = NULL;
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+
+ const yp_item_t *i = yp_schema_find(C_OPT, NULL, schema);
+ ok(i != NULL, "schema find");
+ if (i == NULL) {
+ goto error_schema;
+ }
+ ok(strcmp(i->name + 1, C_OPT + 1) == 0, "name check");
+
+ i = yp_schema_find(C_STR, C_GRP, schema);
+ ok(i != NULL, "schema find with parent");
+ if (i == NULL) {
+ goto error_schema;
+ }
+ ok(strcmp(i->name + 1, C_STR + 1) == 0, "name check");
+
+ i = yp_schema_find(C_ADDR, NULL, schema);
+ ok(i == NULL, "schema not find");
+
+ i = yp_schema_find(C_ADDR, C_GRP, schema);
+ ok(i == NULL, "schema not find with parent");
+
+error_schema:
+ yp_schema_free(schema);
+}
+
+static void schema_merge_test(void)
+{
+ static const yp_item_t items1[] = {
+ { "\x01""1", YP_TSTR, YP_VNONE },
+ { "\x01""2", YP_TSTR, YP_VNONE },
+ { NULL }
+ };
+
+ static const yp_item_t items2[] = {
+ { "\x01""3", YP_TSTR, YP_VNONE },
+ { "\x01""4", YP_TSTR, YP_VNONE },
+ { NULL }
+ };
+
+ yp_item_t *schema = NULL;
+ yp_item_t *tmp = NULL;
+
+ int ret = yp_schema_copy(&tmp, items1);
+ is_int(KNOT_EOK, ret, "schema copy");
+
+ ret = yp_schema_merge(&schema, items1, items2);
+ is_int(KNOT_EOK, ret, "schema merge");
+
+ yp_schema_free(tmp);
+
+ for (uint8_t i = 0; i < 4; i++) {
+ yp_name_t name[3] = { '\x01', '1' + i };
+ const yp_item_t *item = yp_schema_find(name, NULL, schema);
+ ok(item != NULL, "schema find");
+ }
+
+ yp_schema_free(schema);
+}
+
+#define SET_INPUT_STR(str) \
+ ret = yp_set_input_string(yp, str, strlen(str)); \
+ is_int(KNOT_EOK, ret, "set input string");
+
+#define PARSER_CHECK(depth) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse"); \
+ ret = yp_schema_check_parser(ctx, yp); \
+ is_int(KNOT_EOK, ret, "check parser"); \
+ node = &ctx->nodes[ctx->current]; \
+ parent = node->parent; \
+ ok(ctx->current == depth, "depth check");
+
+#define PARSER_RET_CHECK(code) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse"); \
+ ret = yp_schema_check_parser(ctx, yp); \
+ ok(ret == code, "return check parser");
+
+static void parser_test(void)
+{
+ yp_parser_t yparser;
+ yp_parser_t *yp = &yparser;
+ yp_item_t *schema = NULL;
+ yp_check_ctx_t *ctx = NULL;
+
+ yp_init(yp);
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+ if (ret != KNOT_EOK) {
+ goto error_parser;
+ }
+
+ ctx = yp_schema_check_init(&schema);
+ ok(ctx != NULL, "create check ctx");
+ if (ctx == NULL) {
+ goto error_parser;
+ }
+
+ yp_node_t *node;
+ yp_node_t *parent;
+ const yp_item_t *id;
+
+ diag("parser key0 test");
+ SET_INPUT_STR("option: one");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "option") == 0, "name check");
+ ok(node->item->type == YP_TOPT, "type check");
+ ok(yp_opt(node->data) == 1, "value check");
+
+ diag("parser group test");
+ SET_INPUT_STR("group:\n integer: 20\n string: [short, \"long string\"]");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
+ ok(node->item->type == YP_TINT, "type check");
+ ok(yp_int(node->data) == 20, "value check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "short") == 0, "value check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
+
+ diag("parser multi-group test");
+ SET_INPUT_STR("multi-group:\n - id: foo\n base64: Zm9vYmFy\nreference: foo");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ PARSER_CHECK(0);
+ ok(node->id_len > 0, "id check");
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ id = node->item->var.g.id;
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
+ PARSER_CHECK(1);
+ id = parent->item->var.g.id;
+ ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
+ ok(parent->item->type == YP_TGRP, "type check");
+ ok(parent->data_len == 0, "value length check");
+ ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
+ ok(node->item->type == YP_TB64, "type check");
+ ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
+ "value check");
+ ok(node->id_len == 0, "id length check");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
+ ok(node->item->type == YP_TREF, "type check");
+ ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
+
+ diag("parser check return");
+ SET_INPUT_STR("unknown:");
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n unknown:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n - unknown: data");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n - hex: data");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("dname:");
+ PARSER_RET_CHECK(KNOT_EINVAL);
+
+ SET_INPUT_STR("group: data");
+ PARSER_RET_CHECK(KNOT_YP_ENOTSUP_DATA);
+
+ SET_INPUT_STR("group:\n integer:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_EINVAL);
+
+ SET_INPUT_STR("multi-group:\n id:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_ENODATA);
+
+ SET_INPUT_STR("multi-group:\n hex:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_ENOID);
+
+error_parser:
+ yp_schema_check_deinit(ctx);
+ yp_schema_free(schema);
+ yp_deinit(yp);
+}
+
+#define STR_CHECK(depth, key0, key1, id, data) \
+ ret = yp_schema_check_str(ctx, key0, key1, id, data); \
+ is_int(KNOT_EOK, ret, "check str"); \
+ ok(ctx->current == depth, "depth check"); \
+ node = &ctx->nodes[ctx->current]; \
+ parent = node->parent;
+
+#define STR_RET_CHECK(code, key0, key1, id, data) \
+ ret = yp_schema_check_str(ctx, key0, key1, id, data); \
+ ok(ret == code, "return check str");
+
+static void str_test(void)
+{
+ yp_item_t *schema;
+ yp_check_ctx_t *ctx = NULL;
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+ if (ret != KNOT_EOK) {
+ goto error_str;
+ }
+
+ ctx = yp_schema_check_init(&schema);
+ ok(ctx != NULL, "create check ctx");
+ if (ctx == NULL) {
+ goto error_str;
+ }
+
+ yp_node_t *node;
+ yp_node_t *parent;
+ const yp_item_t *id;
+
+ diag("str key0 test");
+ STR_CHECK(0, "option", NULL, NULL, "one");
+ ok(strcmp(node->item->name + 1, "option") == 0, "name check");
+ ok(node->item->type == YP_TOPT, "type check");
+ ok(yp_opt(node->data) == 1, "value check");
+
+ diag("str group test");
+ STR_CHECK(0, "group", NULL, NULL, NULL);
+ ok(strcmp(node->item->name + 1, "group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ STR_CHECK(1, "group", "integer", NULL, "20");
+ ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
+ ok(node->item->type == YP_TINT, "type check");
+ ok(yp_int(node->data) == 20, "value check");
+ STR_CHECK(1, "group", "string", NULL, "short");
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "short") == 0, "value check");
+ STR_CHECK(1, "group", "string", NULL, "long string");
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
+
+ diag("str multi-group test");
+ STR_CHECK(0, "multi-group", NULL, NULL, NULL);
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ STR_CHECK(0, "multi-group", NULL, "foo", NULL);
+ ok(node->id_len > 0, "id check");
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ id = node->item->var.g.id;
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
+ STR_CHECK(1, "multi-group", "base64", "foo", "Zm9vYmFy");
+ id = parent->item->var.g.id;
+ ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
+ ok(parent->item->type == YP_TGRP, "type check");
+ ok(parent->data_len == 0, "value length check");
+ ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
+ ok(node->item->type == YP_TB64, "type check");
+ ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
+ "value check");
+ ok(node->id_len == 0, "id length check");
+ STR_CHECK(0, "reference", NULL, NULL, "foo");
+ ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
+ ok(node->item->type == YP_TREF, "type check");
+ ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
+
+ diag("str check return");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "", "", "", "");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, NULL, NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "unknown", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, NULL, "unknown", NULL, NULL);
+ STR_RET_CHECK(KNOT_EINVAL, "dname", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "dname", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "dname", NULL, NULL, ".");
+ STR_RET_CHECK(KNOT_EINVAL, "dname", NULL, NULL, "..");
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "dname", NULL, "id", NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "dname", "unknown", NULL, NULL);
+
+ STR_RET_CHECK(KNOT_EOK, "group", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "group", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "group", "", "", "data");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "group", "unknown", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, "");
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "group", "", "id", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "group", "string", "id", NULL);
+
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL, NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", NULL, "idval", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL, "idval", "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", "idval", NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", "idval", "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", "", NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", NULL, "idval");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", "idval", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", "id", "idval", "data");
+
+error_str:
+ yp_schema_check_deinit(ctx);
+ yp_schema_free(schema);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ schema_find_test();
+ schema_merge_test();
+ parser_test();
+ str_test();
+
+ return 0;
+}
diff --git a/tests/libknot/test_yptrafo.c b/tests/libknot/test_yptrafo.c
new file mode 100644
index 0000000..3014a27
--- /dev/null
+++ b/tests/libknot/test_yptrafo.c
@@ -0,0 +1,403 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <string.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/libknot.h"
+
+static void int_test(const char *txt, int64_t num, yp_style_t s,
+ int64_t min, int64_t max)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
+
+ diag("integer \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_int(b) == num, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, s | YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void int_bad_test(const char *txt, int code, yp_style_t s,
+ int64_t min, int64_t max)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
+
+ diag("integer \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void bool_test(const char *txt, bool val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
+
+ diag("boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_bool(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void bool_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
+
+ diag("boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void opt_test(const char *txt, unsigned val, const knot_lookup_t *opts)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
+
+ diag("option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(b_len == 1, "compare length");
+ ok(yp_opt(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void opt_bad_test(const char *txt, int code, const knot_lookup_t *opts)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
+
+ diag("option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void str_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TSTR, YP_VNONE };
+
+ diag("string \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(b_len == strlen(txt) + 1, "compare length");
+ ok(memcmp(yp_str(b), val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_test(const char *txt, bool port)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
+
+ diag("address \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ bool no_port;
+ yp_addr(b, &no_port);
+ ok(no_port == port, "compare port presence");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
+
+ diag("address \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void addr_range_test(const char *txt)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TNET, YP_VNONE };
+
+ diag("address range \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_range_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TNET, YP_VNONE };
+
+ diag("address range \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void dname_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TDNAME, YP_VNONE };
+
+ diag("dname \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_dname(b), val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void hex_test(const char *txt, const char *val, const char *txt_out)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_THEX, YP_VNONE };
+
+ if (txt_out == NULL) {
+ txt_out = txt;
+ }
+
+ diag("hex \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt_out) == t_len, "txt length");
+ ok(memcmp(txt_out, t, t_len) == 0, "compare");
+}
+
+static void hex_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_THEX, YP_VNONE };
+
+ diag("hex \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void base64_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TB64, YP_VNONE };
+
+ diag("base64 \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void ref_test(const char *txt, bool val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t id = { NULL, YP_TBOOL, YP_VNONE };
+ yp_item_t ref = { NULL, YP_TGRP, YP_VNONE };
+ yp_item_t i = { NULL, YP_TREF, YP_VNONE };
+ ref.var.g.id = &id;
+ i.var.r.ref = &ref;
+
+ diag("reference to boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_bool(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Integer tests. */
+ int64_t min = -20000000000, max = 20000000000;
+ int_test("5", 5, YP_SNONE, min, max);
+ int_test("0", 0, YP_SNONE, min, max);
+ int_test("-5", -5, YP_SNONE, min, max);
+ int_test("20000000000", max, YP_SNONE, min, max);
+ int_test("-20000000000", min, YP_SNONE, min, max);
+ int_test("11B", 11LL * 1, YP_SSIZE, min, max);
+ int_test("11K", 11LL * 1024, YP_SSIZE, min, max);
+ int_test("11M", 11LL * 1024 * 1024, YP_SSIZE, min, max);
+ int_test("11G", 11LL * 1024 * 1024 * 1024, YP_SSIZE, min, max);
+ int_test("11s", 11LL * 1, YP_STIME, min, max);
+ int_test("11m", 11LL * 60, YP_STIME, min, max);
+ int_test("11h", 11LL * 3600, YP_STIME, min, max);
+ int_test("11d", 11LL * 24 * 3600, YP_STIME, min, max);
+ int_test("1025B", 1025LL, YP_SSIZE, min, max);
+ int_test("61s", 61LL, YP_STIME, min, max);
+ int_bad_test("20000000001", KNOT_ERANGE, YP_SNONE, min, max);
+ int_bad_test("-20000000001", KNOT_ERANGE, YP_SNONE, min, max);
+ int_bad_test("1x", KNOT_EINVAL, YP_SNONE, min, max);
+ int_bad_test("1sx", KNOT_EINVAL, YP_STIME, min, max);
+
+ /* Boolean tests. */
+ bool_test("on", true);
+ bool_test("off", false);
+ bool_bad_test("onx", KNOT_EINVAL);
+ bool_bad_test("enable", KNOT_EINVAL);
+
+ /* Option tests. */
+ static const knot_lookup_t opts[] = {
+ { 1, "one" },
+ { 10, "ten" },
+ { 255, "max" },
+ { 0, NULL }
+ };
+ opt_test("one", 1, opts);
+ opt_test("ten", 10, opts);
+ opt_test("max", 255, opts);
+ opt_bad_test("onex", KNOT_EINVAL, opts);
+ opt_bad_test("word", KNOT_EINVAL, opts);
+
+ /* String tests. */
+ str_test("Test string!", "Test string!");
+
+ /* Address tests. */
+ addr_test("192.168.123.1", true);
+ addr_test("192.168.123.1@12345", false);
+ addr_test("2001:db8::1", true);
+ addr_test("::1@12345", false);
+ addr_test("/tmp/test.sock", true);
+ addr_bad_test("192.168.123.x", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@1x", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@65536", KNOT_ERANGE);
+
+ /* Address range tests. */
+ addr_range_test("1.1.1.1");
+ addr_range_test("1.1.1.1/0");
+ addr_range_test("1.1.1.1/32");
+ addr_range_test("1.1.1.1-1.2.3.4");
+ addr_range_test("::1");
+ addr_range_test("::1/0");
+ addr_range_test("::1/32");
+ addr_range_test("1::-5::");
+ addr_range_bad_test("unix", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1/", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1/33", KNOT_ERANGE);
+ addr_range_bad_test("1.1.1.1-", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1-::1", KNOT_EINVAL);
+
+ /* Dname tests. */
+ dname_test("example.com.", "\x07""example""\x03""com""\x00");
+
+ /* Hex tests. */
+ hex_test("", "", NULL);
+ hex_test("0x", "", "");
+ hex_test("Hello World!", "Hello World!", NULL);
+ hex_test("0x0155FF", "\x01\x55\xFF", NULL);
+ hex_bad_test("0xA", KNOT_EINVAL);
+
+ /* Base64 tests. */
+ base64_test("Zm9vYmFy", "foobar");
+
+ /* Ref tests. */
+ ref_test("on", true);
+
+ return 0;
+}
diff --git a/tests/libzscanner/TESTS b/tests/libzscanner/TESTS
new file mode 100644
index 0000000..f26b858
--- /dev/null
+++ b/tests/libzscanner/TESTS
@@ -0,0 +1,80 @@
+00-0_general
+00-1_general
+00-2_general
+00-3_general
+00-4_general
+01_owner
+02_class
+03_rrttl
+04-0_ORIGIN
+04-1_ORIGIN
+04-2_ORIGIN
+04-3_ORIGIN
+04-4_ORIGIN
+04-5_ORIGIN
+04-6_ORIGIN
+04-7_ORIGIN
+04-8_ORIGIN
+04-9_ORIGIN
+05-0_TTL
+05-1_TTL
+05-2_TTL
+05-3_TTL
+05-4_TTL
+06-0_INCLUDE
+06-1_INCLUDE
+06-2_INCLUDE
+06-3_INCLUDE
+06-4_INCLUDE
+06-5_INCLUDE
+06-6_INCLUDE
+06-7_INCLUDE
+06-8_INCLUDE
+07-0-rdata
+07-1-rdata
+07-2-rdata
+07-3-rdata
+07-4-rdata
+10_A
+11_AAAA
+12_TXT
+13_SPF
+14_NS
+15_CNAME
+16_PTR
+17_DNAME
+18_MX
+19_AFSDB
+20_RT
+21_KX
+22_HINFO
+23_MINFO
+24_RP
+25_SOA
+26_SRV
+27_NAPTR
+28_TYPE
+29_CERT
+30_KEY
+31_DNSKEY
+32_APL
+33_DS
+34_SSHFP
+35_IPSECKEY
+36_RRSIG
+37_NSEC
+38_DHCID
+39_NSEC3
+40_NSEC3PARAM
+41_TLSA
+42_LOC
+43_EUI48
+44_EUI64
+45_NID
+46_L32
+47_L64
+48_LP
+49_CDS
+50_CDNSKEY
+51_URI
+52_CAA
diff --git a/tests/libzscanner/data/00-0_general.in b/tests/libzscanner/data/00-0_general.in
new file mode 100644
index 0000000..de9c3f0
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.in
@@ -0,0 +1,24 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+t01 IN 10 NS @ ; All items are mentioned
+ IN 10 NS @ ; Same as above without OWNER
+t02 20 IN NS @ ; Switched CLASS and TTL
+ 20 IN NS @ ; Same as above without OWNER
+t03 30 NS @ ; Missing CLASS
+ 30 NS @ ; Same as above without OWNER
+t04 IN NS @ ; Missing TTL
+ IN NS @ ; Same as above without OWNER
+t05 NS @ ; Missing CLASS and TTL
+ NS @ ; Same as above without OWNER
+
+@ ( ) NS ( ; Multiline 1/5
+ ) () ( ; Multiline 2/5
+
+ @ ; Multiline 4/5
+ ) (
+) ; Multiline 6/6
+
+; KO
+@ NS ((@)) ; Nested parentheses - ERROR = STOP PROCESSING!
diff --git a/tests/libzscanner/data/00-0_general.out b/tests/libzscanner/data/00-0_general.out
new file mode 100644
index 0000000..9acf1cb
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.out
@@ -0,0 +1,68 @@
+OWNER=0374303100
+CLASS=0001
+RRTTL=0000000A
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303100
+CLASS=0001
+RRTTL=0000000A
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303200
+CLASS=0001
+RRTTL=00000014
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303200
+CLASS=0001
+RRTTL=00000014
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303300
+CLASS=0001
+RRTTL=0000001E
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303300
+CLASS=0001
+RRTTL=0000001E
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+ERROR=ZS_LEFT_PARENTHESIS
+------
diff --git a/tests/libzscanner/data/00-1_general.in b/tests/libzscanner/data/00-1_general.in
new file mode 100644
index 0000000..2423909
--- /dev/null
+++ b/tests/libzscanner/data/00-1_general.in
@@ -0,0 +1,7 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+@ TXT ( "text" ; Unclosed multiline block
+
+;comment
diff --git a/tests/libzscanner/data/00-1_general.out b/tests/libzscanner/data/00-1_general.out
new file mode 100644
index 0000000..85b0653
--- /dev/null
+++ b/tests/libzscanner/data/00-1_general.out
@@ -0,0 +1,2 @@
+ERROR=ZS_UNCLOSED_MULTILINE
+------
diff --git a/tests/libzscanner/data/00-2_general.in b/tests/libzscanner/data/00-2_general.in
new file mode 100644
index 0000000..460eeb5
--- /dev/null
+++ b/tests/libzscanner/data/00-2_general.in
@@ -0,0 +1 @@
+$ORIGIN .
diff --git a/tests/libzscanner/data/00-2_general.out b/tests/libzscanner/data/00-2_general.out
new file mode 100644
index 0000000..79776a7
--- /dev/null
+++ b/tests/libzscanner/data/00-2_general.out
@@ -0,0 +1,2 @@
+ERROR=ZS_DOS_NEWLINE
+------
diff --git a/tests/libzscanner/data/00-3_general.in b/tests/libzscanner/data/00-3_general.in
new file mode 100644
index 0000000..6ce107c
--- /dev/null
+++ b/tests/libzscanner/data/00-3_general.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+$TTL 1
+
+. NS @ ; No newline \ No newline at end of file
diff --git a/tests/libzscanner/data/00-3_general.out b/tests/libzscanner/data/00-3_general.out
new file mode 100644
index 0000000..bc06672
--- /dev/null
+++ b/tests/libzscanner/data/00-3_general.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
diff --git a/tests/libzscanner/data/00-4_general.in b/tests/libzscanner/data/00-4_general.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/00-4_general.in
diff --git a/tests/libzscanner/data/00-4_general.out b/tests/libzscanner/data/00-4_general.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/00-4_general.out
diff --git a/tests/libzscanner/data/01_owner.in b/tests/libzscanner/data/01_owner.in
new file mode 100644
index 0000000..0108be3
--- /dev/null
+++ b/tests/libzscanner/data/01_owner.in
@@ -0,0 +1,37 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+. NS @ ; The simplest owner
+tld. NS @ ; FQD tld owner
+tld NS @ ; Relative form
+ NS @ ; The previous owner
+*. NS @ ; FQD with asterisk
+* NS @ ; Alone asterisk
+*.* NS @ ; More asterisks
+*a.a*a.** NS @ ; Also possible
+@ NS @ ; Use origin
+0123456789 NS @ ; Digits
+0/25.2.0.192.in-addr.arpa. NS @ ; CIDR notation
+_a_.-b-c-./d/. NS @ ; Allowed characters '_' '-' '/' anywhere
+ABCDEFGHIJKLMNOPQRSTUVWXYZ NS @ ; All upper-case letters
+abcdefghijklmnopqrstuvwxyz NS @ ; All lower-case letters
+\000\0320\ \\\"\.\@\*.tld. NS @ ; Label with special chars
+b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. NS @ ; IPv6 reverse
+12345678901234567890123456789012345678901234567890123456789012\051.tld. NS @ ; Label of maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. NS @ ; Domain name of maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901 NS @ ; Domain name of maximal length (after appending origin)
+
+; KO
+& NS @ ; Bad (unslashed) character
+ NS @ ; Bad previous
+.a NS @ ; Leading dot
+@@ NS @ ; Double @@
+.. NS @ ; Missing label between dots
+\1 NS @ ; Slash notation requires 3 digits
+\12 NS @ ; Slash notation requires 3 digits
+12345678901234567890123456789012345678901234567890123456789012\0514.tld. NS @ ; Label exceeded maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. NS @ ; Domain name exceeded maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012 NS @ ; Domain name exceeded maximal length (after appending origin)
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.1 NS @ ; Domain name exceeded maximal length (maximal dname length check is after each valid label)
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.123456789012345678901234567890123456789012345678901234567890123 NS @ ; Domain name exceeded maximal length (maximal dname length check is after each valid label)
diff --git a/tests/libzscanner/data/01_owner.out b/tests/libzscanner/data/01_owner.out
new file mode 100644
index 0000000..26a8454
--- /dev/null
+++ b/tests/libzscanner/data/01_owner.out
@@ -0,0 +1,138 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=022A6103612A61022A2A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0A3031323334353637383900
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=04302F3235013201300331393207696E2D61646472046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=035F615F052D622D632D032F642F00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=1A4142434445464748494A4B4C4D4E4F505152535455565758595A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=1A6162636465666768696A6B6C6D6E6F707172737475767778797A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=09002030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_OWNER
+------
+WARNG=ZS_BAD_PREVIOUS_OWNER
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_LABEL_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
diff --git a/tests/libzscanner/data/02_class.in b/tests/libzscanner/data/02_class.in
new file mode 100644
index 0000000..c4347e7
--- /dev/null
+++ b/tests/libzscanner/data/02_class.in
@@ -0,0 +1,10 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ IN NS @ ; The only accepted class IN
+@ in NS @ ; Class in lower-case
+
+; KO
+@ CH NS @ ; Unsupported class
+@ CLASS1 NS @ ; Unsupported notation
diff --git a/tests/libzscanner/data/02_class.out b/tests/libzscanner/data/02_class.out
new file mode 100644
index 0000000..69daae3
--- /dev/null
+++ b/tests/libzscanner/data/02_class.out
@@ -0,0 +1,16 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
diff --git a/tests/libzscanner/data/03_rrttl.in b/tests/libzscanner/data/03_rrttl.in
new file mode 100644
index 0000000..7c57d73
--- /dev/null
+++ b/tests/libzscanner/data/03_rrttl.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ 0 NS @ ; Minimal ttl
+@ 3600 NS @ ; Inner value
+@ 4294967295 NS @ ; Maximal ttl
+@ 1S NS @ ; 1 second (upper-case)
+@ 1s NS @ ; 1 second (lower-case)
+@ 1M NS @ ; 1 minute (upper-case)
+@ 1m NS @ ; 1 minute (lower-case)
+@ 1H NS @ ; 1 hour (upper-case)
+@ 1h NS @ ; 1 hour (lower-case)
+@ 1D NS @ ; 1 day (upper-case)
+@ 1d NS @ ; 1 day (lower-case)
+@ 1W NS @ ; 1 week (upper-case)
+@ 1w NS @ ; 1 week (lower-case)
+@ 1w1d1h1m1s NS @ ; More time units
+@ 1s1m1m NS @ ; Same time units, non decreasing order
+
+; KO
+@ -1 NS @ ; Negative ttl
+@ 4294967296 NS @ ; 32bit overflow
+@ 100000000W NS @ ; 32bit overflow
+@ 4294967295s1w NS @ ; 32bit overflow
+@ 1x NS @ ; Unknown time unit
diff --git a/tests/libzscanner/data/03_rrttl.out b/tests/libzscanner/data/03_rrttl.out
new file mode 100644
index 0000000..f5cc907
--- /dev/null
+++ b/tests/libzscanner/data/03_rrttl.out
@@ -0,0 +1,100 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=FFFFFFFF
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=000A9A4D
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000079
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_BAD_TIME_UNIT
+------
diff --git a/tests/libzscanner/data/04-0_ORIGIN.in b/tests/libzscanner/data/04-0_ORIGIN.in
new file mode 100644
index 0000000..4d65ec4
--- /dev/null
+++ b/tests/libzscanner/data/04-0_ORIGIN.in
@@ -0,0 +1,29 @@
+$TTL 1
+
+; OK
+$ORIGIN . ; Root domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN tld. ; 1. level domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN second.tld. ; 2. level domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN \0320\ \\\"\.\@\*.tld. ; Label with special chars
+@ NS @ ; Use origin
+$ORIGIN b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ; IPv6 reverse
+@ NS @ ; Use origin
+$ORIGIN 12345678901234567890123456789012345678901234567890123456789012\051.tld. ; Label of maximal length
+@ NS @ ; Use origin
+$ORIGIN 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. ; Domain name of maximal length
+@ NS @ ; Use origin
+$origin . ; Lower-case
+@ NS @ ; Use origin
+
+; KO
+$ORIGIN ; Empty input
+. NS . ; Is OK, but shouldn't be processed due to previous error stop!
diff --git a/tests/libzscanner/data/04-0_ORIGIN.out b/tests/libzscanner/data/04-0_ORIGIN.out
new file mode 100644
index 0000000..92dc459
--- /dev/null
+++ b/tests/libzscanner/data/04-0_ORIGIN.out
@@ -0,0 +1,86 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016103746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=067365636F6E6403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=0161067365636F6E6403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=082030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+------
+OWNER=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-1_ORIGIN.in b/tests/libzscanner/data/04-1_ORIGIN.in
new file mode 100644
index 0000000..871c064
--- /dev/null
+++ b/tests/libzscanner/data/04-1_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN tld ; Not FQD
diff --git a/tests/libzscanner/data/04-1_ORIGIN.out b/tests/libzscanner/data/04-1_ORIGIN.out
new file mode 100644
index 0000000..467de10
--- /dev/null
+++ b/tests/libzscanner/data/04-1_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-2_ORIGIN.in b/tests/libzscanner/data/04-2_ORIGIN.in
new file mode 100644
index 0000000..8dd2e20
--- /dev/null
+++ b/tests/libzscanner/data/04-2_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN % ; Bad (unslashed) character
diff --git a/tests/libzscanner/data/04-2_ORIGIN.out b/tests/libzscanner/data/04-2_ORIGIN.out
new file mode 100644
index 0000000..467de10
--- /dev/null
+++ b/tests/libzscanner/data/04-2_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-3_ORIGIN.in b/tests/libzscanner/data/04-3_ORIGIN.in
new file mode 100644
index 0000000..26d1872
--- /dev/null
+++ b/tests/libzscanner/data/04-3_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN .. ; Missing label between dots
diff --git a/tests/libzscanner/data/04-3_ORIGIN.out b/tests/libzscanner/data/04-3_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-3_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-4_ORIGIN.in b/tests/libzscanner/data/04-4_ORIGIN.in
new file mode 100644
index 0000000..0be4721
--- /dev/null
+++ b/tests/libzscanner/data/04-4_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN \1 ; Slash notation requires 3 digits
diff --git a/tests/libzscanner/data/04-4_ORIGIN.out b/tests/libzscanner/data/04-4_ORIGIN.out
new file mode 100644
index 0000000..03a0738
--- /dev/null
+++ b/tests/libzscanner/data/04-4_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/04-5_ORIGIN.in b/tests/libzscanner/data/04-5_ORIGIN.in
new file mode 100644
index 0000000..170d465
--- /dev/null
+++ b/tests/libzscanner/data/04-5_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN \12 ; Slash notation requires 3 digits
diff --git a/tests/libzscanner/data/04-5_ORIGIN.out b/tests/libzscanner/data/04-5_ORIGIN.out
new file mode 100644
index 0000000..03a0738
--- /dev/null
+++ b/tests/libzscanner/data/04-5_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/04-6_ORIGIN.in b/tests/libzscanner/data/04-6_ORIGIN.in
new file mode 100644
index 0000000..db9652d
--- /dev/null
+++ b/tests/libzscanner/data/04-6_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN .tld ; Leading dot
diff --git a/tests/libzscanner/data/04-6_ORIGIN.out b/tests/libzscanner/data/04-6_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-6_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-7_ORIGIN.in b/tests/libzscanner/data/04-7_ORIGIN.in
new file mode 100644
index 0000000..556646c
--- /dev/null
+++ b/tests/libzscanner/data/04-7_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN tld. x ; Unexpected item
diff --git a/tests/libzscanner/data/04-7_ORIGIN.out b/tests/libzscanner/data/04-7_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-7_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-8_ORIGIN.in b/tests/libzscanner/data/04-8_ORIGIN.in
new file mode 100644
index 0000000..445cbec
--- /dev/null
+++ b/tests/libzscanner/data/04-8_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN 12345678901234567890123456789012345678901234567890123456789012\0514.tld. ; Label exceeded maximal length
diff --git a/tests/libzscanner/data/04-8_ORIGIN.out b/tests/libzscanner/data/04-8_ORIGIN.out
new file mode 100644
index 0000000..788a495
--- /dev/null
+++ b/tests/libzscanner/data/04-8_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_LABEL_OVERFLOW
+------
diff --git a/tests/libzscanner/data/04-9_ORIGIN.in b/tests/libzscanner/data/04-9_ORIGIN.in
new file mode 100644
index 0000000..e0a4b95
--- /dev/null
+++ b/tests/libzscanner/data/04-9_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. ; Domain name exceeded maximal length
diff --git a/tests/libzscanner/data/04-9_ORIGIN.out b/tests/libzscanner/data/04-9_ORIGIN.out
new file mode 100644
index 0000000..eef0ab5
--- /dev/null
+++ b/tests/libzscanner/data/04-9_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_DNAME_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-0_TTL.in b/tests/libzscanner/data/05-0_TTL.in
new file mode 100644
index 0000000..baa8f38
--- /dev/null
+++ b/tests/libzscanner/data/05-0_TTL.in
@@ -0,0 +1,36 @@
+$ORIGIN .
+
+; OK
+$TTL 0 ; Minimal ttl
+@ NS @ ; Use ttl
+$TTL 3600 ; Inner value
+@ NS @ ; Use ttl
+$TTL 4294967295 ; Maximal ttl
+@ NS @ ; Use ttl
+$TTL 1S ; 1 second (upper-case)
+@ NS @ ; Use ttl
+$TTL 1s ; 1 second (lower-case)
+@ NS @ ; Use ttl
+$TTL 1M ; 1 minute (upper-case)
+@ NS @ ; Use ttl
+$TTL 1m ; 1 minute (lower-case)
+@ NS @ ; Use ttl
+$TTL 1H ; 1 hour (upper-case)
+@ NS @ ; Use ttl
+$TTL 1h ; 1 hour (lower-case)
+@ NS @ ; Use ttl
+$TTL 1D ; 1 day (upper-case)
+@ NS @ ; Use ttl
+$TTL 1d ; 1 day (lower-case)
+@ NS @ ; Use ttl
+$TTL 1W ; 1 week (upper-case)
+@ NS @ ; Use ttl
+$TTL 1w ; 1 week (lower-case)
+@ NS @ ; Use ttl
+$TTL 1w1d1h1m1s ; More time units
+@ NS @ ; Use ttl
+$TTL 1s1m1m ; Same time units, non decreasing order
+@ NS @ ; Use ttl
+
+; KO
+$TTL -1 ; Negative ttl
diff --git a/tests/libzscanner/data/05-0_TTL.out b/tests/libzscanner/data/05-0_TTL.out
new file mode 100644
index 0000000..93c7651
--- /dev/null
+++ b/tests/libzscanner/data/05-0_TTL.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=FFFFFFFF
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=000A9A4D
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000079
+RTYPE=0002
+RDATA=00
+------
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/05-1_TTL.in b/tests/libzscanner/data/05-1_TTL.in
new file mode 100644
index 0000000..519fe79
--- /dev/null
+++ b/tests/libzscanner/data/05-1_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 4294967296 ; 32bit overflow
diff --git a/tests/libzscanner/data/05-1_TTL.out b/tests/libzscanner/data/05-1_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-1_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-2_TTL.in b/tests/libzscanner/data/05-2_TTL.in
new file mode 100644
index 0000000..ed112da
--- /dev/null
+++ b/tests/libzscanner/data/05-2_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 100000000W ; 32bit overflow
diff --git a/tests/libzscanner/data/05-2_TTL.out b/tests/libzscanner/data/05-2_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-2_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-3_TTL.in b/tests/libzscanner/data/05-3_TTL.in
new file mode 100644
index 0000000..d96c9ba
--- /dev/null
+++ b/tests/libzscanner/data/05-3_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 4294967295s1w ; 32bit overflow
diff --git a/tests/libzscanner/data/05-3_TTL.out b/tests/libzscanner/data/05-3_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-3_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-4_TTL.in b/tests/libzscanner/data/05-4_TTL.in
new file mode 100644
index 0000000..efaf4d0
--- /dev/null
+++ b/tests/libzscanner/data/05-4_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 1x ; Unknown time unit
diff --git a/tests/libzscanner/data/05-4_TTL.out b/tests/libzscanner/data/05-4_TTL.out
new file mode 100644
index 0000000..d4bef98
--- /dev/null
+++ b/tests/libzscanner/data/05-4_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_TIME_UNIT
+------
diff --git a/tests/libzscanner/data/06-0_INCLUDE.in b/tests/libzscanner/data/06-0_INCLUDE.in
new file mode 100644
index 0000000..962e6c9
--- /dev/null
+++ b/tests/libzscanner/data/06-0_INCLUDE.in
@@ -0,0 +1,29 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+0. NS @
+
+$INCLUDE ./includes/include1 ; Relative path without origin
+1. NS @
+
+$INCLUDE "./includes/include2" . ; Quoted filename and the simplest origin
+2. NS @
+
+$INCLUDE ./includes/include\050 tld. ; Simple origin
+3. NS @
+
+$INCLUDE \./includes/include2 _a_.-b-c-./d/. ; Slashed character in file name, allowed characters in origin
+4. NS @
+
+$INCLUDE ./includes/include2 \0320\ \\\"\.\@\*.tld. ; Origin with special chars
+5. NS @
+
+$INCLUDE @TMPDIR@/includes/include2 ; Absolute path without origin
+6. NS @
+
+$INCLUDE @TMPDIR@/includes/include2 tld. ; Absolute path with origin
+7. NS @
+
+; KO (DISABLED - different results)
+;$INCLUDE ; Empty parameters
diff --git a/tests/libzscanner/data/06-0_INCLUDE.out b/tests/libzscanner/data/06-0_INCLUDE.out
new file mode 100644
index 0000000..2536f72
--- /dev/null
+++ b/tests/libzscanner/data/06-0_INCLUDE.out
@@ -0,0 +1,138 @@
+OWNER=013000
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016105746C64316100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=016105746C64316200
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=05746C64316200
+------
+OWNER=013100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013200
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016203746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013300
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162035F615F052D622D632D032F642F00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=035F615F052D622D632D032F642F00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162082030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013600
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016203746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013700
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
diff --git a/tests/libzscanner/data/06-1_INCLUDE.in b/tests/libzscanner/data/06-1_INCLUDE.in
new file mode 100644
index 0000000..194fb52
--- /dev/null
+++ b/tests/libzscanner/data/06-1_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include1 a ; Origin is not FQDN
diff --git a/tests/libzscanner/data/06-1_INCLUDE.out b/tests/libzscanner/data/06-1_INCLUDE.out
new file mode 100644
index 0000000..6634921
--- /dev/null
+++ b/tests/libzscanner/data/06-1_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_INCLUDE_ORIGIN
+------
diff --git a/tests/libzscanner/data/06-2_INCLUDE.in b/tests/libzscanner/data/06-2_INCLUDE.in
new file mode 100644
index 0000000..21bcf05
--- /dev/null
+++ b/tests/libzscanner/data/06-2_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include1 % ; Bad origin
diff --git a/tests/libzscanner/data/06-2_INCLUDE.out b/tests/libzscanner/data/06-2_INCLUDE.out
new file mode 100644
index 0000000..6634921
--- /dev/null
+++ b/tests/libzscanner/data/06-2_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_INCLUDE_ORIGIN
+------
diff --git a/tests/libzscanner/data/06-3_INCLUDE.in b/tests/libzscanner/data/06-3_INCLUDE.in
new file mode 100644
index 0000000..f7079b4
--- /dev/null
+++ b/tests/libzscanner/data/06-3_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE @TMPDIR@/ ; Given file is a directory
diff --git a/tests/libzscanner/data/06-3_INCLUDE.out b/tests/libzscanner/data/06-3_INCLUDE.out
new file mode 100644
index 0000000..6823154
--- /dev/null
+++ b/tests/libzscanner/data/06-3_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_FILE_INVALID
+------
diff --git a/tests/libzscanner/data/06-4_INCLUDE.in b/tests/libzscanner/data/06-4_INCLUDE.in
new file mode 100644
index 0000000..3cdc79d
--- /dev/null
+++ b/tests/libzscanner/data/06-4_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE @TMPDIR@/file-doesnt-exist ; File doesn't exist
diff --git a/tests/libzscanner/data/06-4_INCLUDE.out b/tests/libzscanner/data/06-4_INCLUDE.out
new file mode 100644
index 0000000..e09e5d1
--- /dev/null
+++ b/tests/libzscanner/data/06-4_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_FILE_OPEN
+------
diff --git a/tests/libzscanner/data/06-5_INCLUDE.in b/tests/libzscanner/data/06-5_INCLUDE.in
new file mode 100644
index 0000000..1d65812
--- /dev/null
+++ b/tests/libzscanner/data/06-5_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include3 ; Blank include file
diff --git a/tests/libzscanner/data/06-5_INCLUDE.out b/tests/libzscanner/data/06-5_INCLUDE.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/06-5_INCLUDE.out
diff --git a/tests/libzscanner/data/06-6_INCLUDE.in b/tests/libzscanner/data/06-6_INCLUDE.in
new file mode 100644
index 0000000..cbfda64
--- /dev/null
+++ b/tests/libzscanner/data/06-6_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include4 ; Include file contains warning
diff --git a/tests/libzscanner/data/06-6_INCLUDE.out b/tests/libzscanner/data/06-6_INCLUDE.out
new file mode 100644
index 0000000..a9c0259
--- /dev/null
+++ b/tests/libzscanner/data/06-6_INCLUDE.out
@@ -0,0 +1,4 @@
+WARNG=ZS_BAD_RDATA
+------
+ERROR=ZS_UNPROCESSED_INCLUDE
+------
diff --git a/tests/libzscanner/data/06-7_INCLUDE.in b/tests/libzscanner/data/06-7_INCLUDE.in
new file mode 100644
index 0000000..24deef7
--- /dev/null
+++ b/tests/libzscanner/data/06-7_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include5 ; Include file contains error
diff --git a/tests/libzscanner/data/06-7_INCLUDE.out b/tests/libzscanner/data/06-7_INCLUDE.out
new file mode 100644
index 0000000..cbbb0c1
--- /dev/null
+++ b/tests/libzscanner/data/06-7_INCLUDE.out
@@ -0,0 +1,4 @@
+ERROR=ZS_BAD_NUMBER
+------
+ERROR=ZS_UNPROCESSED_INCLUDE
+------
diff --git a/tests/libzscanner/data/06-8_INCLUDE.in b/tests/libzscanner/data/06-8_INCLUDE.in
new file mode 100644
index 0000000..c856668
--- /dev/null
+++ b/tests/libzscanner/data/06-8_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include6 ; Include file contains include
diff --git a/tests/libzscanner/data/06-8_INCLUDE.out b/tests/libzscanner/data/06-8_INCLUDE.out
new file mode 100644
index 0000000..4581388
--- /dev/null
+++ b/tests/libzscanner/data/06-8_INCLUDE.out
@@ -0,0 +1,12 @@
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
diff --git a/tests/libzscanner/data/07-0-rdata.in b/tests/libzscanner/data/07-0-rdata.in
new file mode 100644
index 0000000..a79403f
--- /dev/null
+++ b/tests/libzscanner/data/07-0-rdata.in
@@ -0,0 +1 @@
+@ A \ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-0-rdata.out b/tests/libzscanner/data/07-0-rdata.out
new file mode 100644
index 0000000..ed303f6
--- /dev/null
+++ b/tests/libzscanner/data/07-0-rdata.out
@@ -0,0 +1,2 @@
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
diff --git a/tests/libzscanner/data/07-1-rdata.in b/tests/libzscanner/data/07-1-rdata.in
new file mode 100644
index 0000000..7a5a11a
--- /dev/null
+++ b/tests/libzscanner/data/07-1-rdata.in
@@ -0,0 +1 @@
+@ TXT \ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-1-rdata.out b/tests/libzscanner/data/07-1-rdata.out
new file mode 100644
index 0000000..1702677
--- /dev/null
+++ b/tests/libzscanner/data/07-1-rdata.out
@@ -0,0 +1,2 @@
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/07-2-rdata.in b/tests/libzscanner/data/07-2-rdata.in
new file mode 100644
index 0000000..7a4c8fe
--- /dev/null
+++ b/tests/libzscanner/data/07-2-rdata.in
@@ -0,0 +1 @@
+@ TXT \092 \ No newline at end of file
diff --git a/tests/libzscanner/data/07-2-rdata.out b/tests/libzscanner/data/07-2-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-2-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/07-3-rdata.in b/tests/libzscanner/data/07-3-rdata.in
new file mode 100644
index 0000000..338b212
--- /dev/null
+++ b/tests/libzscanner/data/07-3-rdata.in
@@ -0,0 +1 @@
+@ TXT \\ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-3-rdata.out b/tests/libzscanner/data/07-3-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-3-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/07-4-rdata.in b/tests/libzscanner/data/07-4-rdata.in
new file mode 100644
index 0000000..74a45dd
--- /dev/null
+++ b/tests/libzscanner/data/07-4-rdata.in
@@ -0,0 +1 @@
+@ TXT \# 2 015C \ No newline at end of file
diff --git a/tests/libzscanner/data/07-4-rdata.out b/tests/libzscanner/data/07-4-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-4-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/10_A.in b/tests/libzscanner/data/10_A.in
new file mode 100644
index 0000000..6c7c90a
--- /dev/null
+++ b/tests/libzscanner/data/10_A.in
@@ -0,0 +1,19 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ A 0.0.0.0 ; Minimal ipv4 address
+@ A 255.255.255.255 ; Maximal ipv4 address
+@ A \# 4 00000000 ; Hexadecimal rdata
+@ TYPE1 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE1 0.0.0.0 ; TYPE
+@ a 0.0.0.0 ; Type in lower-case
+
+; KO
+@ A
+@ A ; Empty rdata
+@ A \# 0 ; Hex empty rdata
+@ A 0.0.0.256 ; 8bit overflow
+@ A 0.0.0 ; Short address
+@ A 0.0.0.A ; Bad character
+@ A 0.0.0.0 1.1.1.1 ; Unexpected item
diff --git a/tests/libzscanner/data/10_A.out b/tests/libzscanner/data/10_A.out
new file mode 100644
index 0000000..dda68ab
--- /dev/null
+++ b/tests/libzscanner/data/10_A.out
@@ -0,0 +1,50 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/11_AAAA.in b/tests/libzscanner/data/11_AAAA.in
new file mode 100644
index 0000000..48c4b37
--- /dev/null
+++ b/tests/libzscanner/data/11_AAAA.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ AAAA 0:0:0:0:0:0:0:0 ; Minimal ipv6 address
+@ AAAA FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF ; Maximal ipv6 address
+@ AAAA \# 16 00000000 00000000 00000000 00000000 ; Hexadecimal rdata
+@ TYPE28 \# 16 00000000 00000000 00000000 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE28 0:0:0:0:0:0:0:0 ; TYPE
+@ AAAA 0::1.2.3.4 ; ipv6 address based on ipv4 address
+@ AAAA :: ; Double colon
+@ aaAA :: ; Type in lower-case
+
+; KO
+@ AAAA
+@ AAAA ; Empty rdata
+@ AAAA \# 0 ; Hex empty rdata
+@ AAAA 0::FFFFF ; 16bit overflow
+@ AAAA 0:0:0:0:0:0:0 ; Short address
+@ AAAA 0:0:0:0:0:0:0:X ; Bad character
+@ AAAA :: :: ; Unexpected item
diff --git a/tests/libzscanner/data/11_AAAA.out b/tests/libzscanner/data/11_AAAA.out
new file mode 100644
index 0000000..0cb174a
--- /dev/null
+++ b/tests/libzscanner/data/11_AAAA.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/12_TXT.in b/tests/libzscanner/data/12_TXT.in
new file mode 100644
index 0000000..cdd0eaa
--- /dev/null
+++ b/tests/libzscanner/data/12_TXT.in
@@ -0,0 +1,37 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TXT "" ; Blank string
+@ TXT a ; One char string
+@ TXT \ ; One space char
+@ TXT "\ " ; One space char in quotes
+@ TXT \021 ; One unprintable char
+@ TXT "\\ \"" ; Special chars
+@ TXT "" "test1" "\255" test2 ; Array of text strings
+@ TXT "" "" "" ; Array of blank strings
+@ TXT first \# "\#" ; Array with special string
+@ TXT \0320\ \\\"\;\.\@\*.tld. ; Special domain as a string
+@ TXT " !\"#$%&'()*+,-./0123456789:;<=>?@" ; First part of all printables
+@ TXT "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" ; Second part of all printables
+@ TXT "abcdefghijklmnopqrstuvwxyz{|}~" ; Third part of all printables
+@ TXT \# 1 00 ; Hexadecimal rdata
+@ TYPE16 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE16 "" ; TYPE
+@ TXT ( ; Special multi-line string
+"first
+second"
+third ; Second string
+)
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" ; Text string of maximal length (255 chars)
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" ; Minimum overflowed text string which should be divided into two strings.
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053\054" ; Minimum overflowed text string which should be divided into two strings (decimal version).
+@ txt "" ; Type in lower-case
+
+; KO
+@ TXT
+@ TXT ; Empty rdata
+@ TXT \# 0 ; Hex empty rdata
+@ TXT \01 ; Missing digit in decimal notation
+@ TXT \256 ; 8bit overflow in decimal notation
+@ TXT """ ; '"' char without forward slash
diff --git a/tests/libzscanner/data/12_TXT.out b/tests/libzscanner/data/12_TXT.out
new file mode 100644
index 0000000..283b1e0
--- /dev/null
+++ b/tests/libzscanner/data/12_TXT.out
@@ -0,0 +1,138 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0115
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=035C2022
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0005746573743101FF057465737432
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=05666972737401230123
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0E2030205C223B2E402A2E746C642E
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=21202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=204142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=1E6162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0C66697273740A7365636F6E64057468697264
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E3132333435
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E31323334350136
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E31323334350136
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_TEXT
+------
diff --git a/tests/libzscanner/data/13_SPF.in b/tests/libzscanner/data/13_SPF.in
new file mode 100644
index 0000000..0ad6c84
--- /dev/null
+++ b/tests/libzscanner/data/13_SPF.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The SPF is the same as the TXT, so there are the differences and basics only.
+
+; OK
+@ SPF "" "test1" "\255" test2 ; Array of text strings
+@ SPF \# 1 00 ; Hexadecimal rdata
+@ TYPE99 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE99 "" ; TYPE
+@ spf "" ; Type in lower-case
+
+; KO
+@ SPF
diff --git a/tests/libzscanner/data/13_SPF.out b/tests/libzscanner/data/13_SPF.out
new file mode 100644
index 0000000..be7fb35
--- /dev/null
+++ b/tests/libzscanner/data/13_SPF.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=0005746573743101FF057465737432
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/14_NS.in b/tests/libzscanner/data/14_NS.in
new file mode 100644
index 0000000..b56a579
--- /dev/null
+++ b/tests/libzscanner/data/14_NS.in
@@ -0,0 +1,38 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NS . ; The simplest owner
+@ NS tld. ; FQD tld owner
+@ NS tld ; Relative form
+@ NS *. ; FQD with asterisk
+@ NS * ; Alone asterisk
+@ NS *.* ; More asterisks
+@ NS *a.a*a.** ; Also possible
+@ NS @ ; Use origin
+@ NS 0123456789 ; Digits
+@ NS _a_.-b-c-./d/. ; Allowed characters '_' '-' '/' anywhere
+@ NS ABCDEFGHIJKLMNOPQRSTUVWXYZ ; All upper-case letters
+@ NS abcdefghijklmnopqrstuvwxyz ; All lower-case letters
+@ NS \0320\ \\\"\.\@\*.tld. ; Label with special chars
+@ NS b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ; IPv6 reverse
+@ NS 123456789012345678901234567890123456789012345678901234567890123.tld. ; Label of maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. ; Domain name of maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901 ; Domain name of maximal length (after appending origin)
+@ TYPE2 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE2 @ ; TYPE
+@ ns @ ; Type in lower-case
+
+; KO
+@ NS
+@ NS ; Empty rdata
+@ NS \# 0 ; Hex empty rdata
+@ NS & ; Bad (unslashed) character
+@ NS @@ ; Double @@
+@ NS .. ; Missing label between dots
+@ NS \1 ; Slash notation requires 3 digits
+@ NS \12 ; Slash notation requires 3 digits
+@ NS 1234567890123456789012345678901234567890123456789012345678901234.tld. ; Label exceeded maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. ; Domain name exceeded maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012 ; Domain name exceeded maximal length (after appending origin)
+@ NS . x ; Unexpected item
diff --git a/tests/libzscanner/data/14_NS.out b/tests/libzscanner/data/14_NS.out
new file mode 100644
index 0000000..08d739a
--- /dev/null
+++ b/tests/libzscanner/data/14_NS.out
@@ -0,0 +1,144 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=022A6103612A61022A2A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0A3031323334353637383900
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=035F615F052D622D632D032F642F00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=1A4142434445464748494A4B4C4D4E4F505152535455565758595A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=1A6162636465666768696A6B6C6D6E6F707172737475767778797A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_LABEL_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/15_CNAME.in b/tests/libzscanner/data/15_CNAME.in
new file mode 100644
index 0000000..806b8f9
--- /dev/null
+++ b/tests/libzscanner/data/15_CNAME.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The CNAME is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ CNAME test.example.com ; Relative dname
+@ CNAME \# 1 00 ; Hexadecimal rdata
+@ TYPE5 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE5 @ ; TYPE
+@ cname @ ; Type in lower-case
+
+; KO
+@ CNAME
diff --git a/tests/libzscanner/data/15_CNAME.out b/tests/libzscanner/data/15_CNAME.out
new file mode 100644
index 0000000..3c3a55d
--- /dev/null
+++ b/tests/libzscanner/data/15_CNAME.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/16_PTR.in b/tests/libzscanner/data/16_PTR.in
new file mode 100644
index 0000000..3694e7d
--- /dev/null
+++ b/tests/libzscanner/data/16_PTR.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The PTR is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ PTR test.example.com ; Relative dname
+@ PTR \# 1 00 ; Hexadecimal rdata
+@ TYPE12 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE12 @ ; TYPE
+@ ptr @ ; Type in lower-case
+
+; KO
+@ PTR
diff --git a/tests/libzscanner/data/16_PTR.out b/tests/libzscanner/data/16_PTR.out
new file mode 100644
index 0000000..55d94b9
--- /dev/null
+++ b/tests/libzscanner/data/16_PTR.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/17_DNAME.in b/tests/libzscanner/data/17_DNAME.in
new file mode 100644
index 0000000..5e9ba71
--- /dev/null
+++ b/tests/libzscanner/data/17_DNAME.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The DNAME is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ DNAME test.example.com ; Relative dname
+@ DNAME \# 1 00 ; Hexadecimal rdata
+@ TYPE39 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE39 @ ; TYPE
+@ dname @ ; Type in lower-case
+
+; KO
+@ DNAME
diff --git a/tests/libzscanner/data/17_DNAME.out b/tests/libzscanner/data/17_DNAME.out
new file mode 100644
index 0000000..35f11d8
--- /dev/null
+++ b/tests/libzscanner/data/17_DNAME.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/18_MX.in b/tests/libzscanner/data/18_MX.in
new file mode 100644
index 0000000..61e6afb
--- /dev/null
+++ b/tests/libzscanner/data/18_MX.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; For more tests on dname see NS test (same processing)
+
+; OK
+@ MX 0 @ ; Minimal priority
+@ MX 65535 @ ; Maximal priority
+@ MX 1 mail ; Relative dname
+@ MX 1 mail.tld. ; Absolute dname
+@ MX \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE15 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE15 1 @ ; TYPE
+@ mx 1 @ ; Type in lower-case
+
+; KO
+@ MX
+@ MX ; Empty rdata
+@ MX \# 0 ; Hex empty rdata
+@ MX -1 @ ; Negative number
+@ MX 65536 @ ; 16bit overflow
+@ MX 1 $ ; Bad dname
+@ MX 0 @ x ; Unexpected item
diff --git a/tests/libzscanner/data/18_MX.out b/tests/libzscanner/data/18_MX.out
new file mode 100644
index 0000000..0a50038
--- /dev/null
+++ b/tests/libzscanner/data/18_MX.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=0001046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/19_AFSDB.in b/tests/libzscanner/data/19_AFSDB.in
new file mode 100644
index 0000000..be569e4
--- /dev/null
+++ b/tests/libzscanner/data/19_AFSDB.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The AFSDB is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ AFSDB 1 mail ; Relative dname
+@ AFSDB \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE18 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE18 1 @ ; TYPE
+@ afsdb 1 @ ; Type in lower-case
+
+; KO
+@ AFSDB
diff --git a/tests/libzscanner/data/19_AFSDB.out b/tests/libzscanner/data/19_AFSDB.out
new file mode 100644
index 0000000..b5c77eb
--- /dev/null
+++ b/tests/libzscanner/data/19_AFSDB.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/20_RT.in b/tests/libzscanner/data/20_RT.in
new file mode 100644
index 0000000..3bc774d
--- /dev/null
+++ b/tests/libzscanner/data/20_RT.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The RT is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ RT 1 mail ; Relative dname
+@ RT \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE21 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE21 1 @ ; TYPE
+@ rt 1 @ ; Type in lower-case
+
+; KO
+@ RT
diff --git a/tests/libzscanner/data/20_RT.out b/tests/libzscanner/data/20_RT.out
new file mode 100644
index 0000000..8be89a3
--- /dev/null
+++ b/tests/libzscanner/data/20_RT.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/21_KX.in b/tests/libzscanner/data/21_KX.in
new file mode 100644
index 0000000..529480d
--- /dev/null
+++ b/tests/libzscanner/data/21_KX.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The KX is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ KX 1 mail ; Relative dname
+@ KX \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE36 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE36 1 @ ; TYPE
+@ kx 1 @ ; Type in lower-case
+
+; KO
+@ KX
diff --git a/tests/libzscanner/data/21_KX.out b/tests/libzscanner/data/21_KX.out
new file mode 100644
index 0000000..1bc91e6
--- /dev/null
+++ b/tests/libzscanner/data/21_KX.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/22_HINFO.in b/tests/libzscanner/data/22_HINFO.in
new file mode 100644
index 0000000..4cd5ab8
--- /dev/null
+++ b/tests/libzscanner/data/22_HINFO.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ HINFO "" "" ; Blank string
+@ HINFO a b ; One char string
+@ HINFO \ \ ; One space char
+@ HINFO "\ " "\ " ; One space char in quotes
+@ HINFO \021 \022 ; One unprintable char
+@ HINFO "\\ \"" "\\ \"" ; Special chars
+@ HINFO first \# ; Array with special string
+@ HINFO \# 2 00 00 ; Hexadecimal rdata
+@ TYPE13 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE13 "" "" ; TYPE
+@ HINFO "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" "" ; Text string of maximal length (255 chars)
+@ hinfo "" "" ; Type in lower-case
+
+; KO
+@ HINFO
+@ HINFO ; Empty rdata
+@ HINFO \# 0 ; Hex empty rdata
+@ HINFO \01 "" ; Missing digit in decimal notation
+@ HINFO \256 "" ; 8bit overflow in decimal notation
+@ HINFO """ "" ; '"' char without forward slash
+@ HINFO "" "" "" ; Unexpected item
+@ HINFO "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" "" ; Maximal length overflow
diff --git a/tests/libzscanner/data/22_HINFO.out b/tests/libzscanner/data/22_HINFO.out
new file mode 100644
index 0000000..fa66729
--- /dev/null
+++ b/tests/libzscanner/data/22_HINFO.out
@@ -0,0 +1,88 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01610162
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01200120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01200120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01150116
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=035C2022035C2022
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0566697273740123
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E313233343500
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_TEXT
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_ITEM_OVERFLOW
+------
diff --git a/tests/libzscanner/data/23_MINFO.in b/tests/libzscanner/data/23_MINFO.in
new file mode 100644
index 0000000..7b34c60
--- /dev/null
+++ b/tests/libzscanner/data/23_MINFO.in
@@ -0,0 +1,18 @@
+$ORIGIN .
+$TTL 1
+
+; For more tests on dname see NS test (same processing)
+
+; OK
+@ MINFO . . ; The simplest dnames
+@ MINFO @ @ ; Use origin
+@ MINFO mail mail.tld. ; Relative and absolute dnames
+@ MINFO \# 2 00 00 ; Hexadecimal rdata
+@ TYPE14 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE14 @ @ ; TYPE
+@ minfo @ @ ; Type in lower-case
+
+; KO
+@ MINFO
+@ MINFO ; Empty rdata
+@ MINFO \# 0 ; Hex empty rdata
diff --git a/tests/libzscanner/data/23_MINFO.out b/tests/libzscanner/data/23_MINFO.out
new file mode 100644
index 0000000..8154c0c
--- /dev/null
+++ b/tests/libzscanner/data/23_MINFO.out
@@ -0,0 +1,48 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=046D61696C00046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/24_RP.in b/tests/libzscanner/data/24_RP.in
new file mode 100644
index 0000000..a70d792
--- /dev/null
+++ b/tests/libzscanner/data/24_RP.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The RP is the same as the MINFO, so there are the differences and basics only.
+
+; OK
+@ RP mail mail.tld. ; Relative and absolute dnames
+@ RP \# 2 00 00 ; Hexadecimal rdata
+@ TYPE17 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE17 @ @ ; TYPE
+@ rp @ @ ; Type in lower-case
+
+; KO
+@ RP
diff --git a/tests/libzscanner/data/24_RP.out b/tests/libzscanner/data/24_RP.out
new file mode 100644
index 0000000..d13b1ba
--- /dev/null
+++ b/tests/libzscanner/data/24_RP.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=046D61696C00046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/25_SOA.in b/tests/libzscanner/data/25_SOA.in
new file mode 100644
index 0000000..bf093d4
--- /dev/null
+++ b/tests/libzscanner/data/25_SOA.in
@@ -0,0 +1,31 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SOA @ @ 0 0 0 0 0 ; The simplest variant
+@ SOA tld. tld 0 0 0 0 0 ; Absolute and relative dnames.
+@ SOA @ @ 4294967295 0 0 0 0 ; Maximal serial
+@ SOA @ @ 0 4294967295 4294967295 4294967295 4294967295 ; Maximal times
+@ SOA @ @ 0 1d 1h 1m 1s ; Time units
+@ TYPE6 \# 22 00 00 0000000000000000000000000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE6 @ @ 0 0 0 0 0 ; TYPE
+@ SOA ns.tld. first\.second.tld. ( ; Multiline record
+ 2007120710
+ 1w2d3h4m5s
+ 2h
+ 3m
+ 4s
+)
+@ soa @ @ 0 0 0 0 0 ; Type in lower-case
+
+; KO
+@ SOA
+@ SOA ; Empty rdata
+@ SOA \# 0 ; Hex empty rdata
+@ SOA @ @ 1h 0 0 0 0 ; Bad number
+@ SOA @ @ 4294967296 0 0 0 0 ; Serial overflow
+@ SOA @ @ 0 4294967296 0 0 0 ; Refresh overflow
+@ SOA @ @ 0 0 4294967296 0 0 ; Retry overflow
+@ SOA @ @ 0 0 0 4294967296 0 ; Expire overflow
+@ SOA @ @ 0 0 0 0 4294967296 ; Minimum overflow
+@ SOA @ @ 0 0 0 0 0 x ; Unexpected item
diff --git a/tests/libzscanner/data/25_SOA.out b/tests/libzscanner/data/25_SOA.out
new file mode 100644
index 0000000..f658442
--- /dev/null
+++ b/tests/libzscanner/data/25_SOA.out
@@ -0,0 +1,74 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=03746C640003746C64000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=0000FFFFFFFF00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=0000000000000001518000000E100000003C00000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=026E7303746C64000C66697273742E7365636F6E6403746C640077A23B46000C08A500001C20000000B400000004
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/26_SRV.in b/tests/libzscanner/data/26_SRV.in
new file mode 100644
index 0000000..001e1e6
--- /dev/null
+++ b/tests/libzscanner/data/26_SRV.in
@@ -0,0 +1,25 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SRV 0 0 0 @ ; The simplest variant
+_ldap._tcp.test.tld. SRV 0 0 0 @ ; Underscores in owner
+@ SRV 65535 65535 65535 @ ; Maximal numbers
+@ SRV 0 0 0 \0320\ \\\"\.\@\*.tld. ; Dname with specials
+@ TYPE33 \# 7 000000000000 00 ; TYPE + Hexadecimal rdata
+@ TYPE33 0 0 0 @ ; TYPE
+@ srv 0 0 0 @ ; Type in lower-case
+
+; KO
+@ SRV
+@ SRV ; Empty rdata
+@ SRV \# 0 ; Hex empty rdata
+@ SRV 1h 0 0 @ ; Bad priority
+@ SRV 0 1h 0 @ ; Bad weight
+@ SRV 0 0 1h @ ; Bad port
+@ SRV 0 0 0 % ; Bad target
+@ SRV 65536 0 0 @ ; Priority overflow
+@ SRV 0 65536 0 @ ; Weight overflow
+@ SRV 0 0 65536 @ ; Port overflow
+@ SRV 0 0 0 @ x ; Unexpected item
+@ SRV 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/26_SRV.out b/tests/libzscanner/data/26_SRV.out
new file mode 100644
index 0000000..eadb804
--- /dev/null
+++ b/tests/libzscanner/data/26_SRV.out
@@ -0,0 +1,66 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=055F6C646170045F746370047465737403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=FFFFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=000000000000082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/27_NAPTR.in b/tests/libzscanner/data/27_NAPTR.in
new file mode 100644
index 0000000..44fd346
--- /dev/null
+++ b/tests/libzscanner/data/27_NAPTR.in
@@ -0,0 +1,20 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NAPTR 0 0 "" "" "" @ ; The simplest variant
+@ NAPTR 65535 65535 "" "" "" @ ; Maximal numbers
+@ NAPTR 65535 65535 "" "" "!^urn:cid:.+@([^\.]+\.)(.*)$!\\2!i" @ ; Regexp example
+@ NAPTR 0 0 "" "" "" \0320\ \\\"\.\@\*.tld. ; Dname with specials
+@ TYPE35 \# 8 00000000000000 00 ; TYPE + Hexadecimal rdata
+@ TYPE35 0 0 "" "" "" @ ; TYPE
+@ naptr 0 0 "" "" "" @ ; Type in lower-case
+
+; KO
+@ NAPTR
+@ NAPTR ; Empty rdata
+@ NAPTR \# 0 ; Hex empty rdata
+@ NAPTR 65536 0 "" "" "" @ ; Order overflow
+@ NAPTR 0 65536 "" "" "" @ ; Preference overflow
+@ NAPTR 0 0 "" "" "" @ x ; Unexpected item
+@ NAPTR 0 0 "" "" "" ; Missing item
diff --git a/tests/libzscanner/data/27_NAPTR.out b/tests/libzscanner/data/27_NAPTR.out
new file mode 100644
index 0000000..cc34e26
--- /dev/null
+++ b/tests/libzscanner/data/27_NAPTR.out
@@ -0,0 +1,56 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=FFFFFFFF00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=FFFFFFFF00001F215E75726E3A6369643A2E2B40285B5E2E5D2B2E29282E2A2924215C32216900
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=00000000000000082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/28_TYPE.in b/tests/libzscanner/data/28_TYPE.in
new file mode 100644
index 0000000..f8189e8
--- /dev/null
+++ b/tests/libzscanner/data/28_TYPE.in
@@ -0,0 +1,27 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TYPE0 \# 0 ; Minimal type number
+@ TYPE65535 \# 0 ; Maximal type number
+@ TYPE55555 \# 0 ; Without hex rdata
+@ TYPE55555 \# 1 00 ; Without hex rdata
+@ TYPE1 \# 4 00000000 ; Known type
+@ TYPE1 0.0.0.0 ; Known type in text format
+@ TYPE55555 ( ; Multiline begin
+ \#
+ 5
+ 0102 03
+ 04 05
+ ) ; Multiline end
+@ type55555 \# 0 ; Type in lower-case
+
+; KO
+@ TYPE55555
+@ TYPE55555 ; Without text rdata
+@ TYPE65536 ; Type number overflow
+@ TYPE65535x ; Bad type
+@ TYPE55555 \# ; Missing hex length
+@ TYPE55555 \# 1 0000 ; Too long rdata
+@ TYPE55555 \# 2 00 ; Bad rdata length
+@ TYPE55555 \# 1 00 x ; Unexpected data
diff --git a/tests/libzscanner/data/28_TYPE.out b/tests/libzscanner/data/28_TYPE.out
new file mode 100644
index 0000000..0aa409a
--- /dev/null
+++ b/tests/libzscanner/data/28_TYPE.out
@@ -0,0 +1,64 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0000
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=FFFF
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=0102030405
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=
+------
+WARNG=ZS_CANNOT_TEXT_DATA
+------
+WARNG=ZS_CANNOT_TEXT_DATA
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_RDATA_LENGTH
+------
+WARNG=ZS_BAD_RDATA_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/29_CERT.in b/tests/libzscanner/data/29_CERT.in
new file mode 100644
index 0000000..ee70ce2
--- /dev/null
+++ b/tests/libzscanner/data/29_CERT.in
@@ -0,0 +1,58 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CERT 0 0 0 AA== ; The simplest variant
+@ CERT 65535 65535 255 AA== ; Maximal numbers
+@ CERT PKIX 0 0 AA== ; Certificate type mnemo
+@ CERT SPKI 0 0 AA== ; Certificate type mnemo
+@ CERT PGP 0 0 AA== ; Certificate type mnemo
+@ CERT IPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT ISPKI 0 0 AA== ; Certificate type mnemo
+@ CERT IPGP 0 0 AA== ; Certificate type mnemo
+@ CERT ACPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT IACPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT URI 0 0 AA== ; Certificate type mnemo
+@ CERT OID 0 0 AA== ; Certificate type mnemo
+@ CERT 0 0 RSAMD5 AA== ; Algorithm mnemo
+@ CERT 0 0 DH AA== ; Algorithm mnemo
+@ CERT 0 0 DSA AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 DSA-NSEC3-SHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA1-NSEC3-SHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA256 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA512 AA== ; Algorithm mnemo
+@ CERT 0 0 ECC-GOST AA== ; Algorithm mnemo
+@ CERT 0 0 ECDSAP256SHA256 AA== ; Algorithm mnemo
+@ CERT 0 0 ECDSAP384SHA384 AA== ; Algorithm mnemo
+@ CERT 0 0 ED25519 AA== ; Algorithm mnemo
+@ CERT 0 0 ED448 AA== ; Algorithm mnemo
+@ CERT 0 0 INDIRECT AA== ; Algorithm mnemo
+@ CERT 0 0 PRIVATEDNS AA== ; Algorithm mnemo
+@ CERT 0 0 PRIVATEOID AA== ; Algorithm mnemo
+@ CERT 0 0 0 Zm8= ; One char padding
+@ CERT 0 0 0 Zm9v ; Without padding
+@ CERT 0 0 0 Zm9vYg== ; Two base64 blocks
+@ CERT 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE37 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE37 0 0 0 AA== ; TYPE
+@ cert 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ CERT
+@ CERT ; Empty rdata
+@ CERT \# 0 ; Hex empty rdata
+@ CERT 65536 0 0 AA== ; Type overflow
+@ CERT X 0 0 AA== ; Bad type mnemonic
+@ CERT 0 65536 0 AA== ; Key tag overflow
+@ CERT 0 0 256 AA== ; Algorithm overflow
+@ CERT 0 0 0 A ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 AB ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 A=== ; Bad padding
+@ CERT 0 0 0 = ; Bad padding
+@ CERT 0 0 0 == ; Bad padding
+@ CERT 0 0 0 === ; Bad padding
+@ CERT 0 0 0 ==== ; Bad padding
+@ CERT 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/29_CERT.out b/tests/libzscanner/data/29_CERT.out
new file mode 100644
index 0000000..da2c897
--- /dev/null
+++ b/tests/libzscanner/data/29_CERT.out
@@ -0,0 +1,244 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=FFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000100000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000200000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000300000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000400000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000500000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000600000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000700000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000800000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00FD00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00FE00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000200
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000300
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000500
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000600
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000700
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000800
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000E00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000F00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000001000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FC00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FD00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FE00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_CERT_TYPE
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/30_KEY.in b/tests/libzscanner/data/30_KEY.in
new file mode 100644
index 0000000..263fa03
--- /dev/null
+++ b/tests/libzscanner/data/30_KEY.in
@@ -0,0 +1,31 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ KEY 0 0 0 AA== ; The simplest variant
+@ KEY 65535 255 255 AA== ; Maximal numbers
+@ KEY 0 0 0 Zm8= ; One char padding
+@ KEY 0 0 0 Zm9v ; Without padding
+@ KEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ KEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE25 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE25 0 0 0 AA== ; TYPE
+@ key 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ KEY
+@ KEY ; Empty rdata
+@ KEY \# 0 ; Hex empty rdata
+@ KEY 65536 0 0 AA== ; Type overflow
+@ KEY 0 256 0 AA== ; Key tag overflow
+@ KEY 0 0 256 AA== ; Algorithm overflow
+@ KEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 A=== ; Bad padding
+@ KEY 0 0 0 = ; Bad padding
+@ KEY 0 0 0 == ; Bad padding
+@ KEY 0 0 0 === ; Bad padding
+@ KEY 0 0 0 ==== ; Bad padding
+@ KEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/30_KEY.out b/tests/libzscanner/data/30_KEY.out
new file mode 100644
index 0000000..1ec998b
--- /dev/null
+++ b/tests/libzscanner/data/30_KEY.out
@@ -0,0 +1,86 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/31_DNSKEY.in b/tests/libzscanner/data/31_DNSKEY.in
new file mode 100644
index 0000000..871de97
--- /dev/null
+++ b/tests/libzscanner/data/31_DNSKEY.in
@@ -0,0 +1,32 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DNSKEY 0 0 0 AA== ; The simplest variant
+@ DNSKEY 65535 255 255 AA== ; Maximal numbers
+@ DNSKEY 0 0 RSAMD5 AA== ; Algorithm mnemonic
+@ DNSKEY 0 0 0 Zm8= ; One char padding
+@ DNSKEY 0 0 0 Zm9v ; Without padding
+@ DNSKEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ DNSKEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE48 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE48 0 0 0 AA== ; TYPE
+@ dnskey 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ DNSKEY
+@ DNSKEY ; Empty rdata
+@ DNSKEY \# 0 ; Hex empty rdata
+@ DNSKEY 65536 0 0 AA== ; Type overflow
+@ DNSKEY 0 256 0 AA== ; Key tag overflow
+@ DNSKEY 0 0 256 AA== ; Algorithm overflow
+@ DNSKEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 A=== ; Bad padding
+@ DNSKEY 0 0 0 = ; Bad padding
+@ DNSKEY 0 0 0 == ; Bad padding
+@ DNSKEY 0 0 0 === ; Bad padding
+@ DNSKEY 0 0 0 ==== ; Bad padding
+@ DNSKEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/31_DNSKEY.out b/tests/libzscanner/data/31_DNSKEY.out
new file mode 100644
index 0000000..fa034c5
--- /dev/null
+++ b/tests/libzscanner/data/31_DNSKEY.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/32_APL.in b/tests/libzscanner/data/32_APL.in
new file mode 100644
index 0000000..ea9a747
--- /dev/null
+++ b/tests/libzscanner/data/32_APL.in
@@ -0,0 +1,30 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ APL ; The simplest variant - blank list
+@ APL 1:0.0.0.0/0 ; Minimal ipv4 prefix length
+@ APL 1:255.255.255.255/32 ; Maximal ipv4 prefix length
+@ APL 1:255.255.255.255/30 ; Prefix length isn't multiple of 8
+@ APL 2:::/0 ; Minimal ipv6 address
+@ APL 2:0::0/0 ; Minimal ipv6 prefix length
+@ APL 2:0::0/128 ; Maximal ipv6 prefix length
+@ APL 2:FFFF:FFFF:FFFF::/2 ; Trailing zeroes test
+@ APL !1:0.0.0.0/0 ; Negation flag
+@ APL 1:0.0.0.0/0 1:255.255.255.255/32 ; More APLs
+@ TYPE42 \# 4 00010000 ; TYPE + Hexadecimal rdata
+@ TYPE42 1:0.0.0.0/0 ; TYPE
+@ APL \# 0 ; Zero length rdata
+@ apl 1:0.0.0.0/0 ; Type in lower-case
+
+; KO
+@ APL 0:0.0.0.0/32 ; Bad address family
+@ APL x:0.0.0.0/32 ; Bad address family
+@ APL !x:0.0.0.0/32 ; Bad address family
+@ APL 2:0.0.0.0/32 ; Address family mismatch
+@ APL 1:0::0/32 ; Address family mismatch
+@ APL 1:0.0.0.0/33 ; Prefix length is too long
+@ APL 2:0::0/129 ; Prefix length is too long
+@ APL 2::/0 ; Bad ipv6 address
+@ APL 2:0::0/x ; Bad prefix length
+@ APL 1:0.0.0.0/ ; Missing prefix length
diff --git a/tests/libzscanner/data/32_APL.out b/tests/libzscanner/data/32_APL.out
new file mode 100644
index 0000000..50ec101
--- /dev/null
+++ b/tests/libzscanner/data/32_APL.out
@@ -0,0 +1,104 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00012004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00011E04FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00028000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020206FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010080
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=0001000000012004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
diff --git a/tests/libzscanner/data/33_DS.in b/tests/libzscanner/data/33_DS.in
new file mode 100644
index 0000000..a5d805c
--- /dev/null
+++ b/tests/libzscanner/data/33_DS.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DS 0 0 0 00 ; The simplest variant
+@ DS 65535 255 255 00 ; Maximal numbers
+@ DS 0 RSAMD5 0 00 ; Algorithm mnemonic
+@ DS 0 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE43 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE43 0 0 0 00 ; TYPE
+@ ds 0 0 0 00 ; Type in lower-case
+
+; KO
+@ DS
+@ DS ; Empty rdata
+@ DS \# 0 ; Hex empty rdata
+@ DS 65536 0 0 00 ; Key tag overflow
+@ DS 0 256 0 00 ; Algorithm overflow
+@ DS 0 0 256 00 ; Digest type overflow
+@ DS 0 0 0 0 ; Continuous block length must be multiple of 2
+@ DS 0 0 0 00 0 ; Continuous block length must be multiple of 2
+@ DS 0 0 0 XX ; Bad hex character
+@ DS 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/33_DS.out b/tests/libzscanner/data/33_DS.out
new file mode 100644
index 0000000..2257119
--- /dev/null
+++ b/tests/libzscanner/data/33_DS.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/34_SSHFP.in b/tests/libzscanner/data/34_SSHFP.in
new file mode 100644
index 0000000..89c4de3
--- /dev/null
+++ b/tests/libzscanner/data/34_SSHFP.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SSHFP 0 0 00 ; The simplest variant
+@ SSHFP 255 255 00 ; Maximal numbers
+@ SSHFP 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE44 \# 3 000000 ; TYPE + Hexadecimal rdata
+@ TYPE44 0 0 00 ; TYPE
+@ sshfp 0 0 00 ; Type in lower-case
+
+; KO
+@ SSHFP
+@ SSHFP ; Empty rdata
+@ SSHFP \# 0 ; Hex empty rdata
+@ SSHFP 256 0 00 ; Algorithm overflow
+@ SSHFP 0 256 00 ; Fp type overflow
+@ SSHFP 0 0 0 ; Continuous block length must be multiple of 2
+@ SSHFP 0 0 00 0 ; Continuous block length must be multiple of 2
+@ SSHFP 0 0 XX ; Bad hex character
+@ SSHFP 0 0 ; Missing item
diff --git a/tests/libzscanner/data/34_SSHFP.out b/tests/libzscanner/data/34_SSHFP.out
new file mode 100644
index 0000000..da8ce6d
--- /dev/null
+++ b/tests/libzscanner/data/34_SSHFP.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/35_IPSECKEY.in b/tests/libzscanner/data/35_IPSECKEY.in
new file mode 100644
index 0000000..dc56bd7
--- /dev/null
+++ b/tests/libzscanner/data/35_IPSECKEY.in
@@ -0,0 +1,29 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ IPSECKEY 0 0 0 . ; The simplest variant - no gw, no key
+@ IPSECKEY 255 3 255 . AA== ; Maximal numbers
+@ IPSECKEY 0 1 0 0.0.0.0 ; IPv4 address
+@ IPSECKEY 0 2 0 :: ; IPv6 address
+@ IPSECKEY 0 3 0 \0320\ \\\"\.\@\*.tld. ; Special chars in domain name
+@ IPSECKEY 0 0 1 . Zm8= ; One char padding
+@ IPSECKEY 0 0 1 . Zm9v ; Without padding
+@ IPSECKEY 0 0 1 . Zm9vYg== ; Two base64 blocks
+@ IPSECKEY 0 0 1 . Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE45 \# 3 000000 ; TYPE + Hexadecimal rdata
+@ TYPE45 0 0 1 . AA== ; TYPE
+@ ipseckey 0 0 1 . AA== ; Type in lower-case
+
+; KO
+@ IPSECKEY
+@ IPSECKEY ; Empty rdata
+@ IPSECKEY \# 0 ; Hex empty rdata
+@ IPSECKEY 256 0 0 . ; Precedence overflow
+@ IPSECKEY 0 4 0 . ; Unknown gateway
+@ IPSECKEY 0 0 256 . AA== ; Algorithm overflow
+@ IPSECKEY 0 0 0 . AA== ; If alg is 0 then key shouldn't be given
+@ IPSECKEY 0 0 0 a% ; Bad domain name char
+@ IPSECKEY 0 0 1 . A ; Continuous block length must be multiple of 4
+@ IPSECKEY 0 0 1 . = ; Bad padding
+@ IPSECKEY 0 0 ; Missing item
diff --git a/tests/libzscanner/data/35_IPSECKEY.out b/tests/libzscanner/data/35_IPSECKEY.out
new file mode 100644
index 0000000..718ce22
--- /dev/null
+++ b/tests/libzscanner/data/35_IPSECKEY.out
@@ -0,0 +1,94 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=FF03FF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00010000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00020000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000300082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_GATEWAY
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_GATEWAY_KEY
+------
+WARNG=ZS_BAD_GATEWAY
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_GATEWAY_KEY
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/36_RRSIG.in b/tests/libzscanner/data/36_RRSIG.in
new file mode 100644
index 0000000..e970fc5
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.in
@@ -0,0 +1,45 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ RRSIG TYPE0 0 0 0 0 0 0 . AA== ; The simplest variant
+@ RRSIG A 2 3 4 5 6 7 \008. CQ== ; Human visual test - block numbering
+@ RRSIG TYPE65535 255 255 4294967295 4294967295 4294967295 65535 . AA== ; Maximal numbers
+@ RRSIG TYPE0 RSAMD5 0 0 0 0 0 . AA== ; Algorithm mnemonic
+@ RRSIG A 0 0 0 19700101000000 0 0 . AA== ; Minimal date format
+@ RRSIG A 0 0 0 0 21051231235959 0 . AA== ; Maximal date format (zscanner limit)
+@ RRSIG TYPE0 0 0 0 0 0 0 \0320\ \\\"\.\@\*.tld. AA== ; Special chars in domain name
+@ RRSIG A 0 0 0 0 0 0 . Zm8= ; One char padding
+@ RRSIG A 0 0 0 0 0 0 . Zm9v ; Without padding
+@ RRSIG A 0 0 0 0 0 0 . Zm9vYg== ; Two base64 blocks
+@ RRSIG A 0 0 0 0 0 0 . Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE46 \# 20 000100000000000000000000000000000000 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE46 A 0 0 0 0 0 0 . AA== ; TYPE
+@ rrsig A 0 0 0 0 0 0 . AA== ; Type in lower-case
+
+; KO
+@ RRSIG
+@ RRSIG ; Empty rdata
+@ RRSIG \# 0 ; Hex empty rdata
+@ RRSIG X 0 0 0 0 0 0 . AA== ; Unknown type
+@ RRSIG TYPE65536 0 0 0 0 0 0 . AA== ; Type overflow
+@ RRSIG A 256 0 0 0 0 0 . AA== ; Algorithm overflow
+@ RRSIG A 0 256 0 0 0 0 . AA== ; Labels overflow
+@ RRSIG A 0 0 4294967296 0 0 0 . AA== ; TTL overflow
+@ RRSIG A 0 0 0 9294967296 0 0 . AA== ; Sig. exp. overflow
+@ RRSIG A 0 0 0 0 4294967296 0 . AA== ; Sig. inc. overflow
+@ RRSIG A 0 0 0 0 0 65536 . AA== ; Key tag overflow
+@ RRSIG A 0 0 0 0 21060101000000 0 . AA== ; Date overflow
+@ RRSIG A 0 0 0 0 2106010100000x 0 . AA== ; Bad timestamp char
+@ RRSIG A 0 0 0 0 210601010000000 0 . AA== ; Bad timestamp length
+@ RRSIG A 0 0 0 0 0 0 a% AA== ; Bad domain char
+@ RRSIG A 0 0 0 0 0 0 . A ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . AB ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . ABC ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . AA == ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . A=== ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . = ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . == ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . === ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . ==== ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . ; Missing item
diff --git a/tests/libzscanner/data/36_RRSIG.out b/tests/libzscanner/data/36_RRSIG.out
new file mode 100644
index 0000000..b3d48be
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.out
@@ -0,0 +1,134 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010203000000040000000500000006000701080009
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0000010000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=000100000000000000000000FFCEDD7F00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=000000000000000000000000000000000000082030205C222E402A03746C640000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_DATE
+------
+WARNG=ZS_BAD_TIMESTAMP_CHAR
+------
+WARNG=ZS_BAD_TIMESTAMP_LENGTH
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/37_NSEC.in b/tests/libzscanner/data/37_NSEC.in
new file mode 100644
index 0000000..1efa4ea
--- /dev/null
+++ b/tests/libzscanner/data/37_NSEC.in
@@ -0,0 +1,20 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC . ; The simplest variant - without bitmap
+@ NSEC \0320\ \\\"\.\@\*.tld. ; Special chars in domain name
+@ NSEC . TYPE0 ; Minimal type number
+@ NSEC . TYPE65535 ; Maximal type number
+@ NSEC . TYPE0 A NS ; First bitmap window
+@ NSEC . TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE47 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE47 . ; TYPE
+@ nsec . ; Type in lower-case
+
+; KO
+@ NSEC
+@ NSEC ; Empty rdata
+@ NSEC \# 0 ; Hex empty rdata
+@ NSEC . TYPE65536 ; Type number overflow
+@ NSEC . X ; Unknown type
diff --git a/tests/libzscanner/data/37_NSEC.out b/tests/libzscanner/data/37_NSEC.out
new file mode 100644
index 0000000..7a62def
--- /dev/null
+++ b/tests/libzscanner/data/37_NSEC.out
@@ -0,0 +1,64 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/38_DHCID.in b/tests/libzscanner/data/38_DHCID.in
new file mode 100644
index 0000000..4c0642a
--- /dev/null
+++ b/tests/libzscanner/data/38_DHCID.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DHCID AA== ; The simplest variant
+@ DHCID Zm8= ; One char padding
+@ DHCID Zm9v ; Without padding
+@ DHCID Zm9vYg== ; Two base64 blocks
+@ DHCID Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE49 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE49 AA== ; TYPE
+@ dhcid AA== ; Type in lower-case
+
+; KO
+@ DHCID
+@ DHCID ; Empty rdata
+@ DHCID \# 0 ; Hex empty rdata
+@ DHCID A ; Continuous block length must be multiple of 4
+@ DHCID AB ; Continuous block length must be multiple of 4
+@ DHCID ABC ; Continuous block length must be multiple of 4
+@ DHCID AA == ; Continuous block length must be multiple of 4
+@ DHCID A=== ; Bad padding
+@ DHCID = ; Bad padding
+@ DHCID == ; Bad padding
+@ DHCID === ; Bad padding
+@ DHCID ==== ; Bad padding
diff --git a/tests/libzscanner/data/38_DHCID.out b/tests/libzscanner/data/38_DHCID.out
new file mode 100644
index 0000000..8dc8ec9
--- /dev/null
+++ b/tests/libzscanner/data/38_DHCID.out
@@ -0,0 +1,72 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/39_NSEC3.in b/tests/libzscanner/data/39_NSEC3.in
new file mode 100644
index 0000000..cadbfa9
--- /dev/null
+++ b/tests/libzscanner/data/39_NSEC3.in
@@ -0,0 +1,46 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC3 0 0 0 - 00====== ; The simplest variant - without bitmap
+@ NSEC3 255 255 65535 - 00====== ; Maximal numbers
+@ NSEC3 0 0 0 00FF 00====== ; Hex string
+@ NSEC3 0 0 0 - 00====== ; Eight char padding
+@ NSEC3 0 0 0 - CPNG==== ; Four char padding
+@ NSEC3 0 0 0 - CPNMU=== ; Three char padding
+@ NSEC3 0 0 0 - CPNMUOG= ; One char padding
+@ NSEC3 0 0 0 - CPNMUOJ1 ; Without padding
+@ NSEC3 0 0 0 - CPNMUOJ1E8====== ; Two base32hex blocks
+@ NSEC3 0 0 0 - 00====== TYPE0 ; Minimal type number
+@ NSEC3 0 0 0 - 00====== TYPE65535 ; Maximal type number
+@ NSEC3 0 0 0 - 00====== TYPE0 A NS ; First bitmap window
+@ NSEC3 0 0 0 - 00====== TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE50 \# 7 00000000000100 ; TYPE + Hexadecimal rdata
+@ TYPE50 0 0 0 - 00====== ; TYPE
+@ nsec3 0 0 0 - 00====== ; Type in lower-case
+
+; KO
+@ NSEC3
+@ NSEC3 ; Empty rdata
+@ NSEC3 \# 0 ; Hex empty rdata
+@ NSEC3 256 0 0 - 00====== ; Algorithm overflow
+@ NSEC3 0 256 0 - 00====== ; Flags overflow
+@ NSEC3 0 0 65536 - 00====== ; Iterations overflow
+@ NSEC3 0 0 0 0 00====== ; Hex block must be multiple of 2
+@ NSEC3 0 0 0 0X 00====== ; Bad hex char
+@ NSEC3 0 0 0 00 FF 00====== ; Hex string with blank space inside
+@ NSEC3 0 0 0 - 1 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 12 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 1234 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 12345 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123456 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 1234567 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123456 78 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - ======== ; Bad padding
+@ NSEC3 0 0 0 - 1======= ; Bad padding
+@ NSEC3 0 0 0 - 123===== ; Bad padding
+@ NSEC3 0 0 0 - 123456== ; Bad padding
+@ NSEC3 0 0 0 - CPNMUOJ1 E8====== ; Two base32hex blocks with blank space between them
+@ NSEC3 0 0 0 - 00====== TYPE65536 ; Type number overflow
+@ NSEC3 0 0 0 - 00====== X ; Unknown type
diff --git a/tests/libzscanner/data/39_NSEC3.out b/tests/libzscanner/data/39_NSEC3.out
new file mode 100644
index 0000000..1aaec4f
--- /dev/null
+++ b/tests/libzscanner/data/39_NSEC3.out
@@ -0,0 +1,144 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=FFFFFFFF000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000200FF0100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000002666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000003666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000004666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000005666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000006666F6F626172
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000001000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BITMAP
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/40_NSEC3PARAM.in b/tests/libzscanner/data/40_NSEC3PARAM.in
new file mode 100644
index 0000000..694e3f7
--- /dev/null
+++ b/tests/libzscanner/data/40_NSEC3PARAM.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC3PARAM 0 0 0 - ; The simplest variant
+@ NSEC3PARAM 255 255 65535 - ; Maximal numbers
+@ NSEC3PARAM 0 0 0 0102FF ; Hex string
+@ TYPE51 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE51 0 0 0 - ; TYPE
+@ nsec3param 0 0 0 - ; Type in lower-case
+
+; KO
+@ NSEC3PARAM
+@ NSEC3PARAM ; Empty rdata
+@ NSEC3PARAM \# 0 ; Hex empty rdata
+@ NSEC3PARAM 256 0 0 00 ; Algorithm overflow
+@ NSEC3PARAM 0 256 0 00 ; Flags overflow
+@ NSEC3PARAM 0 0 65536 00 ; Iterations overflow
+@ NSEC3PARAM 0 0 0 0 ; Hex block length must be multiple of 2
+@ NSEC3PARAM 0 0 0 0x ; Bad hex char
+@ NSEC3PARAM 0 0 0 00 00 ; Hex block must not contain blank spaces
+@ NSEC3PARAM 0 0 0 00 x ; Unexpected item
+@ NSEC3PARAM 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/40_NSEC3PARAM.out b/tests/libzscanner/data/40_NSEC3PARAM.out
new file mode 100644
index 0000000..83b9bb1
--- /dev/null
+++ b/tests/libzscanner/data/40_NSEC3PARAM.out
@@ -0,0 +1,58 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=00000000030102FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/41_TLSA.in b/tests/libzscanner/data/41_TLSA.in
new file mode 100644
index 0000000..927498e
--- /dev/null
+++ b/tests/libzscanner/data/41_TLSA.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TLSA 0 0 0 00 ; The simplest variant
+@ TLSA 255 255 255 00 ; Maximal numbers
+@ TLSA 0 0 0 0102 00 FF ; Hex string with blank spaces inside
+@ TYPE52 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE52 0 0 0 00 ; TYPE
+@ tlsa 0 0 0 00 ; Type in lower-case
+
+; KO
+@ TLSA
+@ TLSA ; Empty rdata
+@ TLSA \# 0 ; Hex empty rdata
+@ TLSA 256 0 0 00 ; Algorithm overflow
+@ TLSA 0 256 0 00 ; Flags overflow
+@ TLSA 0 0 256 00 ; Iterations overflow
+@ TLSA 0 0 0 0 ; Hex block length must be multiple of 2
+@ TLSA 0 0 0 0x ; Bad hex char
+@ TLSA 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/41_TLSA.out b/tests/libzscanner/data/41_TLSA.out
new file mode 100644
index 0000000..6f5b6a1
--- /dev/null
+++ b/tests/libzscanner/data/41_TLSA.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=FFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=000000010200FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/42_LOC.in b/tests/libzscanner/data/42_LOC.in
new file mode 100644
index 0000000..9eaae1d
--- /dev/null
+++ b/tests/libzscanner/data/42_LOC.in
@@ -0,0 +1,64 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ LOC 1 N 1 E 0 ; The simplest case
+@ LOC 0 1 N 1 E 0 ; Combination of parameters
+@ LOC 0 0 1 N 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 0 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 0 1 E 0m ; Combination of parameters
+@ LOC 1 N 1 E 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 1m ; Combination of parameters
+@ LOC 1 N 1 E 0 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 0 1m ; Combination of parameters
+@ LOC 1 N 1 E 0 0 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 0 0 1m ; Combination of parameters
+@ LOC 0 0 0 N 0 0 0 E -100000.00 0 0 0 ; Minimal values
+@ LOC 90 59 59.999 S 180 59 59.999 W 42849672.95m 90000000.00m 90000000.00m 90000000.00m ; Maximal values
+@ LOC 0 S 0 0 0.001 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 0.01 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 0.1 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 1.0 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 10 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 W 0 0.01 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 0.10 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1.0 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 100 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 100000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1000000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10000000 ; Number to [mantisa,exponent] test
+@ LOC \# 16 00 00 00 00 00000000 00000000 00000000 ; Hexadecimal rdata
+@ TYPE29 \# 16 00 00 00 00 00000000 00000000 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE29 0 N 0 E 0 ; TYPE
+@ loc 0 N 0 E 0 ; Type in lower-case
+
+; KO
+@ LOC
+@ LOC ; Empty rdata
+@ LOC \# 0 ; Hex empty rdata
+@ LOC 91 0 0 N 0 0 0 E 0 0 0 0 ; Degree overflow
+@ LOC 0 60 0 N 0 0 0 E 0 0 0 0 ; Minute overflow
+@ LOC 0 0 60 0 N 0 0 0 E 0 0 0 0 ; Second overflow
+@ LOC 0 0 0 N 181 0 0 E 0 0 0 0 ; Degree overflow
+@ LOC 0 0 0 N 0 60 0 E 0 0 0 0 ; Minute overflow
+@ LOC 0 0 0 N 0 0 60 E 0 0 0 0 ; Second overflow
+@ LOC 0 0 0 N 0 0 0 E 42849672.96 0 0 0 ; Altitude overflow
+@ LOC 0 0 0 N 0 0 0 E 42849673 0 0 0 ; Altitude overflow
+@ LOC 0 0 0 N 0 0 0 E -100000.01 0 0 0 ; Altitude underflow
+@ LOC 0 0 0 N 0 0 0 E -100001 0 0 0 ; Altitude underflow
+@ LOC 0 0 0 N 0 0 0 E 0 90000000.01 0 0 ; Size overflow
+@ LOC 0 0 0 N 0 0 0 E 0 90000001 0 0 ; Size overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 90000000.01 0 ; HP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 90000001 0 ; HP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 90000000.01 ; VP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 90000001 ; VP overflow
+@ LOC 1 1 E 0 ; Missing N or S
+@ LOC 1 x 1 E 0 ; Bad letter
+@ LOC 1 N 1 0 ; Missing E or W
+@ LOC 1 N 1 x 0 ; Bad letter
+@ LOC 1 N 1 E ; Missing altitude
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 0 x ; Unexpected item
diff --git a/tests/libzscanner/data/42_LOC.out b/tests/libzscanner/data/42_LOC.out
new file mode 100644
index 0000000..730cc3b
--- /dev/null
+++ b/tests/libzscanner/data/42_LOC.out
@@ -0,0 +1,248 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138000EA608036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800003E88036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808000EA6000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE80800003E800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE80800003E800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000012138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000012138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000000128036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000000128036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000800000008000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=009999996C79388159295F81FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFFFF00989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFFF600989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFF9C00989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFC1800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFD8F000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00101613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00111613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00131613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00141613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00151613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00161613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00171613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00181613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00191613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
diff --git a/tests/libzscanner/data/43_EUI48.in b/tests/libzscanner/data/43_EUI48.in
new file mode 100644
index 0000000..a2abbec
--- /dev/null
+++ b/tests/libzscanner/data/43_EUI48.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ EUI48 00-00-00-00-00-00 ; The simplest case
+@ EUI48 FF-FF-FF-FF-FF-FF ; The maximal case
+@ EUI48 aa-bb-cc-dd-ee-ff ; Lower-case
+@ EUI48 \# 6 000000000000 ; Hexadecimal rdata
+@ TYPE108 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE108 00-00-00-00-00-00 ; TYPE
+@ eui48 00-00-00-00-00-00 ; Type in lower-case
+
+; KO
+@ EUI48
+@ EUI48 ; Empty rdata
+@ EUI48 \# 0 ; Hex empty rdata
+@ EUI48 00-00-00-00-00 ; Too few hex pairs
+@ EUI48 00-00-00-00-00-00-00 ; Too many hex pairs
+@ EUI48 00-00-00-00-00-0 ; Missing char in a hex pair
+@ EUI48 00:00-00-00-00-00 ; Bad separator
+@ EUI48 00-00-00-x0-00-00 ; Bad character
+@ EUI48 00-00-00-00-00-00 x ; Unexpected item
diff --git a/tests/libzscanner/data/43_EUI48.out b/tests/libzscanner/data/43_EUI48.out
new file mode 100644
index 0000000..a54a314
--- /dev/null
+++ b/tests/libzscanner/data/43_EUI48.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=AABBCCDDEEFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_DASH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/44_EUI64.in b/tests/libzscanner/data/44_EUI64.in
new file mode 100644
index 0000000..10f6346
--- /dev/null
+++ b/tests/libzscanner/data/44_EUI64.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ EUI64 00-00-00-00-00-00-00-00 ; The simplest case
+@ EUI64 FF-FF-FF-FF-FF-FF-FF-FF ; The maximal case
+@ EUI64 aa-bb-cc-dd-ee-ff-01-02 ; Lower-case
+@ EUI64 \# 8 0000000000000000 ; Hexadecimal rdata
+@ TYPE109 \# 8 0000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE109 00-00-00-00-00-00-00-00 ; TYPE
+@ eui64 00-00-00-00-00-00-00-00 ; Type in lower-case
+
+; KO
+@ EUI64
+@ EUI64 ; Empty rdata
+@ EUI64 \# 0 ; Hex empty rdata
+@ EUI64 00-00-00-00-00-00-00 ; Too few hex pairs
+@ EUI64 00-00-00-00-00-00-00-00-00 ; Too many hex pairs
+@ EUI64 00-00-00-00-00-00-00-0 ; Missing char in a hex pair
+@ EUI64 00:00-00-00-00-00-00-00 ; Bad separator
+@ EUI64 00-00-00-x0-00-00-00-00 ; Bad character
+@ EUI64 00-00-00-00-00-00-00-00 x ; Unexpected item
diff --git a/tests/libzscanner/data/44_EUI64.out b/tests/libzscanner/data/44_EUI64.out
new file mode 100644
index 0000000..4cce5f5
--- /dev/null
+++ b/tests/libzscanner/data/44_EUI64.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=FFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=AABBCCDDEEFF0102
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_DASH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/45_NID.in b/tests/libzscanner/data/45_NID.in
new file mode 100644
index 0000000..a85f9c3
--- /dev/null
+++ b/tests/libzscanner/data/45_NID.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The NID is the same as the L64, so there are the differences and basics only.
+
+; OK
+@ NID 0 0000:0000:0000:0000 ; The simplest case
+@ NID \# 10 00000000000000000000 ; Hexadecimal rdata
+@ TYPE104 \# 10 00000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE104 0 0000:0000:0000:0000 ; TYPE
+@ nid 0 0000:0000:0000:0000 ; Type in lower-case
+
+; KO
+@ NID
diff --git a/tests/libzscanner/data/45_NID.out b/tests/libzscanner/data/45_NID.out
new file mode 100644
index 0000000..a2bfb4c
--- /dev/null
+++ b/tests/libzscanner/data/45_NID.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/46_L32.in b/tests/libzscanner/data/46_L32.in
new file mode 100644
index 0000000..866e953
--- /dev/null
+++ b/tests/libzscanner/data/46_L32.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ L32 0 0.0.0.0 ; The simplest case
+@ L32 65535 255.255.255.255 ; The maximal case
+@ L32 \# 6 000000000000 ; Hexadecimal rdata
+@ TYPE105 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE105 0 0.0.0.0 ; TYPE
+@ l32 0 0.0.0.0 ; Type in lower-case
+
+; KO
+@ L32
+@ L32 ; Empty rdata
+@ L32 \# 0 ; Hex empty rdata
+@ L32 65536 0.0.0.0 ; Too big preference
+@ L32 0 0.0.0.256 ; 8-bit overflow
+@ L32 0 0.0.0 ; Short address
+@ L32 0 0.0.0.0.0 ; Long address
+@ L32 0 0.0.0.x ; Bad character
+@ L32 0 0.0.0.0 x ; Unexpected item
diff --git a/tests/libzscanner/data/46_L32.out b/tests/libzscanner/data/46_L32.out
new file mode 100644
index 0000000..e9c77b5
--- /dev/null
+++ b/tests/libzscanner/data/46_L32.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/47_L64.in b/tests/libzscanner/data/47_L64.in
new file mode 100644
index 0000000..b4aabf3
--- /dev/null
+++ b/tests/libzscanner/data/47_L64.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ L64 0 0000:0000:0000:0000 ; The simplest case
+@ L64 65535 FFFF:FFFF:FFFF:FFFF ; The maximal case
+@ L64 0 abcd:ef00:0000:0000 ; Lower-case
+@ L64 \# 10 00000000000000000000 ; Hexadecimal rdata
+@ TYPE106 \# 10 00000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE106 0 0000:0000:0000:0000 ; TYPE
+@ l64 0 0000:0000:0000:0000 ; Type in lower-case
+
+; KO
+@ L64
+@ L64 ; Empty rdata
+@ L64 \# 0 ; Hex empty rdata
+@ L64 65536 ; Too big preference
+@ L64 0 0000:0000:0000 ; Missing label
+@ L64 0 0000:0000:0000:0000:0000 ; Too many labels
+@ L64 0 0000:0000:0000:000 ; Missing hex character
+@ L64 0 0000:0000:0000-0000 ; Bad separator
+@ L64 0 0000:0000:0000:x000 ; Bad hex character
+@ L64 0 0000:0000:0000:0000 x ; Unexpected item
diff --git a/tests/libzscanner/data/47_L64.out b/tests/libzscanner/data/47_L64.out
new file mode 100644
index 0000000..106ee58
--- /dev/null
+++ b/tests/libzscanner/data/47_L64.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=FFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=0000ABCDEF0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_L64_LENGTH
+------
+WARNG=ZS_BAD_L64_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_COLON
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/48_LP.in b/tests/libzscanner/data/48_LP.in
new file mode 100644
index 0000000..bc537d0
--- /dev/null
+++ b/tests/libzscanner/data/48_LP.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The LP is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ LP 1 mail ; Relative dname
+@ LP \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE107 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE107 1 @ ; TYPE
+@ lp 1 @ ; Type in lower-case
+
+; KO
+@ LP
diff --git a/tests/libzscanner/data/48_LP.out b/tests/libzscanner/data/48_LP.out
new file mode 100644
index 0000000..4961c7e
--- /dev/null
+++ b/tests/libzscanner/data/48_LP.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/49_CDS.in b/tests/libzscanner/data/49_CDS.in
new file mode 100644
index 0000000..7b6c347
--- /dev/null
+++ b/tests/libzscanner/data/49_CDS.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CDS 0 0 0 00 ; The simplest variant
+@ CDS 65535 255 255 00 ; Maximal numbers
+@ CDS 0 RSAMD5 0 00 ; Algorithm mnemonic
+@ CDS 0 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE59 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE59 0 0 0 00 ; TYPE
+@ cds 0 0 0 00 ; Type in lower-case
+
+; KO
+@ CDS
+@ CDS ; Empty rdata
+@ CDS \# 0 ; Hex empty rdata
+@ CDS 65536 0 0 00 ; Key tag overflow
+@ CDS 0 256 0 00 ; Algorithm overflow
+@ CDS 0 0 256 00 ; Digest type overflow
+@ CDS 0 0 0 0 ; Continuous block length must be multiple of 2
+@ CDS 0 0 0 00 0 ; Continuous block length must be multiple of 2
+@ CDS 0 0 0 XX ; Bad hex character
+@ CDS 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/49_CDS.out b/tests/libzscanner/data/49_CDS.out
new file mode 100644
index 0000000..de40722
--- /dev/null
+++ b/tests/libzscanner/data/49_CDS.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/50_CDNSKEY.in b/tests/libzscanner/data/50_CDNSKEY.in
new file mode 100644
index 0000000..dc91462
--- /dev/null
+++ b/tests/libzscanner/data/50_CDNSKEY.in
@@ -0,0 +1,32 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CDNSKEY 0 0 0 AA== ; The simplest variant
+@ CDNSKEY 65535 255 255 AA== ; Maximal numbers
+@ CDNSKEY 0 0 RSAMD5 AA== ; Algorithm mnemonic
+@ CDNSKEY 0 0 0 Zm8= ; One char padding
+@ CDNSKEY 0 0 0 Zm9v ; Without padding
+@ CDNSKEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ CDNSKEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE60 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE60 0 0 0 AA== ; TYPE
+@ cdnskey 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ CDNSKEY
+@ CDNSKEY ; Empty rdata
+@ CDNSKEY \# 0 ; Hex empty rdata
+@ CDNSKEY 65536 0 0 AA== ; Type overflow
+@ CDNSKEY 0 256 0 AA== ; Key tag overflow
+@ CDNSKEY 0 0 256 AA== ; Algorithm overflow
+@ CDNSKEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 A=== ; Bad padding
+@ CDNSKEY 0 0 0 = ; Bad padding
+@ CDNSKEY 0 0 0 == ; Bad padding
+@ CDNSKEY 0 0 0 === ; Bad padding
+@ CDNSKEY 0 0 0 ==== ; Bad padding
+@ CDNSKEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/50_CDNSKEY.out b/tests/libzscanner/data/50_CDNSKEY.out
new file mode 100644
index 0000000..3100313
--- /dev/null
+++ b/tests/libzscanner/data/50_CDNSKEY.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/51_URI.in b/tests/libzscanner/data/51_URI.in
new file mode 100644
index 0000000..8d18f42
--- /dev/null
+++ b/tests/libzscanner/data/51_URI.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ URI 0 0 a ; The simplest variant
+@ URI 65535 65535 ftp://a ; Maximal priority and weight
+@ URI 0 0 "ftp://a" ; Quoted target
+@ URI 0 0 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ; Target longer than 255
+@ TYPE256 \# 5 0000000061 ; TYPE + Hexadecimal rdata
+@ TYPE256 0 0 a ; TYPE
+@ uri 0 0 a ; Type in lower-case
+
+; OK extensions
+@ URI 0 0 "" ; Empty target
+
+; KO
+@ URI
+@ URI ; Empty rdata
+@ URI \# 0 ; Hex empty rdata
+@ URI 65536 0 a ; Priority overflow
+@ URI 0 65536 a ; Weight overflow
+@ URI 0 0 a a ; Unexpected item
diff --git a/tests/libzscanner/data/51_URI.out b/tests/libzscanner/data/51_URI.out
new file mode 100644
index 0000000..9dcf933
--- /dev/null
+++ b/tests/libzscanner/data/51_URI.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=FFFFFFFF6674703A2F2F61
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=000000006674703A2F2F61
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=00000000303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/52_CAA.in b/tests/libzscanner/data/52_CAA.in
new file mode 100644
index 0000000..7043ce5
--- /dev/null
+++ b/tests/libzscanner/data/52_CAA.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CAA 0 a a ; The simplest variant
+@ CAA 255 a a ; Maximal flags
+@ CAA 0 a "a ; b" ; Quoted value
+@ CAA 0 a 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ; Value longer than 255
+@ TYPE257 \# 4 00016161 ; TYPE + Hexadecimal rdata
+@ TYPE257 0 a a ; TYPE
+@ caa 0 a a ; Type in lower-case
+
+; OK fallbacks and extensions
+@ CAA 0 "" a ; Empty tag
+@ CAA 0 "a" a ; Quoted tag
+@ CAA 0 abcdefghijklmnopqrstuvwxyz0123456789 a ; All allowed characters, longer than 15
+
+; KO
+@ CAA
+@ CAA ; Empty rdata
+@ CAA \# 0 ; Hex empty rdata
+@ CAA 256 a a ; Flags overflow
+@ CAA 0 a a a ; Unexpected item
diff --git a/tests/libzscanner/data/52_CAA.out b/tests/libzscanner/data/52_CAA.out
new file mode 100644
index 0000000..e5eefdf
--- /dev/null
+++ b/tests/libzscanner/data/52_CAA.out
@@ -0,0 +1,70 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=FF016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161203B2062
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=000161303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00246162636465666768696A6B6C6D6E6F707172737475767778797A3031323334353637383961
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/includes/include1 b/tests/libzscanner/data/includes/include1
new file mode 100644
index 0000000..9de1859
--- /dev/null
+++ b/tests/libzscanner/data/includes/include1
@@ -0,0 +1,9 @@
+$TTL 1
+
+a NS @
+
+$ORIGIN tld1a.
+a NS @
+
+$ORIGIN tld1b.
+a NS @
diff --git a/tests/libzscanner/data/includes/include2 b/tests/libzscanner/data/includes/include2
new file mode 100644
index 0000000..1e14e96
--- /dev/null
+++ b/tests/libzscanner/data/includes/include2
@@ -0,0 +1,6 @@
+$TTL 1H
+
+b NS @
+
+$ORIGIN tld1a.
+b NS @
diff --git a/tests/libzscanner/data/includes/include3 b/tests/libzscanner/data/includes/include3
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/includes/include3
diff --git a/tests/libzscanner/data/includes/include4 b/tests/libzscanner/data/includes/include4
new file mode 100644
index 0000000..7e8d5e2
--- /dev/null
+++ b/tests/libzscanner/data/includes/include4
@@ -0,0 +1 @@
+a NS ; Missing data
diff --git a/tests/libzscanner/data/includes/include5 b/tests/libzscanner/data/includes/include5
new file mode 100644
index 0000000..ac98e01
--- /dev/null
+++ b/tests/libzscanner/data/includes/include5
@@ -0,0 +1 @@
+$TTL x ; Bad number
diff --git a/tests/libzscanner/data/includes/include6 b/tests/libzscanner/data/includes/include6
new file mode 100644
index 0000000..b5e8cb8
--- /dev/null
+++ b/tests/libzscanner/data/includes/include6
@@ -0,0 +1 @@
+$INCLUDE include2 ; Include in include
diff --git a/tests/libzscanner/processing.c b/tests/libzscanner/processing.c
new file mode 100644
index 0000000..a5ddeaa
--- /dev/null
+++ b/tests/libzscanner/processing.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "libzscanner/scanner.h"
+#include "libzscanner/functions.c"
+#include "libzscanner/processing.h"
+#include "libknot/descriptor.c"
+
+const char *separator = "------\n";
+
+static void print_wire_dname(const uint8_t *dname, uint32_t dname_length)
+{
+ uint32_t label_length = 0, i = 0;
+
+ for (i = 0; i < dname_length; i++) {
+ if (label_length == 0) {
+ label_length = dname[i];
+ printf("(%u)", label_length);
+ continue;
+ }
+ printf("%c", (char)dname[i]);
+ label_length--;
+ }
+}
+
+void debug_process_error(zs_scanner_t *s)
+{
+ if (s->error.fatal) {
+ printf("LINE(%03"PRIu64") ERROR(%s) FILE(%s) NEAR(%s)\n",
+ s->line_counter,
+ zs_strerror(s->error.code),
+ s->file.name,
+ s->buffer);
+ } else {
+ printf("LINE(%03"PRIu64") WARNING(%s) FILE(%s) NEAR(%s)\n",
+ s->line_counter,
+ zs_strerror(s->error.code),
+ s->file.name,
+ s->buffer);
+ }
+ fflush(stdout);
+}
+
+void debug_process_record(zs_scanner_t *s)
+{
+ uint32_t i;
+
+ char rclass[32];
+ char rtype[32];
+
+ if (knot_rrclass_to_string(s->r_class, rclass, sizeof(rclass)) > 0 &&
+ knot_rrtype_to_string(s->r_type, rtype, sizeof(rtype)) > 0) {
+ printf("LINE(%03"PRIu64") %s %6u %*s ",
+ s->line_counter, rclass, s->r_ttl, 5, rtype);
+ } else {
+ printf("LINE(%03"PRIu64") %u %6u %*u ",
+ s->line_counter, s->r_class, s->r_ttl, 5, s->r_type);
+ }
+
+ print_wire_dname(s->r_owner, s->r_owner_length);
+
+ printf(" \\# %u ", s->r_data_length);
+
+ for (i = 0; i < s->r_data_length; i++) {
+ printf("%02X", (s->r_data)[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+void test_process_error(zs_scanner_t *s)
+{
+ if (s->error.fatal) {
+ printf("ERROR=%s\n%s", zs_errorname(s->error.code), separator);
+ } else {
+ printf("WARNG=%s\n%s", zs_errorname(s->error.code), separator);
+ }
+ fflush(stdout);
+}
+
+void test_process_record(zs_scanner_t *s)
+{
+ uint32_t i;
+
+ printf("OWNER=");
+ for (i = 0; i < s->r_owner_length; i++) {
+ printf("%02X", s->r_owner[i]);
+ }
+ printf("\n");
+ printf("CLASS=%04X\n", s->r_class);
+ printf("RRTTL=%08X\n", s->r_ttl);
+ printf("RTYPE=%04X\n", s->r_type);
+ printf("RDATA=");
+ for (i = 0; i < s->r_data_length; i++) {
+ printf("%02X", (s->r_data)[i]);
+ }
+ printf("\n%s", separator);
+ fflush(stdout);
+}
+
+int test_date_to_timestamp(void)
+{
+ time_t ref_timestamp, max_timestamp;
+ uint32_t test_timestamp;
+ uint8_t buffer[16];
+ uint64_t val1, val2; // For time_t type unification.
+ struct tm tm;
+
+ // Set UTC for strftime.
+ putenv("TZ=UTC");
+ tzset();
+
+ // Get maximal allowed timestamp.
+ strptime("21051231235959", "%Y%m%d%H%M%S", &tm);
+ max_timestamp = mktime(&tm);
+
+ // Testing loop over whole input interval.
+ for (ref_timestamp = 0;
+ ref_timestamp < max_timestamp;
+ ref_timestamp += 1) {
+ struct tm result;
+ // Get reference (correct) timestamp.
+ strftime((char*)buffer, sizeof(buffer), "%Y%m%d%H%M%S",
+ gmtime_r(&ref_timestamp, &result));
+
+ // Get testing timestamp.
+ test_timestamp = 0U; // prevents Wunitialized
+ date_to_timestamp(buffer, &test_timestamp);
+
+ // Some continuous loging.
+ if (ref_timestamp % 10000000 == 0) {
+ val1 = ref_timestamp;
+ printf("%s = %"PRIu64"\n", buffer, val1);
+ }
+
+ // Comparing results.
+ if (ref_timestamp != test_timestamp) {
+ val1 = ref_timestamp;
+
+ if (ref_timestamp > test_timestamp) {
+ val2 = ref_timestamp - test_timestamp;
+ printf("%s = %"PRIu64", in - out = %"PRIu64"\n",
+ buffer, val1, val2);
+ } else {
+ val2 = test_timestamp - ref_timestamp;
+ printf("%s = %"PRIu64", out - in = %"PRIu64"\n",
+ buffer, val1, val2);
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/libzscanner/processing.h b/tests/libzscanner/processing.h
new file mode 100644
index 0000000..fe66737
--- /dev/null
+++ b/tests/libzscanner/processing.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+ */
+
+#pragma once
+
+#include "libzscanner/scanner.h"
+
+void debug_process_error(zs_scanner_t *scanner);
+
+void debug_process_record(zs_scanner_t *scanner);
+
+void test_process_error(zs_scanner_t *scanner);
+
+void test_process_record(zs_scanner_t *scanner);
+
+int test_date_to_timestamp(void);
diff --git a/tests/libzscanner/test_zscanner.in b/tests/libzscanner/test_zscanner.in
new file mode 100644
index 0000000..2c0c275
--- /dev/null
+++ b/tests/libzscanner/test_zscanner.in
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+SOURCE=@top_srcdir@/tests/libzscanner
+BUILD=@top_builddir@/tests/libzscanner
+
+. @top_srcdir@/tests/tap/libtap.sh
+
+cd "$BUILD"
+
+TMPDIR=$(test_tmpdir)
+TESTS_DIR="$SOURCE"/data
+ZSCANNER_TOOL="$BUILD"/zscanner-tool
+
+plan 80
+
+mkdir -p "$TMPDIR"/includes/
+for a in 1 2 3 4 5 6; do
+ cat "$TESTS_DIR"/includes/include"$a" > "$TMPDIR"/includes/include"$a";
+done
+
+for case in $(cat "$SOURCE"/TESTS); do
+ casein=$(test_file_path data/"$case".in)
+ caseout=$(test_file_path data/"$case".out)
+ filein="$TMPDIR"/"$case".in
+ fileout="$TMPDIR"/"$case".out
+
+ sed -e "s|@TMPDIR@|$TMPDIR|;" < "$casein" > "$filein"
+
+ "$ZSCANNER_TOOL" -m 2 . "$filein" > "$fileout"
+
+ if cmp -s "$fileout" "$caseout"; then
+ ok "$case: output matches" true
+ rm "$filein"
+ rm "$fileout"
+ else
+ ok "$case: output differs" false
+ diff -urNap "$caseout" "$fileout" | while read line; do diag "$line"; done
+ fi
+done
+
+rm -rf "$TMPDIR"/includes/
diff --git a/tests/libzscanner/zscanner-tool.c b/tests/libzscanner/zscanner-tool.c
new file mode 100644
index 0000000..06b795c
--- /dev/null
+++ b/tests/libzscanner/zscanner-tool.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <getopt.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libzscanner/processing.h"
+#include "libzscanner/scanner.h"
+
+#define DEFAULT_MODE 1
+#define DEFAULT_CLASS 1
+#define DEFAULT_TTL 0
+
+static void *timestamp_worker(void *data)
+{
+ int *ret = (int *)data;
+ *ret = test_date_to_timestamp();
+ return NULL;
+}
+
+static void help(void)
+{
+ printf("\nZone scanner testing tool.\n"
+ "Usage: zscanner-tool [parameters] origin zonefile\n"
+ "\n"
+ "Parameters:\n"
+ " -m [0,1,2] Processing mode.\n"
+ " 0 Empty output.\n"
+ " 1 Debug output (DEFAULT).\n"
+ " 2 Test output.\n"
+ " -s State parsing mode.\n"
+ " -t Launch unit tests.\n"
+ " -h Print this help.\n");
+}
+
+static int time_test(void)
+{
+ pthread_t t1, t2, t3;
+ int ret1, ret2, ret3;
+
+ pthread_create(&t1, NULL, timestamp_worker, &ret1);
+ pthread_create(&t2, NULL, timestamp_worker, &ret2);
+ pthread_create(&t3, NULL, timestamp_worker, &ret3);
+
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+ pthread_join(t3, NULL);
+
+ if (ret1 != 0 || ret2 != 0 || ret3 != 0) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int include(zs_scanner_t *s);
+
+static int state_parsing(zs_scanner_t *s)
+{
+ while (zs_parse_record(s) == 0) {
+ switch (s->state) {
+ case ZS_STATE_DATA:
+ if (s->process.record != NULL) {
+ s->process.record(s);
+ }
+ break;
+ case ZS_STATE_ERROR:
+ if (s->process.error != NULL) {
+ s->process.error(s);
+ }
+ if (s->error.fatal) {
+ return -1;
+ }
+ break;
+ case ZS_STATE_INCLUDE:
+ if (include(s) != 0) {
+ return -1;
+ }
+ break;
+ default:
+ return (s->error.counter == 0) ? 0 : -1;
+ }
+ }
+
+ return -1;
+}
+
+static int include(zs_scanner_t *s)
+{
+ zs_scanner_t *ss;
+ int ret = 0;
+
+ if ((ss = malloc(sizeof(zs_scanner_t))) == NULL ||
+ zs_init(ss, (char *)s->buffer, s->default_class, s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error, s->process.data) != 0 ||
+ state_parsing(ss) != 0) {
+ if (ss == NULL) {
+ s->error.code = ZS_ENOMEM;
+ } else if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ s->error.code = ZS_UNPROCESSED_INCLUDE;
+ } else {
+ s->error.code = ss->error.code;
+ }
+
+ if (s->process.error != NULL) {
+ s->buffer[0] = '\0'; // Clear unrelated content.
+ s->buffer_length = 0;
+ s->error.counter++;
+ s->error.fatal = true;
+ s->process.error(s);
+ }
+
+ ret = -1;
+ }
+
+ zs_deinit(ss);
+ free(ss);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int mode = DEFAULT_MODE, state = 0, test = 0;
+
+ // Command line long options.
+ struct option opts[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "state", no_argument, NULL, 's' },
+ { "test", no_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+
+ // Parsed command line arguments.
+ int opt = 0, li = 0;
+ while ((opt = getopt_long(argc, argv, "m:sth", opts, &li)) != -1) {
+ switch (opt) {
+ case 'm':
+ mode = atoi(optarg);
+ break;
+ case 's':
+ state = 1;
+ break;
+ case 't':
+ test = 1;
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ default:
+ help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (test == 1) {
+ return time_test();
+ }
+
+ // Check if there are 2 remaining non-options.
+ if (argc - optind != 2) {
+ help();
+ return EXIT_FAILURE;
+ }
+
+ const char *origin = argv[optind];
+ const char *zone_file = argv[optind + 1];
+
+ // Create a zone scanner.
+ zs_scanner_t *s = malloc(sizeof(zs_scanner_t));
+ if (s == NULL) {
+ printf("Scanner create error!\n");
+ return EXIT_FAILURE;
+ }
+ if (zs_init(s, origin, DEFAULT_CLASS, DEFAULT_TTL) != 0) {
+ printf("Scanner init error!\n");
+ free(s);
+ return EXIT_FAILURE;
+ }
+ if (zs_set_input_file(s, zone_file) != 0) {
+ printf("Scanner file error!\n");
+ zs_deinit(s);
+ free(s);
+ return EXIT_FAILURE;
+ }
+
+ // Set the processing mode.
+ int ret;
+ switch (mode) {
+ case 0:
+ ret = 0;
+ break;
+ case 1:
+ ret = zs_set_processing(s, debug_process_record, debug_process_error, NULL);
+ break;
+ case 2:
+ ret = zs_set_processing(s, test_process_record, test_process_error, NULL);
+ break;
+ default:
+ printf("Bad mode number!\n");
+ help();
+ return EXIT_FAILURE;
+ }
+ if (ret != 0) {
+ printf("Processing setup error!\n");
+ return EXIT_FAILURE;
+ }
+
+ // Parse the file.
+ ret = state ? state_parsing(s) : zs_parse_all(s);
+ if (ret == 0) {
+ if (mode == DEFAULT_MODE) {
+ printf("Zone file has been processed successfully\n");
+ }
+
+ zs_deinit(s);
+ free(s);
+ return EXIT_SUCCESS;
+ } else {
+ if (s->error.counter > 0 && mode == DEFAULT_MODE) {
+ printf("Zone processing has stopped with "
+ "%"PRIu64" warnings/errors!\n",
+ s->error.counter);
+ } else if (mode == DEFAULT_MODE) {
+ printf("%s\n", zs_strerror(s->error.code));
+ }
+
+ zs_deinit(s);
+ free(s);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/tests/modules/test_onlinesign.c b/tests/modules/test_onlinesign.c
new file mode 100644
index 0000000..e2b3e89
--- /dev/null
+++ b/tests/modules/test_onlinesign.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+#include <assert.h>
+
+#include "knot/modules/onlinesign/nsec_next.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+
+/*!
+ * \brief Assert that a domain name in a static buffer is valid.
+ */
+#define _assert_dname(name) \
+ assert(knot_dname_wire_check(name, name + KNOT_DNAME_MAXLEN, NULL) > 0)
+
+static void _test_nsec_next(const char *msg,
+ const knot_dname_t *input,
+ const knot_dname_t *apex,
+ const knot_dname_t *expected)
+{
+ knot_dname_t *next = online_nsec_next(input, apex);
+ ok(next != NULL && knot_dname_is_equal(next, expected),
+ "nsec_next, %s", msg);
+ knot_dname_free(next, NULL);
+}
+
+/*!
+ * \brief Check \a online_nsec_next.
+ *
+ * Intentionally implemented as a macro. The input domain names are copied
+ * into static buffers and validated.
+ */
+#define test_nsec_next(msg, _input, _apex, _expected) \
+{ \
+ uint8_t input[KNOT_DNAME_MAXLEN] = _input; \
+ uint8_t apex[KNOT_DNAME_MAXLEN] = _apex; \
+ uint8_t expected[KNOT_DNAME_MAXLEN] = _expected; \
+ \
+ _assert_dname(input); \
+ _assert_dname(apex); \
+ _assert_dname(expected); \
+ \
+ _test_nsec_next(msg, input, apex, expected); \
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // adding a single zero-byte label
+
+ test_nsec_next(
+ "zero-byte label, apex",
+ "\x7""example""\x3""com",
+ "\x7""example""\x3""com",
+ "\x01\x00""\x07""example""\x03""com"
+ );
+
+ test_nsec_next(
+ "zero-byte label, subdomain",
+ "\x02""nx""\x7""example""\x3""com",
+ "\x7""example""\x3""com",
+ "\x01\x00""\x02""nx""\x07""example""\x03""com"
+ );
+
+ test_nsec_next(
+ "zero-byte label, binary",
+ "\x02\xff\xff""\x7""example""\x3""com",
+ "\x07""example""\x3""com",
+ "\x01\x00""\x02\xff\xff""\x7""example""\x3""com"
+ );
+
+ // zero byte label won't fit, increment
+ #define APEX \
+ "\x05""bacon""\x05""salad"
+
+ #define LONG_SUFFIX \
+ "\x2e""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
+ "\x2e""iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" \
+ "\x2e""mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" \
+ "\x2e""qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" \
+ "\x2c""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \
+ APEX
+ assert(sizeof(LONG_SUFFIX) == 245 + 1);
+
+ test_nsec_next(
+ "increment first label (simple)",
+ "\x08""icecream" LONG_SUFFIX,
+ APEX,
+ "\x08""icecrean" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (binary)",
+ "\x08""walrus\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x08""walrut\x00\x00" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (in place)",
+ "\x07""lobster" LONG_SUFFIX,
+ APEX,
+ "\x07""lobstes" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (extend)",
+ "\x07""\xff\xff\xff\xff\xff\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" LONG_SUFFIX
+ );
+
+ // name too long
+
+ test_nsec_next(
+ "name to long, strip label and increase next (simple)",
+ "\x03""\xff\xff\xff""\x04""newt" LONG_SUFFIX,
+ APEX,
+ "\x04""newu" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "name to long, strip label and increase next (binary)",
+ "\x03""\xff\xff\xff""\x04""cc\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x04""cd\x00\x00" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "name to long, strip label and increase next (extend)",
+ "\x04""\xff\xff\xff\xff""\x03""\xff\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x04""\xff\xff\xff\x00" LONG_SUFFIX
+ );
+
+ // label too long
+
+ #define MAX_LABEL "\x3f" /* 63 */ \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff"
+ assert(sizeof(MAX_LABEL) == 64 + 1);
+
+ #define PAD_LABEL "\x28" /* 40 */ \
+ "iiiiiiiiiioooooooooottttttttttssssssssss"
+ assert(sizeof(PAD_LABEL) == 41 + 1);
+
+ test_nsec_next(
+ "label too long, strip and increase next (simple)",
+ MAX_LABEL "\x08""mandrill" MAX_LABEL MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""mandrilm" MAX_LABEL MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, strip and increase next (extend)",
+ MAX_LABEL "\x07""\xff\xff\xff\xff\xff\xff\xff" MAX_LABEL MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" MAX_LABEL MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, strip multiple",
+ MAX_LABEL MAX_LABEL "\x08""flamingo" MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""flamingp" MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, wrap around to apex",
+ "\x31" /* 49 */
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ MAX_LABEL MAX_LABEL MAX_LABEL APEX,
+ APEX,
+ APEX
+ );
+
+ return 0;
+}
diff --git a/tests/modules/test_rrl.c b/tests/modules/test_rrl.c
new file mode 100644
index 0000000..b7aef4e
--- /dev/null
+++ b/tests/modules/test_rrl.c
@@ -0,0 +1,167 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/random.h"
+#include "libknot/libknot.h"
+#include "contrib/sockaddr.h"
+#include "knot/modules/rrl/functions.c"
+
+/* Enable time-dependent tests. */
+//#define ENABLE_TIMED_TESTS
+#define RRL_SIZE 196613
+#define RRL_THREADS 8
+#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */
+
+/* Disabled as default as it depends on random input.
+ * Table may be consistent even if some collision occur (and they may occur).
+ * Note: Disabled due to reported problems when running on VMs due to time
+ * flow inconsistencies. Should work alright on a host machine.
+ */
+#ifdef ENABLE_TIMED_TESTS
+struct bucketmap {
+ unsigned i;
+ uint64_t x;
+};
+
+/*! \brief Unit runnable. */
+struct runnable_data {
+ int passed;
+ rrl_table_t *rrl;
+ struct sockaddr_storage *addr;
+ rrl_req_t *rq;
+ knot_dname_t *zone;
+};
+
+static void* rrl_runnable(void *arg)
+{
+ struct runnable_data *d = (struct runnable_data *)arg;
+ struct sockaddr_storage addr;
+ memcpy(&addr, d->addr, sizeof(struct sockaddr_storage));
+ int lock = -1;
+ uint32_t now = time(NULL);
+ struct bucketmap *m = malloc(RRL_INSERTS * sizeof(struct bucketmap));
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ m[i].i = dnssec_random_uint32_t();
+ ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ m[i].x = b->netblk;
+ }
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ if (b->netblk != m[i].x) {
+ d->passed = 0;
+ }
+ }
+ free(m);
+ return NULL;
+}
+
+static void rrl_hopscotch(struct runnable_data* rd)
+{
+ rd->passed = 1;
+ pthread_t thr[RRL_THREADS];
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_create(thr + i, NULL, &rrl_runnable, rd);
+ }
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_join(thr[i], NULL);
+ }
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ /* Prepare query. */
+ knot_pkt_t *query = knot_pkt_new(NULL, 512, NULL);
+ if (query == NULL) {
+ return KNOT_ERROR; /* Fatal */
+ }
+
+ knot_dname_t *qname = knot_dname_from_str_alloc("beef.");
+ int ret = knot_pkt_put_question(query, qname, KNOT_CLASS_IN, KNOT_RRTYPE_A);
+ knot_dname_free(qname, NULL);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(query);
+ return KNOT_ERROR; /* Fatal */
+ }
+
+ /* Prepare response */
+ uint8_t rbuf[65535];
+ size_t rlen = sizeof(rbuf);
+ memcpy(rbuf, query->wire, query->size);
+ knot_wire_flags_set_qr(rbuf);
+
+ rrl_req_t rq;
+ rq.w = rbuf;
+ rq.len = rlen;
+ rq.query = query;
+ rq.flags = 0;
+
+ /* 1. create rrl table */
+ const uint32_t rate = 10;
+ rrl_table_t *rrl = rrl_create(RRL_SIZE, rate);
+ ok(rrl != NULL, "rrl: create");
+
+ /* 4. N unlimited requests. */
+ knot_dname_t *zone = knot_dname_from_str_alloc("rrl.");
+
+ struct sockaddr_storage addr;
+ struct sockaddr_storage addr6;
+ sockaddr_set(&addr, AF_INET, "1.2.3.4", 0);
+ sockaddr_set(&addr6, AF_INET6, "1122:3344:5566:7788::aabb", 0);
+ ret = 0;
+ for (unsigned i = 0; i < rate * RRL_CAPACITY; ++i) {
+ if (rrl_query(rrl, &addr, &rq, zone, NULL) != KNOT_EOK ||
+ rrl_query(rrl, &addr6, &rq, zone, NULL) != KNOT_EOK) {
+ ret = KNOT_ELIMIT;
+ break;
+ }
+ }
+ is_int(0, ret, "rrl: unlimited IPv4/v6 requests");
+
+#ifdef ENABLE_TIMED_TESTS
+ /* 5. limited request */
+ ret = rrl_query(rrl, &addr, &rq, zone, NULL);
+ is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv4 request");
+
+ /* 6. limited IPv6 request */
+ ret = rrl_query(rrl, &addr6, &rq, zone, NULL);
+ is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv6 request");
+
+ /* 8. hopscotch test */
+ struct runnable_data rd = {
+ 1, rrl, &addr, &rq, zone
+ };
+ rrl_hopscotch(&rd);
+ ok(rd.passed, "rrl: hashtable is ~ consistent");
+#endif
+
+ knot_dname_free(zone, NULL);
+ knot_pkt_free(query);
+ rrl_destroy(rrl);
+ dnssec_crypto_cleanup();
+ return 0;
+}
diff --git a/tests/tap/basic.c b/tests/tap/basic.c
new file mode 100644
index 0000000..23b595c
--- /dev/null
+++ b/tests/tap/basic.c
@@ -0,0 +1,605 @@
+/*
+ * Some utility routines for writing tests.
+ *
+ * Here are a variety of utility routines for writing tests compatible with
+ * the TAP protocol. All routines of the form ok() or is*() take a test
+ * number and some number of appropriate arguments, check to be sure the
+ * results match the expected output using the arguments, and print out
+ * something appropriate for that test number. Other utility routines help in
+ * constructing more complex tests, skipping tests, reporting errors, setting
+ * up the TAP output format, or finding things in the test environment.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+# include <direct.h>
+#else
+# include <sys/stat.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "basic.h"
+
+/* Windows provides mkdir and rmdir under different names. */
+#ifdef _WIN32
+# define mkdir(p, m) _mkdir(p)
+# define rmdir(p) _rmdir(p)
+#endif
+
+/*
+ * The test count. Always contains the number that will be used for the next
+ * test status.
+ */
+unsigned long testnum = 1;
+
+/*
+ * Status information stored so that we can give a test summary at the end of
+ * the test case. We store the planned final test and the count of failures.
+ * We can get the highest test count from testnum.
+ *
+ * We also store the PID of the process that called plan() and only summarize
+ * results when that process exits, so as to not misreport results in forked
+ * processes.
+ *
+ * If _lazy is true, we're doing lazy planning and will print out the plan
+ * based on the last test number at the end of testing.
+ */
+static unsigned long _planned = 0;
+static unsigned long _failed = 0;
+static pid_t _process = 0;
+static int _lazy = 0;
+
+/*
+ * Our exit handler. Called on completion of the test to report a summary of
+ * results provided we're still in the original process. This also handles
+ * printing out the plan if we used plan_lazy(), although that's suppressed if
+ * we never ran a test (due to an early bail, for example).
+ */
+static void
+finish(void)
+{
+ unsigned long highest = testnum - 1;
+
+ if (_planned == 0 && !_lazy)
+ return;
+ fflush(stderr);
+ if (_process != 0 && getpid() == _process) {
+ if (_lazy && highest > 0) {
+ printf("1..%lu\n", highest);
+ _planned = highest;
+ }
+ if (_planned > highest)
+ printf("# Looks like you planned %lu test%s but only ran %lu\n",
+ _planned, (_planned > 1 ? "s" : ""), highest);
+ else if (_planned < highest)
+ printf("# Looks like you planned %lu test%s but ran %lu extra\n",
+ _planned, (_planned > 1 ? "s" : ""), highest - _planned);
+ else if (_failed > 0)
+ printf("# Looks like you failed %lu test%s of %lu\n", _failed,
+ (_failed > 1 ? "s" : ""), _planned);
+ else if (_planned > 1)
+ printf("# All %lu tests successful or skipped\n", _planned);
+ else
+ printf("# %lu test successful or skipped\n", _planned);
+ }
+}
+
+/*
+ * Initialize things. Turns on line buffering on stdout and then prints out
+ * the number of tests in the test suite.
+ */
+void
+plan(unsigned long count)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
+ strerror(errno));
+ fflush(stderr);
+ printf("1..%lu\n", count);
+ testnum = 1;
+ _planned = count;
+ _process = getpid();
+ atexit(finish);
+}
+
+/*
+ * Initialize things for lazy planning, where we'll automatically print out a
+ * plan at the end of the program. Turns on line buffering on stdout as well.
+ */
+void
+plan_lazy(void)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
+ strerror(errno));
+ testnum = 1;
+ _process = getpid();
+ _lazy = 1;
+ atexit(finish);
+}
+
+/*
+ * Skip the entire test suite and exits. Should be called instead of plan(),
+ * not after it, since it prints out a special plan line.
+ */
+void
+skip_all(const char *format, ...)
+{
+ fflush(stderr);
+ printf("1..0 # skip");
+ if (format != NULL) {
+ va_list args;
+
+ putchar(' ');
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+ exit(0);
+}
+
+/*
+ * Print the test description.
+ */
+static void
+print_desc(const char *format, va_list args)
+{
+ printf(" - ");
+ vprintf(format, args);
+}
+
+/*
+ * Takes a boolean success value and assumes the test passes if that value
+ * is true and fails if that value is false.
+ */
+void
+ok(int success, const char *format, ...)
+{
+ fflush(stderr);
+ printf("%sok %lu", success ? "" : "not ", testnum++);
+ if (!success)
+ _failed++;
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Same as ok(), but takes the format arguments as a va_list.
+ */
+void
+okv(int success, const char *format, va_list args)
+{
+ fflush(stderr);
+ printf("%sok %lu", success ? "" : "not ", testnum++);
+ if (!success)
+ _failed++;
+ if (format != NULL)
+ print_desc(format, args);
+ putchar('\n');
+}
+
+/*
+ * Skip a test.
+ */
+void
+skip(const char *reason, ...)
+{
+ fflush(stderr);
+ printf("ok %lu # skip", testnum++);
+ if (reason != NULL) {
+ va_list args;
+
+ va_start(args, reason);
+ putchar(' ');
+ vprintf(reason, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Report the same status on the next count tests.
+ */
+void
+ok_block(unsigned long count, int status, const char *format, ...)
+{
+ unsigned long i;
+
+ fflush(stderr);
+ for (i = 0; i < count; i++) {
+ printf("%sok %lu", status ? "" : "not ", testnum++);
+ if (!status)
+ _failed++;
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+ }
+}
+
+/*
+ * Skip the next count tests.
+ */
+void
+skip_block(unsigned long count, const char *reason, ...)
+{
+ unsigned long i;
+
+ fflush(stderr);
+ for (i = 0; i < count; i++) {
+ printf("ok %lu # skip", testnum++);
+ if (reason != NULL) {
+ va_list args;
+
+ va_start(args, reason);
+ putchar(' ');
+ vprintf(reason, args);
+ va_end(args);
+ }
+ putchar('\n');
+ }
+}
+
+/*
+ * Takes an expected integer and a seen integer and assumes the test passes
+ * if those two numbers match.
+ */
+void
+is_int(long long wanted, long long seen, const char *format, ...)
+{
+ fflush(stderr);
+ if (wanted == seen)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %lld\n# seen: %lld\n", wanted, seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Takes a string and what the string should be, and assumes the test passes
+ * if those strings match (using strcmp).
+ */
+void
+is_string(const char *wanted, const char *seen, const char *format, ...)
+{
+ if (wanted == NULL)
+ wanted = "(null)";
+ if (seen == NULL)
+ seen = "(null)";
+ fflush(stderr);
+ if (strcmp(wanted, seen) == 0)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %s\n# seen: %s\n", wanted, seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Takes an expected unsigned long and a seen unsigned long and assumes the
+ * test passes if the two numbers match. Otherwise, reports them in hex.
+ */
+void
+is_hex(unsigned long long wanted, unsigned long long seen,
+ const char *format, ...)
+{
+ fflush(stderr);
+ if (wanted == seen)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %llx\n# seen: %llx\n",
+ (unsigned long long) wanted,
+ (unsigned long long) seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Bail out with an error.
+ */
+void
+bail(const char *format, ...)
+{
+ va_list args;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("Bail out! ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ exit(255);
+}
+
+/*
+ * Bail out with an error, appending strerror(errno).
+ */
+void
+sysbail(const char *format, ...)
+{
+ va_list args;
+ int oerrno = errno;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("Bail out! ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf(": %s\n", strerror(oerrno));
+ exit(255);
+}
+
+/*
+ * Report a diagnostic to stderr.
+ */
+void
+diag(const char *format, ...)
+{
+ va_list args;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+/*
+ * Report a diagnostic to stderr, appending strerror(errno).
+ */
+void
+sysdiag(const char *format, ...)
+{
+ va_list args;
+ int oerrno = errno;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf(": %s\n", strerror(oerrno));
+}
+
+/*
+ * Allocate cleared memory, reporting a fatal error with bail on failure.
+ */
+void *
+bcalloc(size_t n, size_t size)
+{
+ void *p;
+
+ p = calloc(n, size);
+ if (p == NULL)
+ sysbail("failed to calloc %lu", (unsigned long)(n * size));
+ return p;
+}
+
+/*
+ * Allocate memory, reporting a fatal error with bail on failure.
+ */
+void *
+bmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL)
+ sysbail("failed to malloc %lu", (unsigned long) size);
+ return p;
+}
+
+/*
+ * Reallocate memory, reporting a fatal error with bail on failure.
+ */
+void *
+brealloc(void *p, size_t size)
+{
+ p = realloc(p, size);
+ if (p == NULL)
+ sysbail("failed to realloc %lu bytes", (unsigned long) size);
+ return p;
+}
+
+/*
+ * Copy a string, reporting a fatal error with bail on failure.
+ */
+char *
+bstrdup(const char *s)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ if (p == NULL)
+ sysbail("failed to strdup %lu bytes", (unsigned long) len);
+ memcpy(p, s, len);
+ return p;
+}
+
+/*
+ * Copy up to n characters of a string, reporting a fatal error with bail on
+ * failure. Don't use the system strndup function, since it may not exist and
+ * the TAP library doesn't assume any portability support.
+ */
+char *
+bstrndup(const char *s, size_t n)
+{
+ const char *p;
+ char *copy;
+ size_t length;
+
+ /* Don't assume that the source string is nul-terminated. */
+ for (p = s; (size_t) (p - s) < n && *p != '\0'; p++)
+ ;
+ length = p - s;
+ copy = malloc(length + 1);
+ if (p == NULL)
+ sysbail("failed to strndup %lu bytes", (unsigned long) length);
+ memcpy(copy, s, length);
+ copy[length] = '\0';
+ return copy;
+}
+
+/*
+ * Locate a test file. Given the partial path to a file, look under BUILD and
+ * then SOURCE for the file and return the full path to the file. Returns
+ * NULL if the file doesn't exist. A non-NULL return should be freed with
+ * test_file_path_free().
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_file_path(const char *file)
+{
+ char *base;
+ char *path = NULL;
+ size_t length;
+ const char *envs[] = { "BUILD", "SOURCE", NULL };
+ int i;
+
+ for (i = 0; envs[i] != NULL; i++) {
+ base = getenv(envs[i]);
+ if (base == NULL)
+ continue;
+ length = strlen(base) + 1 + strlen(file) + 1;
+ path = bmalloc(length);
+ snprintf(path, length, "%s/%s", base, file);
+ if (access(path, R_OK) == 0)
+ break;
+ free(path);
+ path = NULL;
+ }
+ return path;
+}
+
+/*
+ * Free a path returned from test_file_path(). This function exists primarily
+ * for Windows, where memory must be freed from the same library domain that
+ * it was allocated from.
+ */
+void
+test_file_path_free(char *path)
+{
+ if (path != NULL)
+ free(path);
+}
+
+/*
+ * Create a temporary directory, tmp, under BUILD if set and the current
+ * directory if it does not. Returns the path to the temporary directory in
+ * newly allocated memory, and calls bail on any failure. The return value
+ * should be freed with test_tmpdir_free.
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_tmpdir(void)
+{
+ const char *build;
+ char *path = NULL;
+ size_t length;
+
+ build = getenv("BUILD");
+ if (build == NULL)
+ build = ".";
+ length = strlen(build) + strlen("/tmp") + 1;
+ path = bmalloc(length);
+ snprintf(path, length, "%s/tmp", build);
+ if (access(path, X_OK) < 0)
+ if (mkdir(path, 0777) < 0)
+ sysbail("error creating temporary directory %s", path);
+ return path;
+}
+
+/*
+ * Free a path returned from test_tmpdir() and attempt to remove the
+ * directory. If we can't delete the directory, don't worry; something else
+ * that hasn't yet cleaned up may still be using it.
+ */
+void
+test_tmpdir_free(char *path)
+{
+ rmdir(path);
+ if (path != NULL)
+ free(path);
+}
diff --git a/tests/tap/basic.h b/tests/tap/basic.h
new file mode 100644
index 0000000..2e1b7e3
--- /dev/null
+++ b/tests/tap/basic.h
@@ -0,0 +1,132 @@
+/*
+ * Basic utility routines for the TAP protocol.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "macros.h"
+#include <stdarg.h> /* va_list */
+#include <sys/types.h> /* size_t */
+
+/*
+ * Used for iterating through arrays. ARRAY_SIZE returns the number of
+ * elements in the array (useful for a < upper bound in a for loop) and
+ * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
+ * legal to refer to such a pointer as long as it's never dereferenced).
+ */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
+
+BEGIN_DECLS
+
+/*
+ * The test count. Always contains the number that will be used for the next
+ * test status.
+ */
+extern unsigned long testnum;
+
+/* Print out the number of tests and set standard output to line buffered. */
+void plan(unsigned long count);
+
+/*
+ * Prepare for lazy planning, in which the plan will be printed automatically
+ * at the end of the test program.
+ */
+void plan_lazy(void);
+
+/* Skip the entire test suite. Call instead of plan. */
+void skip_all(const char *format, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+
+/*
+ * Basic reporting functions. The okv() function is the same as ok() but
+ * takes the test description as a va_list to make it easier to reuse the
+ * reporting infrastructure when writing new tests.
+ */
+void ok(int success, const char *format, ...)
+ __attribute__((__format__(printf, 2, 3)));
+void okv(int success, const char *format, va_list args);
+void skip(const char *reason, ...)
+ __attribute__((__format__(printf, 1, 2)));
+
+/* Report the same status on, or skip, the next count tests. */
+void ok_block(unsigned long count, int success, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void skip_block(unsigned long count, const char *reason, ...)
+ __attribute__((__format__(printf, 2, 3)));
+
+/* Check an expected value against a seen value. */
+void is_int(long long wanted, long long seen, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void is_string(const char *wanted, const char *seen, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void is_hex(unsigned long long wanted, unsigned long long seen,
+ const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+
+/* Bail out with an error. sysbail appends strerror(errno). */
+void bail(const char *format, ...)
+ __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
+void sysbail(const char *format, ...)
+ __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
+
+/* Report a diagnostic to stderr prefixed with #. */
+void diag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void sysdiag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+
+/* Allocate memory, reporting a fatal error with bail on failure. */
+void *bcalloc(size_t, size_t)
+ __attribute__((__alloc_size__(1, 2), __malloc__));
+void *bmalloc(size_t)
+ __attribute__((__alloc_size__(1), __malloc__));
+void *brealloc(void *, size_t)
+ __attribute__((__alloc_size__(2), __malloc__));
+char *bstrdup(const char *)
+ __attribute__((__malloc__, __nonnull__));
+char *bstrndup(const char *, size_t)
+ __attribute__((__malloc__, __nonnull__));
+
+/*
+ * Find a test file under BUILD or SOURCE, returning the full path. The
+ * returned path should be freed with test_file_path_free().
+ */
+char *test_file_path(const char *file)
+ __attribute__((__malloc__, __nonnull__));
+void test_file_path_free(char *path);
+
+/*
+ * Create a temporary directory relative to BUILD and return the path. The
+ * returned path should be freed with test_tmpdir_free.
+ */
+char *test_tmpdir(void)
+ __attribute__((__malloc__));
+void test_tmpdir_free(char *path);
+
+END_DECLS
diff --git a/tests/tap/files.c b/tests/tap/files.c
new file mode 100644
index 0000000..2e67e9e
--- /dev/null
+++ b/tests/tap/files.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 "files.h"
+
+#include "../../src/contrib/string.c"
+#include "../../src/contrib/files.c"
+
+#include <stdlib.h>
+
+static char *make_temp(bool is_directory)
+{
+ char *tmpdir = getenv("TMPDIR");
+ if (!tmpdir) {
+ tmpdir = "/tmp";
+ }
+
+ char tmp[4096] = { 0 };
+ int r = snprintf(tmp, sizeof(tmp), "%s/knot_unit.XXXXXX", tmpdir);
+ if (r <= 0 || r >= sizeof(tmp)) {
+ return NULL;
+ }
+
+ if (is_directory) {
+ char *ret = mkdtemp(tmp);
+ if (ret == NULL) {
+ return NULL;
+ }
+ } else {
+ int ret = mkstemp(tmp);
+ if (ret == -1) {
+ return NULL;
+ }
+ close(ret);
+ }
+
+ return strdup(tmp);
+}
+
+char *test_mktemp(void)
+{
+ return make_temp(false);
+}
+
+char *test_mkdtemp(void)
+{
+ return make_temp(true);
+}
+
+bool test_rm_rf(const char *path)
+{
+ return remove_path(path);
+}
diff --git a/tests/tap/files.h b/tests/tap/files.h
new file mode 100644
index 0000000..bfc6090
--- /dev/null
+++ b/tests/tap/files.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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/>.
+*/
+
+#pragma once
+
+#include <stdbool.h>
+
+/*!
+ * \brief Create a temporary file.
+ *
+ * If TMPDIR environment variable is set, the file is created within
+ * that directory. If the variable is not set, the file is created
+ * within /tmp.
+ */
+char *test_mktemp(void);
+
+/*!
+ * \brief Create a temporary directory.
+ *
+ * If TMPDIR environment variable is set, the directory is created within
+ * that directory. If the variable is not set, the directory is created
+ * within /tmp.
+ */
+char *test_mkdtemp(void);
+
+/*!
+ * \brief Delete file or directory (recursive).
+ *
+ * \return true on success, false when one or more files failed to be removed.
+ */
+bool test_rm_rf(const char *path);
diff --git a/tests/tap/float.c b/tests/tap/float.c
new file mode 100644
index 0000000..5c0c643
--- /dev/null
+++ b/tests/tap/float.c
@@ -0,0 +1,67 @@
+/*
+ * Utility routines for writing floating point tests.
+ *
+ * Currently provides only one function, which checks whether a double is
+ * equal to an expected value within a given epsilon. This is broken into a
+ * separate source file from the rest of the basic C TAP library because it
+ * may require linking with -lm on some platforms, and the package may not
+ * otherwise care about floating point.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * 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.
+ */
+
+/* Required for isnan() and isinf(). */
+#if defined(__STRICT_ANSI__) || defined(PEDANTIC)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+# endif
+#endif
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "basic.h"
+#include "float.h"
+
+/*
+ * Takes an expected double and a seen double and assumes the test passes if
+ * those two numbers are within delta of each other.
+ */
+void
+is_double(double wanted, double seen, double epsilon, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ fflush(stderr);
+ if ((isnan(wanted) && isnan(seen))
+ || (isinf(wanted) && isinf(seen) && wanted == seen)
+ || fabs(wanted - seen) <= epsilon)
+ okv(1, format, args);
+ else {
+ printf("# wanted: %g\n# seen: %g\n", wanted, seen);
+ okv(0, format, args);
+ }
+}
diff --git a/tests/tap/float.h b/tests/tap/float.h
new file mode 100644
index 0000000..5c58b5b
--- /dev/null
+++ b/tests/tap/float.h
@@ -0,0 +1,39 @@
+/*
+ * Floating point check function for the TAP protocol.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "macros.h"
+
+BEGIN_DECLS
+
+/* Check an expected value against a seen value within epsilon. */
+void is_double(double wanted, double seen, double epsilon,
+ const char *format, ...)
+ __attribute__((__format__(printf, 4, 5)));
+
+END_DECLS
diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh
new file mode 100644
index 0000000..0ffab2d
--- /dev/null
+++ b/tests/tap/libtap.sh
@@ -0,0 +1,246 @@
+# Shell function library for test cases.
+#
+# Note that while many of the functions in this library could benefit from
+# using "local" to avoid possibly hammering global variables, Solaris /bin/sh
+# doesn't support local and this library aspires to be portable to Solaris
+# Bourne shell. Instead, all private variables are prefixed with "tap_".
+#
+# This file provides a TAP-compatible shell function library useful for
+# writing test cases. It is part of C TAP Harness, which can be found at
+# <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+# Copyright 2006, 2007, 2008, 2013
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# 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.
+
+# Print out the number of test cases we expect to run.
+plan () {
+ count=1
+ planned="$1"
+ failed=0
+ echo "1..$1"
+ trap finish 0
+}
+
+# Prepare for lazy planning.
+plan_lazy () {
+ count=1
+ planned=0
+ failed=0
+ trap finish 0
+}
+
+# Report the test status on exit.
+finish () {
+ tap_highest=`expr "$count" - 1`
+ if [ "$planned" = 0 ] ; then
+ echo "1..$tap_highest"
+ planned="$tap_highest"
+ fi
+ tap_looks='# Looks like you'
+ if [ "$planned" -gt 0 ] ; then
+ if [ "$planned" -gt "$tap_highest" ] ; then
+ if [ "$planned" -gt 1 ] ; then
+ echo "$tap_looks planned $planned tests but only ran" \
+ "$tap_highest"
+ else
+ echo "$tap_looks planned $planned test but only ran" \
+ "$tap_highest"
+ fi
+ elif [ "$planned" -lt "$tap_highest" ] ; then
+ tap_extra=`expr "$tap_highest" - "$planned"`
+ if [ "$planned" -gt 1 ] ; then
+ echo "$tap_looks planned $planned tests but ran" \
+ "$tap_extra extra"
+ else
+ echo "$tap_looks planned $planned test but ran" \
+ "$tap_extra extra"
+ fi
+ elif [ "$failed" -gt 0 ] ; then
+ if [ "$failed" -gt 1 ] ; then
+ echo "$tap_looks failed $failed tests of $planned"
+ else
+ echo "$tap_looks failed $failed test of $planned"
+ fi
+ elif [ "$planned" -gt 1 ] ; then
+ echo "# All $planned tests successful or skipped"
+ else
+ echo "# $planned test successful or skipped"
+ fi
+ fi
+}
+
+# Skip the entire test suite. Should be run instead of plan.
+skip_all () {
+ tap_desc="$1"
+ if [ -n "$tap_desc" ] ; then
+ echo "1..0 # skip $tap_desc"
+ else
+ echo "1..0 # skip"
+ fi
+ exit 0
+}
+
+# ok takes a test description and a command to run and prints success if that
+# command is successful, false otherwise. The count starts at 1 and is
+# updated each time ok is printed.
+ok () {
+ tap_desc="$1"
+ if [ -n "$tap_desc" ] ; then
+ tap_desc=" - $tap_desc"
+ fi
+ shift
+ if "$@" ; then
+ echo ok "$count$tap_desc"
+ else
+ echo not ok "$count$tap_desc"
+ failed=`expr $failed + 1`
+ fi
+ count=`expr $count + 1`
+}
+
+# Skip the next test. Takes the reason why the test is skipped.
+skip () {
+ echo "ok $count # skip $*"
+ count=`expr $count + 1`
+}
+
+# Report the same status on a whole set of tests. Takes the count of tests,
+# the description, and then the command to run to determine the status.
+ok_block () {
+ tap_i=$count
+ tap_end=`expr $count + $1`
+ shift
+ while [ "$tap_i" -lt "$tap_end" ] ; do
+ ok "$@"
+ tap_i=`expr $tap_i + 1`
+ done
+}
+
+# Skip a whole set of tests. Takes the count and then the reason for skipping
+# the test.
+skip_block () {
+ tap_i=$count
+ tap_end=`expr $count + $1`
+ shift
+ while [ "$tap_i" -lt "$tap_end" ] ; do
+ skip "$@"
+ tap_i=`expr $tap_i + 1`
+ done
+}
+
+# Portable variant of printf '%s\n' "$*". In the majority of cases, this
+# function is slower than printf, because the latter is often implemented
+# as a builtin command. The value of the variable IFS is ignored.
+#
+# This macro must not be called via backticks inside double quotes, since this
+# will result in bizarre escaping behavior and lots of extra backslashes on
+# Solaris.
+puts () {
+ cat << EOH
+$@
+EOH
+}
+
+# Run a program expected to succeed, and print ok if it does and produces the
+# correct output. Takes the description, expected exit status, the expected
+# output, the command to run, and then any arguments for that command.
+# Standard output and standard error are combined when analyzing the output of
+# the command.
+#
+# If the command may contain system-specific error messages in its output,
+# add strip_colon_error before the command to post-process its output.
+ok_program () {
+ tap_desc="$1"
+ shift
+ tap_w_status="$1"
+ shift
+ tap_w_output="$1"
+ shift
+ tap_output=`"$@" 2>&1`
+ tap_status=$?
+ if [ $tap_status = $tap_w_status ] \
+ && [ x"$tap_output" = x"$tap_w_output" ] ; then
+ ok "$tap_desc" true
+ else
+ echo "# saw: ($tap_status) $tap_output"
+ echo "# not: ($tap_w_status) $tap_w_output"
+ ok "$tap_desc" false
+ fi
+}
+
+# Strip a colon and everything after it off the output of a command, as long
+# as that colon comes after at least one whitespace character. (This is done
+# to avoid stripping the name of the program from the start of an error
+# message.) This is used to remove system-specific error messages (coming
+# from strerror, for example).
+strip_colon_error() {
+ tap_output=`"$@" 2>&1`
+ tap_status=$?
+ tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
+ puts "$tap_output"
+ return $tap_status
+}
+
+# Bail out with an error message.
+bail () {
+ echo 'Bail out!' "$@"
+ exit 255
+}
+
+# Output a diagnostic on standard error, preceded by the required # mark.
+diag () {
+ echo '#' "$@"
+}
+
+# Search for the given file first in $BUILD and then in $SOURCE and echo the
+# path where the file was found, or the empty string if the file wasn't
+# found.
+#
+# This macro uses puts, so don't run it using backticks inside double quotes
+# or bizarre quoting behavior will happen with Solaris sh.
+test_file_path () {
+ if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then
+ puts "$BUILD/$1"
+ elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then
+ puts "$SOURCE/$1"
+ else
+ echo ''
+ fi
+}
+
+# Create $BUILD/tmp for use by tests for storing temporary files and return
+# the path (via standard output).
+#
+# This macro uses puts, so don't run it using backticks inside double quotes
+# or bizarre quoting behavior will happen with Solaris sh.
+test_tmpdir () {
+ if [ -z "$BUILD" ] ; then
+ tap_tmpdir="./tmp"
+ else
+ tap_tmpdir="$BUILD"/tmp
+ fi
+ if [ ! -d "$tap_tmpdir" ] ; then
+ mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
+ fi
+ puts "$tap_tmpdir"
+}
diff --git a/tests/tap/macros.h b/tests/tap/macros.h
new file mode 100644
index 0000000..31081a4
--- /dev/null
+++ b/tests/tap/macros.h
@@ -0,0 +1,85 @@
+/*
+ * Helpful macros for TAP header files.
+ *
+ * This is not, strictly speaking, related to TAP, but any TAP add-on is
+ * probably going to need these macros, so define them in one place so that
+ * everyone can pull them in.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * 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.
+ */
+
+#pragma once
+
+/*
+ * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
+ * could you use the __format__ form of the attributes, which is what we use
+ * (to avoid confusion with other macros), and only with gcc 2.96 can you use
+ * the attribute __malloc__. 2.96 is very old, so don't bother trying to get
+ * the other attributes to work with GCC versions between 2.7 and 2.96.
+ */
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+# define __attribute__(spec) /* empty */
+# endif
+#endif
+
+/*
+ * We use __alloc_size__, but it was only available in fairly recent versions
+ * of GCC. Suppress warnings about the unknown attribute if GCC is too old.
+ * We know that we're GCC at this point, so we can use the GCC variadic macro
+ * extension, which will still work with versions of GCC too old to have C99
+ * variadic macro support.
+ */
+#if !defined(__attribute__) && !defined(__alloc_size__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
+#endif
+
+/*
+ * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
+ * settings that GCC does. For them, suppress warnings about unknown
+ * attributes on declarations. This unfortunately will affect the entire
+ * compilation context, but there's no push and pop available.
+ */
+#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
+# pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+/* Used for unused parameters to silence gcc warnings. */
+/* #define UNUSED __attribute__((__unused__)) */
+
+/*
+ * BEGIN_DECLS is used at the beginning of declarations so that C++
+ * compilers don't mangle their names. END_DECLS is used at the end.
+ */
+#undef BEGIN_DECLS
+#undef END_DECLS
+#ifdef __cplusplus
+# define BEGIN_DECLS extern "C" {
+# define END_DECLS }
+#else
+# define BEGIN_DECLS /* empty */
+# define END_DECLS /* empty */
+#endif
diff --git a/tests/tap/runtests.c b/tests/tap/runtests.c
new file mode 100644
index 0000000..0464fbb
--- /dev/null
+++ b/tests/tap/runtests.c
@@ -0,0 +1,1393 @@
+/*
+ * Run a set of tests, reporting results.
+ *
+ * Usage:
+ *
+ * runtests [-b <build-dir>] [-s <source-dir>] <test-list>
+ * runtests -o [-b <build-dir>] [-s <source-dir>] <test>
+ *
+ * In the first case, expects a list of executables located in the given file,
+ * one line per executable. For each one, runs it as part of a test suite,
+ * reporting results. Test output should start with a line containing the
+ * number of tests (numbered from 1 to this number), optionally preceded by
+ * "1..", although that line may be given anywhere in the output. Each
+ * additional line should be in the following format:
+ *
+ * ok <number>
+ * not ok <number>
+ * ok <number> # skip
+ * not ok <number> # todo
+ *
+ * where <number> is the number of the test. An optional comment is permitted
+ * after the number if preceded by whitespace. ok indicates success, not ok
+ * indicates failure. "# skip" and "# todo" are a special cases of a comment,
+ * and must start with exactly that formatting. They indicate the test was
+ * skipped for some reason (maybe because it doesn't apply to this platform)
+ * or is testing something known to currently fail. The text following either
+ * "# skip" or "# todo" and whitespace is the reason.
+ *
+ * As a special case, the first line of the output may be in the form:
+ *
+ * 1..0 # skip some reason
+ *
+ * which indicates that this entire test case should be skipped and gives a
+ * reason.
+ *
+ * Any other lines are ignored, although for compliance with the TAP protocol
+ * all lines other than the ones in the above format should be sent to
+ * standard error rather than standard output and start with #.
+ *
+ * This is a subset of TAP as documented in Test::Harness::TAP or
+ * TAP::Parser::Grammar, which comes with Perl.
+ *
+ * If the -o option is given, instead run a single test and display all of its
+ * output. This is intended for use with failing tests so that the person
+ * running the test suite can get more details about what failed.
+ *
+ * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP
+ * Harness will export those values in the environment so that tests can find
+ * the source and build directory and will look for tests under both
+ * directories. These paths can also be set with the -b and -s command-line
+ * options, which will override anything set at build time.
+ *
+ * Any bug reports, bug fixes, and improvements are very much welcome and
+ * should be sent to the e-mail address below. This program is part of C TAP
+ * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011
+ * Russ Allbery <rra@stanford.edu>
+ *
+ * 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.
+*/
+
+/* Required for fdopen(), getopt(), and putenv(). */
+#if defined(__STRICT_ANSI__) || defined(PEDANTIC)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+# endif
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+/* sys/time.h must be included before sys/resource.h on some platforms. */
+#include <sys/resource.h>
+
+/* AIX doesn't have WCOREDUMP. */
+#ifndef WCOREDUMP
+# define WCOREDUMP(status) ((unsigned)(status) & 0x80)
+#endif
+
+/*
+ * Used for iterating through arrays. Returns the number of elements in the
+ * array (useful for a < upper bound in a for loop).
+ */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+/*
+ * The source and build versions of the tests directory. This is used to set
+ * the SOURCE and BUILD environment variables and find test programs, if set.
+ * Normally, this should be set as part of the build process to the test
+ * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively.
+ */
+#ifndef SOURCE
+# define SOURCE NULL
+#endif
+#ifndef BUILD
+# define BUILD NULL
+#endif
+
+/* Test status codes. */
+enum test_status {
+ TEST_FAIL,
+ TEST_PASS,
+ TEST_SKIP,
+ TEST_INVALID
+};
+
+/* Indicates the state of our plan. */
+enum plan_status {
+ PLAN_INIT, /* Nothing seen yet. */
+ PLAN_FIRST, /* Plan seen before any tests. */
+ PLAN_PENDING, /* Test seen and no plan yet. */
+ PLAN_FINAL /* Plan seen after some tests. */
+};
+
+/* Error exit statuses for test processes. */
+#define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */
+#define CHILDERR_EXEC 101 /* Couldn't exec child process. */
+#define CHILDERR_STDERR 102 /* Couldn't open stderr file. */
+
+/* Structure to hold data for a set of tests. */
+struct testset {
+ char *file; /* The file name of the test. */
+ char *path; /* The path to the test program. */
+ enum plan_status plan; /* The status of our plan. */
+ unsigned long count; /* Expected count of tests. */
+ unsigned long current; /* The last seen test number. */
+ unsigned int length; /* The length of the last status message. */
+ unsigned long passed; /* Count of passing tests. */
+ unsigned long failed; /* Count of failing lists. */
+ unsigned long skipped; /* Count of skipped tests (passed). */
+ unsigned long allocated; /* The size of the results table. */
+ enum test_status *results; /* Table of results by test number. */
+ unsigned int aborted; /* Whether the set was aborted. */
+ int reported; /* Whether the results were reported. */
+ int status; /* The exit status of the test. */
+ unsigned int all_skipped; /* Whether all tests were skipped. */
+ char *reason; /* Why all tests were skipped. */
+};
+
+/* Structure to hold a linked list of test sets. */
+struct testlist {
+ struct testset *ts;
+ struct testlist *next;
+};
+
+/*
+ * Usage message. Should be used as a printf format with four arguments: the
+ * path to runtests, given three times, and the usage_description. This is
+ * split into variables to satisfy the pedantic ISO C90 limit on strings.
+ */
+static const char usage_message[] = "\
+Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\
+ %s [-b <build-dir>] [-s <source-dir>] -l <test-list> -L <log-file>\n\
+ %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\
+\n%s";
+static const char usage_extra[] = "\
+Options:\n\
+ -b <build-dir> Set the build directory to <build-dir>\n\
+ -l <list> Take the list of tests to run from <test-list>\n\
+ -o Run a single test rather than a list of tests\n\
+ -s <source-dir> Set the source directory to <source-dir>\n\
+ -L <log-file> Set log file for a list of tests\n\
+\n\
+runtests normally runs each test listed on the command line. With the -l\n\
+option, it instead runs every test listed in a file. With the -o option,\n\
+it instead runs a single test and shows its complete output.\n";
+
+/*
+ * Header used for test output. %s is replaced by the file name of the list
+ * of tests.
+ */
+static const char banner[] = "\n\
+Running all tests listed in %s. If any tests fail, run the failing\n\
+test program with runtests -o to see more details.\n\n";
+
+/* Header for reports of failed tests. */
+static const char header[] = "\n\
+Failed Set Fail/Total (%) Skip Stat Failing Tests\n\
+-------------------------- -------------- ---- ---- ------------------------";
+
+/* Include the file name and line number in malloc failures. */
+#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+
+/*
+ * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
+ * could you use the __format__ form of the attributes, which is what we use
+ * (to avoid confusion with other macros).
+ */
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __attribute__(spec) /* empty */
+# endif
+#endif
+
+/*
+ * We use __alloc_size__, but it was only available in fairly recent versions
+ * of GCC. Suppress warnings about the unknown attribute if GCC is too old.
+ * We know that we're GCC at this point, so we can use the GCC variadic macro
+ * extension, which will still work with versions of GCC too old to have C99
+ * variadic macro support.
+ */
+#if !defined(__attribute__) && !defined(__alloc_size__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
+#endif
+
+/*
+ * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
+ * settings that GCC does. For them, suppress warnings about unknown
+ * attributes on declarations. This unfortunately will affect the entire
+ * compilation context, but there's no push and pop available.
+ */
+#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
+# pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+/* Declare internal functions that benefit from compiler attributes. */
+static void sysdie(const char *, ...)
+ __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
+static void *x_calloc(size_t, size_t, const char *, int)
+ __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__));
+static void *x_malloc(size_t, const char *, int)
+ __attribute__((__alloc_size__(1), __malloc__, __nonnull__));
+static void *x_realloc(void *, size_t, const char *, int)
+ __attribute__((__alloc_size__(2), __malloc__, __nonnull__(3)));
+static char *x_strdup(const char *, const char *, int)
+ __attribute__((__malloc__, __nonnull__));
+
+/*
+ * Report a fatal error, including the results of strerror, and exit.
+ */
+static void
+sysdie(const char *format, ...)
+{
+ int oerrno;
+ va_list args;
+
+ oerrno = errno;
+ fflush(stdout);
+ fprintf(stderr, "runtests: ");
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, ": %s\n", strerror(oerrno));
+ exit(1);
+}
+
+/*
+ * Allocate zeroed memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_calloc(size_t n, size_t size, const char *file, int line)
+{
+ void *p;
+
+ n = (n > 0) ? n : 1;
+ size = (size > 0) ? size : 1;
+ p = calloc(n, size);
+ if (p == NULL)
+ sysdie("failed to calloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Allocate memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_malloc(size_t size, const char *file, int line)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL)
+ sysdie("failed to malloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Reallocate memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_realloc(void *p, size_t size, const char *file, int line)
+{
+ p = realloc(p, size);
+ if (p == NULL)
+ sysdie("failed to realloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Copy a string, reporting a fatal error and exiting on failure.
+ */
+static char *
+x_strdup(const char *s, const char *file, int line)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ if (p == NULL)
+ sysdie("failed to strdup %lu bytes at %s line %d",
+ (unsigned long) len, file, line);
+ memcpy(p, s, len);
+ return p;
+}
+
+/*
+ * Given a struct timeval, return the number of seconds it represents as a
+ * double. Use difftime() to convert a time_t to a double.
+ */
+static double
+tv_seconds(const struct timeval *tv)
+{
+ return difftime(tv->tv_sec, 0) + tv->tv_usec * 1e-6;
+}
+
+/*
+ * Given two struct timevals, return the difference in seconds.
+ */
+static double
+tv_diff(const struct timeval *tv1, const struct timeval *tv0)
+{
+ return tv_seconds(tv1) - tv_seconds(tv0);
+}
+
+/*
+ * Given two struct timevals, return the sum in seconds as a double.
+ */
+static double
+tv_sum(const struct timeval *tv1, const struct timeval *tv2)
+{
+ return tv_seconds(tv1) + tv_seconds(tv2);
+}
+
+/*
+ * Given a pointer to a string, skip any leading whitespace and return a
+ * pointer to the first non-whitespace character.
+ */
+static const char *
+skip_whitespace(const char *p)
+{
+ while (isspace((unsigned char)(*p)))
+ p++;
+ return p;
+}
+
+/*
+ * Start a program, connecting its stdout to a pipe on our end and its stderr
+ * to /dev/null, and storing the file descriptor to read from in the two
+ * argument. Returns the PID of the new process. Errors are fatal.
+ */
+static pid_t
+test_start(const char *path, int *fd)
+{
+ int fds[2], errfd;
+ pid_t child;
+
+ if (pipe(fds) == -1) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("can't create pipe");
+ }
+ child = fork();
+ if (child == (pid_t) -1) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("can't fork");
+ } else if (child == 0) {
+ /* In child. Set up our stdout and stderr. */
+ errfd = open("/dev/null", O_WRONLY);
+ if (errfd < 0)
+ _exit(CHILDERR_STDERR);
+ if (dup2(errfd, 2) == -1)
+ _exit(CHILDERR_DUP);
+ close(fds[0]);
+ if (dup2(fds[1], 1) == -1)
+ _exit(CHILDERR_DUP);
+
+ /* Now, exec our process. */
+ if (execl(path, path, (char *) 0) == -1)
+ _exit(CHILDERR_EXEC);
+ } else {
+ /* In parent. Close the extra file descriptor. */
+ close(fds[1]);
+ }
+ *fd = fds[0];
+ return child;
+}
+
+/*
+ * Back up over the output saying what test we were executing.
+ */
+static void
+test_backspace(struct testset *ts)
+{
+ unsigned int i;
+
+ if (!isatty(STDOUT_FILENO))
+ return;
+ for (i = 0; i < ts->length; i++)
+ putchar('\b');
+ for (i = 0; i < ts->length; i++)
+ putchar(' ');
+ for (i = 0; i < ts->length; i++)
+ putchar('\b');
+ ts->length = 0;
+}
+
+/*
+ * Read the plan line of test output, which should contain the range of test
+ * numbers. We may initialize the testset structure here if we haven't yet
+ * seen a test. Return true if initialization succeeded and the test should
+ * continue, false otherwise.
+ */
+static int
+test_plan(const char *line, struct testset *ts)
+{
+ unsigned long i;
+ long n;
+
+ /*
+ * Accept a plan without the leading 1.. for compatibility with older
+ * versions of runtests. This will only be allowed if we've not yet seen
+ * a test result.
+ */
+ line = skip_whitespace(line);
+ if (strncmp(line, "1..", 3) == 0)
+ line += 3;
+
+ /*
+ * Get the count, check it for validity, and initialize the struct. If we
+ * have something of the form "1..0 # skip foo", the whole file was
+ * skipped; record that. If we do skip the whole file, zero out all of
+ * our statistics, since they're no longer relevant. strtol is called
+ * with a second argument to advance the line pointer past the count to
+ * make it simpler to detect the # skip case.
+ */
+ n = strtol(line, (char **) &line, 10);
+ if (n == 0) {
+ line = skip_whitespace(line);
+ if (*line == '#') {
+ line = skip_whitespace(line + 1);
+ if (strncasecmp(line, "skip", 4) == 0) {
+ line = skip_whitespace(line + 4);
+ if (*line != '\0') {
+ ts->reason = xstrdup(line);
+ ts->reason[strlen(ts->reason) - 1] = '\0';
+ }
+ ts->all_skipped = 1;
+ ts->aborted = 1;
+ ts->count = 0;
+ ts->passed = 0;
+ ts->skipped = 0;
+ ts->failed = 0;
+ return 0;
+ }
+ }
+ }
+ if (n <= 0) {
+ puts("ABORTED (invalid test count)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ if (ts->plan == PLAN_INIT && ts->allocated == 0) {
+ ts->count = n;
+ ts->allocated = n;
+ ts->plan = PLAN_FIRST;
+ ts->results = xmalloc(ts->count * sizeof(enum test_status));
+ for (i = 0; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ } else if (ts->plan == PLAN_PENDING) {
+ if ((unsigned long) n < ts->count) {
+ test_backspace(ts);
+ printf("ABORTED (invalid test number %lu)\n", ts->count);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ ts->count = n;
+ if ((unsigned long) n > ts->allocated) {
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ ts->plan = PLAN_FINAL;
+ }
+ return 1;
+}
+
+/*
+ * Given a single line of output from a test, parse it and return the success
+ * status of that test. Anything printed to stdout not matching the form
+ * /^(not )?ok \d+/ is ignored. Sets ts->current to the test number that just
+ * reported status.
+ */
+static void
+test_checkline(const char *line, struct testset *ts)
+{
+ enum test_status status = TEST_PASS;
+ const char *bail;
+ char *end;
+ long number;
+ unsigned long i, current;
+ int outlen;
+
+ /* Before anything, check for a test abort. */
+ bail = strstr(line, "Bail out!");
+ if (bail != NULL) {
+ bail = skip_whitespace(bail + strlen("Bail out!"));
+ if (*bail != '\0') {
+ size_t length;
+
+ length = strlen(bail);
+ if (bail[length - 1] == '\n')
+ length--;
+ test_backspace(ts);
+ printf("ABORTED (%.*s)\n", (int) length, bail);
+ ts->reported = 1;
+ }
+ ts->aborted = 1;
+ return;
+ }
+
+ /*
+ * If the given line isn't newline-terminated, it was too big for an
+ * fgets(), which means ignore it.
+ */
+ if (line[strlen(line) - 1] != '\n')
+ return;
+
+ /* If the line begins with a hash mark, ignore it. */
+ if (line[0] == '#')
+ return;
+
+ /* If we haven't yet seen a plan, look for one. */
+ if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) {
+ if (!test_plan(line, ts))
+ return;
+ } else if (strncmp(line, "1..", 3) == 0) {
+ if (ts->plan == PLAN_PENDING) {
+ if (!test_plan(line, ts))
+ return;
+ } else {
+ test_backspace(ts);
+ puts("ABORTED (multiple plans)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+ }
+
+ /* Parse the line, ignoring something we can't parse. */
+ if (strncmp(line, "not ", 4) == 0) {
+ status = TEST_FAIL;
+ line += 4;
+ }
+ if (strncmp(line, "ok", 2) != 0)
+ return;
+ line = skip_whitespace(line + 2);
+ errno = 0;
+ number = strtol(line, &end, 10);
+ if (errno != 0 || end == line)
+ number = ts->current + 1;
+ current = number;
+ if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) {
+ test_backspace(ts);
+ printf("ABORTED (invalid test number %lu)\n", current);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+
+ /* We have a valid test result. Tweak the results array if needed. */
+ if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) {
+ ts->plan = PLAN_PENDING;
+ if (current > ts->count)
+ ts->count = current;
+ if (current > ts->allocated) {
+ unsigned long n;
+
+ n = (ts->allocated == 0) ? 32 : ts->allocated * 2;
+ if (n < current)
+ n = current;
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < n; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ }
+
+ /*
+ * Handle directives. We should probably do something more interesting
+ * with unexpected passes of todo tests.
+ */
+ while (isdigit((unsigned char)(*line)))
+ line++;
+ line = skip_whitespace(line);
+ if (*line == '#') {
+ line = skip_whitespace(line + 1);
+ if (strncasecmp(line, "skip", 4) == 0)
+ status = TEST_SKIP;
+ if (strncasecmp(line, "todo", 4) == 0)
+ status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL;
+ }
+
+ /* Make sure that the test number is in range and not a duplicate. */
+ if (ts->results[current - 1] != TEST_INVALID) {
+ test_backspace(ts);
+ printf("ABORTED (duplicate test number %lu)\n", current);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+
+ /* Good results. Increment our various counters. */
+ switch (status) {
+ case TEST_PASS: ts->passed++; break;
+ case TEST_FAIL: ts->failed++; break;
+ case TEST_SKIP: ts->skipped++; break;
+ case TEST_INVALID: break;
+ }
+ ts->current = current;
+ ts->results[current - 1] = status;
+ if (isatty(STDOUT_FILENO)) {
+ test_backspace(ts);
+ if (ts->plan == PLAN_PENDING)
+ outlen = printf("%lu/?", current);
+ else
+ outlen = printf("%lu/%lu", current, ts->count);
+ ts->length = (outlen >= 0) ? outlen : 0;
+ fflush(stdout);
+ }
+}
+
+/*
+ * Print out a range of test numbers, returning the number of characters it
+ * took up. Takes the first number, the last number, the number of characters
+ * already printed on the line, and the limit of number of characters the line
+ * can hold. Add a comma and a space before the range if chars indicates that
+ * something has already been printed on the line, and print ... instead if
+ * chars plus the space needed would go over the limit (use a limit of 0 to
+ * disable this).
+ */
+static unsigned int
+test_print_range(unsigned long first, unsigned long last, unsigned int chars,
+ unsigned int limit)
+{
+ unsigned int needed = 0;
+ unsigned long n;
+
+ for (n = first; n > 0; n /= 10)
+ needed++;
+ if (last > first) {
+ for (n = last; n > 0; n /= 10)
+ needed++;
+ needed++;
+ }
+ if (chars > 0)
+ needed += 2;
+ if (limit > 0 && chars + needed > limit) {
+ needed = 0;
+ if (chars <= limit) {
+ if (chars > 0) {
+ printf(", ");
+ needed += 2;
+ }
+ printf("...");
+ needed += 3;
+ }
+ } else {
+ if (chars > 0)
+ printf(", ");
+ if (last > first)
+ printf("%lu-", first);
+ printf("%lu", last);
+ }
+ return needed;
+}
+
+/*
+ * Summarize a single test set. The second argument is 0 if the set exited
+ * cleanly, a positive integer representing the exit status if it exited
+ * with a non-zero status, and a negative integer representing the signal
+ * that terminated it if it was killed by a signal.
+ */
+static void
+test_summarize(struct testset *ts, int status)
+{
+ unsigned long i;
+ unsigned long missing = 0;
+ unsigned long failed = 0;
+ unsigned long first = 0;
+ unsigned long last = 0;
+
+ if (ts->aborted) {
+ fputs("ABORTED", stdout);
+ if (ts->count > 0)
+ printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped);
+ } else {
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_INVALID) {
+ if (missing == 0)
+ fputs("MISSED ", stdout);
+ if (first && i == last)
+ last = i + 1;
+ else {
+ if (first)
+ test_print_range(first, last, missing - 1, 0);
+ missing++;
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first)
+ test_print_range(first, last, missing - 1, 0);
+ first = 0;
+ last = 0;
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_FAIL) {
+ if (missing && !failed)
+ fputs("; ", stdout);
+ if (failed == 0)
+ fputs("FAILED ", stdout);
+ if (first && i == last)
+ last = i + 1;
+ else {
+ if (first)
+ test_print_range(first, last, failed - 1, 0);
+ failed++;
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first)
+ test_print_range(first, last, failed - 1, 0);
+ if (!missing && !failed) {
+ fputs(!status ? "ok" : "dubious", stdout);
+ if (ts->skipped > 0) {
+ if (ts->skipped == 1)
+ printf(" (skipped %lu test)", ts->skipped);
+ else
+ printf(" (skipped %lu tests)", ts->skipped);
+ }
+ }
+ }
+ if (status > 0)
+ printf(" (exit status %d)", status);
+ else if (status < 0)
+ printf(" (killed by signal %d%s)", -status,
+ WCOREDUMP(ts->status) ? ", core dumped" : "");
+ putchar('\n');
+}
+
+/*
+ * Given a test set, analyze the results, classify the exit status, handle a
+ * few special error messages, and then pass it along to test_summarize() for
+ * the regular output. Returns true if the test set ran successfully and all
+ * tests passed or were skipped, false otherwise.
+ */
+static int
+test_analyze(struct testset *ts)
+{
+ if (ts->reported)
+ return 0;
+ if (ts->all_skipped) {
+ if (ts->reason == NULL)
+ puts("skipped");
+ else
+ printf("skipped (%s)\n", ts->reason);
+ return 1;
+ } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) {
+ switch (WEXITSTATUS(ts->status)) {
+ case CHILDERR_DUP:
+ if (!ts->reported)
+ puts("ABORTED (can't dup file descriptors)");
+ break;
+ case CHILDERR_EXEC:
+ if (!ts->reported)
+ puts("ABORTED (execution failed -- not found?)");
+ break;
+ case CHILDERR_STDERR:
+ if (!ts->reported)
+ puts("ABORTED (can't open /dev/null)");
+ break;
+ default:
+ test_summarize(ts, WEXITSTATUS(ts->status));
+ break;
+ }
+ return 0;
+ } else if (WIFSIGNALED(ts->status)) {
+ test_summarize(ts, -WTERMSIG(ts->status));
+ return 0;
+ } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) {
+ puts("ABORTED (no valid test plan)");
+ ts->aborted = 1;
+ return 0;
+ } else {
+ test_summarize(ts, 0);
+ return (ts->failed == 0);
+ }
+}
+
+static void
+cond_fputs(const char *buffer, FILE *stream)
+{
+ if (!stream) {
+ return;
+ }
+
+ fputs(buffer, stream);
+}
+
+/*
+ * Runs a single test set, accumulating and then reporting the results.
+ * Returns true if the test set was successfully run and all tests passed,
+ * false otherwise.
+ */
+static int
+test_run(struct testset *ts, FILE *logfile)
+{
+ pid_t testpid, child;
+ int outfd, status;
+ unsigned long i;
+ FILE *output;
+ char buffer[BUFSIZ];
+
+ /* Run the test program. */
+ testpid = test_start(ts->path, &outfd);
+ output = fdopen(outfd, "r");
+ if (!output) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("fdopen failed");
+ }
+
+ /* Pass each line of output to test_checkline(). */
+ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) {
+ cond_fputs(buffer, logfile);
+ test_checkline(buffer, ts);
+ }
+ if (ferror(output) || ts->plan == PLAN_INIT)
+ ts->aborted = 1;
+ test_backspace(ts);
+
+ /*
+ * Consume the rest of the test output, close the output descriptor,
+ * retrieve the exit status, and pass that information to test_analyze()
+ * for eventual output.
+ */
+ while (fgets(buffer, sizeof(buffer), output))
+ ;
+ fclose(output);
+ child = waitpid(testpid, &ts->status, 0);
+ if (child == (pid_t) -1) {
+ if (!ts->reported) {
+ puts("ABORTED");
+ fflush(stdout);
+ }
+ sysdie("waitpid for %u failed", (unsigned int) testpid);
+ }
+ if (ts->all_skipped)
+ ts->aborted = 0;
+ if (WEXITSTATUS(ts->status) > 0)
+ ts->failed++;
+ status = test_analyze(ts);
+
+ /* Convert missing tests to failed tests. */
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_INVALID) {
+ ts->failed++;
+ ts->results[i] = TEST_FAIL;
+ status = 0;
+ }
+ }
+ return status;
+}
+
+/* Summarize a list of test failures. */
+static void
+test_fail_summary(const struct testlist *fails)
+{
+ struct testset *ts;
+ unsigned int chars;
+ unsigned long i, first, last, total;
+
+ puts(header);
+
+ /* Failed Set Fail/Total (%) Skip Stat Failing (25)
+ -------------------------- -------------- ---- ---- -------------- */
+ for (; fails; fails = fails->next) {
+ ts = fails->ts;
+ total = ts->count - ts->skipped;
+ printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed,
+ total, total ? (ts->failed * 100.0) / total : 0,
+ ts->skipped);
+ if (WIFEXITED(ts->status))
+ printf("%4d ", WEXITSTATUS(ts->status));
+ else
+ printf(" -- ");
+ if (ts->aborted) {
+ puts("aborted");
+ continue;
+ }
+ chars = 0;
+ first = 0;
+ last = 0;
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_FAIL) {
+ if (first != 0 && i == last)
+ last = i + 1;
+ else {
+ if (first != 0)
+ chars += test_print_range(first, last, chars, 19);
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first != 0)
+ test_print_range(first, last, chars, 19);
+ putchar('\n');
+ }
+}
+
+/*
+ * Check whether a given file path is a valid test. Currently, this checks
+ * whether it is executable and is a regular file. Returns true or false.
+ */
+static int
+is_valid_test(const char *path)
+{
+ struct stat st;
+
+ if (access(path, X_OK) < 0)
+ return 0;
+ if (stat(path, &st) < 0)
+ return 0;
+ if (!S_ISREG(st.st_mode))
+ return 0;
+ return 1;
+}
+
+/*
+ * Given the name of a test, a pointer to the testset struct, and the source
+ * and build directories, find the test. We try first relative to the current
+ * directory, then in the build directory (if not NULL), then in the source
+ * directory. In each of those directories, we first try a "-t" extension and
+ * then a ".t" extension. When we find an executable program, we return the
+ * path to that program. If none of those paths are executable, just fill in
+ * the name of the test as is.
+ *
+ * The caller is responsible for freeing the path member of the testset
+ * struct.
+ */
+static char *
+find_test(const char *name, const char *source, const char *build)
+{
+ char *path;
+ const char *bases[3], *suffix, *base;
+ unsigned int i, j;
+ const char *suffixes[3] = { "-t", ".t", "" };
+
+ /* Possible base directories. */
+ bases[0] = ".";
+ bases[1] = build;
+ bases[2] = source;
+
+ /* Try each suffix with each base. */
+ for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+ suffix = suffixes[i];
+ for (j = 0; j < ARRAY_SIZE(bases); j++) {
+ unsigned int path_len;
+
+ base = bases[j];
+ if (base == NULL)
+ continue;
+ path_len = strlen(base) + strlen(name) + strlen(suffix) + 2;
+ path = xmalloc(path_len);
+ snprintf(path, path_len, "%s/%s%s", base, name, suffix);
+ if (is_valid_test(path))
+ return path;
+ free(path);
+ path = NULL;
+ }
+ }
+ if (path == NULL)
+ path = xstrdup(name);
+ return path;
+}
+
+/*
+ * Read a list of tests from a file, returning the list of tests as a struct
+ * testlist. Reports an error to standard error and exits if the list of
+ * tests cannot be read.
+ */
+static struct testlist *
+read_test_list(const char *filename)
+{
+ FILE *file;
+ unsigned int line;
+ size_t length;
+ char buffer[BUFSIZ];
+ struct testlist *listhead, *current;
+
+ /* Create the initial container list that will hold our results. */
+ listhead = xmalloc(sizeof(struct testlist));
+ listhead->ts = NULL;
+ listhead->next = NULL;
+ current = NULL;
+
+ /*
+ * Open our file of tests to run and read it line by line, creating a new
+ * struct testlist and struct testset for each line.
+ */
+ file = fopen(filename, "r");
+ if (file == NULL)
+ sysdie("can't open %s", filename);
+ line = 0;
+ while (fgets(buffer, sizeof(buffer), file)) {
+ line++;
+ length = strlen(buffer) - 1;
+ if (buffer[length] != '\n') {
+ fprintf(stderr, "%s:%u: line too long\n", filename, line);
+ exit(1);
+ }
+ buffer[length] = '\0';
+ if (current == NULL)
+ current = listhead;
+ else {
+ current->next = xmalloc(sizeof(struct testlist));
+ current = current->next;
+ current->next = NULL;
+ }
+ current->ts = xcalloc(1, sizeof(struct testset));
+ current->ts->plan = PLAN_INIT;
+ current->ts->file = xstrdup(buffer);
+ current->ts->reason = NULL;
+ }
+ fclose(file);
+
+ /* Return the results. */
+ return listhead;
+}
+
+/*
+ * Build a list of tests from command line arguments. Takes the argv and argc
+ * representing the command line arguments and returns a newly allocated test
+ * list. The caller is responsible for freeing.
+ */
+static struct testlist *
+build_test_list(char *argv[], int argc)
+{
+ int i;
+ struct testlist *listhead, *current;
+
+ /* Create the initial container list that will hold our results. */
+ listhead = xmalloc(sizeof(struct testlist));
+ listhead->ts = NULL;
+ listhead->next = NULL;
+ current = NULL;
+
+ /* Walk the list of arguments and create test sets for them. */
+ for (i = 0; i < argc; i++) {
+ if (current == NULL)
+ current = listhead;
+ else {
+ current->next = xmalloc(sizeof(struct testlist));
+ current = current->next;
+ current->next = NULL;
+ }
+ current->ts = xcalloc(1, sizeof(struct testset));
+ current->ts->plan = PLAN_INIT;
+ current->ts->file = xstrdup(argv[i]);
+ current->ts->reason = NULL;
+ }
+
+ /* Return the results. */
+ return listhead;
+}
+
+/* Free a struct testset. */
+static void
+free_testset(struct testset *ts)
+{
+ free(ts->file);
+ free(ts->path);
+ free(ts->results);
+ if (ts->reason != NULL)
+ free(ts->reason);
+ free(ts);
+}
+
+/*
+ * Run a batch of tests. Takes two additional parameters: the root of the
+ * source directory and the root of the build directory. Test programs will
+ * be first searched for in the current directory, then the build directory,
+ * then the source directory. Returns true iff all tests passed, and always
+ * frees the test list that's passed in.
+ */
+static int
+test_batch(struct testlist *tests, const char *source, const char *build,
+ const char *logfile_name)
+{
+ size_t length;
+ unsigned int i;
+ unsigned int longest = 0;
+ unsigned int count = 0;
+ struct testset *ts;
+ struct timeval start, end;
+ struct rusage stats;
+ struct testlist *failhead = NULL;
+ struct testlist *failtail = NULL;
+ struct testlist *current, *next;
+ int succeeded;
+ FILE *logfile = NULL;
+ unsigned long total = 0;
+ unsigned long passed = 0;
+ unsigned long skipped = 0;
+ unsigned long failed = 0;
+ unsigned long aborted = 0;
+
+ /* Walk the list of tests to find the longest name. */
+ for (current = tests; current != NULL; current = current->next) {
+ length = strlen(current->ts->file);
+ if (length > longest)
+ longest = length;
+ }
+
+ /*
+ * Add two to longest and round up to the nearest tab stop. This is how
+ * wide the column for printing the current test name will be.
+ */
+ longest += 2;
+ if (longest % 8)
+ longest += 8 - (longest % 8);
+
+ /* Start the wall clock timer. */
+ gettimeofday(&start, NULL);
+
+ /* Open the log (soft error). */
+ if (logfile_name != NULL) {
+ logfile = fopen(logfile_name, "w+");
+ if (!logfile) {
+ fprintf(stderr, "Could not open the log file.\n");
+ }
+ }
+
+ /* Now, plow through our tests again, running each one. */
+ for (current = tests; current != NULL; current = current->next) {
+ ts = current->ts;
+
+ /* Print out the name of the test file. */
+ fputs(ts->file, stdout);
+ for (i = strlen(ts->file); i < longest; i++)
+ putchar('.');
+ if (isatty(STDOUT_FILENO))
+ fflush(stdout);
+
+ /* Run the test. */
+ ts->path = find_test(ts->file, source, build);
+ succeeded = test_run(ts, logfile);
+ fflush(stdout);
+
+ /* Record cumulative statistics. */
+ aborted += ts->aborted;
+ total += ts->count + ts->all_skipped;
+ passed += ts->passed;
+ skipped += ts->skipped + ts->all_skipped;
+ failed += ts->failed;
+ count++;
+
+ /* If the test fails, we shuffle it over to the fail list. */
+ if (!succeeded) {
+ if (failhead == NULL) {
+ failhead = xmalloc(sizeof(struct testset));
+ failtail = failhead;
+ } else {
+ failtail->next = xmalloc(sizeof(struct testset));
+ failtail = failtail->next;
+ }
+ failtail->ts = ts;
+ failtail->next = NULL;
+ }
+ }
+ total -= skipped;
+
+ /* Close the log. */
+ if (logfile) {
+ fclose(logfile);
+ }
+
+ /* Stop the timer and get our child resource statistics. */
+ gettimeofday(&end, NULL);
+ getrusage(RUSAGE_CHILDREN, &stats);
+
+ /* Summarize the failures and free the failure list. */
+ if (failhead != NULL) {
+ test_fail_summary(failhead);
+ while (failhead != NULL) {
+ next = failhead->next;
+ free(failhead);
+ failhead = next;
+ }
+ }
+
+ /* Free the memory used by the test lists. */
+ while (tests != NULL) {
+ next = tests->next;
+ free_testset(tests->ts);
+ free(tests);
+ tests = next;
+ }
+
+ /* Print out the final test summary. */
+ putchar('\n');
+ if (aborted != 0) {
+ if (aborted == 1)
+ printf("Aborted %lu test set", aborted);
+ else
+ printf("Aborted %lu test sets", aborted);
+ printf(", passed %lu/%lu tests", passed, total);
+ }
+ else if (failed == 0)
+ fputs("All tests successful", stdout);
+ else
+ printf("Failed %lu/%lu tests, %.2f%% okay", failed, total,
+ (total - failed) * 100.0 / total);
+ if (skipped != 0) {
+ if (skipped == 1)
+ printf(", %lu test skipped", skipped);
+ else
+ printf(", %lu tests skipped", skipped);
+ }
+ puts(".");
+ printf("Files=%u, Tests=%lu", count, total);
+ printf(", %.2f seconds", tv_diff(&end, &start));
+ printf(" (%.2f usr + %.2f sys = %.2f CPU)\n",
+ tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime),
+ tv_sum(&stats.ru_utime, &stats.ru_stime));
+ return (failed == 0 && aborted == 0);
+}
+
+/*
+ * Run a single test case. This involves just running the test program after
+ * having done the environment setup and finding the test program.
+ */
+static void
+test_single(const char *program, const char *source, const char *build)
+{
+ char *path;
+
+ path = find_test(program, source, build);
+ if (execl(path, path, (char *) 0) == -1)
+ sysdie("cannot exec %s", path);
+}
+
+/*
+ * Main routine. Set the SOURCE and BUILD environment variables and then,
+ * given a file listing tests, run each test listed.
+ */
+int
+main(int argc, char *argv[])
+{
+ int option;
+ int status = 0;
+ int single = 0;
+ char *source_env = NULL;
+ char *build_env = NULL;
+ const char *shortlist;
+ const char *list = NULL;
+ const char *source = SOURCE;
+ const char *build = BUILD;
+ const char *logfile = NULL;
+ struct testlist *tests;
+
+ while ((option = getopt(argc, argv, "b:hl:os:L:")) != EOF) {
+ switch (option) {
+ case 'b':
+ build = optarg;
+ break;
+ case 'h':
+ printf(usage_message, argv[0], argv[0], argv[0], usage_extra);
+ exit(0);
+ break;
+ case 'l':
+ list = optarg;
+ break;
+ case 'o':
+ single = 1;
+ break;
+ case 's':
+ source = optarg;
+ break;
+ case 'L':
+ logfile = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+ if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) {
+ fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra);
+ exit(1);
+ }
+
+ /* Set SOURCE and BUILD environment variables. */
+ if (source != NULL) {
+ unsigned int len = strlen("SOURCE=") + strlen(source) + 1;
+ source_env = xmalloc(len);
+ snprintf(source_env, len, "SOURCE=%s", source);
+ if (putenv(source_env) != 0)
+ sysdie("cannot set SOURCE in the environment");
+ }
+ if (build != NULL) {
+ unsigned int len = strlen("BUILD=") + strlen(build) + 1;
+ build_env = xmalloc(len);
+ snprintf(build_env, len, "BUILD=%s", build);
+ if (putenv(build_env) != 0)
+ sysdie("cannot set BUILD in the environment");
+ }
+
+ /* Run the tests as instructed. */
+ if (single)
+ test_single(argv[0], source, build);
+ else if (list != NULL) {
+ shortlist = strrchr(list, '/');
+ if (shortlist == NULL)
+ shortlist = list;
+ else
+ shortlist++;
+ printf(banner, shortlist);
+ tests = read_test_list(list);
+ status = test_batch(tests, source, build, logfile) ? 0 : 1;
+ } else {
+ tests = build_test_list(argv, argc);
+ status = test_batch(tests, source, build, logfile) ? 0 : 1;
+ }
+
+ /* For valgrind cleanliness, free all our memory. */
+ if (source_env != NULL) {
+ putenv((char *) "SOURCE=");
+ free(source_env);
+ }
+ if (build_env != NULL) {
+ putenv((char *) "BUILD=");
+ free(build_env);
+ }
+ exit(status);
+}
diff --git a/tests/utils/test_cert.c b/tests/utils/test_cert.c
new file mode 100644
index 0000000..3089116
--- /dev/null
+++ b/tests/utils/test_cert.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 "utils/common/cert.h"
+#include "libknot/error.h"
+
+#include <tap/basic.h>
+#include <string.h>
+
+static const uint8_t CERT_DER[] = {
+ 0x30, 0x82, 0x06, 0xad, 0x30, 0x82, 0x05, 0x95, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x07, 0x06, 0xdb, 0x97, 0x5f, 0xf1, 0xa8, 0x9b, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d,
+ 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44,
+ 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72,
+ 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61,
+ 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x36, 0x30, 0x38,
+ 0x31, 0x30, 0x33, 0x36, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x30,
+ 0x36, 0x30, 0x38, 0x31, 0x33, 0x31, 0x33, 0x5a, 0x30, 0x81, 0xa2, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x5a,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x48,
+ 0x6c, 0x61, 0x76, 0x6e, 0x69, 0x20, 0x6d, 0x65, 0x73, 0x74, 0x6f, 0x20,
+ 0x50, 0x72, 0x61, 0x68, 0x61, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x11, 0x50, 0x72, 0x61, 0x68, 0x61, 0x20, 0x2d, 0x20,
+ 0x56, 0x69, 0x6e, 0x6f, 0x68, 0x72, 0x61, 0x64, 0x79, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x43, 0x5a, 0x2e, 0x4e,
+ 0x49, 0x43, 0x2c, 0x20, 0x7a, 0x2e, 0x73, 0x2e, 0x70, 0x2e, 0x6f, 0x2e,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, 0x77,
+ 0x77, 0x77, 0x2e, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e,
+ 0x63, 0x7a, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x68, 0x6f, 0x73, 0x74, 0x6d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x40, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64,
+ 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0x9d, 0xa9, 0x87, 0x3b, 0x2e, 0xfa, 0xfd, 0xf6, 0x0b, 0x63,
+ 0xa8, 0x23, 0xc6, 0x66, 0x3d, 0x02, 0x9e, 0xf0, 0xa0, 0x83, 0x44, 0xbd,
+ 0x1a, 0xea, 0xee, 0xdd, 0xb3, 0x8d, 0xe6, 0xbd, 0xe1, 0xdc, 0xff, 0xf6,
+ 0xa9, 0x10, 0xdd, 0x0e, 0x3e, 0x6b, 0xb2, 0x8d, 0xa7, 0x52, 0x2b, 0xd4,
+ 0xff, 0xc6, 0x7a, 0x65, 0x23, 0x34, 0x02, 0x09, 0xc0, 0x17, 0xcc, 0x5d,
+ 0x47, 0x29, 0x9a, 0xac, 0x40, 0xdc, 0x8a, 0x3a, 0x65, 0xa3, 0xf5, 0x9f,
+ 0x1b, 0xaa, 0xaf, 0xdf, 0xab, 0xa7, 0xd3, 0x14, 0x86, 0xcc, 0xb8, 0x28,
+ 0x9a, 0x65, 0x33, 0xda, 0x22, 0xae, 0x62, 0x1a, 0x6b, 0xb7, 0x67, 0xdc,
+ 0xf0, 0x8c, 0xa3, 0xc1, 0x84, 0x1e, 0xf2, 0xcc, 0xf3, 0xe5, 0xfe, 0xf4,
+ 0xd8, 0x90, 0x50, 0xbc, 0x9d, 0x77, 0xc9, 0x4d, 0xb9, 0x8c, 0xfe, 0xff,
+ 0x33, 0x02, 0x7c, 0x4f, 0xb1, 0x3d, 0x66, 0x30, 0x97, 0xa3, 0xe0, 0x54,
+ 0xc1, 0x3f, 0x4a, 0xd8, 0x3a, 0xa7, 0xcb, 0xe8, 0x16, 0x37, 0x72, 0xb3,
+ 0x36, 0x90, 0x75, 0x1a, 0x2f, 0x95, 0x55, 0xb5, 0x10, 0x18, 0x29, 0xb0,
+ 0xee, 0x32, 0x8b, 0x3e, 0x02, 0x38, 0x5f, 0x53, 0xd6, 0x73, 0x41, 0x4c,
+ 0x1e, 0xae, 0xcf, 0x4f, 0x50, 0xa9, 0x71, 0xbc, 0x79, 0xa5, 0x9e, 0xd6,
+ 0x13, 0x07, 0xa3, 0xaa, 0x89, 0x0d, 0x31, 0x8c, 0x3a, 0x80, 0xe1, 0x53,
+ 0x22, 0x29, 0x43, 0xb9, 0xdf, 0xf6, 0xfb, 0x6d, 0xad, 0xbd, 0xf8, 0xc3,
+ 0xee, 0xbe, 0x59, 0xd0, 0x06, 0x45, 0x35, 0x95, 0xce, 0xcb, 0x61, 0xd9,
+ 0x74, 0x9e, 0x90, 0x96, 0xb8, 0xd6, 0xb2, 0xc9, 0x29, 0xfd, 0x45, 0xaf,
+ 0xea, 0xbe, 0x7b, 0x96, 0x76, 0x59, 0xe8, 0x04, 0x05, 0x5c, 0x8e, 0xc1,
+ 0xb3, 0x7c, 0xeb, 0xd8, 0xc8, 0x3d, 0x84, 0x13, 0x50, 0x07, 0x6f, 0xff,
+ 0x27, 0x69, 0xcb, 0x33, 0x62, 0x87, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x02, 0xfa, 0x30, 0x82, 0x02, 0xf6, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x86, 0x4e, 0x14, 0x78, 0x0e, 0x5d, 0x44, 0x21, 0xe0, 0x80, 0x23,
+ 0x6b, 0x1f, 0x9b, 0xf2, 0xd6, 0x09, 0xc5, 0x50, 0xa6, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x11, 0xdb,
+ 0x23, 0x45, 0xfd, 0x54, 0xcc, 0x6a, 0x71, 0x6f, 0x84, 0x8a, 0x03, 0xd7,
+ 0xbe, 0xf7, 0x01, 0x2f, 0x26, 0x86, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x2f, 0x30, 0x2d, 0x82, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x6b,
+ 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x82, 0x0b,
+ 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x82,
+ 0x0d, 0x2a, 0x2e, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e,
+ 0x63, 0x7a, 0x30, 0x82, 0x01, 0x56, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x82, 0x01, 0x4d, 0x30, 0x82, 0x01, 0x49, 0x30, 0x08, 0x06, 0x06, 0x67,
+ 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x82, 0x01, 0x3b, 0x06, 0x0b, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x03, 0x30, 0x82,
+ 0x01, 0x2a, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70,
+ 0x64, 0x66, 0x30, 0x81, 0xf7, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x02, 0x30, 0x81, 0xea, 0x30, 0x27, 0x16, 0x20, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x03, 0x02, 0x01, 0x01, 0x1a,
+ 0x81, 0xbe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x72, 0x64,
+ 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43,
+ 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2c, 0x20, 0x72, 0x65,
+ 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65,
+ 0x6e, 0x64, 0x65, 0x64, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65,
+ 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e,
+ 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65,
+ 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x72, 0x74, 0x79, 0x20,
+ 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30,
+ 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x74, 0x32, 0x2d,
+ 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x8e, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0x81, 0x30,
+ 0x7f, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x75, 0x62, 0x2f, 0x63, 0x6c, 0x61, 0x73,
+ 0x73, 0x32, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x63, 0x61,
+ 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02,
+ 0x86, 0x36, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x69, 0x61,
+ 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x73, 0x75, 0x62, 0x2e,
+ 0x63, 0x6c, 0x61, 0x73, 0x73, 0x32, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x2e, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x23, 0x06, 0x03,
+ 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72,
+ 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0xae, 0xea, 0xec, 0xe0, 0x6e, 0xe1, 0x5e,
+ 0xe3, 0x06, 0xe6, 0x09, 0xff, 0xf2, 0xea, 0xeb, 0xbd, 0xc2, 0xf9, 0xa2,
+ 0x79, 0xbb, 0xd1, 0xc3, 0x9c, 0x9f, 0xbd, 0x74, 0x0c, 0x9c, 0xeb, 0x73,
+ 0xf1, 0x5c, 0x57, 0x98, 0x8c, 0xaf, 0xaa, 0xfb, 0xcf, 0xfb, 0x55, 0x31,
+ 0x54, 0x71, 0x07, 0xdd, 0x7c, 0x83, 0x70, 0xcb, 0x12, 0xbf, 0x05, 0xd8,
+ 0x62, 0xf1, 0xe0, 0x9d, 0x1c, 0x35, 0xb2, 0x42, 0xb1, 0x37, 0xe8, 0x73,
+ 0x4c, 0xe5, 0xda, 0xd9, 0xcb, 0xe6, 0x5a, 0x50, 0x31, 0x14, 0xce, 0x50,
+ 0xc0, 0xfb, 0x68, 0xb9, 0xe6, 0x48, 0x24, 0xdd, 0x4f, 0xbe, 0x34, 0x28,
+ 0xba, 0x21, 0x53, 0x86, 0x86, 0x91, 0x6f, 0xb0, 0x8e, 0x34, 0x20, 0x4d,
+ 0xdf, 0xef, 0xf3, 0x6f, 0xb0, 0x78, 0x89, 0x4b, 0x80, 0x36, 0xe9, 0x75,
+ 0x3a, 0xd6, 0x18, 0xc6, 0x84, 0xe3, 0x0c, 0xa9, 0x24, 0xcb, 0x49, 0xaf,
+ 0x72, 0x09, 0x3a, 0xb5, 0xdd, 0x59, 0xb9, 0xe0, 0xb6, 0x7e, 0xc2, 0x3c,
+ 0xd0, 0xea, 0xeb, 0x39, 0x06, 0x3f, 0xc6, 0xe9, 0x1f, 0x37, 0x25, 0x3c,
+ 0xdf, 0x0d, 0x95, 0xcc, 0x9f, 0xa3, 0x68, 0x15, 0x3b, 0x80, 0x9b, 0x17,
+ 0x1a, 0x54, 0x65, 0x61, 0xb0, 0xcf, 0xb5, 0x76, 0x7c, 0xc2, 0x7e, 0x54,
+ 0x4d, 0x03, 0xe6, 0x90, 0xa0, 0x59, 0xa9, 0x1c, 0x6d, 0x4b, 0x34, 0x03,
+ 0xc3, 0xbb, 0xcd, 0x77, 0x60, 0x0e, 0xb1, 0x4e, 0x22, 0x81, 0xe4, 0x17,
+ 0xf4, 0xd2, 0x58, 0x2c, 0x72, 0x4e, 0xde, 0xd0, 0x24, 0x25, 0xfb, 0xd8,
+ 0x3f, 0xc8, 0x0f, 0x3b, 0x0d, 0xec, 0x75, 0x81, 0x37, 0x08, 0xd0, 0x0a,
+ 0x29, 0x28, 0x9f, 0x7f, 0x35, 0x83, 0x70, 0x18, 0x6c, 0x4b, 0x24, 0x8e,
+ 0xc0, 0xe5, 0xc1, 0xbb, 0x5b, 0x24, 0xb4, 0x5c, 0x8e, 0xbc, 0x79, 0xc0,
+ 0xad, 0x47, 0x17, 0xdd, 0x7a, 0xf2, 0x26, 0x6e, 0xe4
+};
+
+static gnutls_x509_crt_t get_cert(void)
+{
+ gnutls_x509_crt_t cert = NULL;
+ if (gnutls_x509_crt_init(&cert) != 0) {
+ return NULL;
+ }
+
+ const gnutls_datum_t der = {
+ .data = (uint8_t *)CERT_DER,
+ .size = sizeof(CERT_DER)
+ };
+ if (gnutls_x509_crt_import(cert, &der, GNUTLS_X509_FMT_DER) != 0) {
+ gnutls_x509_crt_deinit(cert);
+ return NULL;
+ }
+
+ return cert;
+}
+
+void test_get_pin(void)
+{
+ gnutls_x509_crt_t cert = get_cert();
+ ok(cert != NULL, "create testing certificate");
+
+ static const uint8_t expected[] = {
+ 0x35, 0xa2, 0x1a, 0x6b, 0x95, 0x34, 0x53, 0xed, 0xf7, 0xf7,
+ 0x08, 0x76, 0x08, 0x17, 0x9c, 0x4a, 0x16, 0x6e, 0xcf, 0xd5,
+ 0xff, 0x46, 0x71, 0x1d, 0xa8, 0x08, 0xb7, 0xef, 0x75, 0xaf,
+ 0xfd, 0xa0
+ };
+
+ uint8_t pin[CERT_PIN_LEN] = { 0 };
+
+ int r = cert_get_pin(cert, NULL, 0);
+ ok(r == KNOT_EINVAL, "cert_get_pin(), no target buffer");
+
+ r = cert_get_pin(cert, pin, sizeof(pin) - 1);
+ ok(r == KNOT_EINVAL, "cert_get_pin(), invalid buffer size");
+
+ r = cert_get_pin(cert, pin, sizeof(pin));
+ ok(r == KNOT_EOK && sizeof(pin) == sizeof(expected) &&
+ memcmp(pin, expected, sizeof(pin)) == 0,
+ "cert_get_pin(), valid input");
+
+ gnutls_x509_crt_deinit(cert);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_get_pin();
+
+ return 0;
+}
diff --git a/tests/utils/test_lookup.c b/tests/utils/test_lookup.c
new file mode 100644
index 0000000..111eb26
--- /dev/null
+++ b/tests/utils/test_lookup.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <tap/basic.h>
+
+#include "libknot/error.h"
+#include "utils/common/lookup.h"
+
+static void check_search_ok(lookup_t *l, const char *in, const char *out)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_EOK, ret, "Check found");
+ ok(strcmp(out, l->found.key) == 0, "Compare key");
+ ok(strcmp(out, l->found.data) == 0, "Compare data");
+ ok(l->iter.first_key == NULL, "Compare no first key");
+ ok(l->iter.count == 1, "Compare 1 count");
+}
+
+static void check_search_multi(lookup_t *l, const char *in, const char *out,
+ const char *first, size_t count)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_EFEWDATA, ret, "Check found multi");
+ ok(strcmp(out, l->found.key) == 0, "Compare key");
+ ok(l->found.data == NULL, "Compare no data");
+ ok(strcmp(first, l->iter.first_key) == 0, "Compare first key");
+ ok(l->iter.count == count, "Compare count");
+}
+
+static void check_search_none(lookup_t *l, const char *in)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_ENOENT, ret, "Check not found");
+ ok(l->found.key == NULL, "Check no key");
+ ok(l->found.data == NULL, "Check no data");
+}
+
+static void init(lookup_t *l, const char **table)
+{
+ int ret = lookup_init(l);
+ is_int(KNOT_EOK, ret, "Init");
+
+ while (*table != NULL) {
+ ret = lookup_insert(l, *table, (void *)*table);
+ is_int(KNOT_EOK, ret, "Insert '%s'", *table);
+ table++;
+ }
+}
+
+static void test_search_basic(void)
+{
+ const char* table[] = {
+ "aa",
+ "bb",
+ NULL
+ };
+
+ lookup_t l;
+ init(&l, table);
+
+ check_search_ok(&l, "a", "aa");
+ check_search_ok(&l, "aa", "aa");
+ check_search_ok(&l, "b", "bb");
+ check_search_ok(&l, "bb", "bb");
+
+ check_search_none(&l, "0");
+ check_search_none(&l, "000");
+ check_search_none(&l, "00000000000000000000000000000000000000000000");
+ check_search_none(&l, "a0");
+ check_search_none(&l, "ab");
+ check_search_none(&l, "aaa");
+ check_search_none(&l, "bbb");
+ check_search_none(&l, "cc");
+ check_search_none(&l, "ccc");
+ check_search_none(&l, "cccccccccccccccccccccccccccccccccccccccccccc");
+
+ check_search_multi(&l, "", "", "aa", 2);
+
+ lookup_deinit(&l);
+}
+
+static void test_search_iter(void)
+{
+ const char* table[] = {
+ "0",
+ "ab",
+ "abc",
+ "abcd",
+ "abc-1",
+ "abc-99",
+ "z",
+ NULL
+ };
+
+ lookup_t l;
+ init(&l, table);
+
+ check_search_multi(&l, "", "", "0", 7);
+ check_search_multi(&l, "a", "ab", "ab", 5);
+ check_search_multi(&l, "ab", "ab", "ab", 5);
+ check_search_multi(&l, "abc", "abc", "abc", 4);
+ check_search_multi(&l, "abc-", "abc-", "abc-1", 2);
+
+ lookup_deinit(&l);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Search tests basic");
+ test_search_basic();
+
+ diag("Search tests multi-result");
+ test_search_iter();
+
+ return 0;
+}