summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am247
-rw-r--r--tests/Makefile.in2769
-rw-r--r--tests/contrib/test_base32hex.c267
-rw-r--r--tests/contrib/test_base64.c237
-rw-r--r--tests/contrib/test_base64url.c252
-rw-r--r--tests/contrib/test_heap.c166
-rw-r--r--tests/contrib/test_inet_ntop.c85
-rw-r--r--tests/contrib/test_net.c718
-rw-r--r--tests/contrib/test_net_shortwrite.c151
-rw-r--r--tests/contrib/test_qp-cow.c282
-rw-r--r--tests/contrib/test_qp-trie.c284
-rw-r--r--tests/contrib/test_siphash.c135
-rw-r--r--tests/contrib/test_sockaddr.c256
-rw-r--r--tests/contrib/test_spinlock.c78
-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_toeplitz.c93
-rw-r--r--tests/contrib/test_wire_ctx.c287
-rw-r--r--tests/knot/semantic_check_data/cdnskey.cds123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.both113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.invalid.cds113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid.param123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocdnskey101
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocds110
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nodnskey111
-rw-r--r--tests/knot/semantic_check_data/cdnskey.orphan.cdnskey135
-rw-r--r--tests/knot/semantic_check_data/cdnskey.orphan.cds138
-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/delegation.signed43
-rw-r--r--tests/knot/semantic_check_data/different_signer_name.signed52
-rw-r--r--tests/knot/semantic_check_data/dname_apex_nsec3.signed25
-rw-r--r--tests/knot/semantic_check_data/dname_children.zone16
-rw-r--r--tests/knot/semantic_check_data/dname_extra_ns.zone16
-rw-r--r--tests/knot/semantic_check_data/dname_multiple.zone16
-rw-r--r--tests/knot/semantic_check_data/dnskey_param_error.signed70
-rw-r--r--tests/knot/semantic_check_data/ds_apex.zone15
-rw-r--r--tests/knot/semantic_check_data/duplicate.signature19
-rw-r--r--tests/knot/semantic_check_data/glue_apex_both.missing14
-rw-r--r--tests/knot/semantic_check_data/glue_apex_one.missing16
-rw-r--r--tests/knot/semantic_check_data/glue_besides.missing17
-rw-r--r--tests/knot/semantic_check_data/glue_deleg.missing17
-rw-r--r--tests/knot/semantic_check_data/glue_in_apex.missing13
-rw-r--r--tests/knot/semantic_check_data/glue_in_deleg.valid16
-rw-r--r--tests/knot/semantic_check_data/glue_no_foreign.valid13
-rw-r--r--tests/knot/semantic_check_data/glue_wildcard.valid22
-rw-r--r--tests/knot/semantic_check_data/invalid_ds.signed108
-rw-r--r--tests/knot/semantic_check_data/missing.signed20
-rw-r--r--tests/knot/semantic_check_data/no_error_delegation_bitmap.signed61
-rw-r--r--tests/knot/semantic_check_data/no_error_nsec3_optout.signed29
-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/ns_apex.missing11
-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.signed57
-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_optout_ent.all15
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout_ent.invalid18
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout_ent.valid20
-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.signed65
-rw-r--r--tests/knot/semantic_check_data/nsec_missing.signed67
-rw-r--r--tests/knot/semantic_check_data/nsec_multiple.signed66
-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.signed52
-rw-r--r--tests/knot/semantic_check_data/rrsig_signed.signed62
-rw-r--r--tests/knot/semantic_check_data/rrsig_ttl.signed52
-rw-r--r--tests/knot/test_acl.c346
-rw-r--r--tests/knot/test_changeset.c166
-rw-r--r--tests/knot/test_conf.c342
-rw-r--r--tests/knot/test_conf.h50
-rw-r--r--tests/knot/test_conf_tools.c74
-rw-r--r--tests/knot/test_confdb.c489
-rw-r--r--tests/knot/test_confio.c1101
-rw-r--r--tests/knot/test_digest.c474
-rw-r--r--tests/knot/test_dthreads.c148
-rw-r--r--tests/knot/test_fdset.c150
-rw-r--r--tests/knot/test_journal.c880
-rw-r--r--tests/knot/test_kasp_db.c233
-rw-r--r--tests/knot/test_node.c128
-rw-r--r--tests/knot/test_process_query.c201
-rw-r--r--tests/knot/test_query_module.c87
-rw-r--r--tests/knot/test_requestor.c189
-rw-r--r--tests/knot/test_semantic_check.in171
-rw-r--r--tests/knot/test_server.c72
-rw-r--r--tests/knot/test_server.h100
-rw-r--r--tests/knot/test_unreachable.c59
-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.c135
-rw-r--r--tests/knot/test_zone-update.c337
-rw-r--r--tests/knot/test_zone_events.c99
-rw-r--r--tests/knot/test_zone_serial.c214
-rw-r--r--tests/knot/test_zone_timers.c117
-rw-r--r--tests/knot/test_zonedb.c115
-rw-r--r--tests/libdnssec/sample_keys.h498
-rw-r--r--tests/libdnssec/test_binary.c93
-rw-r--r--tests/libdnssec/test_crypto.c37
-rw-r--r--tests/libdnssec/test_key.c215
-rw-r--r--tests/libdnssec/test_key_algorithm.c90
-rw-r--r--tests/libdnssec/test_key_ds.c122
-rw-r--r--tests/libdnssec/test_keyid.c82
-rw-r--r--tests/libdnssec/test_keystore_pkcs11.c421
-rw-r--r--tests/libdnssec/test_keystore_pkcs8.c98
-rw-r--r--tests/libdnssec/test_keytag.c61
-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.c203
-rw-r--r--tests/libdnssec/test_sign_der.c203
-rw-r--r--tests/libdnssec/test_tsig.c145
-rw-r--r--tests/libknot/test_control.c221
-rw-r--r--tests/libknot/test_cookies.c174
-rw-r--r--tests/libknot/test_db.c287
-rw-r--r--tests/libknot/test_descriptor.c361
-rw-r--r--tests/libknot/test_dname.c618
-rw-r--r--tests/libknot/test_dynarray.c137
-rw-r--r--tests/libknot/test_edns.c502
-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_probe.c96
-rw-r--r--tests/libknot/test_rdata.c60
-rw-r--r--tests/libknot/test_rdataset.c227
-rw-r--r--tests/libknot/test_rrset-wire.c264
-rw-r--r--tests/libknot/test_rrset.c121
-rw-r--r--tests/libknot/test_tsig.c204
-rw-r--r--tests/libknot/test_wire.c46
-rw-r--r--tests/libknot/test_xdp_tcp.c638
-rw-r--r--tests/libknot/test_yparser.c346
-rw-r--r--tests/libknot/test_ypschema.c417
-rw-r--r--tests/libknot/test_yptrafo.c405
-rw-r--r--tests/libzscanner/TESTS86
-rw-r--r--tests/libzscanner/data/00-0_general.in27
-rw-r--r--tests/libzscanner/data/00-0_general.out70
-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.in46
-rw-r--r--tests/libzscanner/data/36_RRSIG.out140
-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/53_SMIMEA.in13
-rw-r--r--tests/libzscanner/data/53_SMIMEA.out26
-rw-r--r--tests/libzscanner/data/54_OPENPGPKEY.in13
-rw-r--r--tests/libzscanner/data/54_OPENPGPKEY.out26
-rw-r--r--tests/libzscanner/data/55_CSYNC.in22
-rw-r--r--tests/libzscanner/data/55_CSYNC.out68
-rw-r--r--tests/libzscanner/data/56_ZONEMD.in21
-rw-r--r--tests/libzscanner/data/56_ZONEMD.out54
-rw-r--r--tests/libzscanner/data/57_SVCB.in102
-rw-r--r--tests/libzscanner/data/57_SVCB.out306
-rw-r--r--tests/libzscanner/data/58_HTTPS.in19
-rw-r--r--tests/libzscanner/data/58_HTTPS.out46
-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.c184
-rw-r--r--tests/libzscanner/processing.h31
-rw-r--r--tests/libzscanner/test_zscanner.in41
-rw-r--r--tests/libzscanner/zscanner-tool.c257
-rw-r--r--tests/modules/test_onlinesign.c203
-rw-r--r--tests/modules/test_rrl.c178
-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.c1401
-rw-r--r--tests/utils/test_lookup.c136
341 files changed, 35290 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..eb6f1aa
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,247 @@
+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) \
+ $(libkqueue_CFLAGS) \
+ $(lmdb_CFLAGS)
+
+LDADD = \
+ libtap.la
+
+if HAVE_DAEMON
+LDADD += \
+ $(top_builddir)/src/libknotd.la \
+ $(liburcu_LIBS) \
+ $(systemd_LIBS)
+endif HAVE_DAEMON
+
+LDADD += \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la \
+ $(gnutls_LIBS) \
+ $(lmdb_LIBS)
+
+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
+
+check_PROGRAMS = \
+ contrib/test_base32hex \
+ contrib/test_base64 \
+ contrib/test_base64url \
+ contrib/test_heap \
+ contrib/test_inet_ntop \
+ contrib/test_net \
+ contrib/test_net_shortwrite \
+ contrib/test_qp-trie \
+ contrib/test_qp-cow \
+ contrib/test_siphash \
+ contrib/test_sockaddr \
+ contrib/test_spinlock \
+ contrib/test_string \
+ contrib/test_strtonum \
+ contrib/test_time \
+ contrib/test_toeplitz \
+ 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_keytag \
+ 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_digest \
+ 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_unreachable \
+ 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_dynarray \
+ libknot/test_edns \
+ libknot/test_edns_ecs \
+ libknot/test_endian \
+ libknot/test_lookup \
+ libknot/test_pkt \
+ libknot/test_probe \
+ 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 ENABLE_XDP
+AM_CPPFLAGS += $(libbpf_CFLAGS)
+check_PROGRAMS += \
+ libknot/test_xdp_tcp
+endif ENABLE_XDP
+
+if HAVE_LIBUTILS
+check_PROGRAMS += \
+ 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 = \
+ $(top_builddir)/src/libknotus.la \
+ $(libedit_LIBS) \
+ $(LDADD)
+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..b02c209
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,2769 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 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 \
+@HAVE_DAEMON_TRUE@ $(liburcu_LIBS) \
+@HAVE_DAEMON_TRUE@ $(systemd_LIBS)
+
+EXTRA_PROGRAMS = tap/runtests$(EXEEXT) \
+ libzscanner/zscanner-tool$(EXEEXT)
+check_PROGRAMS = contrib/test_base32hex$(EXEEXT) \
+ contrib/test_base64$(EXEEXT) contrib/test_base64url$(EXEEXT) \
+ contrib/test_heap$(EXEEXT) contrib/test_inet_ntop$(EXEEXT) \
+ contrib/test_net$(EXEEXT) contrib/test_net_shortwrite$(EXEEXT) \
+ contrib/test_qp-trie$(EXEEXT) contrib/test_qp-cow$(EXEEXT) \
+ contrib/test_siphash$(EXEEXT) contrib/test_sockaddr$(EXEEXT) \
+ contrib/test_spinlock$(EXEEXT) contrib/test_string$(EXEEXT) \
+ contrib/test_strtonum$(EXEEXT) contrib/test_time$(EXEEXT) \
+ contrib/test_toeplitz$(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_keytag$(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_dynarray$(EXEEXT) \
+ libknot/test_edns$(EXEEXT) libknot/test_edns_ecs$(EXEEXT) \
+ libknot/test_endian$(EXEEXT) libknot/test_lookup$(EXEEXT) \
+ libknot/test_pkt$(EXEEXT) libknot/test_probe$(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) \
+ $(am__EXEEXT_7)
+@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_digest \
+@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_unreachable \
+@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
+
+@ENABLE_XDP_TRUE@am__append_3 = $(libbpf_CFLAGS)
+@ENABLE_XDP_TRUE@am__append_4 = \
+@ENABLE_XDP_TRUE@ libknot/test_xdp_tcp
+
+@HAVE_LIBUTILS_TRUE@am__append_5 = \
+@HAVE_LIBUTILS_TRUE@ utils/test_lookup
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__append_6 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__append_7 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__append_8 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@ modules/test_rrl
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__append_9 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@ modules/test_rrl
+
+@HAVE_LIBUTILS_TRUE@am__append_10 = \
+@HAVE_LIBUTILS_TRUE@ knot/test_semantic_check
+
+subdir = tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.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 =
+@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_digest$(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_unreachable$(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)
+@ENABLE_XDP_TRUE@am__EXEEXT_2 = libknot/test_xdp_tcp$(EXEEXT)
+@HAVE_LIBUTILS_TRUE@am__EXEEXT_3 = utils/test_lookup$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__EXEEXT_4 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__EXEEXT_5 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__EXEEXT_6 = modules/test_rrl$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__EXEEXT_7 = modules/test_rrl$(EXEEXT)
+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 =
+contrib_test_base32hex_SOURCES = contrib/test_base32hex.c
+contrib_test_base32hex_OBJECTS = contrib/test_base32hex.$(OBJEXT)
+contrib_test_base32hex_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+@HAVE_DAEMON_TRUE@am__DEPENDENCIES_2 = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+contrib_test_base32hex_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_base64url_SOURCES = contrib/test_base64url.c
+contrib_test_base64url_OBJECTS = contrib/test_base64url.$(OBJEXT)
+contrib_test_base64url_LDADD = $(LDADD)
+contrib_test_base64url_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_inet_ntop_SOURCES = contrib/test_inet_ntop.c
+contrib_test_inet_ntop_OBJECTS = contrib/test_inet_ntop.$(OBJEXT)
+contrib_test_inet_ntop_LDADD = $(LDADD)
+contrib_test_inet_ntop_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_qp_cow_SOURCES = contrib/test_qp-cow.c
+contrib_test_qp_cow_OBJECTS = contrib/test_qp-cow.$(OBJEXT)
+contrib_test_qp_cow_LDADD = $(LDADD)
+contrib_test_qp_cow_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_spinlock_SOURCES = contrib/test_spinlock.c
+contrib_test_spinlock_OBJECTS = contrib/test_spinlock.$(OBJEXT)
+contrib_test_spinlock_LDADD = $(LDADD)
+contrib_test_spinlock_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_toeplitz_SOURCES = contrib/test_toeplitz.c
+contrib_test_toeplitz_OBJECTS = contrib/test_toeplitz.$(OBJEXT)
+contrib_test_toeplitz_LDADD = $(LDADD)
+contrib_test_toeplitz_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_digest_SOURCES = knot/test_digest.c
+knot_test_digest_OBJECTS = knot/test_digest.$(OBJEXT)
+knot_test_digest_LDADD = $(LDADD)
+knot_test_digest_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_unreachable_SOURCES = knot/test_unreachable.c
+knot_test_unreachable_OBJECTS = knot/test_unreachable.$(OBJEXT)
+knot_test_unreachable_LDADD = $(LDADD)
+knot_test_unreachable_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_keystore_pkcs11_SOURCES = \
+ libdnssec/test_keystore_pkcs11.c
+libdnssec_test_keystore_pkcs11_OBJECTS = \
+ libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.$(OBJEXT)
+libdnssec_test_keystore_pkcs11_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs11_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_dynarray_SOURCES = libknot/test_dynarray.c
+libknot_test_dynarray_OBJECTS = libknot/test_dynarray.$(OBJEXT)
+libknot_test_dynarray_LDADD = $(LDADD)
+libknot_test_dynarray_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_probe_SOURCES = libknot/test_probe.c
+libknot_test_probe_OBJECTS = libknot/test_probe.$(OBJEXT)
+libknot_test_probe_LDADD = $(LDADD)
+libknot_test_probe_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_xdp_tcp_SOURCES = libknot/test_xdp_tcp.c
+libknot_test_xdp_tcp_OBJECTS = libknot/test_xdp_tcp.$(OBJEXT)
+libknot_test_xdp_tcp_LDADD = $(LDADD)
+libknot_test_xdp_tcp_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_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 $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+tap_runtests_SOURCES = tap/runtests.c
+tap_runtests_OBJECTS = tap/runtests.$(OBJEXT)
+tap_runtests_LDADD = $(LDADD)
+tap_runtests_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+utils_test_lookup_SOURCES = utils/test_lookup.c
+utils_test_lookup_OBJECTS = \
+ utils/utils_test_lookup-test_lookup.$(OBJEXT)
+am__DEPENDENCIES_3 = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_DEPENDENCIES = \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_3)
+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__maybe_remake_depfiles = depfiles
+am__depfiles_remade = contrib/$(DEPDIR)/test_base32hex.Po \
+ contrib/$(DEPDIR)/test_base64.Po \
+ contrib/$(DEPDIR)/test_base64url.Po \
+ contrib/$(DEPDIR)/test_heap.Po \
+ contrib/$(DEPDIR)/test_inet_ntop.Po \
+ contrib/$(DEPDIR)/test_net.Po \
+ contrib/$(DEPDIR)/test_net_shortwrite.Po \
+ contrib/$(DEPDIR)/test_qp-cow.Po \
+ contrib/$(DEPDIR)/test_qp-trie.Po \
+ contrib/$(DEPDIR)/test_siphash.Po \
+ contrib/$(DEPDIR)/test_sockaddr.Po \
+ contrib/$(DEPDIR)/test_spinlock.Po \
+ contrib/$(DEPDIR)/test_string.Po \
+ contrib/$(DEPDIR)/test_strtonum.Po \
+ contrib/$(DEPDIR)/test_time.Po \
+ contrib/$(DEPDIR)/test_toeplitz.Po \
+ contrib/$(DEPDIR)/test_wire_ctx.Po knot/$(DEPDIR)/test_acl.Po \
+ knot/$(DEPDIR)/test_changeset.Po knot/$(DEPDIR)/test_conf.Po \
+ knot/$(DEPDIR)/test_conf_tools.Po \
+ knot/$(DEPDIR)/test_confdb.Po knot/$(DEPDIR)/test_confio.Po \
+ knot/$(DEPDIR)/test_digest.Po knot/$(DEPDIR)/test_dthreads.Po \
+ knot/$(DEPDIR)/test_fdset.Po knot/$(DEPDIR)/test_journal.Po \
+ knot/$(DEPDIR)/test_kasp_db.Po knot/$(DEPDIR)/test_node.Po \
+ knot/$(DEPDIR)/test_process_query.Po \
+ knot/$(DEPDIR)/test_query_module.Po \
+ knot/$(DEPDIR)/test_requestor.Po knot/$(DEPDIR)/test_server.Po \
+ knot/$(DEPDIR)/test_unreachable.Po \
+ knot/$(DEPDIR)/test_worker_pool.Po \
+ knot/$(DEPDIR)/test_worker_queue.Po \
+ knot/$(DEPDIR)/test_zone-tree.Po \
+ knot/$(DEPDIR)/test_zone-update.Po \
+ knot/$(DEPDIR)/test_zone_events.Po \
+ knot/$(DEPDIR)/test_zone_serial.Po \
+ knot/$(DEPDIR)/test_zone_timers.Po \
+ knot/$(DEPDIR)/test_zonedb.Po \
+ libdnssec/$(DEPDIR)/test_binary.Po \
+ libdnssec/$(DEPDIR)/test_crypto.Po \
+ libdnssec/$(DEPDIR)/test_key.Po \
+ libdnssec/$(DEPDIR)/test_key_algorithm.Po \
+ libdnssec/$(DEPDIR)/test_key_ds.Po \
+ libdnssec/$(DEPDIR)/test_keyid.Po \
+ libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po \
+ libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po \
+ libdnssec/$(DEPDIR)/test_keytag.Po \
+ libdnssec/$(DEPDIR)/test_nsec_bitmap.Po \
+ libdnssec/$(DEPDIR)/test_nsec_hash.Po \
+ libdnssec/$(DEPDIR)/test_random.Po \
+ libdnssec/$(DEPDIR)/test_shared_bignum.Po \
+ libdnssec/$(DEPDIR)/test_shared_dname.Po \
+ libdnssec/$(DEPDIR)/test_sign.Po \
+ libdnssec/$(DEPDIR)/test_sign_der.Po \
+ libdnssec/$(DEPDIR)/test_tsig.Po \
+ libknot/$(DEPDIR)/test_control.Po \
+ libknot/$(DEPDIR)/test_cookies.Po libknot/$(DEPDIR)/test_db.Po \
+ libknot/$(DEPDIR)/test_descriptor.Po \
+ libknot/$(DEPDIR)/test_dname.Po \
+ libknot/$(DEPDIR)/test_dynarray.Po \
+ libknot/$(DEPDIR)/test_edns.Po \
+ libknot/$(DEPDIR)/test_edns_ecs.Po \
+ libknot/$(DEPDIR)/test_endian.Po \
+ libknot/$(DEPDIR)/test_lookup.Po libknot/$(DEPDIR)/test_pkt.Po \
+ libknot/$(DEPDIR)/test_probe.Po \
+ libknot/$(DEPDIR)/test_rdata.Po \
+ libknot/$(DEPDIR)/test_rdataset.Po \
+ libknot/$(DEPDIR)/test_rrset-wire.Po \
+ libknot/$(DEPDIR)/test_rrset.Po libknot/$(DEPDIR)/test_tsig.Po \
+ libknot/$(DEPDIR)/test_wire.Po \
+ libknot/$(DEPDIR)/test_xdp_tcp.Po \
+ libknot/$(DEPDIR)/test_yparser.Po \
+ libknot/$(DEPDIR)/test_ypschema.Po \
+ libknot/$(DEPDIR)/test_yptrafo.Po \
+ libzscanner/$(DEPDIR)/processing.Po \
+ libzscanner/$(DEPDIR)/zscanner-tool.Po \
+ modules/$(DEPDIR)/test_onlinesign.Po \
+ modules/$(DEPDIR)/test_rrl.Po tap/$(DEPDIR)/basic.Plo \
+ tap/$(DEPDIR)/files.Plo tap/$(DEPDIR)/float.Plo \
+ tap/$(DEPDIR)/runtests.Po \
+ utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+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_base64url.c \
+ contrib/test_heap.c contrib/test_inet_ntop.c \
+ contrib/test_net.c contrib/test_net_shortwrite.c \
+ contrib/test_qp-cow.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_spinlock.c contrib/test_string.c \
+ contrib/test_strtonum.c contrib/test_time.c \
+ contrib/test_toeplitz.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_digest.c 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_unreachable.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_keytag.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_dynarray.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_probe.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_xdp_tcp.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_lookup.c
+DIST_SOURCES = $(libtap_la_SOURCES) contrib/test_base32hex.c \
+ contrib/test_base64.c contrib/test_base64url.c \
+ contrib/test_heap.c contrib/test_inet_ntop.c \
+ contrib/test_net.c contrib/test_net_shortwrite.c \
+ contrib/test_qp-cow.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_spinlock.c contrib/test_string.c \
+ contrib/test_strtonum.c contrib/test_time.c \
+ contrib/test_toeplitz.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_digest.c \
+ 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_unreachable.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_keytag.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_dynarray.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_probe.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_xdp_tcp.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_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)`
+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@
+CFLAGS = @CFLAGS@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+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@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+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_NO_UNDEFINED = @LT_NO_UNDEFINED@
+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@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+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@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_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@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+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) \
+ $(libkqueue_CFLAGS) $(lmdb_CFLAGS) $(am__append_3)
+LDADD = libtap.la $(am__append_1) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(gnutls_LIBS) $(lmdb_LIBS)
+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
+
+@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@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(LDADD)
+
+libzscanner_zscanner_tool_SOURCES = \
+ libzscanner/zscanner-tool.c \
+ libzscanner/processing.h \
+ libzscanner/processing.c
+
+check_SCRIPTS = libzscanner/test_zscanner $(am__append_10)
+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__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ 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-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
+
+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)
+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_base64url.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base64url$(EXEEXT): $(contrib_test_base64url_OBJECTS) $(contrib_test_base64url_DEPENDENCIES) $(EXTRA_contrib_test_base64url_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base64url$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base64url_OBJECTS) $(contrib_test_base64url_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_inet_ntop.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_inet_ntop$(EXEEXT): $(contrib_test_inet_ntop_OBJECTS) $(contrib_test_inet_ntop_DEPENDENCIES) $(EXTRA_contrib_test_inet_ntop_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_inet_ntop$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_inet_ntop_OBJECTS) $(contrib_test_inet_ntop_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-cow.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_qp-cow$(EXEEXT): $(contrib_test_qp_cow_OBJECTS) $(contrib_test_qp_cow_DEPENDENCIES) $(EXTRA_contrib_test_qp_cow_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_qp-cow$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_qp_cow_OBJECTS) $(contrib_test_qp_cow_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_spinlock.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_spinlock$(EXEEXT): $(contrib_test_spinlock_OBJECTS) $(contrib_test_spinlock_DEPENDENCIES) $(EXTRA_contrib_test_spinlock_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_spinlock$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_spinlock_OBJECTS) $(contrib_test_spinlock_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_toeplitz.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_toeplitz$(EXEEXT): $(contrib_test_toeplitz_OBJECTS) $(contrib_test_toeplitz_DEPENDENCIES) $(EXTRA_contrib_test_toeplitz_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_toeplitz$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_toeplitz_OBJECTS) $(contrib_test_toeplitz_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_digest.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_digest$(EXEEXT): $(knot_test_digest_OBJECTS) $(knot_test_digest_DEPENDENCIES) $(EXTRA_knot_test_digest_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_digest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_digest_OBJECTS) $(knot_test_digest_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_unreachable.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_unreachable$(EXEEXT): $(knot_test_unreachable_OBJECTS) $(knot_test_unreachable_DEPENDENCIES) $(EXTRA_knot_test_unreachable_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_unreachable$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_unreachable_OBJECTS) $(knot_test_unreachable_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/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_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_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_dynarray.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_dynarray$(EXEEXT): $(libknot_test_dynarray_OBJECTS) $(libknot_test_dynarray_DEPENDENCIES) $(EXTRA_libknot_test_dynarray_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_dynarray$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_dynarray_OBJECTS) $(libknot_test_dynarray_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_probe.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_probe$(EXEEXT): $(libknot_test_probe_OBJECTS) $(libknot_test_probe_DEPENDENCIES) $(EXTRA_libknot_test_probe_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_probe$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_probe_OBJECTS) $(libknot_test_probe_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_xdp_tcp.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_xdp_tcp$(EXEEXT): $(libknot_test_xdp_tcp_OBJECTS) $(libknot_test_xdp_tcp_DEPENDENCIES) $(EXTRA_libknot_test_xdp_tcp_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_xdp_tcp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_xdp_tcp_OBJECTS) $(libknot_test_xdp_tcp_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_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@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base64.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base64url.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_heap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_inet_ntop.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net_shortwrite.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_qp-cow.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_qp-trie.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_siphash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_sockaddr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_spinlock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_string.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_strtonum.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_time.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_toeplitz.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_wire_ctx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_acl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_changeset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf_tools.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confdb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confio.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_digest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_dthreads.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_fdset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_journal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_kasp_db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_node.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_process_query.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_query_module.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_requestor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_unreachable.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_pool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_queue.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-tree.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-update.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_events.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_serial.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_timers.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zonedb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_binary.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_algorithm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_ds.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keyid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keytag.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_bitmap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_hash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_random.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_bignum.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_dname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign_der.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_tsig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_control.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_cookies.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_descriptor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_dname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_dynarray.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns_ecs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_endian.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_lookup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_pkt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_probe.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdata.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdataset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset-wire.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_tsig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_wire.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_xdp_tcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yparser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_ypschema.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yptrafo.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/processing.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/zscanner-tool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_onlinesign.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_rrl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/basic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/files.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/float.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/runtests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.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/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/test_keystore_pkcs11-test_keystore_pkcs11.o -MD -MP -MF libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o 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)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='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/test_keystore_pkcs11-test_keystore_pkcs11.o `test -f 'libdnssec/test_keystore_pkcs11.c' || echo '$(srcdir)/'`libdnssec/test_keystore_pkcs11.c
+
+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/test_keystore_pkcs11-test_keystore_pkcs11.obj -MD -MP -MF libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o 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)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='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/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_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: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(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_PROGRAMS) $(check_LTLIBRARIES) \
+ $(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 -f contrib/$(DEPDIR)/test_base32hex.Po
+ -rm -f contrib/$(DEPDIR)/test_base64.Po
+ -rm -f contrib/$(DEPDIR)/test_base64url.Po
+ -rm -f contrib/$(DEPDIR)/test_heap.Po
+ -rm -f contrib/$(DEPDIR)/test_inet_ntop.Po
+ -rm -f contrib/$(DEPDIR)/test_net.Po
+ -rm -f contrib/$(DEPDIR)/test_net_shortwrite.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-cow.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-trie.Po
+ -rm -f contrib/$(DEPDIR)/test_siphash.Po
+ -rm -f contrib/$(DEPDIR)/test_sockaddr.Po
+ -rm -f contrib/$(DEPDIR)/test_spinlock.Po
+ -rm -f contrib/$(DEPDIR)/test_string.Po
+ -rm -f contrib/$(DEPDIR)/test_strtonum.Po
+ -rm -f contrib/$(DEPDIR)/test_time.Po
+ -rm -f contrib/$(DEPDIR)/test_toeplitz.Po
+ -rm -f contrib/$(DEPDIR)/test_wire_ctx.Po
+ -rm -f knot/$(DEPDIR)/test_acl.Po
+ -rm -f knot/$(DEPDIR)/test_changeset.Po
+ -rm -f knot/$(DEPDIR)/test_conf.Po
+ -rm -f knot/$(DEPDIR)/test_conf_tools.Po
+ -rm -f knot/$(DEPDIR)/test_confdb.Po
+ -rm -f knot/$(DEPDIR)/test_confio.Po
+ -rm -f knot/$(DEPDIR)/test_digest.Po
+ -rm -f knot/$(DEPDIR)/test_dthreads.Po
+ -rm -f knot/$(DEPDIR)/test_fdset.Po
+ -rm -f knot/$(DEPDIR)/test_journal.Po
+ -rm -f knot/$(DEPDIR)/test_kasp_db.Po
+ -rm -f knot/$(DEPDIR)/test_node.Po
+ -rm -f knot/$(DEPDIR)/test_process_query.Po
+ -rm -f knot/$(DEPDIR)/test_query_module.Po
+ -rm -f knot/$(DEPDIR)/test_requestor.Po
+ -rm -f knot/$(DEPDIR)/test_server.Po
+ -rm -f knot/$(DEPDIR)/test_unreachable.Po
+ -rm -f knot/$(DEPDIR)/test_worker_pool.Po
+ -rm -f knot/$(DEPDIR)/test_worker_queue.Po
+ -rm -f knot/$(DEPDIR)/test_zone-tree.Po
+ -rm -f knot/$(DEPDIR)/test_zone-update.Po
+ -rm -f knot/$(DEPDIR)/test_zone_events.Po
+ -rm -f knot/$(DEPDIR)/test_zone_serial.Po
+ -rm -f knot/$(DEPDIR)/test_zone_timers.Po
+ -rm -f knot/$(DEPDIR)/test_zonedb.Po
+ -rm -f libdnssec/$(DEPDIR)/test_binary.Po
+ -rm -f libdnssec/$(DEPDIR)/test_crypto.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_algorithm.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_ds.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keyid.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keytag.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_bitmap.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_hash.Po
+ -rm -f libdnssec/$(DEPDIR)/test_random.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_bignum.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_dname.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign_der.Po
+ -rm -f libdnssec/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_control.Po
+ -rm -f libknot/$(DEPDIR)/test_cookies.Po
+ -rm -f libknot/$(DEPDIR)/test_db.Po
+ -rm -f libknot/$(DEPDIR)/test_descriptor.Po
+ -rm -f libknot/$(DEPDIR)/test_dname.Po
+ -rm -f libknot/$(DEPDIR)/test_dynarray.Po
+ -rm -f libknot/$(DEPDIR)/test_edns.Po
+ -rm -f libknot/$(DEPDIR)/test_edns_ecs.Po
+ -rm -f libknot/$(DEPDIR)/test_endian.Po
+ -rm -f libknot/$(DEPDIR)/test_lookup.Po
+ -rm -f libknot/$(DEPDIR)/test_pkt.Po
+ -rm -f libknot/$(DEPDIR)/test_probe.Po
+ -rm -f libknot/$(DEPDIR)/test_rdata.Po
+ -rm -f libknot/$(DEPDIR)/test_rdataset.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset-wire.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset.Po
+ -rm -f libknot/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_wire.Po
+ -rm -f libknot/$(DEPDIR)/test_xdp_tcp.Po
+ -rm -f libknot/$(DEPDIR)/test_yparser.Po
+ -rm -f libknot/$(DEPDIR)/test_ypschema.Po
+ -rm -f libknot/$(DEPDIR)/test_yptrafo.Po
+ -rm -f libzscanner/$(DEPDIR)/processing.Po
+ -rm -f libzscanner/$(DEPDIR)/zscanner-tool.Po
+ -rm -f modules/$(DEPDIR)/test_onlinesign.Po
+ -rm -f modules/$(DEPDIR)/test_rrl.Po
+ -rm -f tap/$(DEPDIR)/basic.Plo
+ -rm -f tap/$(DEPDIR)/files.Plo
+ -rm -f tap/$(DEPDIR)/float.Plo
+ -rm -f tap/$(DEPDIR)/runtests.Po
+ -rm -f utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+ -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 -f contrib/$(DEPDIR)/test_base32hex.Po
+ -rm -f contrib/$(DEPDIR)/test_base64.Po
+ -rm -f contrib/$(DEPDIR)/test_base64url.Po
+ -rm -f contrib/$(DEPDIR)/test_heap.Po
+ -rm -f contrib/$(DEPDIR)/test_inet_ntop.Po
+ -rm -f contrib/$(DEPDIR)/test_net.Po
+ -rm -f contrib/$(DEPDIR)/test_net_shortwrite.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-cow.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-trie.Po
+ -rm -f contrib/$(DEPDIR)/test_siphash.Po
+ -rm -f contrib/$(DEPDIR)/test_sockaddr.Po
+ -rm -f contrib/$(DEPDIR)/test_spinlock.Po
+ -rm -f contrib/$(DEPDIR)/test_string.Po
+ -rm -f contrib/$(DEPDIR)/test_strtonum.Po
+ -rm -f contrib/$(DEPDIR)/test_time.Po
+ -rm -f contrib/$(DEPDIR)/test_toeplitz.Po
+ -rm -f contrib/$(DEPDIR)/test_wire_ctx.Po
+ -rm -f knot/$(DEPDIR)/test_acl.Po
+ -rm -f knot/$(DEPDIR)/test_changeset.Po
+ -rm -f knot/$(DEPDIR)/test_conf.Po
+ -rm -f knot/$(DEPDIR)/test_conf_tools.Po
+ -rm -f knot/$(DEPDIR)/test_confdb.Po
+ -rm -f knot/$(DEPDIR)/test_confio.Po
+ -rm -f knot/$(DEPDIR)/test_digest.Po
+ -rm -f knot/$(DEPDIR)/test_dthreads.Po
+ -rm -f knot/$(DEPDIR)/test_fdset.Po
+ -rm -f knot/$(DEPDIR)/test_journal.Po
+ -rm -f knot/$(DEPDIR)/test_kasp_db.Po
+ -rm -f knot/$(DEPDIR)/test_node.Po
+ -rm -f knot/$(DEPDIR)/test_process_query.Po
+ -rm -f knot/$(DEPDIR)/test_query_module.Po
+ -rm -f knot/$(DEPDIR)/test_requestor.Po
+ -rm -f knot/$(DEPDIR)/test_server.Po
+ -rm -f knot/$(DEPDIR)/test_unreachable.Po
+ -rm -f knot/$(DEPDIR)/test_worker_pool.Po
+ -rm -f knot/$(DEPDIR)/test_worker_queue.Po
+ -rm -f knot/$(DEPDIR)/test_zone-tree.Po
+ -rm -f knot/$(DEPDIR)/test_zone-update.Po
+ -rm -f knot/$(DEPDIR)/test_zone_events.Po
+ -rm -f knot/$(DEPDIR)/test_zone_serial.Po
+ -rm -f knot/$(DEPDIR)/test_zone_timers.Po
+ -rm -f knot/$(DEPDIR)/test_zonedb.Po
+ -rm -f libdnssec/$(DEPDIR)/test_binary.Po
+ -rm -f libdnssec/$(DEPDIR)/test_crypto.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_algorithm.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_ds.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keyid.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keytag.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_bitmap.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_hash.Po
+ -rm -f libdnssec/$(DEPDIR)/test_random.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_bignum.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_dname.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign_der.Po
+ -rm -f libdnssec/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_control.Po
+ -rm -f libknot/$(DEPDIR)/test_cookies.Po
+ -rm -f libknot/$(DEPDIR)/test_db.Po
+ -rm -f libknot/$(DEPDIR)/test_descriptor.Po
+ -rm -f libknot/$(DEPDIR)/test_dname.Po
+ -rm -f libknot/$(DEPDIR)/test_dynarray.Po
+ -rm -f libknot/$(DEPDIR)/test_edns.Po
+ -rm -f libknot/$(DEPDIR)/test_edns_ecs.Po
+ -rm -f libknot/$(DEPDIR)/test_endian.Po
+ -rm -f libknot/$(DEPDIR)/test_lookup.Po
+ -rm -f libknot/$(DEPDIR)/test_pkt.Po
+ -rm -f libknot/$(DEPDIR)/test_probe.Po
+ -rm -f libknot/$(DEPDIR)/test_rdata.Po
+ -rm -f libknot/$(DEPDIR)/test_rdataset.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset-wire.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset.Po
+ -rm -f libknot/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_wire.Po
+ -rm -f libknot/$(DEPDIR)/test_xdp_tcp.Po
+ -rm -f libknot/$(DEPDIR)/test_yparser.Po
+ -rm -f libknot/$(DEPDIR)/test_ypschema.Po
+ -rm -f libknot/$(DEPDIR)/test_yptrafo.Po
+ -rm -f libzscanner/$(DEPDIR)/processing.Po
+ -rm -f libzscanner/$(DEPDIR)/zscanner-tool.Po
+ -rm -f modules/$(DEPDIR)/test_onlinesign.Po
+ -rm -f modules/$(DEPDIR)/test_rrl.Po
+ -rm -f tap/$(DEPDIR)/basic.Plo
+ -rm -f tap/$(DEPDIR)/files.Plo
+ -rm -f tap/$(DEPDIR)/float.Plo
+ -rm -f tap/$(DEPDIR)/runtests.Po
+ -rm -f utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+ -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 am--depfiles 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..541667c
--- /dev/null
+++ b/tests/contrib/test_base32hex.c
@@ -0,0 +1,267 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 = knot_base32hex_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode: NULL input buffer");
+ ret = knot_base32hex_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode: NULL output buffer");
+ ret = knot_base32hex_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: input buffer too large");
+ ret = knot_base32hex_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: output buffer too small");
+
+ ret = knot_base32hex_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode_alloc: NULL input buffer");
+ ret = knot_base32hex_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode_alloc: input buffer too large");
+ ret = knot_base32hex_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode_alloc: NULL output buffer");
+
+ ret = knot_base32hex_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode: NULL input buffer");
+ ret = knot_base32hex_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode: NULL output buffer");
+ ret = knot_base32hex_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: input buffer too large");
+ ret = knot_base32hex_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: output buffer too small");
+
+ ret = knot_base32hex_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode_alloc: NULL input buffer");
+ ret = knot_base32hex_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode_aloc: input buffer too large");
+ ret = knot_base32hex_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_base32hex_decode((uint8_t *)"AAAAAA==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 2");
+ ret = knot_base32hex_decode((uint8_t *)"AAA=====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 5");
+ ret = knot_base32hex_decode((uint8_t *)"A=======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 7");
+ ret = knot_base32hex_decode((uint8_t *)"========", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 8");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA=A=", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 2");
+ ret = knot_base32hex_decode((uint8_t *)"AA=A====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 5");
+ ret = knot_base32hex_decode((uint8_t *)"=A======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 7");
+ ret = knot_base32hex_decode((uint8_t *)"CO======CO======", 16, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Two octects with padding");
+
+ // Bad data length
+ ret = knot_base32hex_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 1");
+ ret = knot_base32hex_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 2");
+ ret = knot_base32hex_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 3");
+ ret = knot_base32hex_decode((uint8_t *)"AAAA", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 4");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 5");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAA", 6, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 6");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA", 7, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 7");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAAAA", 9, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 9");
+
+ // Bad data character
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA$", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA ", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character space");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAA$A", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 7");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA$AA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 6");
+ ret = knot_base32hex_decode((uint8_t *)"AAAA$AAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 5");
+ ret = knot_base32hex_decode((uint8_t *)"AAA$AAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 4");
+ ret = knot_base32hex_decode((uint8_t *)"AA$AAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 3");
+ ret = knot_base32hex_decode((uint8_t *)"A$AAAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 2");
+ ret = knot_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..82eee7d
--- /dev/null
+++ b/tests/contrib/test_base64.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 = knot_base64_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode: NULL input buffer");
+ ret = knot_base64_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode: NULL output buffer");
+ ret = knot_base64_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode: input buffer too large");
+ ret = knot_base64_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode: output buffer too small");
+
+ ret = knot_base64_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode_alloc: NULL input buffer");
+ ret = knot_base64_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode_alloc: input buffer too large");
+ ret = knot_base64_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode_alloc: NULL output buffer");
+
+ ret = knot_base64_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode: NULL input buffer");
+ ret = knot_base64_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode: NULL output buffer");
+ ret = knot_base64_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode: input buffer too large");
+ ret = knot_base64_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode: output buffer too small");
+
+ ret = knot_base64_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode_alloc: NULL input buffer");
+ ret = knot_base64_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode_aloc: input buffer too large");
+ ret = knot_base64_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_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 = knot_base64_decode((uint8_t *)"A===", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 3");
+ ret = knot_base64_decode((uint8_t *)"====", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 4");
+ ret = knot_base64_decode((uint8_t *)"AA=A", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding character on position 2");
+ ret = knot_base64_decode((uint8_t *)"Zg==Zg==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Two quartets with padding");
+
+ // Bad data length
+ ret = knot_base64_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 1");
+ ret = knot_base64_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 2");
+ ret = knot_base64_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 3");
+ ret = knot_base64_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 5");
+
+ // Bad data character
+ ret = knot_base64_decode((uint8_t *)"AAA$", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character dollar");
+ ret = knot_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_base64url.c b/tests/contrib/test_base64url.c
new file mode 100644
index 0000000..710aa29
--- /dev/null
+++ b/tests/contrib/test_base64url.c
@@ -0,0 +1,252 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base64url.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(50);
+
+ 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 = knot_base64url_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode: NULL input buffer");
+ ret = knot_base64url_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode: NULL output buffer");
+ ret = knot_base64url_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: input buffer too large");
+ ret = knot_base64url_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: output buffer too small");
+
+ ret = knot_base64url_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode_alloc: NULL input buffer");
+ ret = knot_base64url_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode_alloc: input buffer too large");
+ ret = knot_base64url_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode_alloc: NULL output buffer");
+
+ ret = knot_base64url_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode: NULL input buffer");
+ ret = knot_base64url_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode: NULL output buffer");
+ ret = knot_base64url_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_decode: output buffer too small");
+
+ ret = knot_base64url_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode_alloc: NULL input buffer");
+ ret = knot_base64url_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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 = knot_base64url_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");
+ }
+
+ // 8. ENC (percent-encoded padding) -> DEC
+ strlcpy((char *)in, "Zm9vYmE%3D", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "fooba", BUF_LEN);
+ ref_len = strlen((char *)ref);
+
+ ret = knot_base64url_decode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "8. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "8. test vector - DEC output content");
+ }
+
+ strlcpy((char *)in, "Zm9vYmFyCg%3d%3d", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "foobar\n", BUF_LEN);
+ ref_len = strlen((char *)ref);
+
+ ret = knot_base64url_decode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "9. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "9. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = knot_base64url_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 3");
+ ret = knot_base64url_decode((uint8_t *)"%3D", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 4");
+
+ // Paddings not at the end
+ ret = knot_base64url_decode((uint8_t *)"AB%3DCDEFG", 10, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding 1");
+ ret = knot_base64url_decode((uint8_t *)"AB\0CDEFG", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding 2");
+
+ // Bad data character
+ ret = knot_base64url_decode((uint8_t *)"AAA$", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character dollar");
+ ret = knot_base64url_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_heap.c b/tests/contrib/test_heap.c
new file mode 100644
index 0000000..7dc5975
--- /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 <https://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_inet_ntop.c b/tests/contrib/test_inet_ntop.c
new file mode 100644
index 0000000..33e7b7e
--- /dev/null
+++ b/tests/contrib/test_inet_ntop.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "contrib/musl/inet_ntop.h"
+
+uint8_t bin[sizeof(struct in6_addr)];
+const socklen_t len = INET6_ADDRSTRLEN;
+char buf[INET6_ADDRSTRLEN];
+const char *txt;
+
+#define CHECK4(addr) \
+ ok(inet_pton(AF_INET, addr, bin) == 1, "inet_pton(%s)", addr); \
+ ok((txt = knot_inet_ntop(AF_INET, bin, buf, len)) != NULL, "knot_inet_ntop(%s)", addr); \
+ ok(strcmp(txt, addr) == 0, "match %s", addr);
+
+#define CHECK6(addr, ref) \
+ ok(inet_pton(AF_INET6, addr, bin) == 1, "inet_pton(%s)", addr); \
+ ok((txt = knot_inet_ntop(AF_INET6, bin, buf, len)) != NULL, "knot_inet_ntop(%s)", addr); \
+ ok(strcmp(txt, ref) == 0, "match %s %s", txt, ref);
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("IPv4 addresses");
+ CHECK4("0.0.0.0");
+ CHECK4("1.2.3.4");
+ CHECK4("11.12.13.14");
+ CHECK4("255.255.255.255");
+
+ diag("IPv6 addresses");
+ CHECK6("::0", "::");
+ CHECK6("::00", "::");
+ CHECK6("::000", "::");
+ CHECK6("::0000", "::");
+
+ CHECK6("::1", "::1");
+ CHECK6("::01", "::1");
+ CHECK6("::001", "::1");
+ CHECK6("::0001", "::1");
+
+ CHECK6("::10", "::10");
+ CHECK6("::100", "::100");
+ CHECK6("::1000", "::1000");
+
+ CHECK6("::1:0", "::1:0");
+ CHECK6("::1:0:0", "::1:0:0");
+ CHECK6("::1:0:0:0", "::1:0:0:0");
+ CHECK6("::1:0:0:0:0", "0:0:0:1::");
+ CHECK6("::1:0:0:0:0:0", "0:0:1::");
+ CHECK6("::1:0:0:0:0:0:0", "0:1::");
+ CHECK6("1:0:0:0:0:0:0:0", "1::");
+
+ // IPv4-Compatible IPv6 Addresses (not supported).
+ CHECK6("::0:1:1", "::1:1");
+ CHECK6("::0:1.2.3.4", "::102:304");
+
+ // IPv4-Mapped IPv6 Addresses.
+ CHECK6("::ffff:1:1", "::ffff:0.1.0.1");
+ CHECK6("::ffff:1.2.3.4", "::ffff:1.2.3.4");
+
+ CHECK6("1::1", "1::1");
+ CHECK6("1000::1", "1000::1");
+ CHECK6("1:20:300:4000:0005:006:07:8", "1:20:300:4000:5:6:7:8");
+
+ return 0;
+}
diff --git a/tests/contrib/test_net.c b/tests/contrib/test_net.c
new file mode 100644
index 0000000..c0061cd
--- /dev/null
+++ b/tests/contrib/test_net.c
@@ -0,0 +1,718 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 <unistd.h>
+
+#include "libknot/errcode.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+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_in *addr4 = (struct sockaddr_in *)&addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ return addr;
+}
+
+/*!
+ * \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);
+ int ret = getsockname(sock, (struct sockaddr *)&addr, &len);
+ is_int(0, ret, "check getsockname return");
+
+ 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_base_recv(sock, buffer, sizeof(buffer), addr, TIMEOUT);
+ if (in <= 0) {
+ return;
+ }
+
+ net_base_send(sock, buffer, in, 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, server_addr, source_addr, false);
+ 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);
+ }
+ is_int(out_len, r, "%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);
+ }
+ is_int(out_len, r, "%s, %s: client, receive message length", name, addr_name);
+ ok(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, &local_addr, 0, 0);
+ ok(server >= 0, "%s: server, create bound socket", name);
+
+ if (socktype_is_stream(type)) {
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "%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, &local, 0, 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, &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);
+ is_int(KNOT_ECONN, r, "UDP, send failure on unconnected socket");
+
+ r = net_dgram_recv(sock, buffer, buffer_len, TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "UDP, receive timeout on unconnected socket");
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ r = net_dgram_send(sock, buffer, buffer_len, &server_addr);
+ is_int(buffer_len, r, "UDP, send on defined address");
+
+ close(sock);
+
+ // TCP
+
+ sock = net_unbound_socket(SOCK_STREAM, &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);
+ is_int(expected, r, "TCP, send %s on unconnected socket", expected_msg);
+
+ r = net_stream_recv(sock, buffer, sizeof(buffer), expected_timeout);
+ is_int(expected, r, "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;
+
+ // listening, not accepting
+
+ addr = addr_local();
+ server = net_bound_socket(SOCK_STREAM, &addr, 0, 0);
+ ok(server >= 0, "server, create server");
+ addr = addr_from_socket(server);
+
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "server, start listening");
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "client, connect");
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ is_int(1, r, "client, successful write");
+
+ r = net_stream_recv(client, buffer, sizeof(buffer), TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "client, timeout on read");
+
+ close(client);
+
+ // listening, closed immediately
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "client, connect");
+
+ r = close(server);
+ is_int(0, r, "server, close socket");
+ usleep(50000);
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ is_int(KNOT_ECONN, r, "client, refused on write");
+
+ close(client);
+}
+
+struct dns_handler_ctx {
+ const uint8_t *expected;
+ int len;
+ bool raw;
+ bool success;
+};
+
+static bool _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);
+
+ }
+ return r == sizeof(buf);
+}
+
+static bool sync_signal(int remote)
+{
+ return _sync(remote, true);
+}
+
+static bool sync_wait(int remote)
+{
+ return _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, NULL);
+}
+
+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, &addr, 0, 0);
+ ok(server >= 0, "%s, server, create socket", t->name);
+
+ int r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "%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, &addr, NULL, false);
+ ok(client >= 0, "%s, client, create connected socket", t->name);
+
+ r = sync_wait(client);
+ ok(r, "%s, client, wait for stream read", t->name);
+ 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, &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, &addr, 0, 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);
+ is_int(0, r, "%s: bound, start listening", name);
+ }
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ client = net_connected_socket(type, &server_addr, NULL, false);
+ 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, &addr_server, 0, 0);
+ ok(server >= 0, "server, create socket");
+
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "server, start listening");
+
+ addr_server = addr_from_socket(server);
+
+ // create client
+
+ int client = net_connected_socket(SOCK_STREAM, &addr_server, NULL, false);
+ ok(client >= 0, "client, create connected socket");
+
+ struct sockaddr_storage addr_client = addr_from_socket(client);
+
+ // accept connection
+
+ r = poll_read(server);
+ is_int(1, r, "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(&addr_client, &addr_accepted, false) == 0,
+ "accepted, correct address");
+
+ close(client);
+
+ // client reconnect
+
+ close(client);
+ client = net_connected_socket(SOCK_STREAM, &addr_server, NULL, false);
+ ok(client >= 0, "client, reconnect");
+
+ r = poll_read(server);
+ is_int(1, r, "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, &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, &addr, NET_BIND_MULTIPLE, 0);
+ 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, &addr_one, NET_BIND_MULTIPLE, 0);
+ 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(&addr_one, &addr_two, false) == 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..f3d4c6e
--- /dev/null
+++ b/tests/contrib/test_net_shortwrite.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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, &addr, 0, 0);
+ ok(server >= 0, "server: bind socket");
+
+ r = listen(server, 1);
+ ok(r == 0, "server: start listening");
+
+ struct sockaddr *sa = (struct sockaddr *)&addr;
+ socklen_t salen = sockaddr_len(&addr);
+ r = getsockname(server, sa, &salen);
+ ok(r == 0, "server: get bound address");
+
+ // create TCP client
+
+ int client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ 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, NULL);
+ 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-cow.c b/tests/contrib/test_qp-cow.c
new file mode 100644
index 0000000..4cd8c6c
--- /dev/null
+++ b/tests/contrib/test_qp-cow.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ Copyright (C) 2018 Tony Finch <dot@dotat.at>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include <unistd.h>
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/string.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "tap/basic.h"
+
+/* Constants. */
+#define MAX_KEYLEN 64
+#define MAX_LEAVES 12345
+#define MAX_MUTATIONS 123
+#define MAX_TRANSACTIONS 1234
+
+enum cowstate {
+ cow_absent, // not in trie
+ cow_unmarked,
+ cow_shared,
+ cow_old, // deleted from new trie
+ cow_new, // added to new trie
+ deadbeef,
+};
+
+struct cowleaf {
+ char *key;
+ size_t len;
+ int cowstate;
+};
+
+static inline size_t
+prng(size_t max) {
+ /* good enough these days */
+ return (size_t)rand() % max;
+}
+
+static struct cowleaf *
+grow_leaves(size_t maxlen, size_t leaves)
+{
+ struct cowleaf *leaf = bcalloc(leaves, sizeof(*leaf));
+
+ trie_t *trie = trie_create(NULL);
+ if (!trie) sysbail("trie_create");
+
+ for (size_t i = 0; i < leaves; i++) {
+ trie_val_t *valp;
+ char *str = NULL;
+ size_t len = 0;
+ do {
+ free(str);
+ len = prng(maxlen);
+ str = bmalloc(len + 1);
+ for (size_t j = 0; j < len; j++)
+ str[j] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ [prng(62)];
+ str[len] = '\0';
+ valp = trie_get_ins(trie, (uint8_t *)str, (uint32_t)len);
+ if (!valp) bail("trie_get_ins");
+ } while (*valp != NULL);
+ *valp = &leaf[i];
+ leaf[i].key = str;
+ leaf[i].len = len;
+ leaf[i].cowstate = cow_absent;
+ }
+ trie_free(trie);
+
+ return (leaf);
+}
+
+static void
+dead_leaves(struct cowleaf *leaf, size_t leaves)
+{
+ for (size_t i = 0; i < leaves; i++)
+ free(leaf[i].key);
+ free(leaf);
+}
+
+static void
+mark_cb(trie_val_t val, const uint8_t *key, size_t len, void *d)
+{
+ struct cowleaf *leaf = val;
+ assert(leaf->cowstate == cow_unmarked &&
+ "leaf should go from unmarked to shared exactly once");
+ leaf->cowstate = cow_shared;
+ (void)key;
+ (void)len;
+ (void)d;
+}
+
+static void
+commit_rollback(trie_val_t val, const uint8_t *key, size_t len, void *d)
+{
+ struct cowleaf *leaf = val;
+ int *commit = d;
+ if (*commit)
+ assert((leaf->cowstate == cow_shared ||
+ leaf->cowstate == cow_old) &&
+ "committing deletes from old trie");
+ else
+ assert((leaf->cowstate == cow_shared ||
+ leaf->cowstate == cow_new) &&
+ "roll back deletes from new trie");
+ if (leaf->cowstate != cow_shared)
+ leaf->cowstate = deadbeef;
+ (void)key;
+ (void)len;
+}
+
+static void
+del_cow(trie_cow_t *x, struct cowleaf *leaf)
+{
+ _unused_ trie_val_t val;
+ assert(KNOT_EOK == trie_del_cow(x,
+ (uint8_t *)leaf->key,
+ (uint32_t)leaf->len,
+ &val));
+ assert(val == leaf);
+}
+
+static void
+usage(void) {
+ fprintf(stderr,
+ "usage: test_qp-cow [-k N] [-l N] [-t N]\n"
+ " -k N maximum key length (default %d)\n"
+ " -l N number of leaves (default %d)\n"
+ " -m N mutations per transaction (default %d)\n"
+ " -t N number of transactions (default %d)\n",
+ MAX_KEYLEN,
+ MAX_LEAVES,
+ MAX_MUTATIONS,
+ MAX_TRANSACTIONS);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ size_t keylen = MAX_KEYLEN;
+ size_t leaves = MAX_LEAVES;
+ int mutations = MAX_MUTATIONS;
+ int transactions = MAX_TRANSACTIONS;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "k:l:m:t:h")) != -1)
+ switch (opt) {
+ case('k'):
+ keylen = (unsigned)atoi(optarg);
+ continue;
+ case('l'):
+ leaves = (unsigned)atoi(optarg);
+ continue;
+ case('m'):
+ mutations = atoi(optarg);
+ continue;
+ case('t'):
+ transactions = atoi(optarg);
+ continue;
+ default:
+ usage();
+ }
+
+ if (argc != optind)
+ usage();
+
+ plan(transactions);
+
+ struct cowleaf *leaf = grow_leaves(keylen, leaves);
+ trie_t *t = trie_create(NULL);
+
+ for (int round = 0; round < transactions; round++) {
+ trie_cow_t *x = trie_cow(t, mark_cb, NULL);
+ if (!x) sysbail("trie_cow");
+
+ int hits = prng(mutations);
+ for (int hit = 0; hit < hits; hit++) {
+ size_t i = prng(leaves);
+ switch (leaf[i].cowstate) {
+ case(cow_absent): {
+ trie_val_t *val =
+ trie_get_cow(x,
+ (uint8_t *)leaf[i].key,
+ (uint32_t)leaf[i].len);
+ if (!val) sysbail("trie_get_cow");
+ assert(*val == NULL && "new leaf");
+ *val = &leaf[i];
+ leaf[i].cowstate = cow_new;
+ } break;
+ case(cow_unmarked): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_shared &&
+ "state changed unmarked -> shared");
+ leaf[i].cowstate = cow_old;
+ } break;
+ case(cow_shared): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_shared &&
+ "state remained shared");
+ leaf[i].cowstate = cow_old;
+ } break;
+ case(cow_new): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_new &&
+ "state remained new");
+ leaf[i].cowstate = cow_absent;
+ } break;
+ case(cow_old): {
+ // don't want to mess with old tree
+ } break;
+ case(deadbeef): {
+ assert(!"deadbeef should not be possible");
+ } break;
+ default:
+ assert(!"bug - unhandled state");
+ }
+ }
+
+ int commit = !prng(2);
+ if (commit)
+ t = trie_cow_commit(x, commit_rollback, &commit);
+ else
+ t = trie_cow_rollback(x, commit_rollback, &commit);
+
+ trie_it_t *it = trie_it_begin(t);
+ while (!trie_it_finished(it)) {
+ trie_val_t *val = trie_it_val(it);
+ assert(val != NULL);
+ struct cowleaf *l = *val;
+ if (commit)
+ assert((l->cowstate == cow_unmarked ||
+ l->cowstate == cow_shared ||
+ l->cowstate == cow_new) &&
+ "committing expected state");
+ else
+ assert((l->cowstate == cow_unmarked ||
+ l->cowstate == cow_shared ||
+ l->cowstate == cow_old) &&
+ "roll back expected state");
+ l->cowstate = cow_unmarked;
+ trie_it_next(it);
+ }
+ trie_it_free(it);
+
+ for (size_t i = 0; i < leaves; i++) {
+ assert((leaf[i].cowstate == cow_unmarked ||
+ leaf[i].cowstate == cow_absent ||
+ leaf[i].cowstate == deadbeef) &&
+ "cleanup leaves either unmarked or dead");
+ if (leaf[i].cowstate == deadbeef)
+ leaf[i].cowstate = cow_absent;
+ }
+ ok(1, "transaction done");
+ }
+
+ trie_free(t);
+ dead_leaves(leaf, leaves);
+
+ return 0;
+}
diff --git a/tests/contrib/test_qp-trie.c b/tests/contrib/test_qp-trie.c
new file mode 100644
index 0000000..a50661f
--- /dev/null
+++ b/tests/contrib/test_qp-trie.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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/dname.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
+
+/* 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;
+}
+
+/* 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, (uint8_t *)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, (uint8_t *)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, (uint8_t *)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, (uint8_t *)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;
+
+}
+
+static void test_wildcards(void)
+{
+ /* Test zone. */
+ const char *names[] = {
+ "*",
+ "example.cz",
+ "*.example.cz",
+ "+.example.cz",
+
+ "*.exampld.cz",
+ "www.exampld.cz",
+ };
+ /* Query-answer pairs for wildcard search. */
+ const char *qa_pairs[][2] = {
+ { ".", NULL },
+ { "*", "*" },
+ { "bar", "*" },
+ { "foo.test.", "*" },
+ { "example.cz", "example.cz" },
+ { "*.example.cz", "*.example.cz" },
+ { "a.example.cz", "*.example.cz" },
+ { "ab.cd.example.cz", "*.example.cz" },
+ { "a+.example.cz", "*.example.cz" },
+ { "+.example.cz", "+.example.cz" },
+ { "exampld.cz", NULL },
+ { ":.exampld.cz", "*.exampld.cz" },
+ { "ww.exampld.cz", "*.exampld.cz" },
+ };
+
+ trie_t *trie = trie_create(NULL);
+ if (!trie) ok(false, "trie: create");
+
+ /* Insert the whole zone. */
+ for (int i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
+ knot_dname_storage_t dname_st, lf_st;
+ const knot_dname_t
+ *dname = knot_dname_from_str(dname_st, names[i], sizeof(dname_st)),
+ *lf = knot_dname_lf(dname, lf_st);
+ if (!dname || !lf) {
+ ok(false, "trie: converting '%s'", names[i]);
+ return;
+ }
+
+ trie_val_t *val = trie_get_ins(trie, lf + 1 , lf[0]);
+ if (!val || *val != NULL) {
+ ok(false, "trie: inserting '%s' (as dname_lf)", names[i]);
+ return;
+ }
+ *val = (void *)names[i];
+ }
+
+ /* Perform each test query. */
+ for (int i = 0; i < sizeof(qa_pairs) / sizeof(qa_pairs[0]); ++i) {
+ knot_dname_storage_t q_dname_st, q_lf_st;
+ const knot_dname_t *q_dname =
+ knot_dname_from_str(q_dname_st, qa_pairs[i][0], sizeof(q_dname_st));
+ const knot_dname_t *q_lf = knot_dname_lf(q_dname, q_lf_st);
+ if (!q_dname || !q_lf) {
+ ok(false, "trie: converting '%s'", qa_pairs[i][0]);
+ return;
+ }
+
+ const char **ans = (const char **)trie_get_try_wildcard(trie, q_lf + 1, q_lf[0]);
+ bool is_ok = !!ans == !!qa_pairs[i][1] && (!ans || !strcmp(*ans, qa_pairs[i][1]));
+ if (!is_ok) {
+ ok(false, "trie: wildcard test for '%s' -> '%s'",
+ qa_pairs[i][0], ans ? *ans : "<null>");
+ return;
+ }
+ }
+
+ trie_free(trie);
+ ok(true, "trie: wildcard searches");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Random keys. */
+ srand(time(NULL));
+ unsigned key_count = 100000;
+ char **keys = malloc(sizeof(char*) * key_count);
+ /* key must have at least one char and a nul terminator
+ so that the before/after checks have a char to modify */
+ for (unsigned i = 0; i < key_count; ++i) {
+ keys[i] = str_key_rand(rand() % (KEY_MAXLEN - 2) + 2);
+ }
+
+ /* 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, (uint8_t *)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, (uint8_t *)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 = (const char *)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);
+
+ /* Test trie_get_try_wildcard(). */
+ test_wildcards();
+
+ return 0;
+}
diff --git a/tests/contrib/test_siphash.c b/tests/contrib/test_siphash.c
new file mode 100644
index 0000000..1ca019b
--- /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 <https://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..7a5cf54
--- /dev/null
+++ b/tests/contrib/test_sockaddr.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+
+static void test_sockaddr_is_any(void)
+{
+ struct sockaddr_storage invalid = { 0 };
+ ok(!sockaddr_is_any(&invalid), "sockaddr_is_any: invalid");
+
+ struct sockaddr_storage path = { 0 };
+ path.ss_family = AF_UNIX;
+ ok(!sockaddr_is_any(&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(&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(&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(&ipv6_local), "sockaddr_is_any: IPv6 local");
+
+ struct sockaddr_storage ipv6_any = { 0 };
+ sockaddr_set(&ipv6_any, AF_INET6, "::", 0);
+ ok(sockaddr_is_any(&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(&ref4, &ref6, 32);
+ ok(ret == false, "match: family mismatch");
+
+ ret = sockaddr_net_match(NULL, &ref4, 32);
+ ok(ret == false, "match: NULL first parameter");
+ ret = sockaddr_net_match(&ref4, NULL, 32);
+ ok(ret == false, "match: NULL second parameter");
+
+ ret = sockaddr_net_match(&ref4, &ref4, -1);
+ ok(ret == true, "match: ipv4 - identity, auto full prefix");
+ ret = sockaddr_net_match(&ref4, &ref4, 31);
+ ok(ret == true, "match: ipv4 - identity, subnet");
+ ret = sockaddr_net_match(&ref4, &ref4, 32);
+ ok(ret == true, "match: ipv4 - identity, full prefix");
+ ret = sockaddr_net_match(&ref4, &ref4, 33);
+ ok(ret == true, "match: ipv4 - identity, prefix overflow");
+
+ ret = sockaddr_net_match(&ref6, &ref6, -1);
+ ok(ret == true, "match: ipv6 - identity, auto full prefix");
+ ret = sockaddr_net_match(&ref6, &ref6, 127);
+ ok(ret == true, "match: ipv6 - identity, subnet");
+ ret = sockaddr_net_match(&ref6, &ref6, 128);
+ ok(ret == true, "match: ipv6 - identity, full prefix");
+ ret = sockaddr_net_match(&ref6, &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(&t, &ref4, 5);
+ ok(ret == true, "match: ipv4 - first byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref4, 6);
+ ok(ret == true, "match: ipv4 - first byte, precise prefix");
+ ret = sockaddr_net_match(&t, &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(&t, &ref4, 29);
+ ok(ret == true, "match: ipv4 - last byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref4, 30);
+ ok(ret == true, "match: ipv4 - last byte, precise prefix");
+ ret = sockaddr_net_match(&t, &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(&t, &ref6, 5);
+ ok(ret == true, "match: ipv6 - first byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref6, 6);
+ ok(ret == true, "match: ipv6 - first byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref6, 7);
+ ok(ret == false, "match: ipv6 - first byte, not match");
+
+ check_sockaddr_set(&t, AF_INET6, "7FAA::AA7C", 0);
+ ret = sockaddr_net_match(&t, &ref6, 125);
+ ok(ret == true, "match: ipv6 - last byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref6, 126);
+ ok(ret == true, "match: ipv6 - last byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref6, 127);
+ ok(ret == false, "match: ipv6 - last byte, not match");
+
+ // UNIX socket path tests
+
+ struct sockaddr_storage ref_un = { 0 };
+ check_sockaddr_set(&ref_un, AF_UNIX, "/tmp/knot.listen", 0);
+
+ check_sockaddr_set(&t, AF_UNIX, "/tmp/knot.listen", 0);
+ ret = sockaddr_net_match(&t, &ref_un, 0);
+ ok(ret == true, "match: UNIX, match");
+
+ check_sockaddr_set(&t, AF_UNIX, "/tmp/knot.liste", 0);
+ ret = sockaddr_net_match(&t, &ref_un, 0);
+ ok(ret == false, "match: UNIX, shorter, not match");
+
+ check_sockaddr_set(&t, AF_UNIX, "/tmp/knot.listen.", 0);
+ ret = sockaddr_net_match(&t, &ref_un, 0);
+ ok(ret == false, "match: UNIX, longer, not match");
+
+ check_sockaddr_set(&t, AF_UNIX, "1234567890123456789012345678901234567890", 0);
+ ret = sockaddr_net_match(&t, &ref_un, 0);
+ ok(ret == false, "match: UNIX, longer than max for sockaddr_t, 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(&t, &min, &max);
+ ok(ret == true, "match: ipv4 max range - minimum");
+ check_sockaddr_set(&t, AF_INET, "255.255.255.255", 0);
+ ret = sockaddr_range_match(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - minimum");
+ check_sockaddr_set(&t, AF_INET, "1.13.213.213", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - middle");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.224", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - max");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.225", 0);
+ ret = sockaddr_range_match(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &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(&t, &min, &max);
+ ok(ret == true, "match: ipv6 middle range - middle");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:224", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 middle range - max");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:225", 0);
+ ret = sockaddr_range_match(&t, &min, &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(&t, &min, &max);
+ ok(ret == false, "match: ipv6 middle range - negative far max");
+
+ // UNIX socket path tests
+
+ check_sockaddr_set(&t, AF_UNIX, "/tmp/knot.listen", 0);
+ ret = sockaddr_range_match(&t, &t, &t);
+ ok(ret == false, "match: range not supported for UNIX");
+}
+
+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_spinlock.c b/tests/contrib/test_spinlock.c
new file mode 100644
index 0000000..0d5122b
--- /dev/null
+++ b/tests/contrib/test_spinlock.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <signal.h>
+#include <tap/basic.h>
+
+#include "knot/server/dthreads.h"
+#include "contrib/spinlock.h"
+
+#define THREADS 8
+#define CYCLES 100000
+
+static volatile int counter = 0;
+static volatile int tens_counter = 0;
+static knot_spin_t spinlock;
+
+static int thread(struct dthread *thread)
+{
+ volatile int i, j, k;
+
+ for (i = 0; i < CYCLES; i++) {
+ knot_spin_lock(&spinlock);
+ j = counter;
+ k = tens_counter;
+ if (++j % 10 == 0) {
+ k++;
+ }
+ tens_counter = k;
+ counter = j;
+ knot_spin_unlock(&spinlock);
+ }
+
+ return 0;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // 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
+
+ knot_spin_init(&spinlock);
+
+ dt_unit_t *unit = dt_create(THREADS, thread, NULL, NULL);
+ dt_start(unit);
+ dt_join(unit);
+ dt_delete(&unit);
+
+ knot_spin_destroy(&spinlock);
+
+ is_int(THREADS * CYCLES, counter, "spinlock: protected counter one");
+ is_int(THREADS * CYCLES / 10, tens_counter, "spinlock: protected counter two");
+
+ return 0;
+}
diff --git a/tests/contrib/test_string.c b/tests/contrib/test_string.c
new file mode 100644
index 0000000..681dd61
--- /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 <https://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..e883575
--- /dev/null
+++ b/tests/contrib/test_strtonum.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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"
+#include "libknot/attribute.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);
+}
+
+#define asprintf(args, ...) do { \
+ _unused_ int r = (asprintf)(args, ##__VA_ARGS__); assert(r >= 0); \
+} 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..84518f2
--- /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 <https://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 infinity");
+
+ ret = knot_time_cmp(inf, a);
+ ok(ret == 1, "compare infinity 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 infinity");
+
+ 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 infinity diff");
+
+ d = knot_time_diff(inf, a);
+ ok(d == KNOT_TIMEDIFF_MAX, "positive infinity diff");
+
+ d = knot_time_diff(a, inf);
+ ok(d == KNOT_TIMEDIFF_MIN, "negative infinity 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_toeplitz.c b/tests/contrib/test_toeplitz.c
new file mode 100644
index 0000000..244137c
--- /dev/null
+++ b/tests/contrib/test_toeplitz.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "contrib/toeplitz.h"
+#include "contrib/wire_ctx.h"
+
+// Test vectors come from Intel Ethernet Controller X710/XXV710/XL710 Series Datasheet
+const uint8_t key[] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+ 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+ 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+ 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+ 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+void toeplitz_check(int family, const char *src_ip, const char *dst_ip,
+ uint16_t src_port, uint16_t dst_port, uint32_t expected)
+{
+ uint8_t data[2 * sizeof(struct in6_addr) + 2 * sizeof(uint16_t)];
+
+ wire_ctx_t ctx = wire_ctx_init(data, sizeof(data));
+
+ struct in_addr src_addr4, dst_addr4;
+ struct in6_addr src_addr6, dst_addr6;
+
+ if (family == AF_INET &&
+ inet_pton(AF_INET, src_ip, &src_addr4) == 1 &&
+ inet_pton(AF_INET, dst_ip, &dst_addr4) == 1) {
+ wire_ctx_write(&ctx, (uint8_t *)&(src_addr4.s_addr), sizeof(struct in_addr));
+ wire_ctx_write(&ctx, (uint8_t *)&(dst_addr4.s_addr), sizeof(struct in_addr));
+ } else if (family == AF_INET6 &&
+ inet_pton(AF_INET6, src_ip, &src_addr6) == 1 &&
+ inet_pton(AF_INET6, dst_ip, &dst_addr6) == 1) {
+ wire_ctx_write(&ctx, (uint8_t *)&(src_addr6.s6_addr), sizeof(struct in6_addr));
+ wire_ctx_write(&ctx, (uint8_t *)&(dst_addr6.s6_addr), sizeof(struct in6_addr));
+ } else {
+ assert(0);
+ }
+
+ wire_ctx_write_u16(&ctx, src_port);
+ wire_ctx_write_u16(&ctx, dst_port);
+
+ if (ctx.error != KNOT_EOK) {
+ assert(0);
+ }
+
+ uint32_t value = toeplitz_hash(key, sizeof(key), data, wire_ctx_offset(&ctx));
+ is_int(expected, value, "toeplitz_hash: %u", expected);
+
+ toeplitz_ctx_t toepl;
+ for (int i = 0; i <= wire_ctx_offset(&ctx); i++) {
+ toeplitz_init(&toepl, i, key, sizeof(key), data, wire_ctx_offset(&ctx));
+ value = toeplitz_finish(&toepl);
+ is_int(expected, value, "toeplitz_init to %i: %u", i, expected);
+ }
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ toeplitz_check(AF_INET, "66.9.149.187", "161.142.100.80", 2794, 1766, 0x51ccc178);
+ toeplitz_check(AF_INET, "199.92.111.2", "65.69.140.83", 14230, 4739, 0xc626b0ea);
+ toeplitz_check(AF_INET, "24.19.198.95", "12.22.207.184", 12898, 38024, 0x5c2b394a);
+ toeplitz_check(AF_INET, "38.27.205.30", "209.142.163.6", 48228, 2217, 0xafc7327f);
+ toeplitz_check(AF_INET, "153.39.163.191", "202.188.127.2", 44251, 1303, 0x10e828a2);
+
+ toeplitz_check(AF_INET6, "3ffe:2501:200:1fff::7", "3ffe:2501:200:3::1", 2794, 1766, 0x40207d3d);
+ toeplitz_check(AF_INET6, "3ffe:501:8::260:97ff:fe40:efab", "ff02::1", 14230, 4739, 0xdde51bbf);
+ toeplitz_check(AF_INET6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf", "fe80::200:f8ff:fe21:67cf", 44251, 38024, 0x02d1feef);
+
+ return 0;
+}
diff --git a/tests/contrib/test_wire_ctx.c b/tests/contrib/test_wire_ctx.c
new file mode 100644
index 0000000..81386c9
--- /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 <https://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..6ce5610
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.cds
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ dEDk41MHSAAoc2eboWOXxGQHYFj1gXuD/gfX
+ Qz6HEq44narP0IHuOWt4ni9HUhYDBuanPp7S
+ j/8nYnZc6gdpMg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ 1HFpOHudUJp7hvrsTmdX6qt+X0I4K9RYo/Uy
+ gpWbJBNhNsPVENVrw8AabhnPaETJGbreS/4T
+ slgbxM1Ks/erzA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ EA9rtC9Ub4LPDwS6Q8wE4g9nGddbVrg9ivHN
+ oHQzUjTFlxtn8gFPaJkUfHwqwg3PsSVGagyx
+ Bjsool21k/TG7A== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144147 25752 example.com.
+ YLQPkC55O9bpQI/Hg/Ih91UkieeM3wtQvJMT
+ ro3QJ2eDImSyeoIbWsF+ghtoQ+6IUulXLu3k
+ PtDViOe2tfaL/Q== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ 1J1lDp/FQFgAGv7EFeDTAru7rUIcUCc7bkYj
+ 8OlczfdQjo9IfS5MFg6MqIrE/KPC18CDX1Ki
+ DzaCFaMGDlavjQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 25752
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ hRcbHnvrTqCb215+XsIn96tvHacV5d15lcnS
+ h91pg8Htes3H0vOoG98C5oWXoj7RM4V/tDoH
+ /0ahiLyRzRnvBA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 20197
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ JLKC5uLW1+JPkOyVcc8D6B6lCC/0FOlak/Qd
+ Na6Nb33hi9io1HMFI1eYiG7u7lxWmXsKnBo9
+ ONROz+WYGds++Q== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ pgi1+O/TWU6WCmLLYEibCYj+RzbcOuodnF1i
+ wlBQxDZLTcGYG+1KEC0spZTN1nQncEfdeEKc
+ jnYQUa0izPQRnA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ MaFyQcB908WIXS+RiLeLXiKdjOo/R6tl9AM/
+ 6xokhcvRqQzuyQeoH4snUvcht0m5ghz09Km7
+ MPN0uzJcXIGg0Q== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ Vdo7aYGIByxiC85dyqLKrrNAYYDFBnKXm8uE
+ rYSXBMWiQoFHwzvlavyqhUWlEABfvYD0pUrX
+ PZ27Hz8rPFCSLQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ 9Llt7e4nm8uMLqliT2NZJINmAmLmKDYqjloj
+ Q3/wNI4K+J0RUmWpg3f6xODVkKjjuVnwpxkK
+ eWV9zqY4jUTAGg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ lZSHyLdXGFvoL9fhk26y70ifFwui2A5bpdir
+ Su7VhfsnNdLgNuCceRXbYwxQaUyODCl7dcJ9
+ UkRzq2eDs0evKQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144147 25752 example.com.
+ dDE1XApt4lZ9u20Z/vXwhJxE27AZJQzKwLkk
+ jpwEDVJo6/SdV2smB7s7+qmGnSKhIehVpUFX
+ wv3/3YaFxSTifQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.both b/tests/knot/semantic_check_data/cdnskey.delete.both
new file mode 100644
index 0000000..b3b840b
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.both
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ uHjgn9WEMdw/d//q2ZhGF1GAQItK9UPyByET
+ VDuZgER/JBHuFd1/MMEkkFmCRneXuVudSnki
+ aXiza0GLV0ujfw== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ 39YAhtx1qe9sbJ/6N1fS7F4QLS9iqagdbQN4
+ w6VRyMRrseRY16G2n3Th9yw1+R9aXOazb6iP
+ BL6azQJiUCZJ5g== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ EXv3vV7Njpz59INdubRpDsGANROKfEhqBzQ8
+ zSL1vujpUOdaZWqmS3uoKusxHCghJacCFeUA
+ KQNrWNuZHT2S8g== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144234 36859 example.com.
+ LgXpsIgBZBO03iU6D2nqsbmal6AK51ev21Cj
+ PQFfFBLQ+ARqyE3k7mlTK4A+/UfIpWgpkKnz
+ St4SbtL3r6GK+g== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ l/Uak3BSxeoEO8n42GtZkS1aTdEV590rAuwS
+ Jvt8Gzyj1S5Aqx5Tytm+nb93ZtO3eSL2OpJg
+ p7tdmPjtHKxYpg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 36859
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ jNkK9sXUo8jTJ2snaD+3Mao2q0m5UjyZ7ykD
+ 6yQqTJ2xgldvTCyuu/YlSCoR9gli8pOGz+KT
+ 3YA9HjG46ob8ug==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 65430
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ id6EVGBrg2vZm6vIIGNhSukuI2Uv6/MzZiJk
+ C1N9k5P3zAP6Es9aLp9m4cR8qGIdUu3DZ3AU
+ ngKndEZvk5YUUg== )
+ 3600 CDS 0 0 0 (
+ 00 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ mDmiCviPRxQ1BiinR2+/lQ/KabHgIu/LSKZ2
+ yZFsgiF8YF4IT8mJc/qiKVtaCWLK4Sszxk/F
+ P8kMTmTKORT40Q== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ O1KH8u+VPLnd5TwGPRbv7VpMss+Mjwr+nIOE
+ UxSS7unksPUldU0e9qXby0fydlN5LTf/L0sD
+ daMwGOA2fuD/dA== )
+ 3600 CDNSKEY 0 3 0 (
+ AA==
+ ) ; ZSK; alg = 0 ; key id = 768
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ Hj8WJNT51BdqA6szAI7sn8gZftHY6/1/Y7qQ
+ DRsunh1J1cNRuqHtLBnRKpVdteZ4znNKnavb
+ uoC6kzSzbRiJzQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ 7YGVqSgaiHXwY+GdMkUJXZyqkGvkfA8LliB6
+ 6Nn4AvuETs4lX080MNq3dWmjI/tHSg5ptQz7
+ Hukvd6cYWNgtBQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ SVatJA8FhwAotw625XttyhgD8Rcp4ukcidii
+ By06YX9e5rCgOHOvjsHwA57kBBzcZg0ZXAbF
+ SOhDdUQibKaRSg== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144234 36859 example.com.
+ D+r82Tvm8eGuYrJKVCUMw1Gz+tevXwE2IGoG
+ 7pXErKbDv13p/eFAPsRdUKtdmsOq4mHSxQuZ
+ GVGAULfJjcs3pQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey
new file mode 100644
index 0000000..366edaf
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ wXvCukXPMbON0oD2nKINzyauQRgeYE/kIYKZ
+ pYaMwV5Z6yZ9SKSSy7oRBn7t1+rOmGI69NSx
+ 3WHXaRiLjcH1Sg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ XNdl4tiEhUPOpEgwGO2njssc8QMB8IeP5QDM
+ 9/LZJUPZ0hZ76F7fX9C3X3edgysEoDFR1HAE
+ JdTxkJ5Oqv7Xig== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ Or2a9ZLl2FnBmNM1KbUcgAjgLKRS6O9H4XmK
+ VAGM3QxutaTZuF1sjsz+kNh6yrT38eLm5B8M
+ PLCxUmkTSUmgeA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144623 39533 example.com.
+ 5SBXb1HpSfhPinO3hadK7E0lhRHwyUAsjZpy
+ /7jTO7/uUNXD6asY9V6kvOJmRgMpSeXFJKFw
+ +Vsyx0jifistyg== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ TQSEqjdF8egQ1YjZPdVXrX+pngPHTdCgwJFR
+ AefWVHOLsMADS3/LL5G+pZTSldB3j3Xo4Na/
+ 1tsuCgNmV+58xA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39533
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ VARBBNSEYzAbBYxgdQi/epYgWFaGnL49509p
+ CeZWg4LO4jhjVT7uyhsSQny2wyahP2Y37YeO
+ d+sY503BNpqzMQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 59324
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ YRhAwruTjWmu6drb4+iJ/QOwQg8dnGur8LH7
+ bsn1ZCHQYNDHiIai8JqikqzkhEYKIK8HIqT8
+ F2RY/LqFxKebjg== )
+ 3600 CDS 0 0 0 (
+ 00 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ cHTGBug23nTe/aS09JaakuG4wa9EEbWxL3gu
+ LQpCK8HV/JMsNSGqh1FsUlX92y4tSIvJn+Lx
+ vvdN+Qzh+zASHg== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ GU9Q/CipUscofDL6uhT2ZmhQoyApLX9zbyfN
+ dG5XW6sXYaB94hVSiT2DSyt19fyQwYoKK2Br
+ fJwy4pI890kKoQ== )
+ 3600 CDNSKEY 0 3 0 (
+ BA==
+ ) ; ZSK; alg = 0 ; key id = 1792
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ CXeUfFxa7aT2tivKLovVQ2CA0HYZxxlUrbm1
+ voABTNkU7lb5W9Z7GQ/VDugd8QeKNK8YWOaQ
+ Tdl79jkL1rQKXw== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ sd+fzJmLLIoFIcbKCJ+rHE+tOs0PwHjjY9ml
+ Dsbel1k5sANI4xR8iMv6YAEhcpvb0S+8Nd7h
+ 7BT45SkKVtyFsQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ VGa9LkgVATBLHOwMBNc6g74iXCCSXnWWNs8O
+ ndoXk4ZMMRRkmaxSWXH2pBdJLZPL5f26aEVl
+ 4toVcsE722LoFA== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144623 39533 example.com.
+ i+94RvIQBBEOza7Y963huNEWYrqt/VT/eE1E
+ Gqx5kngvZgZ7wO8tcOsaE7ctb69SvgZwRR9c
+ RBgb2N6ezo9OxA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds
new file mode 100644
index 0000000..9d63eb9
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ 1CRyeUic9BIwBWcjk95VQJktQng6f3dLQm64
+ JwGGqivUM3Hgp7URguNIx0BsCvfo67NIpk7N
+ mMIFwMkMGOHmgg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ pB4+Z3ltuzY+/NkAeCb9LOS7Zlh7QLfHKimR
+ JPtvdOuIhd8vB0NZLzcYX0lIkrqyP3LadbrS
+ u8r9BMIlu4cKpg== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ x8XhP7r3/glI7AenoSLVmfqhZXQfj6YllgxA
+ jkVxExiM9OJZOPdyeDTuRyUD1PFiBOEsP7Wu
+ vNgWA9eyQFOslA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144646 56106 example.com.
+ TCn7V7sHR2TNY5ywyEpbYZMegZwTX+I/TPeO
+ 76D3WORu9pN0kJWjGPAebwTvL/a7p8xS8B9U
+ X9ivUVFORG+mJA== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ cOjtacSzGkoh6bO4clqYPM2y+g5ezQUtCNdx
+ iRqickHCvQnL9OM/h7V8txqEsSulG5ZCeW+O
+ LDhDQDUchpNv7A==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 56106
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ pB2mCNXFJ8e+UaMeMmy1LSCv6TJ92Fs3kFxY
+ I8NyZPyGvfePpMlzWZr7Bw7wS6G6Jhayhj94
+ MMJ4lM/5+ZzVJw==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 45911
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ uOAPEzDkPNI9Uo2N+iiRkIb2p1Y0VhgqwUom
+ +Dssd6X0CEdQEmD8YQ43Cuq9ZNwk8Bm+lgm3
+ X+ImdIKeE4MvNQ== )
+ 3600 CDS 0 0 0 (
+ 01 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ IN5tLpm7OKjIL4VpucR1ero1Gv5UEyVqjzB9
+ rRJefwUtlZFKNaTbU0oQD33vQXEjUiIMr66b
+ zIC3Ju/YtYFDLg== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ f8VJa9GRwSWNmg0AR4nA3OD4X8im7BriZjME
+ 2ypYUOJkdIafolyb0LDz7XWTaVsFHQWO0z+J
+ 14g0CgCroTm3pQ== )
+ 3600 CDNSKEY 0 3 0 (
+ AA==
+ ) ; ZSK; alg = 0 ; key id = 768
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ 89oeIQuH82i2RYIj/fnX/71s8kspDHcI8lIa
+ R02OZZ9bF37bi6LbGkypdXpmxN9/rEjk4ThF
+ IHRX2USEPtl+wQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ Hgf4SgtoV0IHsF6feSP8YqeibPTtwZelLpLs
+ hux/D94MFKtYa6OseyzT3qIDdixav+mlI2ud
+ 0JyflYZ6MCBlxg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ XdhVQ3Na3LsvdtT2HwdsM3ItiD3UH0HO6TZD
+ W6/jy8r0NA6fTN4b4oVr6wSqHAQIQVYUbWER
+ 7pav2Ek03LDa0Q== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144646 56106 example.com.
+ dVTxTNAfZy5sa0SW8eme+KMx3hByBnPIrRlF
+ zGDsGN1Xzw3OBhsTmuOwhbnZSnnvdBrhBOJw
+ 8eU/6zpcZypyFQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.invalid b/tests/knot/semantic_check_data/cdnskey.invalid
new file mode 100644
index 0000000..6937db5
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ fIUb0+hjrELDVphcGgDZemNVpq1TBgyTt184
+ 9YnzaAhADynsscEd5iZRjuA5r7mlI/M9fFtU
+ l6wpEmqAs7sG5w== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ 86HnJEU3jP+bL9JmnY+2TGwna7DGtUVvgdhu
+ slzGQWN3EHb51vx1fHQGGfQlJ4P4ch5US3TE
+ 1rd/OKNUBE+p7w== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ 33SrrSRr8KwasK7qfxYAPxP//dj8Y9i95oza
+ 2Fwvt23QxfZS3TBLqMyMA6G/nmXyavUxsye8
+ C+mks7QsS7HJCA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144725 7800 example.com.
+ WRb17ehBEEjIVl//Zw8vtDmbnTY6eLWe2KQ2
+ +E+pCMEK0QE1qXwcethJ9PkM+gKFmN9RscXH
+ DjrmWIAfgndjsA== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ VxRHPS89GaMJvJ1xL8/HulwW75tDXUZ6nYlI
+ 8VCFOMB7vU+SoZhaaoZu4YcCZqzjzfZLl8Lt
+ SEaXZPQbnpkhyA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 7800
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ MWndPmlRdffYHO8Z2quMkXq80Nm3PNmWpTix
+ xJLJ71Oph+ta4XaTuiza6AQgVkCSzrfwoTuJ
+ UKHL13s4/IrRGg==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 46605
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ GRVgc202uXoxu8f36V/Tc4r9BzCKK07SCmS6
+ MCJ+mXO7PCv4RIzN9Dp8t6sVuDb5smLe6cV6
+ 5lgyPYJwr1TVJA== )
+ 3600 CDS 53851 8 2 (
+ 668159D684EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ r+OpHWsZ0enCPKtUIZFXSb/8YbLdfYb3Ihpt
+ n/5kAWbOkkkVzAJX2/sCrVExMCVcP/nFSIIf
+ hACGKBjTvuLFLA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ buNL2/GqYvtwcXMPSiOeaEB5L6r5InyVxzaJ
+ 1PaaJigmJHbdNKGFl8ijDiH7WBdQECb8M3oU
+ zeuWGebSLuy0AQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ wYB3zuX5/bt3Pg2nz9F0j6MK1bkY19QvDcRb
+ pk/0rHXLbSjTepbIwy8O0KbJndHy+a70fN5p
+ 3dBGN5J56KymFg== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ pXWJCUC0kKqWpjZetDhGJLNPpXGqc8sJZ9wY
+ HKs4Sd734p+Gr45vnJ94pGYjjtZi9bwPo2nF
+ DmFP5K3NLACG+Q== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ Khv6ptUd4l4SgJI/H+L6Ls/gQHnmmQJcg0fB
+ xv7zECmQfQFguVIJ1bmoz4jP26ejsNH1pG+o
+ Wz9U7I5oWsDzYg== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144725 7800 example.com.
+ z8omQAty9S0cNyFATnM8DZ+RbMly/7staAmc
+ RF+PmOp/E7FtdKOZe5+ega/+aQV9VpePYXMA
+ UwmIeeYYU2pAJQ== )
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..2814ddd
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid.param
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ tBomI7xR670RBUw9IjNL2A5eMVKtYqDUdhiq
+ XJI3CFdb4j6plfdUF75SfaiCP70aLX8Atzxm
+ 2RAzpR6M2Q3gbQ== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ 8mHEeq/7fnXpM/CaOFsIqTKyTrixQVZr8V+P
+ Lwn641YbbKniEP+KacrJ7Ul2jt2jCT2cnxC0
+ b9XicHENmd0phA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ f8ZdC3vD/oIltQLyL4zBmwo9rRyijN183BGw
+ L6iZ6DnH4BASlUyrGa0IceRH4yD5pP+gnhCc
+ lBzWFgvtEIyPPQ== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144756 33730 example.com.
+ UK+oQx75Gdn82LKBzht8KxrtwPE5JCBhEMcR
+ hRhHTeMqRUjbbeEOSWRdjg/36329yNYrxC60
+ l7bBcqolo9dDmw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ kr0M2egbhUXhH0i6fYiSl+zRH1pU7XhamCdO
+ nPhMEgFa3CsGp61kCuZFulpY0ODh8WrAPZcO
+ qC0tCj5Bz7nWZQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 33730
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 7fs6TMYYlkkxI1PCunVT9dxcxWVGXu1N7xVv
+ 2EUyVYMXSn/Z04URNTaxXcoWuDafy99G8rcT
+ oPycl2oOhc+s0w==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 60664
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ WpjHrB8ZfAhOSjq79gAaPEiQgSxvEatTi8nC
+ AYYpGs4dc1n54iYZ4IjCfMW/etlkZsMzXbVE
+ s6t+Dj/gJ3JKZg== )
+ 3600 CDS 53851 4 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ IAgBYDhTIYQvmF2vUy72TWoRlPJQGyGErJuT
+ 0xxZDStaSfoAVM3Hr6VEqIq7R3B+Xel/urDM
+ WYUbIAinEnvpOw== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ IpzPg5fx+O1HUqjN0lR1Bbo6Zx/Lq1wrrJvv
+ Y518ooGelg8Q2wH7NgScsyhLY342+MHk0fKX
+ RcxRzfaFohiEZg== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ WMqSVG8Tcq7e5E2y8oHThr6Ip7ASu/35m10m
+ TzsEANrlFf0e1Z6XG5ca/6//NSolXoTu6jBx
+ 2kvnsX2bA222PA== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ LLGAWxuAhlKM/3i9+FFGngy6Zqo6NsxdXScR
+ wgVe3Ilw+3vU/Nih70uRE/xUjZpfFBOlMEk6
+ EBSf/DJr6awY/A== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ mpcGxsR9c/K6wuaJCeFds1kg0af6Xj8K24o6
+ FHzqn60w7HXXNnDjxS0jPTHpaVUkWhuKUcCR
+ 9EcvMW7uwVfULQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144756 33730 example.com.
+ gLwhcu1t0qloiWb5/XHuv0PAQZ+ChmDdMuMS
+ qS3hi0VPk9cscMjd7ZH7shJBH+9KKMI6YbMz
+ VGU4MSCj5/kT0A== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nocdnskey b/tests/knot/semantic_check_data/cdnskey.nocdnskey
new file mode 100644
index 0000000..a7bac63
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocdnskey
@@ -0,0 +1,101 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ 0JDLQ/bZj4SSmqvLPAzt1v/UUb8mfJQnuLC9
+ B1CL4oRD45Hw00KgmbE7xgJVflYZJxfx7KIw
+ ydsB0/1/dMJzbA== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ Mnk/oSM7sdAhGYbWUMLpYFR1ahcvULo/8z42
+ giRwzAX8HiqvxxkqRCFbvzYeRkZLLw0fYTeR
+ Mqit0zQuWuc0ow== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ qPQblbJyzHdmhqYhYx4wfUHWe3SYGUA65hZR
+ UFYcx99Vhs1CXUobjCk9NBedRbBHR04kQ5Bo
+ /72fhuCPJFIC1Q== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144854 39620 example.com.
+ H5So5m0YdxOBU3k0+pi6KOgPNF2V4hU+GLxa
+ c0JdGnALP4Wz6lWCdMRPXIaMjImb3TK9vFti
+ 89lB/2MMDe4dTw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ 6R8b9KzH06NQ/4AUqrmp8rFmY0AmHpbW/vhj
+ xLul6ON720xvdeKBzi0nLSeTdUO8/gK8s8jh
+ RmJ8Fw279eXXZQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39620
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ hfwsa6JnfqjMRma2PlO+gt8qqLytVIygLZHB
+ 5APAuz2cheZCMD8A2kyt5NziCCj6szmCK4oZ
+ fColPGaDgYtpmA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 6821
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144854 6821 example.com.
+ UbEQQoX5j1FVOqpkQBqckaG4WnCd7+4dBJax
+ 5sgjHQnfSSwKGfJx0zxd3ZbPCEKj+Ymrhpsm
+ nqfPzVRZhUPKuQ== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144854 6821 example.com.
+ Sc/K9xI1C9rzujnllO5o7sKoJiEKFUEfPxt8
+ gsxs3sb9Q1s0/uSocrPc2OcaLgEzuFGS5FzA
+ fg7HcgZN63I5TA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ ykGu61Yjp24MJjp0wIYV20LSQ9ovRHT0zqp2
+ CSvlROIVpbUGlNjAAKJdWwYJAqNUD571gJ7E
+ TkhrLEIX02ySqw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ ye5pM/p8OWbdRNhLfbfWsY6lG8lr0Ae80LKv
+ rVOCMhAowrtKmDL6hUByovCV7MjCIYwGM26C
+ Vl9CRmrWwJEULw== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144854 39620 example.com.
+ JHP3TuxCuZ+N0lWtRI7Xl0qIcHSrn/X+WDUr
+ 0cVBfQTsFrAZs14bJhvw0zMGgONAgnFsXlxg
+ QmAqIPmpRvKtnA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nocds b/tests/knot/semantic_check_data/cdnskey.nocds
new file mode 100644
index 0000000..ecb3188
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocds
@@ -0,0 +1,110 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ GDfM/H4m+FRVp3M/KsOv//eMFaL1LnyrIi8O
+ pUSht1KyYDRoVqSL72XTy1aAJJ49Sd0uq+4U
+ acekI3Xi9OpvXg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ ICtOUMZr415dJb22HWsrjbYfW7q6hh6gxD2i
+ EikMQAkPncdBHHd7dCrjy1/4CPhixn/BnDfV
+ ZwF87k2Sa7EV8w== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ IokJy9LCiCaOPsluuBKYnwkesiPwsU/KZdA9
+ jK25UmdfD1uU8AA63OOciTZQSv9NI+Q4nzl3
+ LyqkRWFKToMz9A== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144849 42608 example.com.
+ kuhtgHhoeIwJ8IG08x+Tp5M7kQ+LzWoH/hTs
+ V17ZSyPD06YvMEmv9vdB+ATLd+j3uNYnMd4n
+ HW7Jh/ocOWg6+Q== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ p9BANIrBFV9hX2qwbzydeiubQkm9qstpzvUe
+ OFMDOEyyQxI+8s2nfHI76KmRliHuM7fOM9B5
+ e8wNmEeVd9JJmQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 42608
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 5sv4MetMS4KWSgyzvn658Prs0A8tLaWFhRJD
+ E9IznhGY2ogp8Z/uSIqh8QWzf1kQvfDUQiav
+ kOx4CNa3dSx/ZA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 8616
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 8616 example.com.
+ DeZBLj99QbyGhalCZ4UOmBJO/RLNgrPsAdaW
+ swYSg18lvE7jmLn9vxkUVZu0G6z43tulSb+a
+ lQT8m+U+PlusNA== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 8616 example.com.
+ IlysaALuak04Zbh0+104PHAuQgnYDBTLpvz+
+ BgirzX9Vp+pg4yZVelAXsaDbcj2ZrXrwBjpo
+ +DHj53HmZygj4g== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ dJhB1Xmd3G1ueRVnFU+M4yc379LH0UrpBcNS
+ xHzjVd+vWtpNGPq03Wi3sczA9UUkXE0F5n22
+ 6ZNR5XAswf+SYw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ 3DTwpPojzX4r9ZWeKo+zmJw+2L/uqrtoAZEv
+ ncPJG0AGB9QVzjLFiRg0BV4GiDZCl2Hh4onl
+ OShOi5Nt0GXp5Q== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144849 42608 example.com.
+ 1m2PpD3S6/5x3Kkes+1JgbHtsm0xlnKrNCmF
+ xeBvCl55D98zSvs0DjfRjFowAg22nWJkvsWo
+ 3N1vnfFZpzmPPA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nodnskey b/tests/knot/semantic_check_data/cdnskey.nodnskey
new file mode 100644
index 0000000..461e05a
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nodnskey
@@ -0,0 +1,111 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ Tng1e4Zs8LvGZJqp75aBSX9Ci9bsncY+w8+K
+ rfYdoVe/Smq0I+Hgtygcq0Twc7llW0rwtZ8R
+ jQpbXbp+XNDi3g== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ OcKfgxtnriGsC/9wV9yI71wIVzR+71j3sZ3+
+ ZGVqAo2bWR8QRULa5g5lQpIxlayN7w6xi6vV
+ IVWY3vauy59pPQ== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ CtZFcGvbco6ZreotcmfSYl8SlRdN/JiSuoOG
+ KtdauRz9+a+xkT2k1Wy6dADfLpwHwXL8yElg
+ /LdNXKEWK96HcQ== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144347 19649 example.com.
+ 6GVUlXemDUb6W9IID4qK+PPDSizeURGJEJlN
+ Hoof218/H/k8/BLNphFIGpdhCC2jHnAx2Nxd
+ Af65dTLtt7OBjQ== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ LiJCYpav6haPA3M3GhTZ/L6wtSqS7e9mwKsU
+ TdBkZ71RS8qmXsITLz5bFHMSy7K8mCuQIdTT
+ J3cGkbguNBqgJg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 19649
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ j18Cd+0frtc1WPeWn8bwdxYd9iTe7XsqTwnO
+ W46ZpPJPGBq/n31+7/N9TRAtXulE2r+rJDRF
+ mMooK5qrWOtqvw==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 24385
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ 8O0L6xxTnGMccrMSjaG2/MtljkSOls/BIwoX
+ eUmB9nJvDQNd8jg9XtNYUGG79dmysetBrNQl
+ TohQ1BEVGTJwig== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ CLjvJJOAZVToWUQQX06ySDkKo4QO4YcN2vhl
+ JZZ2a1hA2ranrzpeE8cslGKme5lxHKr8Y1ev
+ ffWfrz8KoQVW+A== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ TaPgzUzL+fPwEUNyusjCb6OZOF3DtlMNh3eY
+ ZTvogl2eRq84NA+mfzPmh0NXqVDbsVHGHq1B
+ mJoxuMtIt4G5Rg== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ 5sY/Q/1tP9qPMAHyQVMtbFQ0gO24rofCLg/D
+ /BaXTvjp5bnWhGuv1wFbSCyEreYr072Va08t
+ JdntIC8Prt/1MQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ UlLhm8Nb6g0jUIs1ldjW4OedzzLXDjCllRSm
+ +6WQuBK1uA7vboyqYVvLxxyFZCxgz6xV02iK
+ eawtsKsOnlfGCg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ ZRDwnV+YyfKPI58ASagzoCo+qWTscYZZa6j+
+ wr4axJ7jtIO6Firy4R1GlO6NXmN5vcjHAj90
+ NZ26ezRgCMCFQQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144347 19649 example.com.
+ c5ILb+AR9BIinFp6mCogN+jwR8067Fm9LT9Y
+ AWaR3pqUC4d+Qdo4pkODLkmhAaSQLJCyPyYB
+ TQ7OFkQCC49MtA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey b/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey
new file mode 100644
index 0000000..70241ab
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey
@@ -0,0 +1,135 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ pdj652v0OfPO/McP8sNpxoE+adY+Qim5je8m
+ TQPcudU3gm7I2L+YqU/ujX1NUOyhUAhzRng7
+ m6nfrudJebq15g== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 7/X57I7FmbSlgxxeaE3Xgoot7KxN6nxtDb0E
+ mEEZNwdLCpjgaftaXXXM3NaZ1W2sdoECCrlz
+ R4/75kqrmNpYPw== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 0tcHIXXPEKy1tpc+Of6s2hTdQ5dGh1IoIoxY
+ se9paUUfhoF2oH5Pb8HP3rNyWLiTqXh4/lxV
+ vFLi4rR5zojxLA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144945 39996 example.com.
+ kbImDj5vgk5VG9MI+4HJ4FtwnJ4ykSbk8vNY
+ e49ibkZChGsTtIzLwdcNAmOk7w/em67FkGBi
+ oxqCj6b3G0C45w== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ R4pG7HF8CbXgbo4N6UqdSnE8CaClNUw6v/di
+ aScNknRS0eLPOKmpANe0tyiwBV1bRQyjpmxq
+ fgZ9Oxac7plIJw==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39996
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ cJdrUmmcxe9JKHwHHAkJ8mO1J63Cm6Qoln56
+ CUya+eWuF1A3u9L3wumvY2rAXvzBpplLXeUN
+ GIN0GgLHejH6QQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 56026
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ srEjUMAQ4Z/yc22bas+P0ly30IVbZaIIlli9
+ H7avBz013fn90vDRDLiLuHAMvW++xdDJypcg
+ Sr+9I9+nv6jzRA== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ inhdpEZ+2W4EM1HSiVZdJa4xT5S319D0x3b5
+ eJpskw/EV/Rx1X87FCr8FP18iBOszsWJjQQq
+ Z66eAxIhpBcb7A== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ TA7UxWd+j6bOXKPxo3XuKlIy87/HvIPGoELS
+ WQyrON5IURgGw/2YWD0M5xw852jl27USezzo
+ pai940D3+VGeOQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ CSk6oHNIsj3XQgXpPtFOhf4dTv/Wu/vnJfJs
+ Lpc3IoApBMxrpSIzfM/c72JtjSVzjJcdo6kL
+ n71WM21CsMcQ4A== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ hsml4IaJtzvMdvaMTR3MzeCT5fMHJ46rCY0y
+ 8DTAvK7/Z6LHbF4G7yRh9ozwcyZbB006cMdc
+ 4XUFDtEPK62DGw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 1cNj6TJEHFxLXFYVt3RU3wC8Wz/F5bfjy8/W
+ jEJdrnVzo1ihmJWoY48e9MlvsGXnGe4+GUrl
+ HSS+2bsGOS7DyA== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144945 39996 example.com.
+ 5mtXYcvidkSnG12dZof3xSEaH2eOsV2fuBvb
+ 8Eb6XEuPfD9v5g2mweyZYrBtowEsTA9IOsly
+ 6AWT5PfZbNAe+Q== )
diff --git a/tests/knot/semantic_check_data/cdnskey.orphan.cds b/tests/knot/semantic_check_data/cdnskey.orphan.cds
new file mode 100644
index 0000000..54732de
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.orphan.cds
@@ -0,0 +1,138 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ ThTlvNtautK64IeJRxNCr5acLrRu8jXkTR3N
+ y5TlXrei2DIagbPja++4vLjhUJAcKTGndD+x
+ wgMrDpCY6pMAYQ== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ 3OJiG3v9Nq9OHkyysT3A6PNPRVn9sYTQkHNS
+ 6JL5BzLCQ+uYKJBCu0ZPxDlYpbYnO0HKQ7Ta
+ iZYCjm7vzqtvwA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ 9vi3n2cVyr+ghB0ql4Wc8vhpLfAuclopapXw
+ BQV328nEwftj0okcPz4Z7Iye9by4X6NDd13x
+ vzWXDKjZCSxLJg== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144942 8996 example.com.
+ HP8iIlUO+EKFRgoHUrQWLcaX8oSGEb/tldEP
+ GcJKM+rGMeJvxXOJnjSskUm7AyRK1TKK4RqE
+ xaOHTgIz1uUkzw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ bkP3kBcYNsUB6jpKA764AJeNBzGJjNIRPxDl
+ 2wK1O7I/bvZDILscWSMUsSRmxZuPWGLjevpp
+ Tve1UMe+dP9VIA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 8996
+ 3600 DNSKEY 257 3 8 (
+ AwEAAaulfU2biYVBiUsGwAyCXbA+gm0yWgH2
+ Z71S16R2YNERlb0he9Od28DcFd0HbaKdFnw/
+ CtX7Z2UWs6/IRu8QmHGn6SKDsLzZ5StdPsJD
+ KilfvSlEcQeqrRAncug1SnA5BogNQSD0/02Y
+ w5KDGn7ALCSYlNgOgy7l+D/urlkuxgsPWvqY
+ XnlxaIcKt96fndwmkfZ5eF+WAqxguaNcvm14
+ 6NA53wRrWx8BQbcHk1R+WcQGqFcVOlifCs9z
+ V+87QJy2H660QKqOVDgt8PF8QmRRJqzOKpu2
+ 9T+Vd1dM3zjBJ7deLaNH2E5p7Bbp1eeOCeOt
+ WpCG6XfaRmZIF3ZWVM6Ways=
+ ) ; KSK; alg = RSASHA256 ; key id = 56474
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 1OgEqruDg7pI2dTIRMdP9ihhdl3wFngZW9bP
+ E4jMg4ByKKoKM/C1QN4Q+BQiQDkcprwE9vLf
+ D/cLgFNspjcBgQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 63865
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ 9d2q8pWH1AftoDmPq3DNblta3oPV+6ROZmVR
+ BvjHj7xJjI27aY514C0qNkQVhioe2mhQjikO
+ gyxvkWwBV/owPg== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 CDS 56474 8 2 (
+ 260E7ADB07D1ECC40DEE79EFF6527CF7119C
+ 0AFC1CFA5DAC1ADFE342568CF32D )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ E7iVsJZjRyGbjMUADsi9Chz74+t1W75zTPmm
+ MYVD77dkRHiEpN41MJB6Z7Fn1lNOE6f8q2B5
+ iL/3UXULB1vpwA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ fsMqYcBDcTBtaDEqDTYrHHivnuQKb629drhm
+ 77RFfBxFJAxlq176PzaddA++zHfWsBgIlJzy
+ VHFy3S3huuyfaQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ hhpJcQ4cMcq9fLNtZrTEVAMGB2bjMwcDvv4C
+ Sss9wWDBNxIVOsi4x3j/08PZTqbfmYePWtK8
+ k2R5GOOK1lpVlw== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ xU82j/dJf8oBd1Ti2lHH0YoxBvgCQo2MOdwJ
+ yOc6fDrT/c39rCMT//VoDmmKj3SavQ92ABBt
+ 18JqxCXK7+tnYQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ D3O6XOYrOT1tlCieJJvw7zys0ClqXcCvs5+D
+ qSEpKcE6RNNeJG2d3SJg95fbO+eTkw30MROF
+ ajnNh5xJ+8xsMQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144942 8996 example.com.
+ sGBFze6wRGj8n0B8izUNHO2ufA72sR55U3OQ
+ RLYTx2XqBRvdmapMKK6QDu/6lmwqgYMbjiBJ
+ XqDLv/1RP4DisQ== )
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/delegation.signed b/tests/knot/semantic_check_data/delegation.signed
new file mode 100644
index 0000000..2007216
--- /dev/null
+++ b/tests/knot/semantic_check_data/delegation.signed
@@ -0,0 +1,43 @@
+; Delegation NS and glue signed despite mustn't.
+
+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 NS dns1.example.com.
+ 0 NSEC3PARAM 1 0 10 -
+
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406103150 20210205090150 25674 example.com. 4tMK6g2B0ITXf2haSSuH45nO53GlpZQ97ofC5Pd/S38oeNzWmhfxIBaGtb597qxRA2NC7rYtGsscLrCa0sthMA==
+example.com. 3600 RRSIG NS 13 2 3600 20400406102301 20210205085301 61806 example.com. TrCJZgu1hVoUK532mmhQpZcEcPdw4FezPCymtUuQH9XjZNBn3DP/OhM8NvAbtailiOIX/djosTC2cNDlqSoVCQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406102301 20210205085301 61806 example.com. h/+XG/WWQsoAuzOM2wiulY8TOslYTj4MyP7Rjj3VXx8frlheIN84yH7NL6Xgt3ibQJpJl7rujkDuoTBH+snnCw==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406102301 20210205085301 61806 example.com. TYk9hqD6hWA8YH/G3VeggrUHb7CwX3ut5GGiAOcl9o8I0gdMIOu8E1uUukexvJsZAt1Fbcjc7ZIbsUmvgs2MVg==
+deleg.example.com. 3600 RRSIG NS 13 3 3600 20400406102301 20210205085301 61806 example.com. /Xg/3viyTMyd88hcByGifSMHGo3up83exBQQt4FC6qexZffRyNiLrHOfnoz/2LqFMg/oDVCsvqaEomiMM6FlZw==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400406102301 20210205085301 61806 example.com. zc6VOVGfgoB9C8/0WPHOVrdikBzK6xh25UtrdIYuSzcPWbFlWSsV3+xS1q20MBDb2dj635jcyBWRep+287rDLA==
+deleg.example.com. 3600 RRSIG A 13 3 3600 20400406103429 20210205090429 61806 example.com. 8hcIsHOARI1XXMcPXwtlmQC071+FBH+I0a6CufDbE7nPa38brBKomqTjiYF26K1KZ4IQASw5vvF0lFg3eEOZog==
+
+deleg.example.com. 3600 NS deleg.example.com.
+deleg.example.com. 3600 A 192.0.2.1
+dns1.example.com. 3600 A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008173641 61552 example.com.
+ URGzLYXySdeOtXWW5ph64pNedd7/cq0WYcbd
+ nArHBIN2S08knOfV/OHOMDaR7WufUbIF8bPQ
+ FxDkURlAhZbH9A== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 3600 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 3600 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS RRSIG )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 3600 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20g1gol477ro51rk9a9nfd54tfqal7iq.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406102301 20210205085301 61806 example.com. LUBULY9667EsrOHecNjp2QkW9JJW1fOSyTmleWul7vGFwuNC1mKVUQu3H3V5ndtwzU1YD69oa6eI2DOERmiJXg==
+mjv836rjqej5ubghvksq7n44rso3q938.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406105733 20210205092733 61806 example.com. zYuSttG565eDv3FPeKfZs4FNuJHD204/8nv8cNx+9iqbxMdh5s1XJx4nolWyiOJcBq+G8CmtiuJK6plUs7x67w==
+utqvuhu2blk3dhmrr5t1hd9vteohqt0a.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406102301 20210205085301 61806 example.com. aMivM0YOs4Il/WRWqf3SRzh21nZXau7VIJOpX2NK46qxBCW41N/+J7rXaeAT15ayWNjCHP1YoDwyuC/lVZtCqg==
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..ff92f7b
--- /dev/null
+++ b/tests/knot/semantic_check_data/different_signer_name.signed
@@ -0,0 +1,52 @@
+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 13 2 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ UH4IJhLwxWI9g2vycAuGAHm5XzsW5LKr6xeI
+ aoaiMeb1pepw9vAWEUO1Byimg7FfhvYpt7+J
+ IhYCvpBb6u3ucA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ ou6B0AgSUxs7//b+c+Gm3XjC83TpgGvRwj9d
+ F48TEZCMRpdvtVNc1hDnNKa8oXA16TafbkqN
+ Z0ekrEo2LlN+hw== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008164859 49259 example.com.
+ uCzqU8DU8ZMt3t/h0jwZjdVgSj33HhwtGwhE
+ ZglZ0gUVDVLndP5Q+psqlz2jBmiXIN16s/+b
+ di0crJ9LULq0NA== )
+ 3600 DNSKEY 256 3 13 (
+ qWpA6ejmc17FHZTN/YoYX4WdNN32LC2IlBmm
+ n2Yoi16OQ1e2ztEusvQaSwzEMbN2pIzfTIlF
+ YQQ1gzLQAhWIpg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 49259
+ 3600 DNSKEY 257 3 13 (
+ rHQi5BOkLnSsZh1v9saRZ38MkzYLL0oGbAK2
+ Dp86tH3lpDqPoR7LM98gyBLZgp81m0YHAYnf
+ 2yK617XStIPw+A==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 3753
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008164859 3753 example.com.
+ 81C/yn0gxkwOMUWNZPszGow4UyDuDn1V4WQJ
+ NXJfNiTvT6edQ0rQakhJPGgVyH4LIwWJV8Uk
+ fOubCv7BBgu0wg== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ x6z2ftS2deCBR9HJeIazQNrDdzw0lEE04UYp
+ npUe2zkIx6aH7MvvgZIjcFTwPOVsI00u7gaU
+ AzuxODSma50TXQ== )
+ 86400 NSEC example.com. A RRSIG NSEC
+; different signer name in RRSIG
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008164859 49259 different.com.
+ K/URrUmli54Noy0E3REXBo/g0LZ/8gneyVfa
+ FrGXLB0kvQydPyceL+BFIoJP6d/Gs/0qkUjT
+ vMQfvF0x3bFS3w== )
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..b083ce9
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_apex_nsec3.signed
@@ -0,0 +1,25 @@
+; Zone without any semantic error
+
+;; Zone dump (Knot DNS 2.6.0)
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNAME bar.example.com.
+example.com. 0 CDNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 CDS 25674 13 2 2EC05563A3537BD32EA3EB92C44794C644F249EE440785CF28207B903E35322D
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 13 2 3600 20400406111136 20210205094136 61806 example.com. WIlxYlV/hn9mfojITrVbIV+Giy9b5pAKofkw62Yli+jIspQ3dC/WWLrM5Y4HcQwTfNp7yuhIS0jPzkuy0xuAxg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406111136 20210205094136 61806 example.com. z71ipK0zBRKKokzXdoZdtkxGC75MJbwmICNjSfd+IX/hneIGvFE7mTose1Zbb0WGgKRdUMEoii7hLZLrx7waqg==
+example.com. 3600 RRSIG DNAME 13 2 3600 20400406111136 20210205094136 61806 example.com. 5tIYeBwbwpVF0X5ZLoSpHeB8IYLU5/2fFYXqvctZYqTO24T0EBfu+++j66VSERAI38xf2Z0KkYcwx1XeIeivBQ==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406111136 20210205094136 25674 example.com. X3n5YVkjpSpK+IOCkhv/wFmF5WIPHUR2LXkNME84i5S4efvQiRRq/jgqos2f7OgfSi/9Q2Q2x6BiMQ1vx/R+Pw==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406111136 20210205094136 61806 example.com. gogp8pZycFopDodl4IOfpaKCbLqXw2v+5DcV2YwmHr/pMwrc28bClQxw4HVGcYQ13HpC9kKmzmcrn3dEumTb3A==
+example.com. 0 RRSIG CDS 13 2 0 20400406111136 20210205094136 25674 example.com. zRQEFycg2sNVVB4TOZO8QcMwRwSA7tHJqkc1l9V+WtEdJY8UvYpYPPgAn9FjWMzzhvRMlws89TBSsQzqCemHiQ==
+example.com. 0 RRSIG CDNSKEY 13 2 0 20400406111136 20210205094136 25674 example.com. hLOpPxmKXU//dmQoE5OdCqzWkkJsuBHa8QITWB/A3Tc2CXQTaqFKqTspZvTLOAYKNaSVu6BOLWM7Fi2Bq3I0mQ==
+;; DNSSEC NSEC3 chain
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS SOA DNAME RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+;; DNSSEC NSEC3 signatures
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406111136 20210205094136 61806 example.com. C3JeKvcKdQO3zTJqg5Z114jTd36tgF7PIL2kCs7X6VnCaVe7E5NtwUuLMLFIw/gUqaLDbE7vQwHMK3Psl536aA==
+;; 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/dname_extra_ns.zone b/tests/knot/semantic_check_data/dname_extra_ns.zone
new file mode 100644
index 0000000..e188742
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_extra_ns.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
+foo NS dns1
diff --git a/tests/knot/semantic_check_data/dname_multiple.zone b/tests/knot/semantic_check_data/dname_multiple.zone
new file mode 100644
index 0000000..2a6c0a2
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_multiple.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 bar1
+foo DNAME bar2
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/ds_apex.zone b/tests/knot/semantic_check_data/ds_apex.zone
new file mode 100644
index 0000000..9b315d5
--- /dev/null
+++ b/tests/knot/semantic_check_data/ds_apex.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
+
+@ DS 40920 13 2 70959CF5D6A0A3F53BD0AE42DF4DA6ADE84DA066AE1F95F6B6B610DA53F6E86A
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/glue_apex_both.missing b/tests/knot/semantic_check_data/glue_apex_both.missing
new file mode 100644
index 0000000..74e37f6
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_apex_both.missing
@@ -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
+
+; missing glue for dns1 and dns2
diff --git a/tests/knot/semantic_check_data/glue_apex_one.missing b/tests/knot/semantic_check_data/glue_apex_one.missing
new file mode 100644
index 0000000..47ee797
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_apex_one.missing
@@ -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 glue for dns2
diff --git a/tests/knot/semantic_check_data/glue_besides.missing b/tests/knot/semantic_check_data/glue_besides.missing
new file mode 100644
index 0000000..38ad890
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_besides.missing
@@ -0,0 +1,17 @@
+$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 dns2
+
+; missing glue for dns2
diff --git a/tests/knot/semantic_check_data/glue_deleg.missing b/tests/knot/semantic_check_data/glue_deleg.missing
new file mode 100644
index 0000000..291b450
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_deleg.missing
@@ -0,0 +1,17 @@
+$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 glue for ns1.deleg
diff --git a/tests/knot/semantic_check_data/glue_in_apex.missing b/tests/knot/semantic_check_data/glue_in_apex.missing
new file mode 100644
index 0000000..a02f6bf
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_in_apex.missing
@@ -0,0 +1,13 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS @
+
+; missing glue for @
diff --git a/tests/knot/semantic_check_data/glue_in_deleg.valid b/tests/knot/semantic_check_data/glue_in_deleg.valid
new file mode 100644
index 0000000..42adf6b
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_in_deleg.valid
@@ -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 ns2.d
+
+d NS ns1.d
+ns1.d A 1.2.3.4
+
+; glue below another delegation is not mandatory
diff --git a/tests/knot/semantic_check_data/glue_no_foreign.valid b/tests/knot/semantic_check_data/glue_no_foreign.valid
new file mode 100644
index 0000000..4cdcbe0
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_no_foreign.valid
@@ -0,0 +1,13 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS foreign.
+
+; glue for foreign. is not mandatory
diff --git a/tests/knot/semantic_check_data/glue_wildcard.valid b/tests/knot/semantic_check_data/glue_wildcard.valid
new file mode 100644
index 0000000..9e36b5e
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_wildcard.valid
@@ -0,0 +1,22 @@
+$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
+deleg1 NS a.ns.abc
+deleg2 NS a.ns.ns.ns.ns.xyz
+
+; wildcard glue
+
+*.ns.abc AAAA ::1
+*.ns.xyz AAAA ::2
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..d838172
--- /dev/null
+++ b/tests/knot/semantic_check_data/invalid_ds.signed
@@ -0,0 +1,108 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 13 0 c5
+ 3600 IN DS 60485 13 1 c584e6295950fa2ff90f9229a1da2efd3cc98cec
+ 3600 IN DS 60485 13 2 c584e6295950fa2ff90f9229a1da2efd3cc98cec3dcdbcaa6eeffc400673246f
+ 3600 IN DS 60485 13 3 c584e6295950fa2ff90f9229a1da2efd3cc98cec3dcdbcaa6eeffc400673246f
+ 3600 IN DS 60485 13 4 cbb868ac75fac9327d19f25fd14971158d4b4ad0e4c75d3e7aa35752bfa5ab9c5e9d02cb805b91cf322588f97e1c486d
+ 3600 IN DS 60485 13 5 c5
+
+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/no_error_delegation_bitmap.signed b/tests/knot/semantic_check_data/no_error_delegation_bitmap.signed
new file mode 100644
index 0000000..abbf088
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_delegation_bitmap.signed
@@ -0,0 +1,61 @@
+; 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 13 2 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ /R/djqaZWRo1zCmz7B58/93D8ZxJoZAAKEbH
+ xuCsAJ5dm2ubvtgvqmhNXMqdVBvpb2OPdBX8
+ VF1j9RsjuE7ORQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ AmyqpqMfMEztA9S1Urv6yEtKd5yc6kkSedRU
+ uLp7velyCkipFzWgpzRVDqn+wp2ZaHig0Fod
+ kryw3j4yHOLlHA== )
+ 86400 NSEC deleg.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ 9tR3kL4pVEYsHzt888pbP0TtS/npeApAEUfZ
+ L5rXQE0WqBLQGtyEPYxujFuaruvxH0SgLl6r
+ n6MKCEB07DjhTA== )
+ 3600 DNSKEY 256 3 13 (
+ C7v6eelCoXgBoUjHe/gKdsnWNw04GH7PpYMo
+ 2hF5jaeq1zkLSXkF2xS/04MgBTFFYuDU+LGt
+ 8kMKNc8o2wH2jQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 32411
+ 3600 DNSKEY 257 3 13 (
+ m6KdGBizfDaUhcW+nIHuRdufZFcSYlZ5Xoky
+ +GcH23OxZtPzPwKwpg5rTx+RCRPlVpmwyiW3
+ aC69n0Q8mr8NpA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 60051
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008173446 60051 example.com.
+ SD4149dui/vuky4G6wiJQLUw5b8XpG+Cy/cf
+ +9CSbuKWHRcC1K0wVEw6xyEah6eD/7Sh0eFA
+ EECgej5etJbL3A== )
+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 13 3 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ HFA1XBdjaUvb8lbyhXVDYxTUn8Nr2HNC5ktc
+ kPBW2AGMQiVUtyR3vPxUIiusxsQn+uyRL2QC
+ NBG3ANo5exT8ug== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ 4NLhmO0Sa3yk1ZikWSRYEX0FpHK0NkTGk++h
+ RHJO3E9M6Og1am/PiPf67DAe/2n4ANItC/SH
+ u/1WSvYQV7OZqg== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ pQgr7WzGpL8gbbAcbeYEIYBLq8lCAuE9NaUf
+ itYKBFh7Cbg4YrLOoeAV6v6V4tfZPpmNpd2U
+ 9VUrY9es4QfX6Q== )
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..a03f4ea
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_nsec3_optout.signed
@@ -0,0 +1,29 @@
+; Zone without any semantic error
+
+;; Zone dump (Knot DNS 3.1.dev.1612270066.d215637a6)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010111222 21600 3600 604800 86400
+example.com. 3600 NS dns1.example.com.
+example.com. 0 CDNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 CDS 25674 13 2 2EC05563A3537BD32EA3EB92C44794C644F249EE440785CF28207B903E35322D
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg.example.com. 3600 NS deleg.example.com.
+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 13 2 3600 20400406110811 20210205093811 61806 example.com. VD3IclxLUSi1tgv4+FJ+9e3EWiRny6de1y4jUFn1Ama8+Cl2vZO2Jc34Q9MKY/S9m4id7Xe8MtkkrKThQcaaXw==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406110811 20210205093811 61806 example.com. BniH53lEM1hYGcorTmqF7At3+neZkifPT1sM15nGlQUQ6RfkPxh7Uy8Pj3PxLL5v7WDTyFGbLVThEFWZUh/h6w==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406110811 20210205093811 25674 example.com. 3FSDEJ9f54++FX/EHWXXnbHW8iJPaDG4kc7qf772y62dtqTfAvb22lq2yKzCOaRFFwpPKEdcS4OEkhx0IbC27w==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406110811 20210205093811 61806 example.com. BTT+7Gj8V2pATxogxJ8xEO5eiVHoVIDxdK60zDS3MWNcbUc/n9vJR8NrCECel9egQUWrejawikO4DkyQxLZpkw==
+example.com. 0 RRSIG CDS 13 2 0 20400406110811 20210205093811 25674 example.com. h43kZiM1EFETWQEtMM8Xls/RFDsAkLIpLf+DUnJ+zzxv37xpGvtf/s//3ew9qEhouBnGh/1FWtNr8vjhzh0tsg==
+example.com. 0 RRSIG CDNSKEY 13 2 0 20400406110811 20210205093811 25674 example.com. 4mf7C/zyWoFRllUEaLHpdxJdlbEQXIRKNH6JOH3sTKSQMGj1SMmkWm9qlO9tVaUm1ggB6r8TPWgrAUBG+4A9gQ==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400406110811 20210205093811 61806 example.com. lMj63MgZYiCl6Fdf0Q5C4/K99AAXTqCI9HSBQcrc7qiZDjRpZXzBUO8yv7+5JSMIo/A3tJtQL/12VFPGZ9NQ5w==
+;; DNSSEC NSEC3 chain
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F rvcd9h11kcnenarqcmtmrhusdmb24rm4 NS SOA RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+rvcd9h11kcnenarqcmtmrhusdmb24rm4.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc A RRSIG
+;; DNSSEC NSEC3 signatures
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406110811 20210205093811 61806 example.com. 7AdxaQLQ16ORwtf3t9lNQrzOP1BKu0TOIiKfx8/7o0JKoVtDYjqTC+ilWSD/Mbfb6PI6ND3NQKsIbnApOa2SUA==
+rvcd9h11kcnenarqcmtmrhusdmb24rm4.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406110811 20210205093811 61806 example.com. bOUzqzuIhV/SPyXiFOgsJbnS77dijFWcLDY/0X3r9aNiAo3/vSE4OTT0f6CkcBQDka+LjIoRaE7NIaTMl24fdg==
+;; Written 21 records
+;; Time 2021-02-05 12:08:11 CET
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/ns_apex.missing b/tests/knot/semantic_check_data/ns_apex.missing
new file mode 100644
index 0000000..fd7b7be
--- /dev/null
+++ b/tests/knot/semantic_check_data/ns_apex.missing
@@ -0,0 +1,11 @@
+$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 apex
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..ad5da7c
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_ds.signed
@@ -0,0 +1,57 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 5 2 ( 4EFB4310DB01A42E7882E102
+ 7A73CC28E2E0FE938F2D5888
+ A0DA0005B99E7FF8 )
+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. 3600 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 3600 IN NSEC3 1 1 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ A RRSIG )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 3600 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 3600 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20g1gol477ro51rk9a9nfd54tfqal7iq.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. k2hYD9qbLM8cRuN1fcLar/GsSufK/5oQYxRnE9bUDiKvC1WhCDF3pee6MSqybb3LoNkQUeOgGV4jdzvslzDlhQ==
+6dfjitu5vml86qnku9fo2ljddqqtqpvt.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. fGQRezo0T9Hd1tGJqhCXPyLONKSxOPmX1Kl7MjD1OVDLg9l5Ei9DmhrpCFxahXMBGIA4yy1J7mSK3PqelPMyFw==
+ui312kqop1ng8iqeiefnpsla94kb5q92.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. 6AmfjUgzO4Ew9FmW6koVhQ+98Vd7xI7kpFXwj8wb4ObmuM8uFu6tpvcT/jDcUduFuUb//DS5fS9fXraLNL8JUQ==
+utqvuhu2blk3dhmrr5t1hd9vteohqt0a.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. WMvFzIf9Ekvr0UVRzbZpxUAjT2Sf2KgsXek6iG786Iw6nZ/rzPVNlfNLhWvdqQmi5LEDp03UExshDlPz3JgOkQ==
+
+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 NS dns1.example.com.
+ 0 NSEC3PARAM 1 0 10 -
+dns1.example.com. 3600 IN A 192.0.2.1
+www.example.com. 3600 IN A 192.0.2.1
+
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400405162736 20210204145736 25674 example.com. 5nIsRsT30KNhPS/i8rNhT/C3uPli0jb+7fLYL+eHKggTHk5UK69Z5EHA/ISKnbEOMIQA3QJ98XNreLJk+sTZ4w==
+
+example.com. 3600 RRSIG NS 13 2 3600 20400405162736 20210204145736 61806 example.com. nzKbYNX9cbrf3zNMSRK4ftG/p4DLn/uB3BM29txIj0nyKxL1cmK0wsTltGmwLzJTegBy/LV1VtMudDLWEFU3sQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400405162736 20210204145736 61806 example.com. HvtxfzCSLHjFSHuAyO+KKymy/vxOGLS8T1DuhfAoUtweHv1zVeYfbFOfCdcfs15PKO31ldqwWRvFWAhM+3hnrA==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400405162736 20210204145736 61806 example.com. 8uDKYTVd+XuYFyzf/aNm6kMjZhbI8r+22v1AuuYYqgP5aH6/ZFXusczSGkPdcauVIgKLV1I7dBBQkQm2LNIqAA==
+deleg.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. beD3O8cnCQ+8HWZpn35gFrR2tLkb9tGpe143BfUA0aOkAr2PdK9CUBs47uSyWAATYoa11gtxxdFUzW6coa7l/w==
+deleg.example.com. 3600 RRSIG NS 13 3 3600 20400405162736 20210204145736 61806 example.com. HJCAXBevueFA2BOP6eOnsbP1X+2VUQRGXRcYI2SDqqq4U2DQHWQMOfI+pKVpkfdc8D6qDYnFZSg6II/dDJQ0AQ==
+deleg.example.com. 3600 RRSIG DS 13 3 3600 20400405162736 20210204145736 61806 example.com. DhZsh6wiPACEUz7GY4WpvcIrMOF+sU27kJAGKaCcpxv9jQBY7Jpf/otRf+yn+Bmm32RZUr5swSXMXAvDtCj6qA==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. z/pEp4EcGkmy+niefZRLgRo1LraBlJABdgpSo94cYEqJM3GBMHsPZeAKmqnMAYA5Nz0hQtTplqS3rsJHJJdQ7w==
+www.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. FpOwodJYlk3NxEEjGvY75r8Ptef13P4Um9N74NxV1QWQlqtBhg+1bndvaY376uBFVDFGsEiDFEgIoFL0Ao+PeA==
+
+
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_optout_ent.all b/tests/knot/semantic_check_data/nsec3_optout_ent.all
new file mode 100644
index 0000000..5ebd917
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.all
@@ -0,0 +1,15 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173442 20210209160442 61806 example.com. laxHzto10anAyWXb/IqVEoBsybVmb/aCMb4SdxEC3YiJFj1IX9rxChVnuXrQ5zgr1f6YaRyc/DDTP8NFvwyTWg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173442 20210209160442 61806 example.com. /eNl2bkB/SJ6qBX+Jpm5KTXIs5Xi978JWRN2jtbEh5Z9udy7liS73oMkBLlJ33amKc7Gwfqi2+SgdHHud4j0Ug==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173442 20210209160442 25674 example.com. TpePckJM7GcsE72vbfSf49LzEM1chUFIiKBN0VyCHdB3YFpRH5d8Qx+XWh8Vs9AuLoKMWTQ0UD4kZK8yF70N4A==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173442 20210209160442 61806 example.com. RfPCpoA94H+dm7fqxhZ+GIf4fQwzN19yJVbhmEOtx6if9U/H6mJalvoy4d5UD/L2bferTBbie4I/TzAIXgVETQ==
+
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS SOA RRSIG DNSKEY NSEC3PARAM
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410181548 20210209164548 61806 example.com. EBPlHXYdARm1T0TaYadx0ETwC6w0g5J1yPR6LB3ur9IItcEWRONhqDrNwUbYGbW5c4nWep/hnJYdmMFq1bTfiw==
diff --git a/tests/knot/semantic_check_data/nsec3_optout_ent.invalid b/tests/knot/semantic_check_data/nsec3_optout_ent.invalid
new file mode 100644
index 0000000..114d5d7
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.invalid
@@ -0,0 +1,18 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173442 20210209160442 61806 example.com. laxHzto10anAyWXb/IqVEoBsybVmb/aCMb4SdxEC3YiJFj1IX9rxChVnuXrQ5zgr1f6YaRyc/DDTP8NFvwyTWg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173442 20210209160442 61806 example.com. /eNl2bkB/SJ6qBX+Jpm5KTXIs5Xi978JWRN2jtbEh5Z9udy7liS73oMkBLlJ33amKc7Gwfqi2+SgdHHud4j0Ug==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173442 20210209160442 25674 example.com. TpePckJM7GcsE72vbfSf49LzEM1chUFIiKBN0VyCHdB3YFpRH5d8Qx+XWh8Vs9AuLoKMWTQ0UD4kZK8yF70N4A==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173442 20210209160442 61806 example.com. RfPCpoA94H+dm7fqxhZ+GIf4fQwzN19yJVbhmEOtx6if9U/H6mJalvoy4d5UD/L2bferTBbie4I/TzAIXgVETQ==
+
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F gtr2v0c3d7eqh7ob8rbad7ta90tq8lci NS SOA RRSIG DNSKEY NSEC3PARAM
+
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173442 20210209160442 61806 example.com. gb3uKByt54iwCsd284xzOVnnpN97r7ARz6UacMdm2Xs4M8t6Ao9bRG7jvbNpFCALfaU/xDQF7K3v31iKBeVwjw==
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173442 20210209160442 61806 example.com. kpuFRuzOhsG5zy0Sdql0AB44IDUtf9ccTwJXdULoIqUNKeRqvgWJ7ekEhBKvntVHlBQZPescgPMvvq7PLcA2Dw==
diff --git a/tests/knot/semantic_check_data/nsec3_optout_ent.valid b/tests/knot/semantic_check_data/nsec3_optout_ent.valid
new file mode 100644
index 0000000..c9fe657
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.valid
@@ -0,0 +1,20 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173236 20210209160236 61806 example.com. C4ierSNpy03xjH5rQEfb01wCj4SVIzX9b15FVEMIbn3lmDo5jXO6stOrW8Z7OjoVuCaRi1Qj997TeCYqOxNXSQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173236 20210209160236 61806 example.com. NNyQzYOcPbfEsqv61I78MuMguN/KIFi/wSJc940pj7rv+riA3J+XVzpaHSSh//q8CmrvpBAk2g8KsQG/6kOXmg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173236 20210209160236 25674 example.com. ZY3nxZJeOfSOEhs02mfhQgt6N1EgZubtPp3HuV69gStFSu4aCLi8a2aseQGilOFW64dOAYNm3LL/WqhPi7MZ1Q==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173236 20210209160236 61806 example.com. JITs/EH8nLaFRidlkT6+mcTwEpjgp2TMjb9fU5TBIlKn94og8YtOWFbNmzdEYBKlGLlkg8LwY2ortrSoRHS6Hw==
+
+ej69a9a2k2j0ntktmdvihrv5ao8fl1jt.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F gtr2v0c3d7eqh7ob8rbad7ta90tq8lci
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ej69a9a2k2j0ntktmdvihrv5ao8fl1jt NS SOA RRSIG DNSKEY NSEC3PARAM
+
+ej69a9a2k2j0ntktmdvihrv5ao8fl1jt.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. yatL/lbFSUyN4UyRtMXymxsiqhOXHp+N+pTI/zNOc0NXCdaaLceh+tZHlc+E4napRfP53XXEhuGavjShTIJ/+g==
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. 20XNZrfJ4l/JIDjCbsba3mUOrNyOxJ2VuCju/yLc0XbdzqMcKJR87g3u967GEnoYY5f5+rJt/IHsuJWHcLApCQ==
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. eLRo9y8Rxf157qcciWM/LSUbtjYks2zLO5xQ9Ff5bidHc9m2XEqjWxqdPZz5gurEf+uPnM8mnix36X4YH4ZXwg==
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..5c5f004
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_broken_chain_02.signed
@@ -0,0 +1,65 @@
+; 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
+
+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 13 2 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ Qwf3qgLbSvE4PmUVU8rpIASe0v1T1K0ie3Lw
+ g+6o3tpBS8vWcmHMUiKns/6rAvoum7vHQRmO
+ dH7X3Pp1/X3xCw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ 92/D4j7CUCKkykxMzdjfJoaNrMwO93OQtZlB
+ APsfcEyYl+W0sSnow/2RgYvKfX+kdcmp5VXD
+ vQxTGC0VqdMwCQ== )
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ shdsucYBfD8/zV1h1QgUBiC7VgYdFxFEcF1k
+ FQfY+UHkfD/AyOkiFPQxysimgzqJn2/z5Q+v
+ GT1CzzzemgzoXw== )
+ 3600 DNSKEY 256 3 13 (
+ /4RnFpCmaYIIrL/zP1T6LvfhXdpun0ZyYDKL
+ ho0zuUD+RMDe31IQCzr9AuSn1BAIQWIunxFs
+ EaTSlvpiUd+CAg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 31772
+ 3600 DNSKEY 257 3 13 (
+ /KEwa6qUWHdkpEMGX55UaIvl7do5l2IADCDq
+ iNnawoCLu7Tm4MU6ylzYS1htz1mTd8Zcuzl0
+ gkRe4FXwOmOzvQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 14119
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008171141 14119 example.com.
+ FPftK2atu4GMOspSR24p5iIvmq2VKgPJMUTu
+ 5RiwflEf8UgD7s2WFe7A6/JLurwEhqa/313T
+ eEURk7m313h4jQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ +Xvcx4bZ536B8DtNwzurqmPPoDVdtS5nlRhQ
+ pMZ+OLsHECDnFaI50dSw4F1/c3DERz1ktM0+
+ QCC96MZ7QdAYQw== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ 64MPHHZG8wrbAtk+LY/5ISicI1vU7V19q9lF
+ wOm0mcpvoBERyDwadgZpmHsvin1sRt/LZYDr
+ iBKxcnaviHfULg== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ +/+14BFYf5Iq9IIeX1Oz5XqxsaPaw3T6PTPH
+ neJz6N9QhnI6aKkGZFYBuqY0Zhmcr52zbhPi
+ 1yZAUTP7OvouhA== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ fPqL9hFml35JLmfX3MA32hMnMhh9UA1Mc2OZ
+ nY+0j4wTtVR0PVMWHOv9UaULzTCM+5mlpFXm
+ nRUMj8sMTGzFzw== )
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..0cd6aec
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_multiple.signed
@@ -0,0 +1,66 @@
+; 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
+
+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 13 2 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ dEcgYVtA8cRE8ErOZGO/aaMat99+KuJdKoDc
+ 0+8fauQ3dcTUHVg2I+v4hdizjlmAJzGXJN+7
+ 6ssZgcvXCnWOsQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ 2OEk6Lpt+1c58vnCEHBrV7//7gyoo1bGJSHo
+ k+oWaF9Uh07XVkVWznq6mmCErqukUPLnW1Bn
+ rysjk4i5Yflqkg== )
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ icB72dzHg9d9klcTL/mW53mGIX6KzF0GLWUt
+ DKLCcu2Ailyp3kdM64dyJxRYTr7F7KfxyHi4
+ 3KJtphYNEA6ZWA== )
+ 3600 DNSKEY 256 3 13 (
+ H1roLYze5AZ+ouWMduBJtoJ8N5BPFdF3n6Pv
+ +Nfw5bNHUtCzgvMhmtX2gcRlmZ70Ycv1C/U+
+ mCvLWVdfJm08lA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 19445
+ 3600 DNSKEY 257 3 13 (
+ MSWkrHjEr7zi143oQdRthBBzl70MXeILunB7
+ 8j55a5a9+Q39YKaIiRM4zyCV6WTXpm9H6eOS
+ RRgdQqGNL1gsKQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 23836
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008170543 23836 example.com.
+ ejlk2L0CVBWuAxr1g+qivdvyIXqzp3+9U0tu
+ a2geLUtaVx8ErYnIvUug15S54g75+lZoZ1uK
+ l2WFWuy751kIsw== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ 8k4wk4+kCs1kO3+8sL6zZdpkHw0U58oua/Ur
+ C8CHo6TjlLx/jRrLdQKcFy5H7gBMcJY76SDs
+ mT91HuWH+BpwNA== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ 3XbwYx32/Y8sLtQ+dW1lg+s1eaOSZlmkdJeO
+ IsLOAF6U9kq/2zrUTYCtFBMfqs5yYDEISK6X
+ W5UfBBdFRdYzgw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ DDTolVJ5Mxfm8srRVi/SRu0+5y3OBTQCVFuQ
+ ywdv4IahQoE11pjXRCBUXvroTeDgoHrmD7PD
+ b1aIBxHLiC/2pg== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ DDhuGYMEij4vbJZlscX3os8qj/wgq55w63jc
+ 8mPr/LquDr6o6lrEYdcnZl4Rz22snnF2+po1
+ 3SEjRSJ0ROmTbw== )
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..28b118c
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed
@@ -0,0 +1,52 @@
+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 13 2 3600 (
+ 20601231235959 20201008172615 16105 example.com.
+ UkbSKt1soIfnM7ZkNAfOcS4D3eHBzMQOef1d
+ bFK+ne+MtJsKEGM9brUD23v0f0CdvteVkeNS
+ 2oRrfrb3avZ08A== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008172615 16105 example.com.
+ Mu/BsXIC10V5uRFUGR42/ntmT5eYt4192AQe
+ a5zdWnLo7A3GYHlPcOcZRMdqvsa3SAPOK2Br
+ UmFkHsWTawhWJQ== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008172615 16105 example.com.
+ IexJzu8x2GxGzGrWlceYZmUbry2D+E67py6B
+ /7j2K5IPjNQVGKbItfvqjQTUm+eVrdcwFbyK
+ iiEuVeU7qG5hIw== )
+ 3600 DNSKEY 256 3 13 (
+ tGxruia7b3JYm32MDdFLYX1M1e44DQJmXpVM
+ EWDjcNulSNY5sWR/zgDzhqiQSKEKCFolwhB/
+ MFVIF71WNjE65Q==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 16105
+ 3600 DNSKEY 257 3 13 (
+ 24gAMJg6uXIBEdWkrAXmwP6znng79lTelLDg
+ WxeHbXriSxVPLSTYxrp7SO1FUi2N03v1RXcn
+ 5jONJdQYlxLtSg==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 17031
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008172615 17031 example.com.
+ Tm4MkXCDkavltvRYnEp/enJzzjyjX3EgI8yY
+ OF2VuJY8uQHD0/uzZF3JTmXj7pkGShAUpFKI
+ Uzn5e3jrGqtMGA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+; wrong RRSIG original-ttl
+ 3600 RRSIG A 13 3 600 (
+ 20601231235959 20201008172615 16105 example.com.
+ 7J01Zyly+ky0F94kfaDtERQDVyxhHexzqETa
+ qgsemJkH0pP9FKsEY/dTkeZUwCY4EFZeps7C
+ AOKyGTKdqR5N7Q== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008172615 16105 example.com.
+ 0evb+3+rXrrx0f8Za//w6q2acUZPvYbW+Ezj
+ BoJFvwBYHrhyiiVHlfUzmr/jJh9cTEdxPnL3
+ ow6ZUsfF0HJ4hg== )
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..1aeef78
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_ttl.signed
@@ -0,0 +1,52 @@
+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 13 2 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ NaUbzn4tb3bsVI4O2YgrefFtZPJSYlLKbVKB
+ HyIqwfQjwdkbIKZ5tqH/IGJagvj8oxeStwF/
+ vEoG9c/o/MNs4g== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ YZqxQKpj3kxfRHxoQda1z9JD9nmX8uNJTBGV
+ qdMMU3cPOVamTzOqymseQYjBPaaeoxL1kyqk
+ K2w/ixOUCFp8qg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008165912 34876 example.com.
+ 88QLNDpFWd2FIag2vcKGvY1HQFVeOaRIiMU5
+ 2VZfLFOPBmuTniTcnPvCt76i5ObPVsWdwJhM
+ /7NVMxoRPfMC1w== )
+ 3600 DNSKEY 256 3 13 (
+ 9+7buhxES5wZQZ54+O1qQGuRcKz3P3URZwws
+ 30CacknPsdcWAy7RN1yYmUjP80geUrxJVQt3
+ boo1BwFW4Rnnsg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 34876
+ 3600 DNSKEY 257 3 13 (
+ eYNrBYFUn5JIhTlS3N0i2aFj1YE8127h3tlb
+ VJP9JAfMMxQT+Mg6lwDpUa0oQkNFbEoHhqrD
+ 0pcMvp4VeMgJ7g==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 36952
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008165912 36952 example.com.
+ AVF7u7FzDx2ORApl74nP2hcJd4Szs1o1LXH5
+ OWe6JULh80kITEb9zogpCryQu41bYSZYuxMk
+ yeblfo1OEI2DZg== )
+dns1.example.com. 3600 IN A 192.0.2.1
+; TTL of RRSIG differs from original-ttl
+ 600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ +PPg6tDZVS2mbxWXOtVEYTQtjK+CkwRk/WFZ
+ dWgX3rzHPQ9AIexC9vKbXdont3s0xdHpcV/8
+ +Sf+N2h44ZTwMQ== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008165912 34876 example.com.
+ OCjWQ/5e4SUIWgR84IJLlghKyuowctiZ+b0q
+ eXB0o2qpcWoX6wfxzMlYxGtpgyq3OWKF+R8H
+ UBVCdT+qBt5VOA== )
diff --git a/tests/knot/test_acl.c b/tests/knot/test_acl.c
new file mode 100644
index 0000000..2460e8a
--- /dev/null
+++ b/tests/knot/test_acl.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "test_conf.h"
+#include "libknot/libknot.h"
+#include "knot/updates/acl.c"
+#include "contrib/sockaddr.h"
+
+#define ZONE "example.zone"
+#define ZONE2 "example2.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);
+}
+
+void check_update(conf_t *conf, knot_rrset_t *authority, knot_tsig_key_t *key,
+ knot_dname_t *zone_name, bool allowed, const char *desc)
+{
+ struct sockaddr_storage addr;
+ check_sockaddr_set(&addr, AF_INET, "1.2.3.4", 0);
+
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ assert(query);
+ knot_pkt_begin(query, KNOT_AUTHORITY);
+ knot_pkt_put(query, 0, authority, 0);
+
+ knot_pkt_t *parsed = knot_pkt_new(query->wire, query->size, NULL);
+ ok(knot_pkt_parse(parsed, 0) == KNOT_EOK, "Parse update packet");
+
+ conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+
+ bool ret = acl_allowed(conf, &acl, ACL_ACTION_UPDATE, &addr, key,
+ zone_name, parsed, NULL);
+ ok(ret == allowed, "%s", desc);
+
+ knot_pkt_free(parsed);
+ knot_pkt_free(query);
+}
+
+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 *zone2_name = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2_name != NULL, "create zone2 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"
+ "remote:\n"
+ " - id: remote_v6_ko\n"
+ " address: [ 2009::1 ]\n"
+ " key: key1_md5\n"
+ " - id: remote_v6_ok\n"
+ " address: [ 127.0.0.1, 2001::1 ]\n"
+ " key: key1_md5\n"
+ "\n"
+ "acl:\n"
+ " - id: acl_key_addr\n"
+ " remote: [ remote_v6_ko, remote_v6_ok ]\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"
+ " - id: acl_deny_no_action_no_key\n"
+ " address: [ 240.0.0.4 ]\n"
+ " deny: on\n"
+ " - id: acl_notify_key\n"
+ " address: [ 240.0.0.0/24 ]\n"
+ " key: "KEY1"\n"
+ " action: [ notify ]\n"
+ " - id: acl_update_key\n"
+ " key: "KEY1"\n"
+ " update-owner: key\n"
+ " update-type: [ AAAA, A ]\n"
+ " action: [ update ]\n"
+ " - id: acl_update_name\n"
+ " key: "KEY2"\n"
+ " update-owner: name\n"
+ " update-owner-name: [ a, b."KEY2". ]\n"
+ " update-owner-match: equal\n"
+ " action: [ update ]\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 ]\n"
+ " - domain: "ZONE2"\n"
+ " acl: [ acl_deny_no_action_no_key, acl_notify_key ]\n"
+ " - domain: "KEY1"\n"
+ " acl: acl_update_key\n"
+ " - domain: "KEY2"\n"
+ " acl: acl_update_name";
+
+ 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_QUERY, &addr, &key1, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ 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, zone_name, NULL, NULL);
+ ok(ret == true, "IPv6 address from range, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone2_name);
+ ok(acl.code == KNOT_EOK, "Get zone2 ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.4", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone2_name, NULL, NULL);
+ ok(ret == false, "Address, key, action, denied");
+
+ acl = conf_zone_get(conf(), C_ACL, zone2_name);
+ ok(acl.code == KNOT_EOK, "Get zone2 ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone2_name, NULL, NULL);
+ ok(ret == true, "Address, key, action, match");
+
+ knot_rrset_t A;
+ knot_rrset_init(&A, key1_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&A, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &A, &key1, key1_name, true, "Update, tsig, type");
+
+ check_update(conf(), &A, &key2, key2_name, false, "Update, tsig, bad name");
+ knot_rdataset_clear(&A.rrs, NULL);
+
+ knot_rrset_t MX;
+ knot_rrset_init(&MX, key1_name, KNOT_RRTYPE_MX, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&MX, (uint8_t *)"\x00\x00\x00", 3, NULL);
+ check_update(conf(), &MX, &key1, key1_name, false, "Update, tsig, bad type");
+ knot_rdataset_clear(&MX.rrs, NULL);
+
+ knot_rrset_t aA;
+ knot_dname_t *a_key2_name = knot_dname_from_str_alloc("a."KEY2".");
+ ok(a_key2_name != NULL, "create a."KEY2".");
+ knot_rrset_init(&aA, a_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&aA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &aA, &key2, key2_name, true, "Update, tsig, relative name");
+ knot_dname_free(a_key2_name, NULL);
+ knot_rdataset_clear(&aA.rrs, NULL);
+
+ knot_rrset_t bA;
+ knot_dname_t *b_key2_name = knot_dname_from_str_alloc("b."KEY2".");
+ ok(b_key2_name != NULL, "create b."KEY2".");
+ knot_rrset_init(&bA, b_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&bA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &bA, &key2, key2_name, true, "Update, tsig, absolute name");
+ knot_dname_free(b_key2_name, NULL);
+ knot_rdataset_clear(&bA.rrs, NULL);
+
+ knot_rrset_t aaA;
+ knot_dname_t *aa_key2_name = knot_dname_from_str_alloc("a.a."KEY2);
+ ok(aa_key2_name != NULL, "create a.a."KEY2);
+ knot_rrset_init(&aaA, aa_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&aaA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &aaA, &key2, key2_name, false, "Update, tsig, bad name");
+ knot_dname_free(aa_key2_name, NULL);
+ knot_rdataset_clear(&aaA.rrs, NULL);
+
+ conf_free(conf());
+ knot_dname_free(zone_name, NULL);
+ knot_dname_free(zone2_name, NULL);
+ knot_dname_free(key1_name, NULL);
+ knot_dname_free(key2_name, NULL);
+ knot_dname_free(key3_name, NULL);
+}
+
+void check_pattern(const char *name_str, const char *pattern_str, bool match)
+{
+ knot_dname_t *name = knot_dname_from_str_alloc(name_str);
+ knot_dname_t *pattern = knot_dname_from_str_alloc(pattern_str);
+
+ ok(match_pattern(name, pattern) == match, "'%s' %s '%s'",
+ name_str, match ? "matched" : "not matched by", pattern_str);
+
+ knot_dname_free(name, NULL);
+ knot_dname_free(pattern, NULL);
+}
+
+static void test_match_pattern(void)
+{
+ check_pattern(".", "*", false);
+ check_pattern("a", "a", true);
+ check_pattern("a", "*", true);
+ check_pattern("*", "*", true);
+ check_pattern("a", "aa", false);
+ check_pattern("aa", "a", false);
+ check_pattern("a.b", "*", false);
+ check_pattern("a.b", "*.*", true);
+ check_pattern("a.b", "a.b", true);
+ check_pattern("a.b", "*.*b", false);
+ check_pattern("a.b", "*.*.*", false);
+ check_pattern("abc", "*", true);
+ check_pattern("a.bc.*", "a.*.*", true);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("acl_allowed");
+ test_acl_allowed();
+
+ diag("match_pattern");
+ test_match_pattern();
+
+ return 0;
+}
diff --git a/tests/knot/test_changeset.c b/tests/knot/test_changeset.c
new file mode 100644
index 0000000..6775b76
--- /dev/null
+++ b/tests/knot/test_changeset.c
@@ -0,0 +1,166 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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), "changeset: 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) == 0, "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, true);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, apex_spf_rr, true);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, other_rr, true);
+
+ 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, true),
+ "changeset: rem traversal");
+ changeset_iter_clear(&it);
+
+ // Test all traversal - just count.
+ ret = changeset_iter_all(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: 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 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..401fba5
--- /dev/null
+++ b/tests/knot/test_conf.c
@@ -0,0 +1,342 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#define CONFIG_DIR "/tmp"
+
+#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(conf(), 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);
+
+ char *filename = get_filename(conf(), NULL, z, name);
+ ok(filename == NULL, "Invalid name %s", name);
+ free(filename);
+
+ knot_dname_free(z, NULL);
+}
+
+static void test_get_filename(void)
+{
+ int ret = test_conf("", NULL);
+ is_int(KNOT_EOK, ret, "Prepare empty configuration");
+
+ // 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]", "/");
+
+ test_conf_free();
+}
+
+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);
+ }
+
+ test_conf_free();
+ 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);
+}
+
+static void test_mix_ref(void)
+{
+ const char *conf_string =
+ "remote:\n"
+ " - id: r1\n"
+ " address: ::1\n"
+ " - id: r2\n"
+ " address: ::2\n"
+ " - id: r3\n"
+ " address: ::3\n"
+ " - id: r4\n"
+ " address: ::4\n"
+ " - id: r5\n"
+ " address: ::5\n"
+ "remotes:\n"
+ " - id: rs2\n"
+ " remote: [r2]\n"
+ " - id: rs45\n"
+ " remote: [r4, r5]\n"
+ "\n"
+ "submission:\n"
+ " - id: t1\n"
+ " parent: [r1, rs2, r3, rs45]\n"
+ " - id: t2\n"
+ " parent: [rs45, r2, r1]\n";
+
+ int ret = test_conf(conf_string, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ size_t cnt1 = 0;
+ conf_val_t test1 = conf_rawid_get(conf(), C_SBM, C_PARENT, (const uint8_t *)"t1", 3);
+ conf_mix_iter_t iter1;
+ conf_mix_iter_init(conf(), &test1, &iter1);
+ while (iter1.id->code == KNOT_EOK) {
+ cnt1++;
+ conf_mix_iter_next(&iter1);
+ }
+ is_int(5, cnt1, "number of mixed references 1");
+
+ size_t cnt2 = 0;
+ conf_val_t test2 = conf_rawid_get(conf(), C_SBM, C_PARENT, (const uint8_t *)"t2", 3);
+ conf_mix_iter_t iter2;
+ conf_mix_iter_init(conf(), &test2, &iter2);
+ while (iter2.id->code == KNOT_EOK) {
+ cnt2++;
+ conf_mix_iter_next(&iter2);
+ }
+ is_int(4, cnt2, "number of mixed references 2");
+
+ test_conf_free();
+}
+
+static void check_addrs(struct sockaddr_storage *addr, struct sockaddr_storage *via,
+ int family, const char *addr_str, const char *via_str)
+{
+ struct sockaddr_storage ref = { 0 };
+
+ int set_ret = sockaddr_set(&ref, family, addr_str, 0);
+ int cmp_ret = sockaddr_cmp(&ref, addr, true);
+ is_int(KNOT_EOK, set_ret, "set address '%s'", addr_str);
+ is_int(KNOT_EOK, cmp_ret, "cmp address '%s'", addr_str);
+
+ if (via_str != NULL) {
+ set_ret = sockaddr_set(&ref, family, via_str, 0);
+ cmp_ret = sockaddr_cmp(&ref, via, true);
+ is_int(KNOT_EOK, set_ret, "set via '%s'", via_str);
+ is_int(KNOT_EOK, cmp_ret, "cmp via '%s'", via_str);
+ } else {
+ is_int(AF_UNSPEC, via->ss_family, "empty via");
+ }
+}
+
+static void test_conf_remote(void)
+{
+ const char *conf_string =
+ "remote:\n"
+ " - id: r1\n"
+ " address: [1::2, 1.0.0.2, 2::2, 3::2, 2.0.0.2]\n"
+ " via: [1::1, 2::1, 1.0.0.1, 2.0.0.1]\n"
+ " - id: r2\n"
+ " address: [1::2, 1.0.0.2, 2::2]\n"
+ " via: [1::1, 2::1, 3::1]\n"
+ "template:\n"
+ " - id: t1\n"
+ " notify: r1\n"
+ " - id: t2\n"
+ " notify: r2\n";
+
+ int ret = test_conf(conf_string, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ conf_remote_t r;
+ conf_val_t id;
+
+ id = conf_rawid_get(conf(), C_TPL, C_NOTIFY, (const uint8_t *)"t1", 3);
+ r = conf_remote(conf(), &id, 0);
+ check_addrs(&r.addr, &r.via, AF_INET6, "1::2", "1::1");
+ r = conf_remote(conf(), &id, 1);
+ check_addrs(&r.addr, &r.via, AF_INET, "1.0.0.2", "1.0.0.1");
+ r = conf_remote(conf(), &id, 2);
+ check_addrs(&r.addr, &r.via, AF_INET6, "2::2", "2::1");
+ r = conf_remote(conf(), &id, 3);
+ check_addrs(&r.addr, &r.via, AF_INET6, "3::2", "2::1");
+ r = conf_remote(conf(), &id, 4);
+ check_addrs(&r.addr, &r.via, AF_INET, "2.0.0.2", "2.0.0.1");
+
+ id = conf_rawid_get(conf(), C_TPL, C_NOTIFY, (const uint8_t *)"t2", 3);
+ r = conf_remote(conf(), &id, 0);
+ check_addrs(&r.addr, &r.via, AF_INET6, "1::2", "1::1");
+ r = conf_remote(conf(), &id, 1);
+ check_addrs(&r.addr, &r.via, AF_INET, "1.0.0.2", NULL);
+ r = conf_remote(conf(), &id, 2);
+ check_addrs(&r.addr, &r.via, AF_INET6, "2::2", "2::1");
+
+ test_conf_free();
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("get_filename");
+ test_get_filename();
+
+ diag("conf_zonefile");
+ test_conf_zonefile();
+
+ diag("mixed references");
+ test_mix_ref();
+
+ diag("conf_remote");
+ test_conf_remote();
+
+ return 0;
+}
diff --git a/tests/knot/test_conf.h b/tests/knot/test_conf.h
new file mode 100644
index 0000000..65adffb
--- /dev/null
+++ b/tests/knot/test_conf.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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, 0);
+ if (ret != KNOT_EOK) {
+ conf_free(new_conf);
+ return ret;
+ }
+
+ conf_update(new_conf, CONF_UPD_FNONE);
+
+ return KNOT_EOK;
+}
+
+static inline void test_conf_free(void)
+{
+ conf_update(NULL, CONF_UPD_FNONE);
+}
diff --git a/tests/knot/test_conf_tools.c b/tests/knot/test_conf_tools.c
new file mode 100644
index 0000000..dc83a06
--- /dev/null
+++ b/tests/knot/test_conf_tools.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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");
+}
+
+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);
+
+ return 0;
+}
diff --git a/tests/knot/test_confdb.c b/tests/knot/test_confdb.c
new file mode 100644
index 0000000..e149f8d
--- /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 <https://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 Wuninitialized
+ 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..44bc7c5
--- /dev/null
+++ b/tests/knot/test_confio.c
@@ -0,0 +1,1101 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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)
+{
+ knot_dname_txt_storage_t id;
+ 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.
+ {
+ char idx[2] = { '0' };
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ 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.
+ {
+ char idx[2] = { '0' };
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ 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");
+ char idx[2] = { '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("unknown", 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("unknown", 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("unknown", 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("unknown", 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("", NULL, NULL, false, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "list empty key0");
+ ok(conf_io_list("unknown", NULL, NULL, false, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "list unknown key0");
+ ok(conf_io_list("include", NULL, NULL, false, true, &io) ==
+ KNOT_EOK, "list non-group item");
+ ok(conf_io_list("template", NULL, NULL, false, false, &io) ==
+ KNOT_TXN_ENOTEXISTS, "no active txn");
+
+ // Desc schema.
+ *out = '\0';
+ ok(conf_io_list(NULL, NULL, NULL, true, true, &io) ==
+ KNOT_EOK, "list schema");
+ ref = "server\n"
+ "xdp\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", NULL, NULL, true, true, &io) ==
+ KNOT_EOK, "list group");
+ ref = "server.version\n"
+ "server.listen\n"
+ "server.tcp-idle-timeout\n"
+ "server.tcp-io-timeout\n"
+ "server.tcp-remote-io-timeout\n"
+ "server.tcp-max-clients\n"
+ "server.tcp-reuseport\n"
+ "server.tcp-fastopen\n"
+ "server.quic-max-clients\n"
+ "server.quic-idle-close-timeout\n"
+ "server.quic-outbuf-max-size\n"
+ "server.socket-affinity\n"
+ "server.udp-workers\n"
+ "server.tcp-workers\n"
+ "server.background-workers\n"
+ "server.udp-max-payload\n"
+ "server.udp-max-payload-ipv4\n"
+ "server.udp-max-payload-ipv6\n"
+ "server.edns-client-subnet\n"
+ "server.answer-rotation\n"
+ "server.automatic-acl\n"
+ "server.dbus-event";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List item options.
+ *out = '\0';
+ ok(conf_io_list("zone", "zonefile-load", NULL, true, true, &io) ==
+ KNOT_EOK, "list item options");
+ ref = "zone.zonefile-load = \"opt1\"\n"
+ "zone.zonefile-load = \"opt2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List zone identifiers 1.
+ *out = '\0';
+ ok(conf_io_list("zone", NULL, NULL, false, true, &io) ==
+ KNOT_EOK, "list zone identifiers 1");
+ ref = "zone[zone1.]\n"
+ "zone[zone2.]\n"
+ "zone[zone3.]";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List zone identifiers 2.
+ *out = '\0';
+ ok(conf_io_list("zone", "domain", NULL, false, true, &io) ==
+ KNOT_EOK, "list zone identifiers 2");
+ ref = "zone = \"zone1.\"\n"
+ "zone = \"zone2.\"\n"
+ "zone = \"zone3.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List item values.
+ *out = '\0';
+ ok(conf_io_list("server", "listen", NULL, false, true, &io) ==
+ KNOT_EOK, "list item values");
+ ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List multi-group item values.
+ *out = '\0';
+ ok(conf_io_list("zone", "file", "zone1", false, true, &io) ==
+ KNOT_EOK, "list multi-group item values");
+ ref = "zone[zone1.].file = \"name\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+}
+
+static const yp_item_t desc_server[] = {
+ { C_VERSION, YP_TSTR, YP_VNONE },
+ { C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI },
+ // Required config cache items - assert fix.
+ { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_IO_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE },
+ { C_TCP_FASTOPEN, YP_TBOOL, YP_VNONE },
+ { C_QUIC_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_QUIC_IDLE_CLOSE, YP_TINT, YP_VNONE },
+ { C_QUIC_OUTBUF_MAX_SIZE, YP_TINT, YP_VNONE },
+ { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE },
+ { C_UDP_WORKERS, YP_TINT, YP_VNONE },
+ { C_TCP_WORKERS, YP_TINT, YP_VNONE },
+ { C_BG_WORKERS, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD_IPV4, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD_IPV6, YP_TINT, YP_VNONE },
+ { C_ECS, YP_TBOOL, YP_VNONE },
+ { C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
+ { C_AUTO_ACL, YP_TBOOL, YP_VNONE },
+ { C_DBUS_EVENT, YP_TOPT, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_xdp[] = {
+ { C_UDP, YP_TBOOL, YP_VNONE },
+ { C_TCP, YP_TBOOL, YP_VNONE },
+ { C_QUIC, YP_TBOOL, YP_VNONE },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_TCP_INBUF_MAX_SIZE, YP_TINT, YP_VNONE },
+ { C_TCP_OUTBUF_MAX_SIZE,YP_TINT, YP_VNONE },
+ { C_TCP_IDLE_CLOSE, YP_TINT, YP_VNONE },
+ { C_TCP_IDLE_RESET, YP_TINT, YP_VNONE },
+ { C_TCP_RESEND, YP_TINT, YP_VNONE },
+ { C_ROUTE_CHECK, 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 }
+};
+
+static const knot_lookup_t opts[] = {
+ { 1, "opt1" },
+ { 2, "opt2" },
+ { 0, NULL }
+};
+
+#define ZONE_ITEMS \
+ { C_FILE, YP_TSTR, YP_VNONE }, \
+ { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
+ { C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE }, \
+ { C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE }, \
+ { C_SERIAL_MODULO, YP_TSTR, YP_VSTR = { "0/1" } }, \
+ { C_CATALOG_ROLE, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_CATALOG_TPL, YP_TREF, YP_VREF = { C_RMT } }, \
+ { 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_XDP, YP_TGRP, YP_VGRP = { desc_xdp } },
+ { 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_digest.c b/tests/knot/test_digest.c
new file mode 100644
index 0000000..a694e49
--- /dev/null
+++ b/tests/knot/test_digest.c
@@ -0,0 +1,474 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "knot/zone/digest.h"
+
+#include <string.h>
+#include <tap/basic.h>
+
+#include "knot/zone/zonefile.h"
+#include "libzscanner/scanner.h"
+
+// copy-pasted from knot/zone/zonefile.c
+static void process_data(zs_scanner_t *scanner)
+{
+ zcreator_t *zc = scanner->process.data;
+ if (zc->ret != KNOT_EOK) {
+ scanner->state = ZS_STATE_STOP;
+ return;
+ }
+
+ knot_dname_t *owner = knot_dname_copy(scanner->r_owner, NULL);
+ if (owner == NULL) {
+ zc->ret = KNOT_ENOMEM;
+ return;
+ }
+
+ knot_rrset_t rr;
+ knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class, scanner->r_ttl);
+
+ int ret = knot_rrset_add_rdata(&rr, scanner->r_data, scanner->r_data_length, NULL);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ ret = knot_rrset_rr_to_canonical(&rr);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ zc->ret = zcreator_step(zc, &rr);
+ knot_rrset_clear(&rr, NULL);
+}
+
+static void process_error(zs_scanner_t *s)
+{
+ (void)s;
+ assert(0);
+}
+
+static zone_contents_t *str2contents(const char *zone_str)
+{
+ knot_dname_txt_storage_t origin_str;
+ sscanf(zone_str, "%s", origin_str); // NOTE assuming that first token in zone_str is origin name!
+
+ knot_dname_t *origin = knot_dname_from_str_alloc(origin_str);
+ assert(origin != NULL);
+
+ zone_contents_t *cont = zone_contents_new(origin, false);
+ assert(cont != NULL);
+ knot_dname_free(origin, NULL);
+
+ zcreator_t zc = { cont, true, KNOT_EOK };
+
+ zs_scanner_t sc;
+ ok(zs_init(&sc, origin_str, KNOT_CLASS_IN, 3600) == 0 &&
+ zs_set_input_string(&sc, zone_str, strlen(zone_str)) == 0 &&
+ zs_set_processing(&sc, process_data, process_error, &zc) == 0 &&
+ zs_parse_all(&sc) == 0, "zscanner initialization");
+ zs_deinit(&sc);
+
+ return cont;
+}
+
+static int check_contents(const char *zone_str)
+{
+ zone_contents_t *cont = str2contents(zone_str);
+ int ret = zone_contents_digest_verify(cont);
+ zone_contents_deep_free(cont);
+ return ret;
+}
+
+const char *simple_zone = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *complex_zone = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ a3b69bad980a3504 \n\
+ e1cffcb0fd6397f9 \n\
+ 3848071c93151f55 \n\
+ 2ae2f6b1711d4bd2 \n\
+ d8b39808226d7b9d \n\
+ b71e34b72077f8fe ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+NS2 3600 IN AAAA 2001:db8::63 \n\
+occluded.sub 7200 IN TXT \"I'm occluded but must be digested\" \n\
+sub 7200 IN NS ns1 \n\
+duplicate 300 IN TXT \"I must be digested just once\" \n\
+duplicate 300 IN TXT \"I must be digested just once\" \n\
+foo.test. 555 IN TXT \"out-of-zone data must be excluded\" \n\
+UPPERCASE 3600 IN TXT \"canonicalize uppercase owner names\" \n\
+* 777 IN PTR dont-forget-about-wildcards \n\
+mail 3600 IN MX 20 MAIL1 \n\
+mail 3600 IN MX 10 Mail2.Example. \n\
+sortme 3600 IN AAAA 2001:db8::5:61 \n\
+sortme 3600 IN AAAA 2001:db8::3:62 \n\
+sortme 3600 IN AAAA 2001:db8::4:63 \n\
+sortme 3600 IN AAAA 2001:db8::1:65 \n\
+sortme 3600 IN AAAA 2001:db8::2:64 \n\
+non-apex 900 IN ZONEMD 2018031900 1 1 ( \n\
+ 616c6c6f77656420 \n\
+ 6275742069676e6f \n\
+ 7265642e20616c6c \n\
+ 6f77656420627574 \n\
+ 2069676e6f726564 \n\
+ 2e20616c6c6f7765 )";
+
+const char *multiple_digests = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+example. 86400 IN NS ns1.example. \n\
+example. 86400 IN NS ns2.example. \n\
+example. 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ 62e6cf51b02e54b9 \n\
+ b5f967d547ce4313 \n\
+ 6792901f9f88e637 \n\
+ 493daaf401c92c27 \n\
+ 9dd10f0edb1c56f8 \n\
+ 080211f8480ee306 ) \n\
+example. 86400 IN ZONEMD 2018031900 1 2 ( \n\
+ 08cfa1115c7b948c \n\
+ 4163a901270395ea \n\
+ 226a930cd2cbcf2f \n\
+ a9a5e6eb85f37c8a \n\
+ 4e114d884e66f176 \n\
+ eab121cb02db7d65 \n\
+ 2e0cc4827e7a3204 \n\
+ f166b47e5613fd27 ) \n\
+example. 86400 IN ZONEMD 2018031900 1 240 ( \n\
+ e2d523f654b9422a \n\
+ 96c5a8f44607bbee ) \n\
+example. 86400 IN ZONEMD 2018031900 241 1 ( \n\
+ e1846540e33a9e41 \n\
+ 89792d18d5d131f6 \n\
+ 05fc283e ) \n\
+ns1.example. 3600 IN A 203.0.113.63 \n\
+ns2.example. 86400 IN TXT \"This example has multiple digests\" \n\
+NS2.EXAMPLE. 3600 IN AAAA 2001:db8::63";
+
+const char *signed_zone = "\
+uri.arpa. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018100702 10800 3600 1209600 3600 \n\
+uri.arpa. 3600 IN RRSIG SOA 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. GzQw+QzwLDJr13REPGVmpEChjD1D2XlX0ie1DnWHpgaEw1E/dhs3lCN3 +BmHd4Kx3tffTRgiyq65HxR6feQ5v7VmAifjyXUYB1DZur1eP5q0Ms2y gCB3byoeMgCNsFS1oKZ2LdzNBRpy3oace8xQn1SpmHGfyrsgg+WbHKCT 1dY= \n\
+uri.arpa. 86400 IN NS a.iana-servers.net. \n\
+uri.arpa. 86400 IN NS b.iana-servers.net. \n\
+uri.arpa. 86400 IN NS c.iana-servers.net. \n\
+uri.arpa. 86400 IN NS ns2.lacnic.net. \n\
+uri.arpa. 86400 IN NS sec3.apnic.net. \n\
+uri.arpa. 86400 IN RRSIG NS 8 2 86400 20210217232440 20210120232440 37444 uri.arpa. M+Iei2lcewWGaMtkPlrhM9FpUAHXFkCHTVpeyrjxjEONeNgKtHZor5e4 V4qJBOzNqo8go/qJpWlFBm+T5Hn3asaBZVstFIYky38/C8UeRLPKq1hT THARYUlFrexr5fMtSUAVOgOQPSBfH3xBq/BgSccTdRb9clD+HE7djpqr LS4= \n\
+uri.arpa. 600 IN MX 10 pechora.icann.org. \n\
+uri.arpa. 600 IN RRSIG MX 8 2 600 20210217232440 20210120232440 37444 uri.arpa. kQAJQivmv6A5hqYBK8h6Z13ESY69gmosXwKI6WE09I8RFetfrxr24ecd nYd0lpnDtgNNSoHkYRSOoB+C4+zuJsoyAAzGo9uoWMWj97/2xeGhf3PT C9meQ9Ohi6hul9By7OR76XYmGhdWX8PBi60RUmZ1guslFBfQ8izwPqzu phs= \n\
+uri.arpa. 3600 IN NSEC ftp.uri.arpa. NS SOA MX RRSIG NSEC DNSKEY ZONEMD \n\
+uri.arpa. 3600 IN RRSIG NSEC 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. dU/rXLM/naWd1+1PiWiYVaNJyCkiuyZJSccr91pJI673T8r3685B4ODM YFafZRboVgwnl3ZrXddY6xOhZL3n9V9nxXZwjLJ2HJUojFoKcXTlpnUy YUYvVQ2kj4GHAo6fcGCEp5QFJ2KbCpeJoS+PhKGRRx28icCiNT4/uXQv O2E= \n\
+uri.arpa. 3600 IN DNSKEY 256 3 8 AwEAAbMxuFuLeVDuOwIMzYOTD/bTREjLflo7wOi6ieIJhqltEzgjNzmW Jf9kGwwDmzxU7kbthMEhBNBZNn84zmcyRSCMzuStWveL7xmqqUlE3swL 8kLOvdZvc75XnmpHrk3ndTyEb6eZM7slh2C63Oh6K8VR5VkiZAkEGg0u ZIT3NjsF \n\
+uri.arpa. 3600 IN DNSKEY 257 3 8 AwEAAdkTaWkZtZuRh7/OobBUFxM+ytTst+bCu0r9w+rEwXD7GbDs0pIM hMenrZzoAvmv1fQxw2MGs6Ri6yPKfNULcFOSt9l8i6BVBLI+SKTY6XXe DUQpSEmSaxohHeRPMQFzpysfjxINp/L2rGtZ7yPmxY/XRiFPSO0myqwG Ja9r06Zw9CHM5UDHKWV/E+zxPFq/I7CfPbrrzbUotBX7Z6Vh3Sarllbe 8cGUB2UFNaTRgwB0TwDBPRD5ER3w2Dzbry9NhbElTr7vVfhaGWeOGuqA UXwlXEg6CrNkmJXJ2F1Rzr9WHUzhp7uWxhAbmJREGfi2dEyPAbUAyCjB qhFaqglknvc= \n\
+uri.arpa. 3600 IN DNSKEY 257 3 8 AwEAAenQaBoFmDmvRT+/H5oNbm0Tr5FmNRNDEun0Jpj/ELkzeUrTWhNp QmZeIMC8I0kZ185tEvOnRvn8OvV39B17QIdrvvKGIh2HlgeDRCLolhao jfn2QM0DStjF/WWHpxJOmE6CIuvhqYEU37yoJscGAPpPVPzNvnL1HhYT aao1VRYWQ/maMrJ+bfHg+YX1N6M/8MnRjIKBif1FWjbCKvsn6dnuGGL9 oCWYUFJ3DwofXuhgPyZMkzPc88YkJj5EMvbMH4wtelbCwC+ivx732l0w /rXJn0ciQSOgoeVvDio8dIJmWQITWQAuP+q/ZHFEFHPlrP3gvQh5mcVS 48eLX71Bq7c= \n\
+uri.arpa. 3600 IN RRSIG DNSKEY 8 2 3600 20210217232440 20210120232440 12670 uri.arpa. DBE2gkKAoxJCfz47KKxzoImN/0AKArhIVHE7TyTwy0DdRPo44V5R+vL6 thUxlQ1CJi2Rw0jwAXymx5Y3Q873pOEllH+4bJoIT4dmoBmPXfYWW7Cl vw9UPKHRP0igKHmCVwIeBYDTU3gfLcMTbR4nEWPDN0GxlL1Mf7ITaC2I oabo79Ip3M/MR8I3Vx/xZ4ZKKPHtLn3xUuJluPNanqJrED2gTslL2xWZ 1tqjsAjJv7JnJo2HJ8XVRB5zBto0IaJ2oBlqcjdcQ/0VlyoM8uOy1pDw HQ2BJl7322gNMHBP9HSiUPIOaIDNUCwW8eUcW6DIUk+s9u3GN1uTqwWz sYB/rA== \n\
+uri.arpa. 3600 IN RRSIG DNSKEY 8 2 3600 20210217232440 20210120232440 30577 uri.arpa. Kx6HwP4UlkGc1UZ7SERXtQjPajOF4iUvkwDj7MEG1xbQFB1KoJiEb/ei W0qmSWdIhMDv8myhgauejRLyJxwxz8HDRV4xOeHWnRGfWBk4XGYwkejV zOHzoIArVdUVRbr2JKigcTOoyFN+uu52cNB7hRYu7dH5y1hlc6UbOnzR pMtGxcgVyKQ+/ARbIqGG3pegdEOvV49wTPWEiyY65P2urqhvnRg5ok/j zwAdMx4XGshiib7Ojq0sRVl2ZIzj4rFgY/qsSO8SEXEhMo2VuSkoJNio fVzYoqpxEeGnANkIT7Tx2xJL1BWyJxyc7E8Wr2QSgCcc+rYL6IkHDtJG Hy7TaQ== \n\
+uri.arpa. 3600 IN ZONEMD 2018100702 1 1 0DBC3C4DBFD75777C12CA19C337854B1577799901307C482E9D91D5D 15CD934D16319D98E30C4201CF25A1D5A0254960 \n\
+uri.arpa. 3600 IN RRSIG ZONEMD 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. QDo4XZcL3HMyn8aAHyCUsu/Tqj4Gkth8xY1EqByOb8XOTwVtA4ZNQORE 1siqNqjtJUbeJPtJSbLNqCL7rCq0CzNNnBscv6IIf4gnqJZjlGtHO30o hXtKvEc4z7SU3IASsi6bB3nLmEAyERdYSeU6UBfx8vatQDIRhkgEnnWU Th4= \n\
+ftp.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^ftp://([^:/?#]*).*$!\\\\1!i\" . \n\
+ftp.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. EygekDgl+Lyyq4NMSEpPyOrOywYf9Y3FAB4v1DT44J3R5QGidaH8l7ZF jHoYFI8sY64iYOCV4sBnX/dh6C1L5NgpY+8l5065Xu3vvjyzbtuJ2k6Y YwJrrCbvl5DDn53zAhhO2hL9uLgyLraZGi9i7TFGd0sm3zNyUF/EVL0C cxU= \n\
+ftp.uri.arpa. 3600 IN NSEC http.uri.arpa. NAPTR RRSIG NSEC \n\
+ftp.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. pbP4KxevPXCu/bDqcvXiuBppXyFEmtHyiy0eAN5gS7mi6mp9Z9bWFjx/ LdH9+6oFGYa5vGmJ5itu/4EDMe8iQeZbI8yrpM4TquB7RR/MGfBnTd8S +sjyQtlRYG7yqEu77Vd78Fme22BKPJ+MVqjS0JHMUE/YUGomPkAjLJJw wGw= \n\
+http.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^http://([^:/?#]*).*$!\\\\1!i\" . \n\
+http.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. eTqbWvt1GvTeXozuvm4ebaAfkXFQKrtdu0cEiExto80sHIiCbO0WL8UD a/J3cDivtQca7LgUbOb6c17NESsrsVkc6zNPx5RK2tG7ZQYmhYmtqtfg 1oU5BRdHZ5TyqIXcHlw9Blo2pir1Y9IQgshhD7UOGkbkEmvB1Lrd0aHh AAg= \n\
+http.uri.arpa. 3600 IN NSEC mailto.uri.arpa. NAPTR RRSIG NSEC \n\
+http.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. R9rlNzw1CVz2N08q6DhULzcsuUm0UKcPaGAWEU40tr81jEDHsFHNM+kh CdOI8nDstzA42aee4rwCEgijxJpRCcY9hrO1Ysrrr2fdqNz60JikMdar vU5O0p0VXeaaJDfJQT44+o+YXaBwI7Qod3FTMx7aRib8i7istvPm1Rr7 ixA= \n\
+mailto.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^mailto:(.*)@(.*)$!\\\\2!i\" . \n\
+mailto.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. Ch2zTG2F1plEvQPyIH4Yd80XXLjXOPvMbiqDjpJBcnCJsV8QF7kr0wTL nUT3dB+asQudOjPyzaHGwFlMzmrrAsszN4XAMJ6htDtFJdsgTMP/NkHh YRSmVv6rLeAhd+mVfObY12M//b/GGVTjeUI/gJaLW0fLVZxr1Fp5U5CR jyw= \n\
+mailto.uri.arpa. 3600 IN NSEC urn.uri.arpa. NAPTR RRSIG NSEC \n\
+mailto.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. fQUbSIE6E7JDi2rosah4SpCOTrKufeszFyj5YEavbQuYlQ5cNFvtm8Ku E2xXMRgRI4RGvM2leVqcoDw5hS3m2pOJLxH8l2WE72YjYvWhvnwc5Rof e/8yB/vaSK9WCnqN8y2q6Vmy73AGP0fuiwmuBra7LlkOiqmyx3amSFiz wms= \n\
+urn.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"/urn:([^:]+)/\\\\1/i\" . \n\
+urn.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. CVt2Tgz0e5ZmaSXqRfNys/8OtVCk9nfP0zhezhN8Bo6MDt6yyKZ2kEEW JPjkN7PCYHjO8fGjnUn0AHZI2qBNv7PKHcpR42VY03q927q85a65weOO 1YE0vPYMzACpua9TOtfNnynM2Ws0uN9URxUyvYkXBdqOC81N3sx1dVEL cwc= \n\
+urn.uri.arpa. 3600 IN NSEC uri.arpa. NAPTR RRSIG NSEC \n\
+urn.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. JuKkMiC3/j9iM3V8/izcouXWAVGnSZjkOgEgFPhutMqoylQNRcSkbEZQ zFK8B/PIVdzZF0Y5xkO6zaKQjOzz6OkSaNPIo1a7Vyyl3wDY/uLCRRAH RJfpknuY7O+AUNXvVVIEYJqZggd4kl/Rjh1GTzPYZTRrVi5eQidI1LqC Oeg=";
+
+const char *nsec3_zone = "\
+arpa. 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2021051902 1800 900 604800 86400 \n\
+arpa. 518400 IN NS a.root-servers.net. \n\
+arpa. 518400 IN NS b.root-servers.net. \n\
+arpa. 518400 IN NS c.root-servers.net. \n\
+arpa. 518400 IN NS d.root-servers.net. \n\
+arpa. 518400 IN NS e.root-servers.net. \n\
+arpa. 518400 IN NS f.root-servers.net. \n\
+arpa. 518400 IN NS g.root-servers.net. \n\
+arpa. 518400 IN NS h.root-servers.net. \n\
+arpa. 518400 IN NS i.root-servers.net. \n\
+arpa. 518400 IN NS k.root-servers.net. \n\
+arpa. 518400 IN NS l.root-servers.net. \n\
+arpa. 518400 IN NS m.root-servers.net. \n\
+arpa. 518400 IN RRSIG NS 8 1 518400 20210616170429 20210519170429 29094 arpa. gyq/RdMYEGuTElq9QCbqmZSEUAF3aeBc+MGOMVK0hgmYKfVr8DDrh9UZJy4Ht+24+FHXGgAh8OkW4UbnmiIHQnsSflbQiyHljNYZGX3/H2fUs2FFWAjjAww2iPKuuPUkHgjZZQk0683FQuI9Ium0VK7dXGAvNKFh4Ay4LMjkQ6Y= \n\
+arpa. 86400 IN RRSIG SOA 8 1 86400 20210616170429 20210519170429 29094 arpa. BnSptCxxljkkYItDfsphqUzCz4fALNhOqWrLtYx5aDRWAydcG0N7owhGTqy56VBop+lTzYKmlHfO5/bb/fRCYAXkDhsmVEqS00cDYTqpygTJbVB8Xd+ia1tBeF8cqsbngRhigF4y0cts+bkn7Wrvw21j7nhs01KROimudGH08hs= \n\
+arpa. 86400 IN RRSIG DNSKEY 8 1 86400 20210616170429 20210519170429 18949 arpa. be6sPsu3+7kzDMkAHDsUM0FSoUhULtajWemX95PIVS4wpiEpVMsvF71YLIGRTzw+GfFI2NgsL/idFbUW2Fo7bZIBhbj8JXyZwvsoxt+cLfSfZtVGllKO1XQn5u7/PGU6U8YRSyzRA+ocpdjKqyohkMmOqiqkM7mOSvchDkcZDiw= \n\
+arpa. 3600 IN RRSIG NSEC3PARAM 8 1 3600 20210616170429 20210519170429 29094 arpa. CHmmYN1DJGWraPdMPurcXadDO7ODWoz6gv0B7ln0Gwz5L4Mwb5SEtGAinO5R0T2M4OxQEkN0xhy73VERrZb5FvsxyEGJu0M5S6icvyKkJ1Zq+US5b3FX6MI/bIKu2pI5x7/ubpzWKZJ9itNWBRONBiuBsGT9c3Tb2IreuQWziH0= \n\
+arpa. 86400 IN RRSIG TYPE63 8 1 86400 20210616170429 20210519170429 29094 arpa. Aqb9IQoNaga9euw67potZbiQYeyEAqd/zVYhDFxfLNfC4Qf6v7aPxW8Tyl+foNob91/KX5JGcS5tD4pq+G+IV+heLRH57s+moF3C0lsid8oZLqCbctmR/hr0YUQc5+dGQ/iy2erEPZq1W4eLsWX+YlUsQfajb5y4ggp7OMTmRuY= \n\
+arpa. 86400 IN DNSKEY 256 3 8 AwEAAdMaRW2okM0GrfInisiH9HWsqokdnmeXnJjKUwVQ8dy5sxm0DyCtzNapj54SF4ofgJxYufQCzYoe3Y3WsB6dKW15pTvu6ggqwuTTxvAnkMSHAlMGBE0sybRBIM38WswPcjAXmpITj7Zvgm8qh80dcusK5vwqJhb2CDWHRezUwiIB ;{id = 29094 (zsk), size = 1024b} \n\
+arpa. 86400 IN DNSKEY 257 3 8 AwEAAdQP1t2ookuQYFNUNGDmLHcoA6LFSImvULaUgChKiIO6Vv5yDyHB0Ng6ZkfHM0586cLcbXNBLj/9u5A4vqzOFj8phzW4WLZREZBLYMcuHhvQdqzuDJ0J5mxmLLis5eNaCwukVm6Zpf/otzCJsx9LyrhQBTyx6FF+h7dbSCvjh7tD ;{id = 18949 (ksk), size = 1024b} \n\
+arpa. 3600 IN NSEC3PARAM 1 0 1 - \n\
+arpa. 86400 IN TYPE63 \\# 54 7876cdfe01019a84145013e13e3de2328868888c65aa46b7381213990f83d496c642d2324029cc852e09bffa38afd8e9197977776591 \n\
+0js82oec35lbbc4hl35476cm5icacksf.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. PRVkH4+Nm17QlFgwFLnoqwaiIwWZ4pvscanHdMb6HOKkSxwtDoWAGhZubvYGt/Je735nQkGQPPXW2tkMkJa3D7e6RkX/8AoxcqqXOimC6BlG6LuSL4rSousDlbrulyh87qgIHXkUtrHyYUNAMZMKOjMHo7t5IxwjBO0SGADoglk= \n\
+0js82oec35lbbc4hl35476cm5icacksf.arpa. 86400 IN NSEC3 1 0 1 - 2UB8EN7BK0T6DENIGO3I729IVQVME3VE NS \n\
+2ub8en7bk0t6denigo3i729ivqvme3ve.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. JsSiqDiPs0juQxKEcCKTFvKXzUdvIvCILEzcN79+qAxaiQuulHUxTSMDvrsxm83m9juvoOUYtBlPyZdI9erAfiEkpF71ZIl8iP7AKGgqTeV1C4SHnf2KsFi69qimdLbWeIfFGYEq+54Vj5vF1SrRounvj63avhI/Zf0tTWz11+4= \n\
+2ub8en7bk0t6denigo3i729ivqvme3ve.arpa. 86400 IN NSEC3 1 0 1 - 3MKQ4F9MV3H6JSJNUJ6G31KRJLHKN9KJ NS DS RRSIG \n\
+3mkq4f9mv3h6jsjnuj6g31krjlhkn9kj.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. NAt7ul6uWzK19LyTcxbtfIt0SppVHyVjj4S/j0zxqcOH7gkJwf36+uIsb0lP7QzdYoB7dDeMFKnZfOCjBu+OkXTnOmfdwS5XA5OTM3dpi6g8plVRkcBDoWqz+UtQljD66A2XyuVl5vBmhP3OWe8TnlnA3jrHYO5zneEM/MdsoEE= \n\
+3mkq4f9mv3h6jsjnuj6g31krjlhkn9kj.arpa. 86400 IN NSEC3 1 0 1 - BA4462JFP3IQK2KT4COIMT6532KSV55K NS DS RRSIG \n\
+as112.arpa. 172800 IN NS a.iana-servers.net. \n\
+as112.arpa. 172800 IN NS b.iana-servers.net. \n\
+as112.arpa. 172800 IN NS c.iana-servers.net. \n\
+as112.arpa. 86400 IN DS 20236 8 1 1307e5595598b25fe2eb07bcef767c9d96c3ecdc \n\
+as112.arpa. 86400 IN DS 20236 8 2 72c9e5d15accc54a32c8c76fe5944bcbf3aabc2b13dc417609763e57bd89d515 \n\
+as112.arpa. 86400 IN DS 49400 8 1 0236339d6c1fb0fdf6069a9babe455b443fe2f95 \n\
+as112.arpa. 86400 IN DS 49400 8 2 f8e230e43e20e14200e46beb6e0a67ced274790c8c8c169df7fec5fb7dfa321f \n\
+as112.arpa. 86400 IN DS 53690 8 1 85d712965f3aa6556f40e11ba29c638565444acf \n\
+as112.arpa. 86400 IN DS 53690 8 2 354c6ef7b8b46a4c87ce6a21f3a9043898e68427ad64d029097ce2a38933b82e \n\
+as112.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. Hs6t8f1s8NCPO1yzQIqCWWpGADwHqTVLCRVJIxMkpiWpDPP8zXxQRFp2BHNQ8jAcsp5w5OwIfIR27+5N7O73/y5qjcjDe6Yyzeh7L/nut0fuOuqne47a6VkuXJHmdilGeNFitAFZ+1iP9KnFVxb3NxNLByemx8mO30jYDw14O4Y= \n\
+ba4462jfp3iqk2kt4coimt6532ksv55k.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. MrpAQuo8eH4CAA2jjsLHGiMJ8DexXMDI7LHzQbX7k5L4oUTtBNoTnKFdxqKdxZoEXvO39GB5s0nD0qgR8g5xFAFfj+pcF2y4GC+LqXqV5N6gXKa23zEEN5mfxSuwnQ/JXw95ct2IuQkuU80MIU0ZdE/FVhSyHnlJYMGE3uB2DyY= \n\
+ba4462jfp3iqk2kt4coimt6532ksv55k.arpa. 86400 IN NSEC3 1 0 1 - C26TIAI64HA5JPB4P8KII6P9JHH3TJFH NS DS RRSIG \n\
+c26tiai64ha5jpb4p8kii6p9jhh3tjfh.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. FSQuCmqKEUtYHqhkXDC8uikAIi5ZpMtS14jeaeWEn6Mip3uP1pFNuSQHgFhX9L20hdbeuOG3ribTqs3d4kz9VQ51g4KqD3uhHMVuQZyzpBJWq4Xwynt9cetvSK0f/kaf/wtAARo9HLkciJTBYiYUmYZVdmknIto4TqDNy2kkMrA= \n\
+c26tiai64ha5jpb4p8kii6p9jhh3tjfh.arpa. 86400 IN NSEC3 1 0 1 - DKAS8UE0E261D6338P2GMF52ALH64LA6 NS DS RRSIG \n\
+dkas8ue0e261d6338p2gmf52alh64la6.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. pPD9lqm6kAoLwagCrQwBWBq4McfrHywg4RkQ20ZjuVcnmopggO6UkjlmYUnBn53Si5eqRY9CwtSEvYjKztXcyXnkwbD1xWExAsYucRYVbUPZmOllulYezphTHi1Qp7fRrhEjb/TCYcBUXvLJfU+S9OeVqefruYnIw3VevMPp518= \n\
+dkas8ue0e261d6338p2gmf52alh64la6.arpa. 86400 IN NSEC3 1 0 1 - EARMJ48JEL1C2RDHIGD36N68U3V8Q1KV NS DS RRSIG \n\
+e164.arpa. 172800 IN NS ns3.lacnic.net. \n\
+e164.arpa. 172800 IN NS ns3.afrinic.net. \n\
+e164.arpa. 172800 IN NS ns4.apnic.net. \n\
+e164.arpa. 172800 IN NS pri.authdns.ripe.net. \n\
+e164.arpa. 172800 IN NS rirns.arin.net. \n\
+e164.arpa. 86400 IN DS 46334 8 2 550664875d1121c6edd01f9602577640fed5ad19a749ae1e3fd68476af454578 \n\
+e164.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. A07roaG8r7ns0YydNMhaURb741akipIL8UCgRRMAs3BzzneUtXW3EmS50C7vxb5ikH84a39FerXHOetifGTKETjVMtuQmdPw1F8ClHMkWfdRyR5a+lWwosV3fgnSItoekfbggUZop1dZxzie93pv4RM89Jf/SMlOW/3bYJ1p7Hk= \n\
+earmj48jel1c2rdhigd36n68u3v8q1kv.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. yNYXtZ4dGDdJW3VNoLRtktV93mZmQsQv3Tvy6+iBTGx+W7T0ipSCZq+l5yvblfqGKXXnWWzYf/xKktaLmXnAzvdsacWaKGtudvvtSwLkhlxNWlL018Eoe2md0tsSLd5tSiTbufahrd4p1lv09ne//sGoSw/amfvY5hsRvmnhNhA= \n\
+earmj48jel1c2rdhigd36n68u3v8q1kv.arpa. 86400 IN NSEC3 1 0 1 - H2D0RTQ108UOOUB5UDNN9D2PGQBVABC9 NS DS RRSIG \n\
+h2d0rtq108uooub5udnn9d2pgqbvabc9.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. isNpvWJ3TpDmEl66a9J9Q2GdlNqh9HculGjNFVIbiSfTb5aNgCITkgrKSoxjfZ8go3pDSeqwo5fhaBlbZQ4xGNGlc/T5U2qh2hJPGZpBwHYkR9a1YzMhzMx33oRXfMzsuC+6sasS8BLRHPmS4X89jPeA+lItEJPd1rQlHb1wt1I= \n\
+h2d0rtq108uooub5udnn9d2pgqbvabc9.arpa. 86400 IN NSEC3 1 0 1 - KSH70CK6POGI86ENT4ONT3I9UJ71QE8K NS SOA RRSIG DNSKEY NSEC3PARAM TYPE63 \n\
+home.arpa. 172800 IN NS blackhole-1.iana.org. \n\
+home.arpa. 172800 IN NS blackhole-2.iana.org. \n\
+in-addr.arpa. 172800 IN NS a.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS b.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS c.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS d.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS e.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS f.in-addr-servers.arpa. \n\
+in-addr.arpa. 86400 IN DS 47054 8 2 5cafccec201d1933b4c9f6a9c8f51e51f3b39979058ac21b8df1b1f281cbc6f2 \n\
+in-addr.arpa. 86400 IN DS 53696 8 2 13e5501c56b20394da921b51412d48b7089c5eb6957a7c58553c4d4d424f04df \n\
+in-addr.arpa. 86400 IN DS 63982 8 2 aaf4fb5d213ef25ae44679032ebe3514c487d7abd99d7f5fec3383d030733c73 \n\
+in-addr.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. lr32Q5rTcwVyBASuYq2Mc1t8XPCSSXJDNtK+MzisWifCZ0b0m/GARo34QKR2y3afqeFdqVXWrYrBVjAF2Rg21izsWqpMNyfLloesNNl63A9uQi4dFT3Zfz3OdQOGhWcy51ydn8KVtieIubRTBQAgExgZsDzyRC4PXjzh4Jj872g= \n\
+in-addr-servers.arpa. 172800 IN NS a.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS b.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS c.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS d.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS e.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS f.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 86400 IN DS 1987 8 2 dacfdeb02a489a514c6408d0d54e0904fe6e09a6e111abc9eacb27f6552805e1 \n\
+in-addr-servers.arpa. 86400 IN DS 45104 8 2 50136f7a8d3ffe4f9887ad234ff8ce945cabd331feb12569b2f61f99ce40fdbf \n\
+in-addr-servers.arpa. 86400 IN DS 62996 8 2 836537710efc1e5570e3aeff7c0c80d3957a16ddf8005034bc9082898968dc81 \n\
+in-addr-servers.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. j+2AVMMc1xfd/ua7lHpNQUr95kUTcr8SIQJk6prTkYnPdDvMNZPIhhdVNw7WzFjIvGLF3iumbYY46I3KN3P1eZUKtn0OFvTZ/UG/tlbWaj473XNxWnbwp8sPuT46nuLH6P14gNEhbPGGrh2VE+hFPkM/4ZdfwlCbDC5vEsQNYko= \n\
+a.in-addr-servers.arpa. 172800 IN A 199.180.182.53 \n\
+a.in-addr-servers.arpa. 172800 IN AAAA 2620:37:e000::53 \n\
+b.in-addr-servers.arpa. 172800 IN A 199.253.183.183 \n\
+b.in-addr-servers.arpa. 172800 IN AAAA 2001:500:87::87 \n\
+c.in-addr-servers.arpa. 172800 IN A 196.216.169.10 \n\
+c.in-addr-servers.arpa. 172800 IN AAAA 2001:43f8:110::10 \n\
+d.in-addr-servers.arpa. 172800 IN A 200.10.60.53 \n\
+d.in-addr-servers.arpa. 172800 IN AAAA 2001:13c7:7010::53 \n\
+e.in-addr-servers.arpa. 172800 IN A 203.119.86.101 \n\
+e.in-addr-servers.arpa. 172800 IN AAAA 2001:dd8:6::101 \n\
+f.in-addr-servers.arpa. 172800 IN A 193.0.9.1 \n\
+f.in-addr-servers.arpa. 172800 IN AAAA 2001:67c:e0::1 \n\
+ip6.arpa. 172800 IN NS a.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS b.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS c.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS d.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS e.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS f.ip6-servers.arpa. \n\
+ip6.arpa. 86400 IN DS 13880 8 2 068554efcb5861f42af93ef8e79c442a86c16fc5652e6b6d2419ed527f344d17 \n\
+ip6.arpa. 86400 IN DS 45094 8 2 e6b54e0a20ce1edbfcb6879c02f5782059cecb043a31d804a04afa51af01d5fb \n\
+ip6.arpa. 86400 IN DS 64060 8 2 8a11501086330132be2c23f22dedf0634ad5ff668b4aa1988e172c6a2a4e5f7b \n\
+ip6.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. aNklM0l2ixPusry6KMt0PYGuKgLXqAJArq3KSZgG0QgMjGC0ChVwAO2+vq4wwR8QuqA6vAWHKKpw79l8MYV9I7+a50WPFyEOugl1s+konVjzkgMboPaOZbg52g47mPdQ7Q0N9MPLA8/FJx13cHauimQjZ+1FOiiWhveqgR2Jg8o= \n\
+ip6-servers.arpa. 172800 IN NS a.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS b.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS c.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS d.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS e.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS f.ip6-servers.arpa. \n\
+ip6-servers.arpa. 86400 IN DS 16169 8 2 27fb5354c3c011c2851ee25ba32929b645d63262779ac101a6f28cd631991269 \n\
+ip6-servers.arpa. 86400 IN DS 19720 8 2 f154d00f5759c274de9cad621910cc0b87d720d35b7de4b0b566e135196c38e2 \n\
+ip6-servers.arpa. 86400 IN DS 54832 8 2 ff0d5f44a086a7a31b99c81cfd1135524b5896878e6de78f12b3f609bf7279dc \n\
+ip6-servers.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. fYShlxJWViKV2SbFCqyxUa64AKAedJ2udqcw/VtKNxg2T6i5IQzFc2aPB7V/+MtE64vHWwbrThgOvNC4Xmc7jVqKNsSc1X4Q8ZSQy+/CgmS5pBkI4XpLBb6kTUJMGorgAOI1ek1OMpl25mGmeJ6lE8e5PTNUisz/7ybIx5pBTz0= \n\
+a.ip6-servers.arpa. 172800 IN A 199.180.182.53 \n\
+a.ip6-servers.arpa. 172800 IN AAAA 2620:37:e000::53 \n\
+b.ip6-servers.arpa. 172800 IN A 199.253.182.182 \n\
+b.ip6-servers.arpa. 172800 IN AAAA 2001:500:86::86 \n\
+c.ip6-servers.arpa. 172800 IN A 196.216.169.11 \n\
+c.ip6-servers.arpa. 172800 IN AAAA 2001:43f8:110::11 \n\
+d.ip6-servers.arpa. 172800 IN A 200.7.86.53 \n\
+d.ip6-servers.arpa. 172800 IN AAAA 2001:13c7:7012::53 \n\
+e.ip6-servers.arpa. 172800 IN A 203.119.86.101 \n\
+e.ip6-servers.arpa. 172800 IN AAAA 2001:dd8:6::101 \n\
+f.ip6-servers.arpa. 172800 IN A 193.0.9.2 \n\
+f.ip6-servers.arpa. 172800 IN AAAA 2001:67c:e0::2 \n\
+ipv4only.arpa. 172800 IN NS a.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS b.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS c.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS ns.icann.org. \n\
+iris.arpa. 172800 IN NS a.iana-servers.net. \n\
+iris.arpa. 172800 IN NS b.iana-servers.net. \n\
+iris.arpa. 172800 IN NS c.iana-servers.net. \n\
+iris.arpa. 172800 IN NS ns3.lacnic.net. \n\
+iris.arpa. 172800 IN NS ns4.apnic.net. \n\
+iris.arpa. 86400 IN DS 38534 8 2 163416c9dcaf8d1babfec16552ed109029607907ab80b195e1dab40f1792a59c \n\
+iris.arpa. 86400 IN DS 39464 8 2 1e09a2d6374800d54cfd0e52293906ccf7db7e923dcab7015e4bb697d76d9846 \n\
+iris.arpa. 86400 IN DS 44285 8 2 05cbf77375a8bf5702cf8e261ff947be8c8ab7a0b9485a0241edcfe2f155c7f3 \n\
+iris.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. oikOvs9AfaPv1Po/E76SZ7VBoYjqHqzZEzrA0N4gWXlemmsUKyXh9fiXqtusFIZD7QUBJMvOYkIpWnAOliWnk/oj4lmmwnYMqqLWDMWVoXiUAUtmwQHm89cAjyWc9nRuDVBweKtqH5GQKtEWxu4nkKPIbuUVNHBgxtKZP7Jbzic= \n\
+ksh70ck6pogi86ent4ont3i9uj71qe8k.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. YPnC0imYz+x2dNwUQwvp2CB1Ini1dEcn9Vur9T4KwzAMqVr+PPkheMRiIQcAbmkSLG1D1p/qVzaFEC7ixlaxuEFlvGwM+c5OvukbWek1QtdCDJpgtse3HBajoRTgBDGRwvj+DFej9ppygZpe+vlgSDmiC2fgPMhcG4Z6jMmVAec= \n\
+ksh70ck6pogi86ent4ont3i9uj71qe8k.arpa. 86400 IN NSEC3 1 0 1 - MKQDDR5C3MPRP6DRU5TO19BB27TDVCVT NS DS RRSIG \n\
+mkqddr5c3mprp6dru5to19bb27tdvcvt.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. sUTu2ijBQlhCmn/fNl8O+UofW4ERQ0tgmK0LY8ggHCnvY26k4RCrGieZ6YXl8lCereSyx1DEPuScBA7YRCUEw/FtrW8rCKMo+wQhb4Uon2UUZRl/mrjNNsYxtYwjIN7u/BzfDhBHq2/8vVCybAS8GhqqJhOYpEcDgsITuDKVFOE= \n\
+mkqddr5c3mprp6dru5to19bb27tdvcvt.arpa. 86400 IN NSEC3 1 0 1 - SRGGVLP1DI07IJT2IA31AGJRPFCNC616 NS DS RRSIG \n\
+srggvlp1di07ijt2ia31agjrpfcnc616.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. ep49bJfQ1c1dNMIlFO+EgeG4iW7pHyJvKbK6MJBBj/LJwVfhzwTa8ellqgHp3AH63j8tNPutowc1shlQwE7G/f3KfiVBUwPtAZHtqNYBFdNm0WdxoqRueJmyVR0h+vUfY+r1F4IYzwfjn+ldfj5lhKqQ+gX2HFR3M/FI6H97nHQ= \n\
+srggvlp1di07ijt2ia31agjrpfcnc616.arpa. 86400 IN NSEC3 1 0 1 - SSTSS4TF3ICJ43RCMUQTSJORRDDSRSRL NS \n\
+sstss4tf3icj43rcmuqtsjorrddsrsrl.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. 0GVjQFd8YAYSXMh526fZ5Rx4WDHIf84MTzIsAYuLwM00H6uagrFxQv8mrGExWPummQ+Q+nHDuCBC5lEXjTF4/1qAu7MI627/mKtpcQevTvF3iE2ocf1/vfAFWVCzyLQ3AuFbGGuYQ6nlZzbOu2oRtma6/m4WpDhNszOhuONNlbY= \n\
+sstss4tf3icj43rcmuqtsjorrddsrsrl.arpa. 86400 IN NSEC3 1 0 1 - 0JS82OEC35LBBC4HL35476CM5ICACKSF NS DS RRSIG \n\
+uri.arpa. 172800 IN NS a.iana-servers.net. \n\
+uri.arpa. 172800 IN NS b.iana-servers.net. \n\
+uri.arpa. 172800 IN NS c.iana-servers.net. \n\
+uri.arpa. 172800 IN NS ns3.lacnic.net. \n\
+uri.arpa. 172800 IN NS ns4.apnic.net. \n\
+uri.arpa. 86400 IN DS 15796 8 2 7f8fa18fdd9a826eb08a4d4e9ce94dbba7a5b7b2b3ce1d74afd150242e9f572f \n\
+uri.arpa. 86400 IN DS 28547 8 2 deaefd0c163175350152da7b127dc7c4f9ec8bdf04ccc02829455df86c5ca035 \n\
+uri.arpa. 86400 IN DS 57851 8 2 8feda13f642ed9be2e4aaa3d50099dd422ca6081b6bf8188f804343b58d39cb7 \n\
+uri.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. jwQhmqBE2EWCE2yi14CqgjMfYWq4/W//IuL/EHSRZPJjyP7R7cnUgh/7rDO4JUcYebviO4s9hidjfpnLQWxpR2Jy2SH6aeNERLo76O28UW2Y28eused7aWMDWAnWW4HxURsQSBy2cyQbNwPCLGVLeQZaeZbKRBJUbWJ4MT4UpDE= \n\
+urn.arpa. 172800 IN NS a.iana-servers.net. \n\
+urn.arpa. 172800 IN NS b.iana-servers.net. \n\
+urn.arpa. 172800 IN NS c.iana-servers.net. \n\
+urn.arpa. 172800 IN NS ns3.lacnic.net. \n\
+urn.arpa. 172800 IN NS ns4.apnic.net. \n\
+urn.arpa. 86400 IN DS 28996 8 2 8e66d01a1e5864bcdb8e1f85579aec7c8c536c9d6fc7032ee708e869fd27f3d3 \n\
+urn.arpa. 86400 IN DS 34555 8 2 bd743967def1caf0812fe9eff2371d3adf29e27251db272145a5d523c92f7101 \n\
+urn.arpa. 86400 IN DS 45052 8 2 7685b675f93ada412cfe534820c8dcc55654b1711f677ba83a8564c12943f695 \n\
+urn.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. BHHa1YLYUOABgiloeQQRIMXRKxXNIwRken6E6ETFAWw3Js1ocu6H/X3bcPvBTjID/B+GRGgIyCnDnZ9iWeU41Tw1GnMNT9EM35DmnUgfzUU79shVzRtiYDV6JHF9Kidc90IxNrQOGAcUy0J9jhMa4KYEjfQab8sJSo0M+uJkNMw=";
+
+const char *no_zonemd = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *wrong_soa = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031901 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *duplicate_schemalg = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ 86400 IN ZONEMD 2018031901 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *wrong_hash = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044d ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ int ret = check_contents(simple_zone);
+ is_int(KNOT_EOK, ret, "simple zone");
+
+ ret = check_contents(complex_zone);
+ is_int(KNOT_EOK, ret, "complex zone");
+
+ ret = check_contents(multiple_digests);
+ is_int(KNOT_EOK, ret, "multiple digests");
+
+ ret = check_contents(signed_zone);
+ is_int(KNOT_EOK, ret, "signed zone");
+
+ ret = check_contents(nsec3_zone);
+ is_int(KNOT_EOK, ret, "nsec3 zone");
+
+ ret = check_contents(no_zonemd);
+ is_int(KNOT_ENOENT, ret, "no zonemd");
+
+ ret = check_contents(wrong_soa);
+ is_int(KNOT_ENOTSUP, ret, "wrong SOA serial");
+ // TODO tests for different scheme / algorithm ?
+
+ ret = check_contents(duplicate_schemalg);
+ is_int(KNOT_ESEMCHECK, ret, "duplicate scheme+algorithm pair");
+
+ ret = check_contents(wrong_hash);
+ is_int(KNOT_EMALF, ret, "wrong hash");
+
+ return 0;
+}
diff --git a/tests/knot/test_dthreads.c b/tests/knot/test_dthreads.c
new file mode 100644
index 0000000..3bdfa3a
--- /dev/null
+++ b/tests/knot/test_dthreads.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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_lazy();
+
+ int cpus = dt_online_cpus();
+ ok(cpus > 0, "dthread: online cpus is positive value");
+
+ // 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..d0282f0
--- /dev/null
+++ b/tests/knot/test_fdset.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+#include <tap/basic.h>
+#include <unistd.h>
+
+#include "knot/common/fdset.h"
+#include "libknot/attribute.h"
+#include "contrib/time.h"
+
+#define PATTERN1 "0x45"
+#define PATTERN2 "0xED"
+
+#define TIMEOUT0 2000
+#define TIMEOUT1 10
+#define TIMEOUT2 400
+#define JITTER (TIMEOUT2 - TIMEOUT1 - 10)
+
+void *thr_action1(void *arg)
+{
+ usleep(1000 * TIMEOUT1);
+ _unused_ int ret = write(*((int *)arg), &PATTERN1, 1);
+ return NULL;
+}
+
+void *thr_action2(void *arg)
+{
+ usleep(1000 * TIMEOUT2);
+ _unused_ int ret = write(*((int *)arg), &PATTERN2, 1);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ fdset_t fdset;
+ int ret = fdset_init(&fdset, 32);
+ ok(ret == KNOT_EOK, "fdset_init");
+
+ int fds0[2], fds1[2], fds2[2];
+
+ ret = pipe(fds0);
+ ok(ret >= 0, "create pipe 0");
+ ret = fdset_add(&fdset, fds0[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 0 to fdset");
+
+ ret = pipe(fds1);
+ ok(ret >= 0, "create pipe 1");
+ ret = fdset_add(&fdset, fds1[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 1 to fdset");
+
+ ret = pipe(fds2);
+ ok(ret >= 0, "create pipe 2");
+ ret = fdset_add(&fdset, fds2[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 2 to fdset");
+
+ ok(fdset_get_length(&fdset) == 3, "fdset size full");
+
+ struct timespec time0 = time_now();
+
+ pthread_t t1, t2;
+ ret = pthread_create(&t1, 0, thr_action1, &fds1[1]);
+ ok(ret == 0, "create thread 1");
+ ret = pthread_create(&t2, 0, thr_action2, &fds2[1]);
+ ok(ret == 0, "create thread 2");
+
+ fdset_it_t it;
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time1 = time_now();
+ double diff1 = time_diff_ms(&time0, &time1);
+ ok(ret == 1, "fdset_poll return 1");
+ ok(diff1 >= TIMEOUT1 && diff1 < TIMEOUT1 + JITTER, "fdset_poll timeout 1 (%f)", diff1);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds1[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN1[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 2, "fdset size 2");
+ close(fds1[1]);
+
+ int fd2_dup = dup(fds2[0]);
+ ok(fd2_dup >= 0, "duplicate fd");
+
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time2 = time_now();
+ double diff2 = time_diff_ms(&time0, &time2);
+ ok(ret == 1, "fdset_poll return 2");
+ ok(diff2 >= TIMEOUT2 && diff2 < TIMEOUT2 + JITTER, "fdset_poll timeout 2 (%f)", diff2);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds2[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN2[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 1, "fdset size 1");
+
+ pthread_join(t1, 0);
+ pthread_join(t2, 0);
+
+ ret = fdset_remove(&fdset, 0);
+ ok(ret == KNOT_EOK, "fdset remove");
+ close(fds0[1]);
+ ok(fdset_get_length(&fdset) == 0, "fdset size 0");
+
+ ret = write(fds2[1], &PATTERN2, 1);
+ ok(ret == 1, "write to removed fd");
+ ret = fdset_poll(&fdset, &it, 0, 100);
+ ok(ret == 0, "fdset_poll return 3");
+
+
+ close(fds2[1]);
+ if (fd2_dup >= 0) {
+ close(fd2_dup);
+ }
+ fdset_clear(&fdset);
+
+ return 0;
+}
diff --git a/tests/knot/test_journal.c b/tests/knot/test_journal.c
new file mode 100644
index 0000000..748ab02
--- /dev/null
+++ b/tests/knot/test_journal.c
@@ -0,0 +1,880 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 "knot/journal/journal_read.h"
+#include "knot/journal/journal_write.h"
+
+#include "libknot/attribute.h"
+#include "libknot/libknot.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zone-diff.h"
+#include "test_conf.h"
+
+#define RAND_RR_LABEL 16
+#define RAND_RR_PAYLOAD 64
+#define MIN_SOA_SIZE 22
+
+char *test_dir_name;
+
+knot_lmdb_db_t jdb;
+zone_journal_t jj;
+
+unsigned env_flag;
+
+static unsigned lmdb_page_size(knot_lmdb_db_t *db)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ MDB_stat st = { 0 };
+ mdb_stat(txn.txn, txn.db->dbi, &st);
+ knot_lmdb_abort(&txn);
+ return st.ms_psize;
+}
+
+static void set_conf(int zonefile_sync, size_t journal_usage, const knot_dname_t *apex)
+{
+ (void)apex;
+ char conf_str[512];
+ snprintf(conf_str, sizeof(conf_str),
+ "template:\n"
+ " - id: default\n"
+ " zonefile-sync: %d\n"
+ " journal-max-usage: %zu\n"
+ " journal-max-depth: 1000\n",
+ zonefile_sync, journal_usage);
+ _unused_ int ret = test_conf(conf_str, NULL);
+ assert(ret == KNOT_EOK);
+ jj.conf = conf();
+}
+
+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 };
+ _unused_ int ret = knot_rrset_add_rdata(rr, soa_data, sizeof(soa_data), NULL);
+ knot_soa_serial_set(rr->rrs.rdata, serial);
+ 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);
+
+ _unused_ int ret = knot_rrset_add_rdata(rr, txt, RAND_RR_PAYLOAD, NULL);
+ 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;
+ zone_contents_deep_free(ch->remove);
+ ch->remove = 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);
+ _unused_ int ret = changeset_add_addition(ch, &rr, 0);
+ 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);
+ _unused_ int ret = changeset_add_removal(ch, &rr, 0);
+ 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)
+{
+ bool bootstrap = (ch1->remove == NULL);
+
+ if (!bootstrap && changeset_size(ch1) != changeset_size(ch2)) {
+ return false;
+ }
+
+ if ((bootstrap && ch2->remove != NULL) ||
+ (!bootstrap && ch2->remove == NULL)) {
+ return false;
+ }
+
+ changeset_iter_t it1;
+ changeset_iter_t it2;
+ if (bootstrap) {
+ changeset_iter_add(&it1, ch1);
+ changeset_iter_add(&it2, ch2);
+ } else {
+ changeset_iter_all(&it1, ch1);
+ 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, true)) {
+ 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)
+{
+ env_flag = journal_env_flags(JOURNAL_MODE_ASYNC, false);
+ knot_lmdb_init(&jdb, test_dir_name, 2048 * 1024, env_flag, NULL);
+
+ int ret = knot_lmdb_open(&jdb);
+ is_int(KNOT_EOK, ret, "journal: open db (%s)", knot_strerror(ret));
+
+ ret = knot_lmdb_reconfigure(&jdb, test_dir_name, 4096 * 1024, env_flag);
+ is_int(KNOT_EOK, ret, "journal: re-open with bigger mapsize (%s)", knot_strerror(ret));
+
+ ret = knot_lmdb_reconfigure(&jdb, test_dir_name, 1024 * 1024, env_flag);
+ is_int(KNOT_EOK, ret, "journal: re-open with smaller mapsize (%s)", knot_strerror(ret));
+
+ knot_lmdb_deinit(&jdb);
+}
+
+static int load_j_list(zone_journal_t *zj, bool zij, uint32_t serial,
+ journal_read_t **read, list_t *list)
+{
+ changeset_t *ch;
+ init_list(list);
+ int ret = journal_read_begin(*zj, zij, serial, read);
+ if (ret == KNOT_EOK) {
+ while ((ch = calloc(1, sizeof(*ch))) != NULL &&
+ journal_read_changeset(*read, ch)) {
+ add_tail(list, &ch->n);
+ }
+ free(ch);
+ ret = journal_read_get_error(*read, KNOT_EOK);
+ }
+ return ret;
+}
+
+/*! \brief Test behavior with real changesets. */
+static void test_store_load(const knot_dname_t *apex)
+{
+ set_conf(1000, 512 * 1024, apex);
+
+ knot_lmdb_init(&jdb, test_dir_name, 1536 * 1024, env_flag, NULL);
+ assert(knot_lmdb_open(&jdb) == KNOT_EOK);
+
+ jj.db = &jdb;
+ jj.zone = apex;
+ list_t l, k;
+
+ changeset_t *m_ch = changeset_new(apex), r_ch, e_ch;
+ init_random_changeset(m_ch, 0, 1, 128, apex, false);
+ int ret = journal_insert(jj, m_ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: store changeset (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal: check after store changeset (%s)", knot_strerror(ret));
+ journal_read_t *read = NULL;
+
+ ret = load_j_list(&jj, false, changeset_from(m_ch), &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read single changeset (%s)", knot_strerror(ret));
+ ok(1 == list_size(&l), "journal: read exactly one changeset");
+ ok(changesets_eq(m_ch, HEAD(l)), "journal: changeset equal after read");
+ changesets_free(&l);
+
+ journal_read_end(read);
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: first simple flush (%s)", knot_strerror(ret));
+
+ init_list(&k);
+ 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_insert(jj, m_ch2, NULL, NULL);
+ 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_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after read");
+ ok(test_continuity(&l) == KNOT_EOK, "journal: changesets are in order");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list 2nd (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after 2nd read");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: flush after overfill (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after flush");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changesets_free(&k);
+
+ changeset_t ch;
+ ret = changeset_init(&ch, apex);
+ ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret);
+ init_random_changeset(&ch, serial, serial + 1, 555, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: store after flush (%d)", ret);
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, serial, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load after store after flush after overfill (%s)", knot_strerror(ret));
+ is_int(1, list_size(&l), "journal: single changeset in list");
+ ok(changesets_eq(&ch, HEAD(l)), "journal: changeset not malformed after overfill");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_clear(&ch);
+
+ changeset_init(&ch, apex);
+ init_random_changeset(&ch, 2, 3, 100, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert discontinuous changeset (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, 2, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read after discontinuity (%s)", knot_strerror(ret));
+ is_int(1, list_size(&l), "journal: discontinuity caused journal to drop");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ // 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. *
+ uint32_t serials[6] = { 0, 1, 2, 2147483647, 4294967294, 1 };
+ for (int i = 0; i < 5; i++) {
+ changeset_clear(&ch);
+ changeset_init(&ch, apex);
+ init_random_changeset(&ch, serials[i], serials[i + 1], 100, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(i == 4 ? KNOT_EBUSY : KNOT_EOK, ret, "journal: inserting cycle (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ }
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: flush in cycle (%s)", knot_strerror(ret));
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: inserted cycle (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = journal_read_begin(jj, false, 0, &read);
+ is_int(KNOT_ENOENT, ret, "journal: cycle removed first changeset (%d should= %d)", ret, KNOT_ENOENT);
+ ret = journal_read_begin(jj, false, 1, &read);
+ is_int(KNOT_ENOENT, ret, "journal: cycle removed second changeset (%d should= %d)", ret, KNOT_ENOENT);
+ ret = load_j_list(&jj, false, 4294967294, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read after cycle (%s)", knot_strerror(ret));
+ ok(3 >= list_size(&l), "journal: cycle caused journal to partly drop");
+ ok(changesets_eq(&ch, HEAD(l)), "journal: changeset not malformed after cycle");
+ changesets_free(&l);
+ journal_read_end(read);
+ changeset_clear(&ch);
+ changeset_free(m_ch);
+
+ changeset_init(&e_ch, apex);
+ init_random_changeset(&e_ch, 0, 1, 200, apex, true);
+ zone_node_t *n = NULL;
+ zone_contents_add_rr(e_ch.add, e_ch.soa_to, &n);
+ ret = journal_insert_zone(jj, e_ch.add);
+ zone_contents_remove_rr(e_ch.add, e_ch.soa_to, &n);
+ is_int(KNOT_EOK, ret, "journal: insert zone-in-journal (%s)", knot_strerror(ret));
+ changeset_init(&r_ch, apex);
+ init_random_changeset(&r_ch, 1, 2, 200, apex, false);
+ ret = journal_insert(jj, &r_ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert after zone-in-journal (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: read two changesets from zone-in-journal");
+ ok(changesets_eq(&e_ch, HEAD(l)), "journal: zone-in-journal not malformed");
+ ok(changesets_eq(&r_ch, TAIL(l)), "journal: after zone-in-journal not malformed");
+ changesets_free(&l);
+ journal_read_end(read);
+ changeset_clear(&e_ch);
+ changeset_clear(&r_ch);
+
+ ret = journal_scrape_with_md(jj, true);
+ is_int(KNOT_EOK, ret, "journal: scrape with md (%s)", knot_strerror(ret));
+
+ unset_conf();
+}
+
+static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zone2)
+{
+ set_conf(-1, 100 * 1024, zone1);
+
+ zone_journal_t jj2 = { &jdb, zone2, conf() };
+ changeset_t *small_ch2 = changeset_new(zone2);
+ init_random_changeset(small_ch2, 1, 2, 100, zone2, false);
+ int ret = journal_insert(jj2, small_ch2, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok");
+
+ changeset_t *big_zij = changeset_new(zone1);
+ init_random_changeset(big_zij, 0, 1, 1200, zone1, true);
+ zone_node_t *n = NULL;
+ zone_contents_add_rr(big_zij->add, big_zij->soa_to, &n);
+ ret = journal_insert_zone(jj, big_zij->add);
+ is_int(KNOT_EOK, ret, "journal: store big zone-in-journal");
+
+ changeset_t *big_ch2 = changeset_new(zone2);
+ init_random_changeset(big_ch2, 2, 3, 750, zone2, false);
+ ret = journal_insert(jj2, big_ch2, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: second zone is not affected by storing big zij of other zone");
+
+ journal_read_t *read = NULL;
+ list_t l;
+ init_list(&l);
+ changeset_t *medium_ch1 = changeset_new(zone1);
+ init_random_changeset(medium_ch1, 1, 2, 300, zone1, false);
+ ret = journal_insert(jj, medium_ch1, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing medium changeset must be ok");
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: read two changesets from journal");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_t *small_ch1 = changeset_new(zone1);
+ init_random_changeset(small_ch1, 2, 3, 100, zone1, false);
+ ret = journal_insert(jj, small_ch1, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok");
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: previous chs merged into zone-in-journal due to size limits");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_t *medium_ch1b = changeset_new(zone1);
+ init_random_changeset(medium_ch1b, 3, 4, 300, zone1, false);
+ ret = journal_insert(jj, medium_ch1b, NULL, NULL);
+ is_int(KNOT_ESPACE, ret, "journal: not able to free space for changeset by merging");
+
+ changeset_t *too_big_zij = changeset_new(zone1);
+ init_random_changeset(too_big_zij, 0, 1, 2200, zone1, true);
+ zone_contents_add_rr(too_big_zij->add, too_big_zij->soa_to, &n);
+ ret = journal_insert_zone(jj, too_big_zij->add);
+ is_int(KNOT_ESPACE, ret, "journal: store too big zone-in-journal");
+
+ changeset_free(big_ch2);
+ changeset_free(big_zij);
+ changeset_free(too_big_zij);
+ changeset_free(small_ch2);
+ changeset_free(small_ch1);
+ changeset_free(medium_ch1);
+ changeset_free(medium_ch1b);
+
+ 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)
+{
+ size_t prefix_len = strlen(prefix);
+ size_t apex_len = knot_dname_size(apex);
+
+ knot_dname_t *out = malloc(prefix_len + apex_len + 2);
+ out[0] = prefix_len;
+ memcpy(out + 1, prefix, prefix_len);
+ memcpy(out + 1 + prefix_len, apex, apex_len);
+ return out;
+}
+
+static knot_dname_t *tm_owner_int(int x, const knot_dname_t *apex)
+{
+ char buf[12] = { 0 };
+ (void)snprintf(buf, sizeof(buf), "i%d", x);
+ return tm_owner(buf, apex);
+}
+
+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;
+}
+
+#define TM_RRS_INT_MAX 1000
+
+static knot_rrset_t *tm_rrs_int(const knot_dname_t *apex, int x)
+{
+ assert(x < TM_RRS_INT_MAX);
+ static knot_rrset_t *stat_rrs[TM_RRS_INT_MAX] = { 0 };
+
+ if (apex == NULL) {
+ for (int i = 0; i < TM_RRS_INT_MAX; i++) {
+ knot_rrset_free(stat_rrs[i], NULL);
+ stat_rrs[i] = NULL;
+ }
+ return NULL;
+ }
+
+ if (stat_rrs[x] == NULL) {
+ stat_rrs[x] = tm_rrset(tm_owner_int(x, apex), rdA);
+ }
+ return stat_rrs[x];
+}
+
+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 (x < 0) {
+ serial = 0;
+ return NULL;
+ }
+
+ 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 void tm2_add_all(zone_contents_t *toadd)
+{
+ assert(toadd != NULL);
+ for (int i = 1; i < TM_RRS_INT_MAX; i++) {
+ zone_node_t *unused = NULL;
+ _unused_ int ret = zone_contents_add_rr(toadd, tm_rrs_int(toadd->apex->owner, i), &unused);
+ assert(ret == KNOT_EOK);
+ }
+}
+
+static zone_contents_t *tm2_zone(const knot_dname_t *apex)
+{
+ zone_contents_t *z = zone_contents_new(apex, false);
+ if (z != NULL) {
+ knot_rrset_t soa;
+ zone_node_t *unused = NULL;
+ init_soa(&soa, 1, apex);
+ _unused_ int ret = zone_contents_add_rr(z, &soa, &unused);
+ knot_rrset_clear(&soa, NULL);
+ assert(ret == KNOT_EOK);
+ tm2_add_all(z);
+ }
+ return z;
+}
+
+static changeset_t *tm2_chs_unzone(const knot_dname_t *apex)
+{
+ changeset_t *ch = changeset_new(apex);
+ if (ch != NULL) {
+ changeset_set_soa_serials(ch, 1, 2, apex);
+ tm2_add_all(ch->remove);
+ _unused_ int ret = changeset_add_addition(ch, tm_rrs_int(apex, 0), 0);
+ assert(ret == KNOT_EOK);
+ }
+ return ch;
+}
+
+static int merged_present(void)
+{
+ bool exists, has_merged;
+ return journal_info(jj, &exists, NULL, NULL, NULL, &has_merged, NULL, NULL, NULL) == KNOT_EOK && exists && has_merged;
+}
+
+static void test_merge(const knot_dname_t *apex)
+{
+ int i, ret;
+ list_t l;
+
+ // allow merge
+ set_conf(-1, 400 * 1024, apex);
+ ok(!journal_allow_flush(jj), "journal: merge allowed");
+
+ ret = journal_scrape_with_md(jj, false);
+ 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_insert(jj, tm_chs(apex, i), NULL, NULL);
+ assert(ret == KNOT_EOK);
+ }
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal: sem check (%s)", knot_strerror(ret));
+ journal_read_t *read = NULL;
+ ret = load_j_list(&jj, false, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets must be ok");
+ assert(ret == KNOT_EOK);
+ 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) == 0, "journal: merged removals # = 0");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ // insert one more and check the #s of results
+ ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert one more (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, 0, &read, &l);
+ 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);
+ journal_read_end(read);
+ ret = load_j_list(&jj, false, i - 3, &read, &l);
+ 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);
+ journal_read_end(read);
+
+ // insert large zone-in-journal taking more than one chunk
+ zone_contents_t *bigz = tm2_zone(apex);
+ ret = journal_insert_zone(jj, bigz);
+ zone_contents_deep_free(bigz);
+ is_int(KNOT_EOK, ret, "journal: insert large zone-in-journal");
+
+ // insert changeset that will cancel it mostly out
+ changeset_t *bigz_cancelout = tm2_chs_unzone(apex);
+ ret = journal_insert(jj, bigz_cancelout, NULL, NULL);
+ changeset_free(bigz_cancelout);
+ is_int(KNOT_EOK, ret, "journal: insert cancel-out changeset");
+
+ // now fill up with dumy changesets to enforce merge
+ tm_chs(apex, -1);
+ while (changeset_to(tm_chs(apex, 0)) != 2) { }
+ for (i = 0; i < 1600; i++) {
+ ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL);
+ assert(ret == KNOT_EOK);
+ }
+
+ // finally: the test case. Reading the journal now must be no EMALF and
+ // the zone-in-journal must be little
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read chunks-shrinked zone-in-journal");
+ is_int(4, trie_weight(((changeset_t *)HEAD(l))->add->nodes->trie), "journal: small merged zone-in-journal");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = journal_scrape_with_md(jj, false);
+ assert(ret == KNOT_EOK);
+
+ // disallow merge
+ unset_conf();
+ set_conf(1000, 512 * 1024, apex);
+ ok(journal_allow_flush(jj), "journal: merge disallowed");
+
+ tm_rrs(NULL, 0);
+ tm_chs(NULL, 0);
+ tm_rrs_int(NULL, 0);
+ unset_conf();
+}
+
+static void test_stress_base(const knot_dname_t *apex,
+ size_t update_size, size_t file_size)
+{
+ uint32_t serial = 0;
+
+ int ret = knot_lmdb_reconfigure(&jdb, test_dir_name, file_size, journal_env_flags(JOURNAL_MODE_ASYNC, false));
+ is_int(KNOT_EOK, ret, "journal: reconfigure to mapsize %zu (%s)", file_size, knot_strerror(ret));
+
+ set_conf(1000, file_size / 2, apex);
+
+ 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_insert(jj, &ch, NULL, NULL);
+ if (ret == KNOT_EOK) {
+ serial++;
+ } else {
+ break;
+ }
+ }
+
+ ret = journal_set_flushed(jj);
+ if (ret == KNOT_EOK) {
+ ret = journal_sem_check(jj);
+ }
+ ok(serial > 0 && ret == KNOT_EOK, "journal: pass #%d fillup run (%d inserts) (%s)", i, serial, knot_strerror(ret));
+ }
+
+ changeset_clear(&ch);
+
+ unset_conf();
+}
+
+/*! \brief Test behavior when writing to the journal and flushing it. */
+static void test_stress(const knot_dname_t *apex)
+{
+ diag("stress test: small data");
+ test_stress_base(apex, 40, (1024 + 512) * 1024);
+
+ diag("stress test: medium data");
+ test_stress_base(apex, 400, 3 * 1024 * 1024);
+
+ diag("stress test: large data");
+ test_stress_base(apex, 4000, 10 * 1024 * 1024);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ const knot_dname_t *apex = (const uint8_t *)"\4test";
+ const knot_dname_t *apex2 = (const uint8_t *)"\4ufoo";
+
+ test_dir_name = test_mkdtemp();
+
+ test_journal_db();
+
+ test_store_load(apex);
+
+ if (lmdb_page_size(&jdb) == 4096) {
+ test_size_control(apex, apex2);
+ } // else it is not manually optimized enough to test correctly
+
+ test_merge(apex);
+
+ test_stress(apex);
+
+ knot_lmdb_deinit(&jdb);
+
+ 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..e4f9766
--- /dev/null
+++ b/tests/knot/test_kasp_db.c
@@ -0,0 +1,233 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#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";
+
+knot_lmdb_db_t _db, *db = &_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));
+}
+
+static void init_key_records(key_records_t *r)
+{
+ knot_rrset_init(&r->dnskey, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, 3600);
+ knot_rrset_init(&r->cdnskey, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->cds, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_CDS, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->rrsig, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&r->rrsig, (uint8_t *)CHARS500_1, 500, NULL);
+}
+
+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;
+
+ knot_lmdb_init(db, test_dir_name, 500*1024*1024, 0, "keys_db");
+ int ret = knot_lmdb_open(db);
+ is_int(KNOT_EOK, ret, "kasp_db: open eok");
+ ok(db->env != NULL, "kasp_db: lmdb env 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);
+
+ dnssec_binary_t salt1 = { 500, (uint8_t *)CHARS500_1 }, salt2 = { 0 };
+ knot_time_t time = 0;
+ ret = kasp_db_store_nsec3salt(db, zone1, &salt1, 1234);
+ is_int(KNOT_EOK, ret, "kasp_db: store nsec3salt");
+ ret = kasp_db_load_nsec3salt(db, zone1, &salt2, &time);
+ is_int(KNOT_EOK, ret, "kasp_db: load nsec3salt");
+ is_int(1234, time, "kasp_db: salt_created preserved");
+ is_int(0, dnssec_binary_cmp(&salt1, &salt2), "kasp_db: salt preserved");
+ dnssec_binary_free(&salt2);
+ salt1.size = 0;
+ ret = kasp_db_store_nsec3salt(db, zone2, &salt1, 0);
+ is_int(KNOT_EOK, ret, "kasp_db: store empty nsec3salt");
+ ret = kasp_db_load_nsec3salt(db, zone2, &salt2, &time);
+ is_int(KNOT_EOK, ret, "kasp_db: load empty nsec3salt");
+ is_int(0, time, "kasp_db: empty salt_created preserved");
+ is_int(0, salt2.size, "kasp_db: empty salt preserved");
+ dnssec_binary_free(&salt2);
+
+ ret = kasp_db_delete_all(db, zone2);
+ is_int(KNOT_EOK, ret, "kasp_db: delete all");
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: delete all deleted keys");
+ ret = kasp_db_load_nsec3salt(db, zone2, &salt2, &time);
+ is_int(KNOT_ENOENT, ret, "kasp_db: delete all removed nsec3salt");
+ dnssec_binary_free(&salt2);
+
+ ret = kasp_db_store_serial(db, zone2, KASPDB_SERIAL_MASTER, 1);
+ is_int(KNOT_EOK, ret, "kasp_db: store master_serial");
+ ret = kasp_db_store_serial(db, zone2, KASPDB_SERIAL_LASTSIGNED, 2);
+ is_int(KNOT_EOK, ret, "kasp_db: store lastsigned_serial");
+ uint32_t serial = 0;
+ ret = kasp_db_load_serial(db, zone2, KASPDB_SERIAL_MASTER, &serial);
+ is_int(KNOT_EOK, ret, "kasp_db: load master_serial");
+ is_int(1, serial, "kasp_db: master_serial preserved");
+ ret = kasp_db_load_serial(db, zone2, KASPDB_SERIAL_LASTSIGNED, &serial);
+ is_int(KNOT_EOK, ret, "kasp_db: load lastsigned_serial");
+ is_int(2, serial, "kasp_db: lastsigned_serial preserved");
+
+ ret = kasp_db_add_key(db, zone1, &params1);
+ ok(ret == KNOT_EOK, "kasp_db: add key1");
+ ret = kasp_db_add_key(db, zone2, &params2);
+ ok(ret == KNOT_EOK, "kasp_db: add key2");
+
+ ret = kasp_db_set_policy_last(db, "policy1", NULL, zone1, params1.id);
+ is_int(KNOT_EOK, ret, "kasp_db: set policylast");
+ knot_dname_t *zoneX;
+ char *keyidX;
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: get policylast");
+ ok(knot_dname_cmp(zoneX, zone1) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params1.id) == 0, "kasp_db: policy last preserved");
+ free(zoneX);
+ free(keyidX);
+ ret = kasp_db_set_policy_last(db, "policy1", params1.id, zone2, params2.id);
+ is_int(KNOT_EOK, ret, "kasp_db: reset policylast");
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: reget policylast");
+ ok(knot_dname_cmp(zoneX, zone2) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params2.id) == 0, "kasp_db: policy last represerved");
+ free(zoneX);
+ free(keyidX);
+ ret = kasp_db_set_policy_last(db, "policy1", params1.id, zone1, params1.id);
+ is_int(KNOT_ESEMCHECK, ret, "kasp_db: refused policylast with wrong keyid");
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: reget policylast2");
+ ok(knot_dname_cmp(zoneX, zone2) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params2.id) == 0, "kasp_db: policy last represerved2");
+ free(zoneX);
+ free(keyidX);
+
+ knot_time_t two = 2;
+ key_records_t kr;
+ init_key_records(&kr);
+ ret = kasp_db_store_offline_records(db, 1, &kr);
+ is_int(KNOT_EOK, ret, "kasp_db: store key records");
+ //key_records_clear_rdatasets(&kr);
+ ret = kasp_db_load_offline_records(db, zone1, &two, &time, &kr);
+ is_int(KNOT_EOK, ret, "kasp_db: load key records");
+ ok(kr.cds.type == KNOT_RRTYPE_CDS && kr.rrsig.rrs.count == 1, "kasp_db: key records ok");
+ is_int(0, time, "kasp_db: no next key records");
+ key_records_clear(&kr);
+ ret = kasp_db_delete_offline_records(db, zone1, 1, 3);
+ is_int(KNOT_EOK, ret, "kasp_db: delete key records");
+ ret = kasp_db_load_offline_records(db, zone1, &two, &time, &kr);
+ is_int(KNOT_ENOENT, ret, "kasp_db: no more key records");
+
+ knot_lmdb_deinit(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..e8c6cdb
--- /dev/null
+++ b/tests/knot/test_node.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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, false, false, NULL);
+ ok(node != NULL, "Node: new");
+ assert(node);
+ ok(knot_dname_is_equal(node->owner, dummy_owner), "Node: new - set fields");
+
+ // 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 RRSet getters
+ knot_rrset_t *n_rrset = node_create_rrset(node, KNOT_RRTYPE_TXT);
+ ok(n_rrset && knot_rrset_equal(n_rrset, dummy_rrset, true),
+ "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, true), "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, true),
+ "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.");
+ node_remove_rdataset(node, KNOT_RRTYPE_TXT);
+ ok(node->rrset_count == 1, "Node: remove existing rdataset.");
+
+ // "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..83f62d8
--- /dev/null
+++ b/tests/knot/test_process_query.c
@@ -0,0 +1,201 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <tap/basic.h>
+#include <tap/files.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 temporary storage directory. */
+ char *temp_dir = test_mkdtemp();
+ ok(temp_dir != NULL, "make temporary directory");
+
+ /* Create fake server environment. */
+ server_t server;
+ int ret = create_fake_server(&server, proc.mm, temp_dir);
+ is_int(KNOT_EOK, ret, "ns: fake server initialization");
+ if (ret != KNOT_EOK) {
+ goto fatal;
+ }
+
+ 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 = {
+ .proto = KNOTD_QUERY_PROTO_TCP,
+ .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" );
+
+fatal:
+ /* Cleanup. */
+ mp_delete((struct mempool *)mm.ctx);
+ server_deinit(&server);
+ conf_free(conf());
+ test_rm_rf(temp_dir);
+ free(temp_dir);
+
+ 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..4ab14d2
--- /dev/null
+++ b/tests/knot/test_query_module.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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");
+ if (plan == NULL) {
+ goto fatal;
+ }
+
+ /* 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");
+
+fatal:
+ /* 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..809c62f
--- /dev/null
+++ b/tests/knot/test_requestor.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <tap/basic.h>
+#include <pthread.h>
+#include <stdio.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"
+
+bool TFO = false;
+
+/* @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, NULL);
+ close(client);
+ }
+
+ return NULL;
+}
+
+/* Test implementations. */
+
+static knot_request_t *make_query(knot_requestor_t *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);
+
+ knot_request_flag_t flags = TFO ? KNOT_REQUEST_TFO: KNOT_REQUEST_NONE;
+
+ return knot_request_make_generic(requestor->mm, dst, src, pkt, NULL,
+ NULL, NULL, NULL, 0, flags);
+}
+
+static void test_disconnected(knot_requestor_t *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ knot_request_t *req = make_query(requestor, dst, src);
+ int ret = knot_requestor_exec(requestor, req, TIMEOUT);
+ /* ECONNREFUSED on FreeBSD, ETIMEOUT on NetBSD/OpenBSD/macOS. */
+ ret = (ret == KNOT_ECONNREFUSED || ret == KNOT_ETIMEOUT) ? KNOT_ECONN : ret;
+ is_int(KNOT_ECONN, ret, "requestor: disconnected/exec");
+ knot_request_free(req, requestor->mm);
+
+}
+
+static void test_connected(knot_requestor_t *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ /* Enqueue packet. */
+ knot_request_t *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[])
+{
+#if defined(__linux__)
+ FILE *fd = fopen("/proc/sys/net/ipv4/tcp_fastopen", "r");
+ if (fd != NULL) {
+ int val = fgetc(fd);
+ fclose(fd);
+ // 0 - disabled, 1 - server TFO (client fallbacks),
+ // 2 - client TFO, 3 - both
+ if (val == '1' || val == '3') {
+ TFO = true;
+ }
+ }
+#endif
+ plan_lazy();
+
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Initialize requestor. */
+ knot_requestor_t 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, &server, 0, 0);
+ assert(responder_fd >= 0);
+ socklen_t addr_len = sockaddr_len(&server);
+ int ret = getsockname(responder_fd, (struct sockaddr *)&server, &addr_len);
+ ok(ret == 0, "check getsockname return");
+
+ /* Test requestor in disconnected environment. */
+ test_disconnected(&requestor, &server, &client);
+
+ /* Start responder. */
+ ret = listen(responder_fd, 10);
+ ok(ret == 0, "check listen return");
+
+ if (TFO) {
+ ret = net_bound_tfo(responder_fd, 10);
+ ok(ret == KNOT_EOK, "check bound TFO return");
+ }
+
+ 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, &server, NULL, false);
+ assert(conn > 0);
+ conn = net_dns_tcp_send(conn, (uint8_t *)"", 1, TIMEOUT, NULL);
+ 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..cd50ef7
--- /dev/null
+++ b/tests/knot/test_semantic_check.in
@@ -0,0 +1,171 @@
+#!/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" 2>&1
+ ok "$1 - check program return" test $? -eq 1
+
+ fatal=$(grep -E "^error: 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 2>&1
+ ok "$1 - correct zone, without error" test $? -eq 0
+}
+
+#param zonefile
+test_correct_no_dnssec()
+{
+ $KZONECHECK -o example.com -d off "$DATA/$1" > /dev/null 2>&1
+ 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_NONE="missing CDNSKEY"
+CDNSKEY_NO_CDS="CDNSKEY without corresponding CDS"
+CDNSKEY_DELETE="invalid CDNSKEY/CDS for DNSSEC delete algorithm"
+CDS_NONE="missing CDS"
+CDS_NOT_MATCH="CDS not match CDNSKEY"
+CNAME_EXTRA_RECORDS="another record exists beside CNAME"
+CNAME_MULTIPLE="multiple CNAME records"
+DNAME_CHILDREN="child record exists under DNAME"
+DNAME_MULTIPLE="multiple DNAME records"
+DNAME_EXTRA_NS="NS record exists beside DNAME"
+DNSKEY_INVALID="invalid DNSKEY"
+DS_ALG="unknown algorithm in DS"
+DS_APEX="DS at the zone apex"
+NSEC3PARAM_FLAGS="invalid flags in NSEC3PARAM"
+NSEC_NONE="missing NSEC\(3\) record"
+NSEC_RDATA_BITMAP="wrong NSEC\(3\) bitmap"
+NSEC_RDATA_CHAIN="inconsistent NSEC\(3\) chain"
+NSEC3_INSECURE_DELEGATION_OPT="wrong NSEC3 opt-out"
+NS_APEX="missing NS at the zone apex"
+NS_GLUE="missing glue record"
+RRSIG_UNVERIFIABLE="no valid signature for a record"
+
+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 "dname_multiple.zone" 1 1 "$DNAME_MULTIPLE"
+expect_error "dname_extra_ns.zone" 1 1 "$DNAME_EXTRA_NS"
+expect_error "ds_apex.zone" 1 1 "$DS_APEX"
+
+expect_error "ns_apex.missing" 0 1 "$NS_APEX"
+expect_error "glue_apex_both.missing" 0 2 "$NS_GLUE"
+expect_error "glue_apex_one.missing" 0 1 "$NS_GLUE"
+expect_error "glue_besides.missing" 0 1 "$NS_GLUE"
+expect_error "glue_deleg.missing" 0 1 "$NS_GLUE"
+expect_error "glue_in_apex.missing" 0 1 "$NS_GLUE"
+expect_error "different_signer_name.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig_with_delegation.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+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_NONE"
+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 "$NSEC_NONE"
+expect_error "nsec3_optout_ent.invalid" 0 1 "$NSEC_NONE"
+expect_error "nsec3_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_ds.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec3_optout.signed" 0 1 "$NSEC3_INSECURE_DELEGATION_OPT"
+expect_error "nsec3_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_03.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3PARAM_FLAGS"
+expect_error "rrsig_signed.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "rrsig_rdata_ttl.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "duplicate.signature" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "missing.signed" 0 1 "$NSEC_NONE"
+expect_error "dnskey_param_error.signed" 0 1 "$DNSKEY_INVALID"
+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.orphan.cds" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.orphan.cdnskey" 0 1 "$CDNSKEY_NO_CDS"
+expect_error "cdnskey.delete.invalid.cds" 0 1 "$CDNSKEY_DELETE"
+expect_error "cdnskey.delete.invalid.cdnskey" 0 1 "$CDNSKEY_DELETE"
+expect_error "delegation.signed" 0 1 "$NSEC_RDATA_BITMAP"
+
+test_correct "rrsig_ttl.signed"
+test_correct "no_error_delegation_bitmap.signed"
+test_correct "no_error_nsec3_optout.signed"
+test_correct "glue_wildcard.valid"
+test_correct "glue_no_foreign.valid"
+test_correct "glue_in_deleg.valid"
+test_correct "cdnskey.cds"
+test_correct "cdnskey.delete.both"
+test_correct "dname_apex_nsec3.signed"
+test_correct "nsec3_optout_ent.valid"
+test_correct "nsec3_optout_ent.all"
+
+test_correct_no_dnssec "no_rrsig.signed"
+test_correct_no_dnssec "no_rrsig_with_delegation.signed"
+test_correct_no_dnssec "nsec_broken_chain_01.signed"
+test_correct_no_dnssec "nsec_broken_chain_02.signed"
+test_correct_no_dnssec "nsec_missing.signed"
+test_correct_no_dnssec "nsec_multiple.signed"
+test_correct_no_dnssec "nsec_wrong_bitmap_01.signed"
+test_correct_no_dnssec "nsec_wrong_bitmap_02.signed"
+test_correct_no_dnssec "nsec3_missing.signed"
+test_correct_no_dnssec "nsec3_wrong_bitmap_01.signed"
+test_correct_no_dnssec "nsec3_wrong_bitmap_02.signed"
+test_correct_no_dnssec "nsec3_ds.signed"
+test_correct_no_dnssec "nsec3_optout.signed"
+test_correct_no_dnssec "nsec3_chain_01.signed"
+test_correct_no_dnssec "nsec3_chain_02.signed"
+test_correct_no_dnssec "nsec3_chain_03.signed"
+test_correct_no_dnssec "nsec3_param_invalid.signed"
+test_correct_no_dnssec "rrsig_signed.signed"
+test_correct_no_dnssec "rrsig_rdata_ttl.signed"
+test_correct_no_dnssec "duplicate.signature"
+test_correct_no_dnssec "missing.signed"
+test_correct_no_dnssec "dnskey_param_error.signed"
+test_correct_no_dnssec "cdnskey.invalid"
+test_correct_no_dnssec "cdnskey.invalid.param"
+test_correct_no_dnssec "cdnskey.nocds"
+test_correct_no_dnssec "cdnskey.nocdnskey"
+test_correct_no_dnssec "cdnskey.nodnskey"
+test_correct_no_dnssec "cdnskey.orphan.cds"
+test_correct_no_dnssec "cdnskey.orphan.cdnskey"
+test_correct_no_dnssec "cdnskey.delete.invalid.cds"
+test_correct_no_dnssec "cdnskey.delete.invalid.cdnskey"
+test_correct_no_dnssec "delegation.signed"
+
+rm $LOG
diff --git a/tests/knot/test_server.c b/tests/knot/test_server.c
new file mode 100644
index 0000000..bde3d10
--- /dev/null
+++ b/tests/knot/test_server.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <signal.h>
+#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);
+
+ /* Destroy the server structure. */
+ server_deinit(&server);
+
+ /* Remove the configuration. */
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_server.h b/tests/knot/test_server.h
new file mode 100644
index 0000000..d68561d
--- /dev/null
+++ b/tests/knot/test_server.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "test_conf.h"
+#include "knot/server/server.h"
+#include "knot/zone/adjust.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->server = server;
+ root->contents = zone_contents_new(root->name, true);
+
+ 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. */
+ (void)zone_adjust_full(root->contents, 1);
+
+ /* 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, const char *db_storage)
+{
+ int ret;
+
+ /* Create test configuration. */
+ /* String `db_storage' obtained from test_mkdtemp() may be up to 4096 bytes. */
+ char conf_str[4096 + 512];
+ (void)snprintf(conf_str, sizeof(conf_str),
+ "server:\n"
+ " identity: bogus.ns\n"
+ " version: 0.11\n"
+ " nsid: \n"
+ "database:\n"
+ " storage: %s\n"
+ "zone:\n"
+ " - domain: .\n"
+ " zonefile-sync: -1\n",
+ db_storage);
+
+ /* Load test configuration. */
+ 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_unreachable.c b/tests/knot/test_unreachable.c
new file mode 100644
index 0000000..826544d
--- /dev/null
+++ b/tests/knot/test_unreachable.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "knot/common/unreachable.h"
+
+#include "contrib/sockaddr.h"
+
+#define UR_TEST_ADDRS 32
+struct sockaddr_storage ur_test_addrs[UR_TEST_ADDRS] = { { 0 } };
+struct sockaddr_storage ur_test_via[2] = { { 0 } };
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ global_unreachables = knot_unreachables_init(10);
+ ok(global_unreachables != NULL, "unreachables: init");
+
+ // ur_test_via[0] left empty - AF_UNSPEC
+ sockaddr_set(&ur_test_via[1], AF_INET6, "::1", 0);
+
+ for (int i = 0; i < UR_TEST_ADDRS; i++) {
+ struct sockaddr_storage *s = &ur_test_addrs[i];
+ sockaddr_set(s, AF_INET6, "::2", i + 1);
+ struct sockaddr_storage *via = &ur_test_via[i % 2];
+ struct sockaddr_storage *not_via = &ur_test_via[1 - i % 2];
+
+ ok(!knot_unreachable_is(global_unreachables, s, via), "unreachables: pre[%d]", i);
+ knot_unreachable_add(global_unreachables, s, via);
+ ok(knot_unreachable_is(global_unreachables, s, via), "unreachables: post[%d]", i);
+ ok(!knot_unreachable_is(global_unreachables, s, not_via), "unreachables: via[%d]", i);
+
+ usleep(1000);
+ if (i >= 10) {
+ ok(!knot_unreachable_is(global_unreachables, &ur_test_addrs[i - 10], via),
+ "unreachables: expired[%d]", i - 10);
+ }
+ }
+
+ knot_unreachables_deinit(&global_unreachables);
+ ok(global_unreachables == NULL, "unreachables: deinit");
+
+ return 0;
+}
diff --git a/tests/knot/test_worker_pool.c b/tests/knot/test_worker_pool.c
new file mode 100644
index 0000000..59dee36
--- /dev/null
+++ b/tests/knot/test_worker_pool.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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(worker_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
+
+ worker_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..2d20afd
--- /dev/null
+++ b/tests/knot/test_worker_queue.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "knot/worker/queue.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ worker_task_t task_one = { 0 };
+ worker_task_t task_two = { 0 };
+ worker_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..59207ae
--- /dev/null
+++ b/tests/knot/test_zone-tree.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 NODEE[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(NODEE + i, 0, sizeof(zone_node_t));
+ NODEE[i].owner = NAME[i];
+ NODEE[i].prev = NODEE + ((NCOUNT + i - 1) % NCOUNT);
+ NODEE[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;
+}
+
+static int ztree_node_counter(zone_node_t *node, void *data)
+{
+ (void)node;
+ int *counter = data;
+ (*counter)++;
+ return KNOT_EOK;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ztree_init_data();
+
+ /* 1. create test */
+ zone_tree_t* t = zone_tree_create(false);
+ ok(t != NULL, "ztree: created");
+
+ /* 2. insert test */
+ unsigned passed = 1;
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ zone_node_t *node = NODEE + i;
+ if (zone_tree_insert(t, &node) != 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 != NODEE + 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 == NODEE + 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");
+
+ /* 6. subtree apply */
+ int counter = 0;
+ ret = zone_tree_sub_apply(t, (const knot_dname_t *)"\x02""ac", false, ztree_node_counter, &counter);
+ ok(ret == KNOT_EOK && counter == 2, "ztree: subtree iteration");
+ counter = 0;
+ ret = zone_tree_sub_apply(t, (const knot_dname_t *)"\x02""ac", true, ztree_node_counter, &counter);
+ ok(ret == KNOT_EOK && counter == 1, "ztree: subtree iteration excluding root");
+
+ 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..1346aaf
--- /dev/null
+++ b/tests/knot/test_zone-update.c
@@ -0,0 +1,337 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <pthread.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+#include <unistd.h>
+
+#include "test_conf.h"
+#include "contrib/getline.h"
+#include "knot/server/server.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/node.h"
+#include "libzscanner/scanner.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. 600 IN TXT \"test\"\n";
+static const char *add_str = "test. 600 IN TXT \"test2\"\n";
+static const char *del_str = "test. 600 IN TXT \"test\"\n";
+static const char *node_str1 = "node.test. 601 IN TXT \"abc\"\n";
+static const char *node_str2 = "node.test. 601 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 *data)
+{
+ const knot_rdataset_t *zone_rrs = node_rdataset(node, data->type);
+ if (zone_rrs != NULL) {
+ knot_rdata_t *rr = data->rrs.rdata;
+ for (size_t i = 0; i < data->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);
+}
+
+static int rr_data_cmp(struct rr_data *a, struct rr_data *b)
+{
+ if (a->type != b->type) {
+ return 1;
+ }
+ if (a->ttl != b->ttl) {
+ return 1;
+ }
+ if (a->rrs.count != b->rrs.count) {
+ return 1;
+ }
+ if (a->rrs.rdata != b->rrs.rdata) {
+ return 1;
+ }
+ if (a->additional != b->additional) {
+ return 1;
+ }
+ return 0;
+}
+
+static int test_node_unified(zone_node_t *n1, _unused_ void *v)
+{
+ zone_node_t *n2 = binode_node(n1, false);
+ if (n2 == n1) {
+ n2 = binode_node(n1, true);
+ }
+ ok(n1->owner == n2->owner, "binode %s has equal %s owner", n1->owner, n2->owner);
+ ok(n1->rrset_count == n2->rrset_count, "binode %s has equal rrset_count", n1->owner);
+ for (uint16_t i = 0; i < n1->rrset_count; i++) {
+ ok(rr_data_cmp(&n1->rrs[i], &n2->rrs[i]) == 0, "binode %s has equal rrs", n1->owner);
+ }
+ if (n1->flags & NODE_FLAGS_BINODE) {
+ ok((n1->flags ^ n2->flags) == NODE_FLAGS_SECOND, "binode %s has correct flags", n1->owner);
+ }
+ ok(n1->children == n2->children, "binode %s has equal children count", n1->owner);
+ return KNOT_EOK;
+}
+
+static void test_zone_unified(zone_t *z)
+{
+ knot_sem_wait(&z->cow_lock);
+ zone_tree_apply(z->contents->nodes, test_node_unified, NULL);
+ knot_sem_post(&z->cow_lock);
+}
+
+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);
+
+ /* 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 (max TTL: %u)", zone->contents->max_ttl);
+
+ test_zone_unified(zone);
+
+ 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),
+ "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 = update.new_cont->apex;
+ 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);
+
+ ok(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);
+
+ /* 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);
+ const zone_node_t *iter_node = zone_contents_find_node_for_rr(zone->contents, &rrset);
+ bool rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "incremental zone update: commit");
+
+ test_zone_unified(zone);
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ size_t zone_size1 = zone->contents->size;
+ uint32_t zone_max_ttl1 = zone->contents->max_ttl;
+ ret = zone_adjust_full(zone->contents, 2);
+ ok(ret == KNOT_EOK, "zone adjust full shall work");
+ size_t zone_size2 = zone->contents->size;
+ uint32_t zone_max_ttl2 = zone->contents->max_ttl;
+ ok(zone_size1 == zone_size2, "zone size measured the same incremental vs full (%zu, %zu)", zone_size1, zone_size2);
+ ok(zone_max_ttl1 == zone_max_ttl2, "zone max TTL measured the same incremental vs full (%u, %u)", zone_max_ttl1, zone_max_ttl2);
+ // TODO test more things after re-adjust, search for non-unified bi-nodes
+}
+
+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"
+ "database:\n"
+ " journal-db-max-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->server = &server;
+
+ /* 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..9c95558
--- /dev/null
+++ b/tests/knot/test_zone_events.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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, (time_t)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, (time_t)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);
+ 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..5728e97
--- /dev/null
+++ b/tests/knot/test_zone_serial.c
@@ -0,0 +1,214 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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_unixtime(uint32_t current, uint32_t increment, uint32_t expected, const char *msg)
+{
+ uint32_t next = serial_next_generic(current, SERIAL_POLICY_UNIXTIME, increment, 0, 1);
+ ok(next == expected, "unixtime: %s", msg);
+}
+
+/* Test will wrongly fail if the next second starts while running;
+ * this is unlikely, so not taking any action */
+static void test_unixtime(void)
+{
+ time_t serial0 = time(NULL);
+
+ check_unixtime(1000000000, 1, serial0, "from old second or policy");
+ check_unixtime(serial0, 0, serial0, "reuse current second");
+ check_unixtime(serial0, 1, serial0 + 1, "this second's first increment");
+ check_unixtime(serial0 + 1, 1, serial0 + 2, "this second's second increment");
+ check_unixtime(3000000000, 1, 3000000001, "from future second");
+ check_unixtime(3000000000, 0, 3000000000, "at future second");
+}
+
+static void check_dateserial(uint32_t current, uint32_t increment, uint32_t expected, const char *msg)
+{
+ uint32_t next = serial_next_generic(current, SERIAL_POLICY_DATESERIAL, increment, 0, 1);
+ ok(next == expected, "dateserial: %s", msg);
+}
+
+/* Test will wrongly fail if the next day starts while running;
+ * this is EXTREMELY unlikely, so definitely not taking any action */
+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,
+ "dateserial: prepare current value");
+
+ check_dateserial(2000010100, 1, serial0, "from old date or policy");
+ check_dateserial(serial0, 1, serial0 + 1, "today's first increment");
+ check_dateserial(serial0 + 98, 1, serial0 + 99, "today's last increment");
+ check_dateserial(serial0 + 99, 1, serial0 + 100, "wrap from today");
+ check_dateserial(2100010100, 1, 2100010101, "from future date");
+ check_dateserial(2100010100, 0, 2100010100, "at future date");
+}
+
+static void check_modulo(uint32_t current, uint32_t expected, uint8_t rem, uint8_t mod)
+{
+ uint32_t next = serial_next_generic(current, SERIAL_POLICY_INCREMENT, 1, rem, mod);
+ ok(next == expected, "modulo: %u->%u %u/%u", current, expected, rem, mod);
+}
+
+static void test_modulo(void)
+{
+ // mod 1
+
+ check_modulo(0, 1, 0, 1);
+ check_modulo(1, 2, 0, 1);
+ check_modulo(2, 3, 0, 1);
+ check_modulo(3, 4, 0, 1);
+ check_modulo(S_2HIGHEST, S_HIGHEST, 0, 1);
+ check_modulo(S_HIGHEST, S_LOWEST, 0, 1);
+
+ // mod 2
+
+ check_modulo(0, 2, 0, 2);
+ check_modulo(1, 2, 0, 2);
+ check_modulo(2, 4, 0, 2);
+ check_modulo(3, 4, 0, 2);
+ check_modulo(4, 6, 0, 2);
+ check_modulo(S_2HIGHEST, S_LOWEST, 0, 2);
+
+ check_modulo(0, 1, 1, 2);
+ check_modulo(1, 3, 1, 2);
+ check_modulo(2, 3, 1, 2);
+ check_modulo(3, 5, 1, 2);
+ check_modulo(4, 5, 1, 2);
+ check_modulo(S_2HIGHEST, S_HIGHEST, 1, 2);
+
+ // mod 3
+
+ check_modulo(0, 3, 0, 3);
+ check_modulo(1, 3, 0, 3);
+ check_modulo(2, 3, 0, 3);
+ check_modulo(3, 6, 0, 3);
+ check_modulo(4, 6, 0, 3);
+
+ check_modulo(0, 1, 1, 3);
+ check_modulo(1, 4, 1, 3);
+ check_modulo(2, 4, 1, 3);
+ check_modulo(3, 4, 1, 3);
+ check_modulo(4, 7, 1, 3);
+
+ check_modulo(0, 2, 2, 3);
+ check_modulo(1, 2, 2, 3);
+ check_modulo(2, 5, 2, 3);
+ check_modulo(3, 5, 2, 3);
+ check_modulo(4, 5, 2, 3);
+}
+
+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();
+ test_unixtime();
+ test_modulo();
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_timers.c b/tests/knot/test_zone_timers.c
new file mode 100644
index 0000000..da1fe99
--- /dev/null
+++ b/tests/knot/test_zone_timers.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 = {
+ .last_flush = 1474559960,
+ .next_refresh = 1474559961,
+ .last_refresh_ok = true,
+ .last_notified_serial = 123456,
+ .next_ds_check = 1474559962,
+ .next_ds_push = 1474559963,
+ .catalog_member = 1474559964,
+ .next_expire = 1474559965,
+ .last_master = { .sin6_family = AF_INET, .sin6_port = 53 },
+ .master_pin_hit = 1474559966,
+};
+
+static bool timers_eq(const zone_timers_t *val, const zone_timers_t *ref)
+{
+ return val->last_flush == ref->last_flush &&
+ val->next_refresh == ref->next_refresh &&
+ val->last_refresh_ok == ref->last_refresh_ok &&
+ val->last_notified_serial == ref->last_notified_serial &&
+ val->next_ds_check == ref->next_ds_check &&
+ val->next_ds_push == ref->next_ds_push &&
+ val->catalog_member == ref->catalog_member &&
+ val->next_expire == ref->next_expire &&
+ sockaddr_cmp((struct sockaddr_storage *)&val->last_master,
+ (struct sockaddr_storage *)&ref->last_master, false) == 0 &&
+ val->master_pin_hit == ref->master_pin_hit;
+}
+
+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;
+}
+
+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_lmdb_db_t _db = { 0 }, *db = &_db;
+ knot_lmdb_init(db, dbid, 1024 * 1024, 0, NULL);
+ int ret = knot_lmdb_open(db);
+ ok(ret == KNOT_EOK && db != NULL, "open timers");
+
+ // 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);
+ 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, "zone_timers_read()");
+ ok(timers_eq(&timers, &MOCK_TIMERS), "inconsistent timers");
+
+ // 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.
+ knot_lmdb_deinit(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..3ef7632
--- /dev/null
+++ b/tests/knot/test_zonedb.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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. */
+ knot_dname_txt_storage_t buf;
+ 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, false);
+ return 0;
+}
diff --git a/tests/libdnssec/sample_keys.h b/tests/libdnssec/sample_keys.h
new file mode 100644
index 0000000..cd9f18f
--- /dev/null
+++ b/tests/libdnssec/sample_keys.h
@@ -0,0 +1,498 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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
+ }},
+};
+
+/*
+ * Private-key-format: v1.2
+ * Algorithm: 16 (ED448)
+ * PrivateKey: MEcCAQAwBQYDK2VxBDsEOVsk9cLaP+mD8n97xRET5bCqCUDZpPCIkCGrXLJG
+ * JgXCGlz4mOxH4qva7fIhEXm/62vtdbd55WRXIA==
+ *
+ * example.com. 3600 IN DNSKEY 256 3 16 (
+ * 3y0sCUZKI+DSyZQTTbgHOFppOzYz01iZQQVUWw9gCb1bLWNqqC/5qq2PL9T
+ * c6YaK2vHthBtaY0iA )
+ *
+ * example.com. 3600 IN DS 28205 16 2 (
+ * 7a27236ccb5193f696cdb4c5fd58af3500634bd836e6defacaac5dd4d76
+ * 6dcbe )
+ *
+ * example.com. 3600 IN MX 10 mail.example.com.
+ *
+ * example.com. 3600 IN RRSIG MX 16 2 3600 20211108134038 (
+ * 20211105115718 28205 example.com.
+ * uzvY2twOxxSas25N1HNQPRmhVgIaOHrEm6TccvwU
+ * WeJkecp2Nd0om5+Em1/91cfJWc/ZCFSYBcIAUFVB
+ * bMKoK0SXbmY5dM6rl0wQRk+Sl8VLyNttLMn5tpFk
+ * c74nWRGfXebz/HALT5WTdOidgIR8HCcA )
+ */
+
+static const key_parameters_t SAMPLE_ED448_KEY = {
+ .name = (uint8_t *)"\x07""example""\x03""com",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 16,
+ .public_key = { .size = 57, .data = (uint8_t []) {
+ 0xdf, 0x2d, 0x2c, 0x09, 0x46, 0x4a, 0x23, 0xe0, 0xd2, 0xc9, 0x94, 0x13,
+ 0x4d, 0xb8, 0x07, 0x38, 0x5a, 0x69, 0x3b, 0x36, 0x33, 0xd3, 0x58, 0x99,
+ 0x41, 0x05, 0x54, 0x5b, 0x0f, 0x60, 0x09, 0xbd, 0x5b, 0x2d, 0x63, 0x6a,
+ 0xa8, 0x2f, 0xf9, 0xaa, 0xad, 0x8f, 0x2f, 0xd4, 0xdc, 0xe9, 0x86, 0x8a,
+ 0xda, 0xf1, 0xed, 0x84, 0x1b, 0x5a, 0x63, 0x48, 0x80,
+ }},
+ .rdata = { .size = 61, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x10,
+ 0xdf, 0x2d, 0x2c, 0x09, 0x46, 0x4a, 0x23, 0xe0, 0xd2, 0xc9, 0x94, 0x13,
+ 0x4d, 0xb8, 0x07, 0x38, 0x5a, 0x69, 0x3b, 0x36, 0x33, 0xd3, 0x58, 0x99,
+ 0x41, 0x05, 0x54, 0x5b, 0x0f, 0x60, 0x09, 0xbd, 0x5b, 0x2d, 0x63, 0x6a,
+ 0xa8, 0x2f, 0xf9, 0xaa, 0xad, 0x8f, 0x2f, 0xd4, 0xdc, 0xe9, 0x86, 0x8a,
+ 0xda, 0xf1, 0xed, 0x84, 0x1b, 0x5a, 0x63, 0x48, 0x80,
+ }},
+ .keytag = 28205,
+ .key_id = "501a69b2d8ad46c721ffabaa9eaf8e7fa49c1454",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x01,
+ 0x69, 0xac, 0x45, 0x1c, 0xfa, 0xbb, 0xbb, 0x16, 0x5e, 0xf3, 0x82, 0x08,
+ 0x1f, 0xd3, 0x7e, 0x7a, 0xb4, 0xd9, 0x13, 0xbf,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x02,
+ 0x7a, 0x27, 0x23, 0x6c, 0xcb, 0x51, 0x93, 0xf6, 0x96, 0xcd, 0xb4, 0xc5,
+ 0xfd, 0x58, 0xaf, 0x35, 0x00, 0x63, 0x4b, 0xd8, 0x36, 0xe6, 0xde, 0xfa,
+ 0xca, 0xac, 0x5d, 0xd4, 0xd7, 0x66, 0xdc, 0xbe,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x04,
+ 0x6e, 0x06, 0x6f, 0xfb, 0xd5, 0xa2, 0x6c, 0xf3, 0x04, 0xa1, 0x2b, 0x76,
+ 0xf1, 0x83, 0xdb, 0xd3, 0x8b, 0x5e, 0x7c, 0xcb, 0x19, 0x79, 0xff, 0x3f,
+ 0x46, 0xee, 0xf2, 0x61, 0xf7, 0xa0, 0x48, 0x96, 0xbd, 0xe6, 0x6a, 0xe8,
+ 0xd0, 0x30, 0x54, 0xc8, 0x3a, 0xa1, 0x2d, 0xb3, 0x77, 0x37, 0xf1, 0xbb,
+ }},
+ .bit_size = 456,
+ .pem = { .size = 156, .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, 0x45, 0x63, 0x43, 0x41, 0x51, 0x41, 0x77,
+ 0x42, 0x51, 0x59, 0x44, 0x4b, 0x32, 0x56, 0x78, 0x42, 0x44, 0x73, 0x45,
+ 0x4f, 0x56, 0x73, 0x6b, 0x39, 0x63, 0x4c, 0x61, 0x50, 0x2b, 0x6d, 0x44,
+ 0x38, 0x6e, 0x39, 0x37, 0x78, 0x52, 0x45, 0x54, 0x35, 0x62, 0x43, 0x71,
+ 0x43, 0x55, 0x44, 0x5a, 0x70, 0x50, 0x43, 0x49, 0x6b, 0x43, 0x47, 0x72,
+ 0x58, 0x4c, 0x4a, 0x47, 0x4a, 0x67, 0x58, 0x43, 0x0a, 0x47, 0x6c, 0x7a,
+ 0x34, 0x6d, 0x4f, 0x78, 0x48, 0x34, 0x71, 0x76, 0x61, 0x37, 0x66, 0x49,
+ 0x68, 0x45, 0x58, 0x6d, 0x2f, 0x36, 0x32, 0x76, 0x74, 0x64, 0x62, 0x64,
+ 0x35, 0x35, 0x57, 0x52, 0x58, 0x49, 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
+ }},
+ /* raw private key outside of PEM:
+ *
+ * 0x5b, 0x24, 0xf5, 0xc2, 0xda, 0x3f, 0xe9, 0x83, 0xf2, 0x7f, 0x7b, 0xc5,
+ * 0x11, 0x13, 0xe5, 0xb0, 0xaa, 0x09, 0x40, 0xd9, 0xa4, 0xf0, 0x88, 0x90,
+ * 0x21, 0xab, 0x5c, 0xb2, 0x46, 0x26, 0x05, 0xc2, 0x1a, 0x5c, 0xf8, 0x98,
+ * 0xec, 0x47, 0xe2, 0xab, 0xda, 0xed, 0xf2, 0x21, 0x11, 0x79, 0xbf, 0xeb,
+ * 0x6b, 0xed, 0x75, 0xb7, 0x79, 0xe5, 0x64, 0x57, 0x20,
+ *
+ */
+};
diff --git a/tests/libdnssec/test_binary.c b/tests/libdnssec/test_binary.c
new file mode 100644
index 0000000..4c78556
--- /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 <https://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..eb2601d
--- /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 <https://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..c3643f0
--- /dev/null
+++ b/tests/libdnssec/test_key.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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"); \
+ 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, true);
+
+ 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
+#ifdef HAVE_ED448
+ { "ED448", &SAMPLE_ED448_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..6c62106
--- /dev/null
+++ b/tests/libdnssec/test_key_algorithm.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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
+#ifdef HAVE_ED448
+ is_int(456, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ED448), "ed448 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
+#ifdef HAVE_ED448
+ ok_range(DNSSEC_KEY_ALGORITHM_ED448, 456, 456, "ED448");
+#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..fbc6327
--- /dev/null
+++ b/tests/libdnssec/test_key_ds.c
@@ -0,0 +1,122 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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
+#ifdef HAVE_ED448
+ test_key("ED448", &SAMPLE_ED448_KEY);
+#endif
+
+ test_errors(&SAMPLE_ECDSA_KEY);
+#ifdef HAVE_ED25519
+ test_errors(&SAMPLE_ED25519_KEY);
+#endif
+#ifdef HAVE_ED448
+ test_errors(&SAMPLE_ED448_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..10b6297
--- /dev/null
+++ b/tests/libdnssec/test_keyid.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 == NULL ? "(null)" : param);
+}
+
+static void test_keyid_is_valid(void)
+{
+ test_keyid_is_valid_run(NULL, false);
+ test_keyid_is_valid_run("a1b1", true);
+ 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..9828fce
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs11.c
@@ -0,0 +1,421 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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.level = INFO\n");
+ fprintf(file, "slots.removable = false\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_key_set_algorithm()");
+
+ r = dnssec_keystore_get_private(keystore, id, p11_key);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_keystore_get_private()");
+
+ // 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, DNSSEC_SIGN_NORMAL, &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, false, &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, false, &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(store, params->algorithm, params->bit_size,
+ NULL, &id_generate);
+ ok(r == DNSSEC_EOK && id_generate != NULL, "dnssec_keystore_generate()");
+ 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);
+}
+
+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()");
+
+ // 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]);
+ }
+
+ 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..26acbd4
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs8.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "error.h"
+#include "key.h"
+#include "keystore.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ char *dir = test_tmpdir();
+ if (!dir) {
+ return 1;
+ }
+
+ // create context
+
+ dnssec_keystore_t *store = NULL;
+ int r = dnssec_keystore_init_pkcs8(&store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init_pkcs8()");
+
+ r = dnssec_keystore_init(store, dir);
+ ok(r == DNSSEC_EOK, "init");
+
+ r = dnssec_keystore_open(store, dir);
+ ok(r == DNSSEC_EOK, "open");
+
+ // writing new content
+
+ char *id_A = NULL;
+ r = dnssec_keystore_generate(store, DNSSEC_KEY_ALGORITHM_RSA_SHA256,
+ 1024, NULL, &id_A);
+ ok(r == DNSSEC_EOK, "generate A");
+
+ char *id_B = NULL;
+ r = dnssec_keystore_generate(store, DNSSEC_KEY_ALGORITHM_RSA_SHA256,
+ 1024, NULL, &id_B);
+ ok(r == DNSSEC_EOK, "generate B");
+
+ // reading existing content
+
+ dnssec_key_t *key = NULL;
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_A, key);
+ ok(r == DNSSEC_EOK, "read A");
+ dnssec_key_free(key);
+
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_B, key);
+ ok(r == DNSSEC_EOK, "read B");
+ dnssec_key_free(key);
+
+ // content removal
+
+ r = dnssec_keystore_remove(store, id_A);
+ ok(r == DNSSEC_EOK, "remove A");
+
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_A, key);
+ ok(r == DNSSEC_ENOENT, "read removed");
+ dnssec_key_free(key);
+
+ // cleanup
+
+ free(id_A);
+ free(id_B);
+
+ r = dnssec_keystore_close(store);
+ ok(r == DNSSEC_EOK, "close");
+
+ r = dnssec_keystore_deinit(store);
+ ok(r == DNSSEC_EOK, "deinit");
+
+ test_rm_rf(dir);
+ free(dir);
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keytag.c b/tests/libdnssec/test_keytag.c
new file mode 100644
index 0000000..ac5f57a
--- /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 <https://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_nsec_bitmap.c b/tests/libdnssec/test_nsec_bitmap.c
new file mode 100644
index 0000000..f013131
--- /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 <https://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..595a314
--- /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 <https://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..622c967
--- /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 <https://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..6571d90
--- /dev/null
+++ b/tests/libdnssec/test_shared_bignum.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <string.h>
+
+#include "bignum.c"
+#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..c3d8fd2
--- /dev/null
+++ b/tests/libdnssec/test_shared_dname.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "dname.c"
+
+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..8f57a41
--- /dev/null
+++ b/tests/libdnssec/test_sign.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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
+
+#ifdef HAVE_ED448
+static const dnssec_binary_t signed_ed448 = { .size = 114, .data = (uint8_t []) {
+ 0x8d, 0x79, 0x27, 0xbd, 0xe2, 0xc4, 0x23, 0xd8, 0x26, 0xc1, 0xd4, 0xab,
+ 0x6a, 0x0d, 0xdf, 0xe5, 0x5c, 0xf1, 0x8d, 0x3f, 0x1b, 0x13, 0x81, 0x94,
+ 0xb2, 0x2d, 0xf0, 0x94, 0x58, 0x38, 0x6c, 0xf7, 0xe8, 0xc0, 0x92, 0xab,
+ 0x33, 0x1f, 0x1c, 0xe8, 0x18, 0x3f, 0xab, 0x24, 0x41, 0x10, 0xf7, 0x04,
+ 0xf5, 0x81, 0x68, 0x0d, 0x0c, 0x38, 0x8d, 0xd6, 0x80, 0xb4, 0x6b, 0xe8,
+ 0x65, 0xc1, 0xce, 0x73, 0xc7, 0x54, 0x20, 0x32, 0x21, 0x7c, 0x63, 0x5e,
+ 0x55, 0xe0, 0xdf, 0x2b, 0xdd, 0xd7, 0xd1, 0x82, 0xe0, 0x41, 0x75, 0xd4,
+ 0xe9, 0xb9, 0x76, 0xb8, 0xa6, 0xa9, 0x0a, 0x4f, 0x18, 0xe1, 0x62, 0x27,
+ 0x74, 0x99, 0x01, 0x98, 0x5f, 0xdb, 0xea, 0xdf, 0xab, 0x59, 0x6c, 0x79,
+ 0xe2, 0xc2, 0x2a, 0x91, 0x29, 0x00
+ }};
+#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, false, 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, DNSSEC_SIGN_NORMAL, &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, false, &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, DNSSEC_SIGN_NORMAL, &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, false, &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
+#ifdef HAVE_ED448
+ diag("ED448 signing");
+ check_key(&SAMPLE_ED448_KEY, &input_data, &signed_ed448, 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..18745a9
--- /dev/null
+++ b/tests/libdnssec/test_sign_der.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "bignum.c"
+#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..bddb17f
--- /dev/null
+++ b/tests/libdnssec/test_tsig.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <string.h>
+
+#include "binary.h"
+#include "dname.c"
+#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..3846f31
--- /dev/null
+++ b/tests/libknot/test_control.c
@@ -0,0 +1,221 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 = KNOT_CTL_TYPE_DATA;
+ 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 = KNOT_CTL_TYPE_DATA;
+ 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..82ba502
--- /dev/null
+++ b/tests/libknot/test_cookies.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+#include <time.h>
+
+#include "libknot/cookies.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "contrib/sockaddr.h"
+
+static knot_edns_cookie_t client_generate(
+ struct sockaddr_storage *s_addr, const uint8_t *c_secret,
+ const char *msg, int code, const char *ref)
+{
+ knot_edns_cookie_params_t params = {
+ .server_addr = s_addr,
+ };
+ memcpy(params.secret, c_secret, sizeof(params.secret));
+
+ knot_edns_cookie_t cc;
+ int ret = knot_edns_cookie_client_generate(&cc, &params);
+ is_int(code, ret, "client_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ ok(cc.len == KNOT_EDNS_COOKIE_CLNT_SIZE && memcmp(cc.data, ref, cc.len) == 0,
+ "client_generate value: %s", msg);
+ }
+ return cc;
+}
+
+static knot_edns_cookie_t server_generate(
+ struct sockaddr_storage *c_addr, const uint8_t *s_secret, uint32_t timestamp,
+ const knot_edns_cookie_t *cc, const char *msg, int code, const char *ref)
+{
+ knot_edns_cookie_params_t params = {
+ .version = KNOT_EDNS_COOKIE_VERSION,
+ .timestamp = timestamp,
+ .client_addr = c_addr,
+ };
+ memcpy(params.secret, s_secret, sizeof(params.secret));
+
+ knot_edns_cookie_t sc;
+ int ret = knot_edns_cookie_server_generate(&sc, cc, &params);
+ is_int(code, ret, "server_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ ok(sc.len == 16 && memcmp(sc.data, ref, sc.len) == 0,
+ "server_generate value: %s", msg);
+ }
+ return sc;
+}
+
+static void client_check(
+ struct sockaddr_storage *s_addr, const uint8_t *secret,
+ knot_edns_cookie_t *cc, const char *msg, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .server_addr = s_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ int ret = knot_edns_cookie_client_check(cc, &params);
+ is_int(code, ret, "client_check ret: %s", msg);
+}
+
+static void server_check(
+ struct sockaddr_storage *c_addr, const uint8_t *secret,
+ knot_edns_cookie_t *sc, knot_edns_cookie_t *cc, uint32_t timestamp,
+ const char *msg, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .version = KNOT_EDNS_COOKIE_VERSION,
+ .timestamp = timestamp,
+ .lifetime_before = 3600,
+ .lifetime_after = 300,
+ .client_addr = c_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ int ret = knot_edns_cookie_server_check(sc, cc, &params);
+ is_int(code, ret, "server_check ret: %s", msg);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_edns_cookie_t cc;
+ knot_edns_cookie_t sc;
+
+ const uint8_t c_secret1[] = "\x3F\x66\x51\xC9\x81\xC1\xD7\x3E\x58\x79\x25\xD2\xF9\x98\x5F\x08";
+ const uint8_t c_secret2[] = "\x4C\x31\x15\x17\xFA\xB6\xBF\xE2\xE1\x49\xAB\x74\xEC\x1B\xC9\xA0";
+ const uint8_t s_secret1[] = "\xE5\xE9\x73\xE5\xA6\xB2\xA4\x3F\x48\xE7\xDC\x84\x9E\x37\xBF\xCF";
+
+ struct sockaddr_storage c4_sa1 = { 0 };
+ struct sockaddr_storage c4_sa2 = { 0 };
+ struct sockaddr_storage s4_sa = { 0 };
+ sockaddr_set(&c4_sa1, AF_INET, "198.51.100.100", 0);
+ sockaddr_set(&c4_sa2, AF_INET, "203.0.113.203", 0);
+ sockaddr_set(&s4_sa, AF_INET, "192.0.2.53", 0);
+
+ struct sockaddr_storage c6_sa = { 0 };
+ struct sockaddr_storage s6_sa = { 0 };
+ sockaddr_set(&c6_sa, AF_INET6, "2001:db8:220:1:59de:d0f4:8769:82b8", 0);
+ sockaddr_set(&s6_sa, AF_INET6, "2001:db8:8f::53", 0);
+
+ const uint8_t c_secret6[] = "\x3B\x49\x5B\xA6\xA5\xB7\xFD\x87\x73\x5B\xD5\x8F\x1E\xF7\x26\x1D";
+ const uint8_t s_secret6[] = "\xDD\x3B\xDF\x93\x44\xB6\x78\xB1\x85\xA6\xF5\xCB\x60\xFC\xA7\x15";
+ const uint8_t s_secret7[] = "\x44\x55\x36\xBC\xD2\x51\x32\x98\x07\x5A\x5D\x37\x96\x63\xC9\x62";
+
+ // Learning a new Server Cookie
+
+ cc = client_generate(&s4_sa, c_secret1, "IPv4", KNOT_EOK,
+ "\x24\x64\xC4\xAB\xCF\x10\xC9\x57");
+ client_check(&s4_sa, c_secret1, &cc, "IPv4", KNOT_EOK);
+ sc = server_generate(&c4_sa1, s_secret1, 1559731985, &cc, "IPv4", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\x9F\x11\x1F\x81\x30\xC3\xEE\xE2\x94\x80");
+ server_check(&c4_sa1, s_secret1, &sc, &cc, 1559731985, "IPv4", KNOT_EOK);
+
+ // The same client learning a renewed (fresh) Server Cookie
+
+ server_generate(&c4_sa1, s_secret1, 1559734385, &cc, "IPv4", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xA8\x71\xD4\xA5\x64\xA1\x44\x2A\xCA\x77");
+
+ // Another client learning a renewed Server Cookie
+
+ cc = client_generate(&s4_sa, c_secret2, "IPv4", KNOT_EOK,
+ "\xFC\x93\xFC\x62\x80\x7D\xDB\x86");
+ char *sc_reserved = "\x01\xAB\xCD\xEF\x5C\xF7\x8F\x71\xA3\x14\x22\x7B\x66\x79\xEB\xF5";
+ memcpy(sc.data, sc_reserved, strlen(sc_reserved));
+ server_check(&c4_sa2, s_secret1, &sc, &cc, 1559727985, "IPv4", KNOT_EOK);
+
+ // Version check
+
+ sc.data[0] = 10;
+ server_check(&c4_sa2, s_secret1, &sc, &cc, 1559727985, "version", KNOT_ENOTSUP);
+
+ // IPv6 query with rolled over secret
+
+ cc = client_generate(&s6_sa, c_secret6, "IPv6", KNOT_EOK,
+ "\x22\x68\x1A\xB9\x7D\x52\xC2\x98");
+ client_check(&s6_sa, c_secret6, &cc, "IPv6", KNOT_EOK);
+ sc = server_generate(&c6_sa, s_secret6, 1559741817, &cc, "IPv6", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xC5\x79\x26\x55\x6B\xD0\x93\x4C\x72\xF8");
+ server_check(&c6_sa, s_secret6, &sc, &cc, 1559741961, "IPv6", KNOT_EOK);
+ sc = server_generate(&c6_sa, s_secret7, 1559741961, &cc, "IPv6", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xC6\x09\xA6\xBB\x79\xD1\x66\x25\x50\x7A");
+
+ // Past lifetime check
+
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 + 3600, "last old", KNOT_EOK);
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 + 3601, "too old", KNOT_ERANGE);
+
+ // Future lifetime check
+
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 - 300, "last new", KNOT_EOK);
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 - 301, "too new", KNOT_ERANGE);
+
+ return 0;
+}
diff --git a/tests/libknot/test_db.c b/tests/libknot/test_db.c
new file mode 100644
index 0000000..e409ad8
--- /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 <https://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..8c25e2c
--- /dev/null
+++ b/tests/libknot/test_descriptor.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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] = "";
+ 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..2480135
--- /dev/null
+++ b/tests/libknot/test_dname.c
@@ -0,0 +1,618 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#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 = NULL;
+ if (w != NULL) {
+ 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)
+{
+ knot_dname_storage_t d1;
+ knot_dname_txt_storage_t s1;
+ 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";
+ assert(strlen((const char *)in) == KNOT_DNAME_MAXLEN - 1);
+ 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);
+
+ /* DNAME EQUALITY CHECKS IGNORING CASE */
+
+ t = "aB.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_case_equal(d, d), "dname_is_case_equal: equal case");
+
+ t = "aB.cD.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different case");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.dc.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different name");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.cd";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different length");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.cdx.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different label");
+ 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_dynarray.c b/tests/libknot/test_dynarray.c
new file mode 100644
index 0000000..ac1e0f1
--- /dev/null
+++ b/tests/libknot/test_dynarray.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/dynarray.h"
+
+#define test_capacity 5
+// minimum 3
+
+typedef struct {
+ int x;
+ int x2;
+} quadrate_t;
+
+knot_dynarray_declare(q, quadrate_t, DYNARRAY_VISIBILITY_STATIC, test_capacity);
+knot_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;
+ knot_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);
+}
+
+static size_t q_set_dups(q_dynarray_t *q, double dup_percentage, const quadrate_t *dupval)
+{
+ size_t dup_cnt = 0;
+ int threshold = (int)(dup_percentage / 100 * ((double)RAND_MAX + 1.0));
+
+ knot_dynarray_foreach(q, quadrate_t, item, *q) {
+ if (rand() < threshold && q_dynarray_memb_cmp(item, dupval) != 0) {
+ *item = *dupval;
+ dup_cnt++;
+ }
+ }
+
+ return dup_cnt;
+}
+
+static void check_dups(q_dynarray_t *q, const quadrate_t *dupval,
+ const size_t expected, const char *msg)
+{
+ size_t cnt = 0;
+ knot_dynarray_foreach(q, quadrate_t, item, *q) {
+ if (q_dynarray_memb_cmp(item, dupval) == 0) {
+ cnt++;
+ }
+ }
+ ok(cnt == expected, "duplicate items: %s", 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);
+
+ // duplicate items removal test
+ q = q_fill(test_capacity * 10);
+ quadrate_t dup_item = { .x = 0, .x2 = 0 }; // matches the first item
+ size_t dups = q_set_dups(&q, 50, &dup_item);
+ ok(q.size == test_capacity * 10, "duplicate items: created");
+ check_dups(&q, &dup_item, dups + 1, "created all");
+
+ q_dynarray_remove(&q, &dup_item);
+ ok(q.size == test_capacity * 10 - dups - 1, "duplicate items: removed");
+ check_dups(&q, &dup_item, 0, "removed all");
+
+ q_dynarray_free(&q);
+
+ // binary search removal test
+ q = q_fill(test_capacity * 10);
+ for (int i = 0; i < test_capacity * 10; i++) {
+ quadrate_t qu = { i, i * i };
+ if ((qu.x % 2) == 0) {
+ q_dynarray_remove(&q, &qu);
+ }
+ }
+ q_dynarray_sort(&q);
+ for (int i = 0; i < test_capacity * 10; i++) {
+ quadrate_t qu = { i, i * i };
+ int present = (q_dynarray_bsearch(&q, &qu) != NULL ? 1 : 0);
+ ok(present == (i % 2), "presence in sorted array %d", i);
+ }
+ q_dynarray_free(&q);
+
+ return 0;
+}
diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c
new file mode 100644
index 0000000..d2b4fd8
--- /dev/null
+++ b/tests/libknot/test_edns.c
@@ -0,0 +1,502 @@
+/* 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 <https://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 char *E_NSID_STR2 = "BarFoo";
+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
+ * 4) KNOT_EDNS_OPTION_NSID, E_NSID_LEN, E_NSID_STR2
+ */
+
+ /* 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
+ + 2 * 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 */
+ uint8_t *nsid1 = knot_edns_get_option(opt_rr, KNOT_EDNS_OPTION_NSID, NULL);
+ check = nsid1 != NULL;
+ ok(check, "OPT RR getters: NSID check");
+ check = memcmp(knot_edns_opt_get_data(nsid1), E_NSID_STR, knot_edns_opt_get_length(nsid1)) == 0;
+ ok(check, "OPT RR getters: NSID value check");
+
+ /* Another NSID */
+ uint8_t *nsid2 = knot_edns_get_option(opt_rr, KNOT_EDNS_OPTION_NSID, nsid1);
+ check = nsid2 != NULL;
+ ok(check, "OPT RR getters: another NSID check");
+ check = memcmp(knot_edns_opt_get_data(nsid2), E_NSID_STR2, knot_edns_opt_get_length(nsid2)) == 0;
+ ok(check, "OPT RR getters: another NSID value check");
+
+ /* Other OPTIONs */
+ check = knot_edns_get_option(opt_rr, E_OPT3_CODE, NULL) != NULL;
+ ok(check, "OPT RR getters: empty option 1");
+
+ check = knot_edns_get_option(opt_rr, E_OPT4_CODE, NULL) != 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 NSID. */
+ 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));
+
+ /* Another option NSID. */
+ ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
+ E_NSID_LEN, (uint8_t *)E_NSID_STR2, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add option with data (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 NSID */
+ 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..9397d84
--- /dev/null
+++ b/tests/libknot/test_edns_ecs.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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(a, b, true) == 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..30f16f7
--- /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 <https://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..3f7b514
--- /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 <https://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..fe31d3c
--- /dev/null
+++ b/tests/libknot/test_pkt.c
@@ -0,0 +1,199 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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], true) > 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_probe.c b/tests/libknot/test_probe.c
new file mode 100644
index 0000000..37437f4
--- /dev/null
+++ b/tests/libknot/test_probe.c
@@ -0,0 +1,96 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/packet/pkt.c"
+#include "libknot/probe/probe.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_probe_t *probe_out = knot_probe_alloc();
+ ok(probe_out != NULL, "probe: initialize output probe");
+ knot_probe_t *probe_in = knot_probe_alloc();
+ ok(probe_in != NULL, "probe: initialize input probe");
+
+ int fd = knot_probe_fd(probe_out);
+ ok(fd < 0, "probe: unavailable fd");
+
+ char *workdir = test_mkdtemp();
+ ok(workdir != NULL, "probe: create temporary workdir");
+
+ int ret = knot_probe_set_producer(probe_out, workdir, 1);
+ ok(ret == KNOT_ECONN, "probe: connect producer");
+
+ ret = knot_probe_set_consumer(probe_in, workdir, 1);
+ ok(ret == KNOT_EOK, "probe: connect consumer");
+ fd = knot_probe_fd(probe_in);
+ ok(fd >= 0, "probe: get input probe fd");
+
+ ret = knot_probe_set_producer(probe_out, workdir, 1);
+ ok(ret == KNOT_EOK, "probe: reconnect producer");
+ fd = knot_probe_fd(probe_out);
+ ok(fd >= 0, "probe: get output probe fd");
+
+ struct sockaddr_storage addr;
+ ret = sockaddr_set(&addr, AF_INET, "192.168.0.1", 53);
+ ok(ret == KNOT_EOK, "probe: set address");
+
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ ok(query != NULL, "probe: create query");
+ knot_pkt_t *reply = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ ok(reply != NULL, "probe: create reply");
+
+ ret = knot_pkt_put_question(query, (const uint8_t *)"\x04test\x00",
+ KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ ok(ret == KNOT_EOK, "probe: put query");
+
+ knot_probe_data_t data_out;
+ ret = knot_probe_data_set(&data_out, KNOT_PROBE_PROTO_UDP,
+ &addr, &addr, query, reply, KNOT_RCODE_NXDOMAIN);
+ ok(ret == KNOT_EOK, "probe: connect producer");
+
+ knot_pkt_free(query);
+ knot_pkt_free(reply);
+
+ ret = knot_probe_produce(probe_out, &data_out, 1);
+ ok(ret == KNOT_EOK, "probe: produce datagram");
+
+ knot_probe_data_t data_in;
+ ret = knot_probe_consume(probe_in, &data_in, 1, 20);
+ ok(ret == 1, "probe: consume datagram");
+
+ ret = memcmp(&data_in, &data_out, offsetof(knot_probe_data_t, query.qname));
+ ok(ret == 0, "probe: data comparison");
+
+ ret = knot_dname_cmp(data_in.query.qname, data_out.query.qname);
+ ok(ret == 0, "probe: qname comparison");
+
+ knot_probe_free(probe_in);
+ knot_probe_free(probe_out);
+
+ test_rm_rf(workdir);
+ free(workdir);
+
+ return 0;
+}
diff --git a/tests/libknot/test_rdata.c b/tests/libknot/test_rdata.c
new file mode 100644
index 0000000..754cd7f
--- /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 <https://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..d364be3
--- /dev/null
+++ b/tests/libknot/test_rdataset.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <tap/basic.h>
+#include <string.h>
+
+#include "libknot/rdataset.c"
+#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);
+
+static size_t rdataset_size(const knot_rdataset_t *rrs)
+{
+ if (rrs == NULL || rrs->count == 0) {
+ return 0;
+ }
+
+ const knot_rdata_t *last = rr_seek(rrs, rrs->count - 1);
+ return (uint8_t *)last + knot_rdata_size(last->len) - (uint8_t *)rrs->rdata;
+}
+
+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.size == 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(rdataset_size(&rdataset) == knot_rdata_size(4) * 2,
+ "rdataset: size.");
+ ok(rdataset.size == rdataset_size(&rdataset), "rdataset: size precomputed (%u %zu).",
+ rdataset.size, rdataset_size(&rdataset));
+
+ // 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 &&
+ rdataset_size(&copy) == rdataset_size(&rdataset) &&
+ rdataset.rdata != NULL && copy.rdata != NULL &&
+ memcmp(rdataset.rdata, copy.rdata,
+ rdataset_size(&rdataset)) == 0;
+ ok(copy_ok, "rdataset: copy");
+ ok(copy.size == rdataset_size(&copy), "copy: size precomputed.");
+
+ // 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 intersect2
+ ok(knot_rdataset_intersect2(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: intersect2 NULL.");
+
+ ret = knot_rdataset_intersect2(&rdataset, &rdataset, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset, &rdataset);
+ ok(intersect_ok, "rdataset: intersect2 self.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_intersect2(&rdataset_lo, &rdataset_gt, NULL);
+ intersect_ok = ret == KNOT_EOK && rdataset_lo.count == 0;
+ ok(intersect_ok, "rdataset: intersect2 no common.");
+
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ret = knot_rdataset_intersect2(&copy, &rdataset_lo, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&copy, &rdataset_lo);
+ ok(intersect_ok, "rdataset: intersect2 normal.");
+ knot_rdataset_clear(&copy, 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.");
+
+ 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..35dc1bc
--- /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 <https://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..cc67e0f
--- /dev/null
+++ b/tests/libknot/test_rrset.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 - same TTL
+ ok(knot_rrset_equal(rrset, copy, true), "rrset: cmp same TTL");
+
+ // Test equal - different TTL
+ copy->ttl++;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different TTL");
+
+ // Test equal - ignore TTL
+ ok(knot_rrset_equal(rrset, copy, false), "rrset: cmp ignore TTL");
+
+ copy->ttl = rrset->ttl;
+
+ // Test equal - different type
+ copy->type++;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different type");
+
+ copy->type = rrset->type;
+
+ // Test equal - owners
+ knot_dname_free(rrset->owner, NULL);
+ rrset->owner = NULL;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp NULL owner");
+
+ knot_dname_free(copy->owner, NULL);
+ copy->owner = NULL;
+ ok(knot_rrset_equal(rrset, copy, true), "rrset: cmp NULL owners");
+
+ // Test equal - different rdata
+ knot_rrset_add_rdata(copy, (const uint8_t *)"abc", 3, NULL);
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different rdata");
+
+ // 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..a34e7ca
--- /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 <https://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..3e2468c
--- /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 <https://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_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c
new file mode 100644
index 0000000..3e366b1
--- /dev/null
+++ b/tests/libknot/test_xdp_tcp.c
@@ -0,0 +1,638 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+
+#include "tap/basic.h"
+#include "libknot/error.h"
+#include "libknot/xdp/msg_init.h"
+#include "libknot/xdp/tcp.c"
+#include "libknot/xdp/tcp_iobuf.c"
+#include "libknot/xdp/bpf-user.h"
+
+#define INFTY INT32_MAX
+
+knot_tcp_table_t *test_table = NULL;
+knot_tcp_table_t *test_syn_table = NULL;
+#define TEST_TABLE_SIZE 100
+
+size_t sent_acks = 0;
+size_t sent_rsts = 0;
+size_t sent_syns = 0;
+size_t sent_fins = 0;
+uint32_t sent_seqno = 0;
+uint32_t sent_ackno = 0;
+size_t sent2_data = 0;
+size_t send2_mss = 0;
+
+knot_xdp_socket_t *test_sock = NULL;
+
+struct sockaddr_in test_addr = { AF_INET, 0, { 127 + (1 << 24) }, { 0 } };
+
+knot_tcp_conn_t *test_conn = NULL;
+
+/*!
+ * \brief Length of timeout-watching list.
+ */
+static size_t tcp_table_timeout_length(knot_tcp_table_t *table)
+{
+ return list_size(tcp_table_timeout(table));
+}
+
+/*!
+ * \brief Clean up old TCP connection w/o sending RST or FIN.
+ *
+ * \param tcp_table TCP connection table to clean up.
+ * \param timeout Remove connections older than this (usecs).
+ * \param at_least Remove at least this number of connections.
+ */
+static void tcp_cleanup(knot_tcp_table_t *tcp_table, uint32_t timeout,
+ uint32_t at_least)
+{
+ uint32_t now = get_timestamp(), i = 0;
+ knot_tcp_conn_t *conn, *next;
+ WALK_LIST_DELSAFE(conn, next, *tcp_table_timeout(tcp_table)) {
+ if (i++ < at_least || now - conn->last_active >= timeout) {
+ tcp_table_remove(tcp_table_re_lookup(conn, tcp_table), tcp_table);
+ del_conn(conn);
+ }
+ }
+}
+
+/*!
+ * \brief Find connection related to incoming message.
+ */
+static knot_tcp_conn_t *tcp_table_find(knot_tcp_table_t *table, knot_xdp_msg_t *msg_recv)
+{
+ uint64_t unused = 0;
+ return *tcp_table_lookup(&msg_recv->ip_from, &msg_recv->ip_to, &unused, table);
+}
+
+static int mock_send(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ ok(n_msgs <= 20, "send: not too many at once");
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+
+ ok(msg->flags & KNOT_XDP_MSG_TCP, "send: is TCP message");
+ ok(msg->payload.iov_len == 0, "send: is empty payload");
+
+ if (msg->flags & KNOT_XDP_MSG_RST) {
+ ok(!(msg->flags & KNOT_XDP_MSG_ACK), "send: no RST+ACK");
+ sent_rsts++;
+ } else if (msg->flags & KNOT_XDP_MSG_SYN) {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is SYN+ACK");
+ sent_syns++;
+ } else if (msg->flags & KNOT_XDP_MSG_FIN) {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: FIN has always ACK");
+ sent_fins++;
+ } else {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is ACK");
+ sent_acks++;
+ }
+
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static int mock_send_nocheck(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+ if (msg->flags & KNOT_XDP_MSG_RST) {
+ sent_rsts++;
+ } else if (msg->flags & KNOT_XDP_MSG_SYN) {
+ sent_syns++;
+ } else if (msg->flags & KNOT_XDP_MSG_FIN) {
+ sent_fins++;
+ } else {
+ sent_acks++;
+ }
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static int mock_send2(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ ok(n_msgs <= 20, "send2: not too many at once");
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+ ok(msg->flags & KNOT_XDP_MSG_TCP, "send2: is TCP message");
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send2: has ACK");
+ ok(msg->payload.iov_len <= send2_mss, "send2: fulfilled MSS");
+ sent2_data += msg->payload.iov_len;
+
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static void clean_table(void)
+{
+ (void)tcp_cleanup(test_table, 0, INFTY);
+}
+
+static void clean_sent(void)
+{
+ sent_acks = 0;
+ sent_rsts = 0;
+ sent_syns = 0;
+ sent_fins = 0;
+}
+
+static void check_sent(size_t expect_acks, size_t expect_rsts, size_t expect_syns, size_t expect_fins)
+{
+ is_int(expect_acks, sent_acks, "sent ACKs");
+ is_int(expect_rsts, sent_rsts, "sent RSTs");
+ is_int(expect_syns, sent_syns, "sent SYNs");
+ is_int(expect_fins, sent_fins, "sent FINs");
+ clean_sent();
+}
+
+static void prepare_msg(knot_xdp_msg_t *msg, int flags, uint16_t sport, uint16_t dport)
+{
+ msg_init(msg, flags | KNOT_XDP_MSG_TCP);
+ memcpy(&msg->ip_from, &test_addr, sizeof(test_addr));
+ memcpy(&msg->ip_to, &test_addr, sizeof(test_addr));
+ msg->ip_from.sin6_port = htobe16(sport);
+ msg->ip_to.sin6_port = htobe16(dport);
+}
+
+static void prepare_seqack(knot_xdp_msg_t *msg, int seq_shift, int ack_shift)
+{
+ msg->seqno = sent_ackno + seq_shift;
+ msg->ackno = sent_seqno + ack_shift;
+}
+
+static void prepare_data(knot_xdp_msg_t *msg, const char *bytes, size_t n)
+{
+ msg->payload.iov_len = n;
+ msg->payload.iov_base = (void *)bytes;
+}
+
+static void fix_seqack(knot_xdp_msg_t *msg)
+{
+ knot_tcp_conn_t *conn = tcp_table_find(test_table, msg);
+ if (conn == NULL) {
+ conn = tcp_table_find(test_syn_table, msg);
+ }
+ assert(conn != NULL);
+ msg->seqno = conn->seqno;
+ msg->ackno = conn->ackno;
+}
+
+static void fix_seqacks(knot_xdp_msg_t *msgs, size_t count)
+{
+ for (size_t i = 0; i < count; i++) {
+ fix_seqack(&msgs[i]);
+ }
+}
+
+void test_syn(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "SYN: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN: send OK");
+ is_int(msg.seqno + 1, sent_ackno, "SYN: ackno");
+ check_sent(0, 0, 1, 0);
+ is_int(XDP_TCP_SYN, rl.action, "SYN: relay action");
+ is_int(XDP_TCP_NOOP, rl.answer, "SYN: relay answer");
+ ok(NULL == rl.inbf, "SYN: no payload");
+ is_int(0, test_table->usage, "SYN: no connection in normal table");
+ is_int(1, test_syn_table->usage, "SYN: one connection in SYN table");
+ knot_tcp_conn_t *conn = tcp_table_find(test_syn_table, &msg);
+ ok(conn != NULL, "SYN: connection present");
+ assert(conn);
+ ok(conn == rl.conn, "SYN: relay points to connection");
+ is_int(XDP_TCP_ESTABLISHING, conn->state, "SYN: connection state");
+ ok(memcmp(&conn->ip_rem, &msg.ip_from, sizeof(msg.ip_from)) == 0, "SYN: conn IP from");
+ ok(memcmp(&conn->ip_loc, &msg.ip_to, sizeof(msg.ip_to)) == 0, "SYN: conn IP to");
+
+ knot_tcp_cleanup(test_syn_table, &rl, 1);
+ test_conn = conn;
+}
+
+void test_establish(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
+ prepare_seqack(&msg, 0, 1);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "establish: relay OK");
+ is_int(0, test_syn_table->usage, "SYN: no connection in SYN table");
+ is_int(1, test_table->usage, "SYN: one connection in normal table");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "establish: send OK");
+ check_sent(0, 0, 0, 0);
+ is_int(0, rl.auto_answer, "establish: no auto answer");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
+ clean_table();
+}
+
+void test_syn_ack(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK, 1000, 2000);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "SYN+ACK: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN+ACK: send OK");
+ is_int(msg.seqno + 1, sent_ackno, "SYN+ACK: ackno");
+ check_sent(1, 0, 0, 0);
+ is_int(XDP_TCP_ESTABLISH, rl.action, "SYN+ACK: relay action");
+ ok(rl.conn != NULL, "SYN+ACK: connection present");
+
+ test_conn = rl.conn;
+ knot_tcp_cleanup(test_table, &rl, 1);
+}
+
+void test_data_fragments(void)
+{
+ const size_t CONNS = 4;
+ knot_xdp_msg_t msgs[CONNS];
+ knot_tcp_relay_t rls[CONNS];
+ memset(rls, 0, CONNS * sizeof(*rls));
+
+ // first msg contains one whole payload and one fragment
+ prepare_msg(&msgs[0], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[0], 0, 0);
+ prepare_data(&msgs[0], "\x00\x03""xyz""\x00\x04""ab", 9);
+
+ // second msg contains just fragment not completing anything
+ prepare_msg(&msgs[1], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[1], 9, 0);
+ prepare_data(&msgs[1], "c", 1);
+
+ // third msg finishes fragment, contains one whole, and starts new fragment by just half of length info
+ prepare_msg(&msgs[2], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[2], 10, 0);
+ prepare_data(&msgs[2], "d""\x00\x01""i""\x00", 5);
+
+ // fourth msg completes fragment and starts never-finishing one
+ prepare_msg(&msgs[3], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[3], 15, 0);
+ prepare_data(&msgs[3], "\x02""AB""\xff\xff""abcdefghijklmnopqrstuvwxyz...", 34);
+
+ assert(test_table);
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "fragments: relay OK");
+ assert(test_sock);
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "fragments: send OK");
+ is_int(msgs[3].ackno, sent_seqno, "fragments: seqno");
+ is_int(msgs[3].seqno + msgs[3].payload.iov_len, sent_ackno, "fragments: ackno");
+ check_sent(4, 0, 0, 0);
+
+ is_int(KNOT_XDP_MSG_ACK, rls[0].auto_answer, "fragments[0]: auto answer");
+ ok(rls[0].conn != NULL, "fragments0: connection present");
+ ok(rls[0].conn == test_conn, "fragments0: same connection");
+ is_int(1, rls[0].inbf->n_inbufs, "fragments0: inbufs count");
+ struct iovec *inbufs = rls[0].inbf->inbufs;
+ is_int(3, inbufs[0].iov_len, "fragments0: data length");
+ is_int(0, memcmp("xyz", inbufs[0].iov_base, inbufs[0].iov_len), "fragments0: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[1].auto_answer, "fragments[1]: auto answer");
+ is_int(XDP_TCP_NOOP, rls[1].action, "fragments[1]: action"); // NOTE: NOOP
+ ok(rls[0].conn != NULL, "fragments1: connection present");
+ ok(rls[0].conn == test_conn, "fragments1: same connection");
+ ok(NULL == rls[1].inbf, "fragments1: inbufs count");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[2].auto_answer, "fragments[2]: auto answer");
+ ok(rls[0].conn != NULL, "fragments2: connection present");
+ ok(rls[0].conn == test_conn, "fragments2: same connection");
+ is_int(2, rls[2].inbf->n_inbufs, "fragments2: inbufs count");
+ inbufs = rls[2].inbf->inbufs;
+ is_int(4, inbufs[0].iov_len, "fragments2-0: data length");
+ is_int(0, memcmp("abcd", inbufs[0].iov_base, inbufs[0].iov_len), "fragments2-0: data");
+ is_int(1, inbufs[1].iov_len, "fragments2-1: data length");
+ is_int(0, memcmp("i", inbufs[1].iov_base, inbufs[1].iov_len), "fragments2-1: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[3].auto_answer, "fragments[3]: auto answer");
+ ok(rls[0].conn != NULL, "fragments3: connection present");
+ ok(rls[0].conn == test_conn, "fragments3: same connection");
+ is_int(1, rls[3].inbf->n_inbufs, "fragments3: inbufs count");
+ inbufs = rls[3].inbf->inbufs;
+ is_int(2, inbufs[0].iov_len, "fragments3: data length");
+ is_int(0, memcmp("AB", inbufs[0].iov_base, inbufs[0].iov_len), "fragments3: data");
+
+ knot_tcp_cleanup(test_table, rls, 4);
+}
+
+void test_close(void)
+{
+ size_t conns_pre = test_table->usage;
+
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK,
+ be16toh(test_conn->ip_rem.sin6_port),
+ be16toh(test_conn->ip_loc.sin6_port));
+ prepare_seqack(&msg, 0, 0);
+
+ // test wrong ackno synack, shall reply with RST with same
+ knot_xdp_msg_t wrong = msg;
+ wrong.seqno += INT32_MAX;
+ wrong.ackno += INT32_MAX;
+ int ret = knot_tcp_recv(&rl, &wrong, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 0 OK");
+ is_int(KNOT_XDP_MSG_RST, rl.auto_answer, "close: reset wrong ackno");
+ is_int(rl.auto_seqno, wrong.ackno, "close: reset seqno");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send 0 OK");
+ check_sent(0, 1, 0, 0);
+ is_int(sent_seqno, wrong.ackno, "close: reset seqno sent");
+
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 1 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send OK");
+ check_sent(0, 0, 0, 1);
+ is_int(XDP_TCP_CLOSE, rl.action, "close: relay action");
+ assert(rl.conn);
+ ok(rl.conn == test_conn, "close: same connection");
+ is_int(XDP_TCP_CLOSING2, rl.conn->state, "close: conn state");
+
+ msg.flags &= ~KNOT_XDP_MSG_FIN;
+ prepare_seqack(&msg, 0, 0);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 2 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send 2 OK");
+ check_sent(0, 0, 0, 0);
+ is_int(conns_pre - 1, test_table->usage, "close: connection removed");
+ is_int(conns_pre - 1, tcp_table_timeout_length(test_table), "close: timeout list size");
+ knot_tcp_cleanup(test_table, &rl, 1);
+}
+
+void test_many(void)
+{
+ size_t CONNS = test_table->size * test_table->size;
+ size_t i_survive = CONNS / 2;
+ uint32_t timeout_time = 1000000;
+
+ knot_xdp_msg_t *msgs = malloc(CONNS * sizeof(*msgs));
+ assert(msgs != NULL);
+ for (size_t i = 0; i < CONNS; i++) {
+ prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2, 1);
+ }
+ knot_tcp_relay_t *rls = malloc(CONNS * sizeof(*rls));
+
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "many: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many: relay send OK");
+ check_sent(0, 0, CONNS, 0);
+ is_int(CONNS, test_table->usage, "many: table usage");
+
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ memset(rls, 0, CONNS * sizeof(*rls));
+ usleep(timeout_time);
+ knot_xdp_msg_t *survive = &msgs[i_survive];
+ knot_tcp_relay_t surv_rl = { 0 };
+ survive->flags = (KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK);
+ knot_tcp_conn_t *surv_conn = tcp_table_find(test_table, survive);
+ fix_seqack(survive);
+ prepare_data(survive, "\x00\x00", 2);
+ assert(test_table);
+ ret = knot_tcp_recv(&surv_rl, survive, 1, test_table, NULL, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "many/survivor: OK");
+ clean_sent();
+
+ knot_sweep_stats_t stats = { 0 };
+ ret = knot_tcp_sweep(test_table, timeout_time, INFTY, INFTY, INFTY, INFTY,
+ INFTY, rls, CONNS, &stats);
+ is_int(KNOT_EOK, ret, "many/timeout1: OK");
+ is_int(CONNS - 1, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "many/timeout1: close count");
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_LIMIT_CONN], "may/timeout1: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout1: send OK");
+ check_sent(0, 0, 0, CONNS - 1);
+
+ knot_sweep_stats_reset(&stats);
+ ret = knot_tcp_sweep(test_table, INFTY, timeout_time, INFTY, INFTY, INFTY,
+ INFTY, rls, CONNS, &stats);
+ is_int(KNOT_EOK, ret, "many/timeout2: OK");
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "many/timeout2: close count");
+ is_int(CONNS - 1, stats.counters[KNOT_SWEEP_CTR_TIMEOUT_RST], "may/timeout2: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout2: send OK");
+ check_sent(0, CONNS - 1, 0, 0);
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ is_int(1, test_table->usage, "many/timeout: one survivor");
+ is_int(1, tcp_table_timeout_length(test_table), "many/timeout: one survivor in timeout list");
+ ok(surv_conn != NULL, "many/timeout: survivor connection present");
+ ok(surv_conn == surv_rl.conn, "many/timeout: same connection");
+ knot_tcp_cleanup(test_table, &surv_rl, 1);
+
+ free(msgs);
+ free(rls);
+}
+
+void test_ibufs_size(void)
+{
+ int CONNS = 4;
+ knot_xdp_msg_t msgs[CONNS];
+ knot_tcp_relay_t rls[CONNS];
+
+ // just open connections
+ for (int i = 0; i < CONNS; i++) {
+ prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2000, 1);
+ }
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "ibufs: open OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "ibufs: first send OK");
+ check_sent(0, 0, CONNS, 0);
+ for (int i = 0; i < CONNS; i++) {
+ msgs[i].flags = KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK;
+ }
+ fix_seqacks(msgs, CONNS);
+ (void)knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+
+ is_int(0, test_table->inbufs_total, "inbufs: initial total zero");
+
+ // first connection will start a fragment buf then finish it
+ fix_seqack(&msgs[0]);
+ prepare_data(&msgs[0], "\x00\x0a""lorem", 7);
+ ret = knot_tcp_recv(&rls[0], &msgs[0], 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "ibufs: must be OK");
+ ret = knot_tcp_send(test_sock, &rls[0], 1, 1);
+ is_int(KNOT_EOK, ret, "ibufs: must send OK");
+ check_sent(1, 0, 0, 0);
+ is_int(64, test_table->inbufs_total, "inbufs: first inbuf");
+ knot_tcp_cleanup(test_table, &rls[0], 1);
+
+ // other connection will just store fragments
+ fix_seqacks(msgs, CONNS);
+ prepare_data(&msgs[0], "ipsum", 5);
+ prepare_data(&msgs[1], "\x00\xff""12345", 7);
+ prepare_data(&msgs[2], "\xff\xff""abcde", 7);
+ prepare_data(&msgs[3], "\xff\xff""abcde", 7);
+ ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "inbufs: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: send OK");
+ check_sent(CONNS, 0, 0, 0);
+ is_int(192, test_table->inbufs_total, "inbufs: after change");
+ is_int(0, rls[1].action, "inbufs: one relay");
+ is_int(10, rls[0].inbf->inbufs[0].iov_len, "inbufs: data length");
+ knot_tcp_cleanup(test_table, rls, CONNS);
+
+ // now free some
+ knot_sweep_stats_t stats = { 0 };
+ ret = knot_tcp_sweep(test_table, INFTY, INFTY, INFTY, INFTY,
+ 64, INFTY, rls,
+ CONNS, &stats);
+ is_int(KNOT_EOK, ret, "inbufs: timeout OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: timeout send OK");
+ check_sent(0, 2, 0, 0);
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "inbufs: close count");
+ is_int(2, stats.counters[KNOT_SWEEP_CTR_LIMIT_IBUF], "inbufs: reset count");
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ is_int(64, test_table->inbufs_total, "inbufs: final state");
+ ok(NULL != tcp_table_find(test_table, &msgs[0]), "inbufs: first conn survived");
+ ok(NULL == tcp_table_find(test_table, &msgs[1]), "inbufs: second conn not survived");
+ ok(NULL == tcp_table_find(test_table, &msgs[2]), "inbufs: third conn not survived");
+ ok(NULL != tcp_table_find(test_table, &msgs[3]), "inbufs: fourth conn survived");
+
+ clean_table();
+}
+
+void test_obufs(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE); // SYN
+ (void)knot_tcp_send(test_sock, &rl, 1, 1); // SYN+ACK
+ prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
+ prepare_seqack(&msg, 0, 1);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE); // ACK
+
+ size_t TEST_MSS = 1111;
+ size_t DATA_LEN = 65535; // with 2-byte len prefix, this is > 64k == window_size
+ uint8_t *data = calloc(DATA_LEN, 1);
+ assert(rl.conn);
+ rl.conn->mss = TEST_MSS;
+ rl.conn->window_size = 65536;
+ send2_mss = TEST_MSS;
+
+ int ret = knot_tcp_reply_data(&rl, test_table, false, data, DATA_LEN), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: fill with data");
+ for (knot_tcp_outbuf_t *ob = rl.conn->outbufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(ob->len > 0, "init last ob[%d]: non-trivial", i);
+ ok(ob->len <= TEST_MSS, "init last ob[%d]: fulfills MSS", i);
+ } else {
+ is_int(TEST_MSS, ob->len, "init ob[%d]: exactly MSS", i);
+ }
+ ok(!ob->sent, "init ob[%d]: not sent", i);
+ }
+ ret = knot_tcp_send(test_sock, &rl, 1, 20), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: send OK");
+ is_int((DATA_LEN + 2) / TEST_MSS * TEST_MSS, sent2_data, "obufs: sent all but one MSS");
+ for (knot_tcp_outbuf_t *ob = rl.conn->outbufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(!ob->sent, "last ob[%d]: not sent", i);
+ } else {
+ ok(ob->sent, "ob[%d]: sent", i);
+ if (ob->next->next != NULL) {
+ is_int(ob->seqno + ob->len, ob->next->seqno, "init ob[%d+1]: seqno", i);
+ }
+ }
+ }
+ knot_tcp_cleanup(test_table, &rl, 1);
+ memset(&rl, 0, sizeof(rl));
+
+ prepare_seqack(&msg, 0, TEST_MSS);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "obufs: ACKed data");
+ assert(rl.conn);
+ rl.conn->window_size = 65536;
+ knot_tcp_outbuf_t *surv_ob = rl.conn->outbufs;
+ ok(surv_ob != NULL, "obufs: unACKed survived");
+ assert(surv_ob);
+ ok(surv_ob->next == NULL, "obufs: just one survived");
+ ok(!surv_ob->sent, "obufs: survivor not sent");
+ ret = knot_tcp_send(test_sock, &rl, 1, 20);
+ is_int(KNOT_EOK, ret, "obufs: send rest OK");
+ is_int(DATA_LEN + 2, sent2_data, "obufs: sent all");
+ ok(surv_ob->sent, "obufs: survivor sent");
+ is_int(sent_seqno, surv_ob->seqno, "obufs: survivor seqno");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
+ clean_table();
+ free(data);
+}
+
+static void init_mock(knot_xdp_socket_t **socket, void *send_mock)
+{
+ *socket = calloc(1, sizeof(**socket));
+ if (*socket != NULL) {
+ (*socket)->send_mock = send_mock;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_table = knot_tcp_table_new(TEST_TABLE_SIZE, NULL);
+ assert(test_table != NULL);
+ test_syn_table = knot_tcp_table_new(TEST_TABLE_SIZE, test_table);
+
+ init_mock(&test_sock, mock_send);
+
+ test_syn();
+ test_establish();
+
+ test_syn_ack();
+ test_data_fragments();
+ test_close();
+
+ test_ibufs_size();
+
+ knot_xdp_deinit(test_sock);
+ init_mock(&test_sock, mock_send_nocheck);
+ test_many();
+
+ knot_xdp_deinit(test_sock);
+ init_mock(&test_sock, mock_send2);
+ test_obufs();
+
+ knot_xdp_deinit(test_sock);
+ knot_tcp_table_free(test_table);
+ knot_tcp_table_free(test_syn_table);
+
+ return 0;
+}
diff --git a/tests/libknot/test_yparser.c b/tests/libknot/test_yparser.c
new file mode 100644
index 0000000..8655096
--- /dev/null
+++ b/tests/libknot/test_yparser.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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 *tab_error1 =
+ "a:\n"
+ "b:\t\n";
+
+const char *tab_error2 =
+ "a:\n"
+ "b: c\t\n";
+
+const char *tab_error3 =
+ "a:\n"
+ "\t\n";
+
+const char *dname_ok =
+ ".:\n"
+ "dom-ain:\n"
+ "\\070-\\071.\\072.:\n"
+ "*.wildchar.com:\n"
+ "_ldap._tcp.example.com:\n";
+
+const char *quotes_ok =
+ "g: \"\"\n"
+ "g: a\\ b\n"
+ "g: \"\\# 1 00\"\n"
+ "g: \"\\\"\\\"\"\n"
+ "g: \" a \\\" b \\\" \\\"c\\\" \"\n"
+ "g: \"\\@ \\[ \\# \\, \\]\"\n";
+
+const char *utf8_ok =
+ "key: příšera\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 syntax 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");
+ is_int(yp->line_count, 3, "invalid indentation line");
+}
+
+static void test_tab_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 tab error input string %i", count++);
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ret = yp_parse(yp);
+ is_int(KNOT_YP_ECHAR_TAB, ret, "invalid tabulator");
+ is_int(yp->line_count, 2, "invalid tabulator line");
+}
+
+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.");
+ CHECK_DNAME("*.wildchar.com");
+ CHECK_DNAME("_ldap._tcp.example.com");
+}
+
+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("\\@ \\[ \\# \\, \\]");
+}
+
+// Check that wrong wildcard dname is NOT parsed as valid dname.
+static void test_wildcard(yp_parser_t *yp)
+{
+#define CHECK_NOT_WILDCARD(str) \
+ ret = yp_set_input_string(yp, str, strlen(str)); \
+ is_int(KNOT_EOK, ret, "set input string");\
+ ret = yp_parse(yp); \
+ is_int(KNOT_EPARSEFAIL, ret, str " is not wildcard"); \
+ ok(yp->key_len != strlen(str) || strcmp(yp->key, str) != 0 || \
+ yp->event != YP_EKEY0, "compare " str);
+
+ int ret;
+ CHECK_NOT_WILDCARD("a.*.example.com.");
+ CHECK_NOT_WILDCARD("**.example.com.");
+ CHECK_NOT_WILDCARD("*example.com.");
+}
+
+static void test_utf8(yp_parser_t *yp)
+{
+ int ret = yp_set_input_string(yp, utf8_ok, strlen(utf8_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key with a value in UTF-8");
+ ok(yp->key_len == 3 && strcmp(yp->key, "key") == 0 &&
+ yp->data_len == 10 && strcmp(yp->data, "p""\xC5\x99\xC3\xAD\xC5\xA1""era") == 0,
+ "compare UTF-8 value");
+}
+
+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_tab_error(&yp, tab_error1);
+ test_tab_error(&yp, tab_error2);
+ test_tab_error(&yp, tab_error3);
+ test_dname(&yp);
+ test_quotes(&yp);
+ test_wildcard(&yp);
+ test_utf8(&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..728a847
--- /dev/null
+++ b/tests/libknot/test_ypschema.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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..cd26632
--- /dev/null
+++ b/tests/libknot/test_yptrafo.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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_test("eth1@53", 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("/tmp/unix.sock");
+ 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..227cdb4
--- /dev/null
+++ b/tests/libzscanner/TESTS
@@ -0,0 +1,86 @@
+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
+53_SMIMEA
+54_OPENPGPKEY
+55_CSYNC
+56_ZONEMD
+57_SVCB
+58_HTTPS
diff --git a/tests/libzscanner/data/00-0_general.in b/tests/libzscanner/data/00-0_general.in
new file mode 100644
index 0000000..21e9d03
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.in
@@ -0,0 +1,27 @@
+$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
+@ A (
+ 1.1.1
+ ) ; Error in multiline
+@ 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..522b3a6
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.out
@@ -0,0 +1,70 @@
+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
+------
+WARNG=ZS_BAD_IPV4
+------
+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..cb873fc
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.in
@@ -0,0 +1,46 @@
+$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 original limit)
+@ RRSIG A 0 0 0 0 22251231235959 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 22260101000000 0 . AA== ; Date overflow
+@ RRSIG A 0 0 0 0 2226010100000x 0 . AA== ; Bad timestamp char
+@ RRSIG A 0 0 0 0 222601010000000 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..dff1471
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.out
@@ -0,0 +1,140 @@
+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=000100000000000000000000E1853CFF00000000
+------
+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/53_SMIMEA.in b/tests/libzscanner/data/53_SMIMEA.in
new file mode 100644
index 0000000..aec390f
--- /dev/null
+++ b/tests/libzscanner/data/53_SMIMEA.in
@@ -0,0 +1,13 @@
+$ORIGIN .
+$TTL 1
+
+; SMIMEA is the same as TLSA, so there are differences and basics only.
+
+; OK
+@ SMIMEA 0 0 0 00 ; The simplest variant
+@ TYPE53 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE53 0 0 0 00 ; TYPE
+@ smimea 0 0 0 00 ; Type in lower-case
+
+; KO
+@ SMIMEA
diff --git a/tests/libzscanner/data/53_SMIMEA.out b/tests/libzscanner/data/53_SMIMEA.out
new file mode 100644
index 0000000..c270a7d
--- /dev/null
+++ b/tests/libzscanner/data/53_SMIMEA.out
@@ -0,0 +1,26 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/54_OPENPGPKEY.in b/tests/libzscanner/data/54_OPENPGPKEY.in
new file mode 100644
index 0000000..a788a28
--- /dev/null
+++ b/tests/libzscanner/data/54_OPENPGPKEY.in
@@ -0,0 +1,13 @@
+$ORIGIN .
+$TTL 1
+
+; OPENPGPKEY is the same as DHCID, so there are differences and basics only.
+
+; OK
+@ OPENPGPKEY AA== ; The simplest variant
+@ TYPE61 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE61 AA== ; TYPE
+@ openpgpkey AA== ; Type in lower-case
+
+; KO
+@ OPENPGPKEY
diff --git a/tests/libzscanner/data/54_OPENPGPKEY.out b/tests/libzscanner/data/54_OPENPGPKEY.out
new file mode 100644
index 0000000..8741e4e
--- /dev/null
+++ b/tests/libzscanner/data/54_OPENPGPKEY.out
@@ -0,0 +1,26 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/55_CSYNC.in b/tests/libzscanner/data/55_CSYNC.in
new file mode 100644
index 0000000..97dcfa2
--- /dev/null
+++ b/tests/libzscanner/data/55_CSYNC.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CSYNC 0 0 ; The simplest variant - without bitmap
+@ CSYNC 4294967295 65535 ; Maximal numbers
+@ CSYNC 0 0 TYPE0 ; Minimal type number
+@ CSYNC 0 0 TYPE65535 ; Maximal type number
+@ CSYNC 0 0 TYPE0 A NS ; First bitmap window
+@ CSYNC 0 0 TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE62 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE62 0 0 ; TYPE
+@ csync 0 0 ; Type in lower-case
+
+; KO
+@ CSYNC
+@ CSYNC ; Empty rdata
+@ CSYNC \# 0 ; Hex empty rdata
+@ CSYNC 4294967296 0 ; Serial overflow
+@ CSYNC 0 65536 ; Flags overflow
+@ CSYNC 0 0 TYPE65536 ; Type number overflow
+@ CSYNC 0 0 X ; Unknown type
diff --git a/tests/libzscanner/data/55_CSYNC.out b/tests/libzscanner/data/55_CSYNC.out
new file mode 100644
index 0000000..d762c2b
--- /dev/null
+++ b/tests/libzscanner/data/55_CSYNC.out
@@ -0,0 +1,68 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=0000000000000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/56_ZONEMD.in b/tests/libzscanner/data/56_ZONEMD.in
new file mode 100644
index 0000000..2342350
--- /dev/null
+++ b/tests/libzscanner/data/56_ZONEMD.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ ZONEMD 0 0 0 00 ; The simplest variant
+@ ZONEMD 4294967295 255 255 00 ; Maximal numbers
+@ ZONEMD 0 0 0 0102 00 FF ; Hex string with blank spaces inside
+@ TYPE63 \# 7 00000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE63 0 0 0 00 ; TYPE
+@ zonemd 0 0 0 00 ; Type in lower-case
+
+; KO
+@ ZONEMD
+@ ZONEMD ; Empty rdata
+@ ZONEMD \# 0 ; Hex empty rdata
+@ ZONEMD 4294967296 0 0 00 ; Serial overflow
+@ ZONEMD 0 256 0 00 ; Type overflow
+@ ZONEMD 0 0 256 00 ; Reserved overflow
+@ ZONEMD 0 0 0 0 ; Hex block length must be multiple of 2
+@ ZONEMD 0 0 0 0x ; Bad hex char
+@ ZONEMD 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/56_ZONEMD.out b/tests/libzscanner/data/56_ZONEMD.out
new file mode 100644
index 0000000..0f56c24
--- /dev/null
+++ b/tests/libzscanner/data/56_ZONEMD.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=FFFFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=000000000000010200FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER32_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/57_SVCB.in b/tests/libzscanner/data/57_SVCB.in
new file mode 100644
index 0000000..c4ce534
--- /dev/null
+++ b/tests/libzscanner/data/57_SVCB.in
@@ -0,0 +1,102 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SVCB 0 .
+@ SVCB 0 @ ; Comment
+@ SVCB 65535 . mandatory=alpn alpn=h2
+@ SVCB 1 . mandatory="alpn" alpn=h2
+@ SVCB 1 . alpn=h2
+@ SVCB 1 . alpn="abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" ; Text string of maximal length (255 chars)
+@ SVCB 1 . alpn="part1,part2,part3\\,part4\\\\"
+@ SVCB 1 . alpn=part1\,\p\a\r\t2\044part3\092,part4\092\\
+@ SVCB 1 . no-default-alpn
+@ SVCB 1 . port=0
+@ SVCB 1 . port="65535"
+@ SVCB 1 . ipv4hint=0.0.0.0
+@ SVCB 1 . ipv4hint="255.255.255.255"
+@ SVCB 1 . ech=AA==
+@ SVCB 1 . ech="Zm9vYg=="
+@ SVCB 1 . ipv6hint=::1
+@ SVCB 1 . ipv6hint="::1"
+@ SVCB 1 . key7
+@ SVCB 1 . key65535
+@ SVCB 1 . key65535=a
+@ SVCB 1 . key65535="a"
+@ SVCB 1 . key65535="a" key1000=b port=4 key7 mandatory=key7,port
+@ TYPE64 \# 3 000000
+@ TYPE64 0 .
+@ svcb 0 .
+
+; RFC OK examples
+example.com. HTTPS 0 foo.example.com.
+example.com. SVCB 1 .
+example.com. SVCB 16 foo.example.com. port=53
+example.com. SVCB 1 foo.example.com. key667=hello
+example.com. SVCB 1 foo.example.com. key667="hello\210qoo"
+example.com. SVCB 1 foo.example.com. (
+ ipv6hint="2001:db8::1,2001:db8::53:1"
+ )
+example.com. SVCB 1 example.com. ipv6hint="::ffff:198.51.100.100"
+example.com. SVCB 16 foo.example.org. (
+ alpn=h2,h3-19 mandatory=ipv4hint,alpn
+ ipv4hint=192.0.2.1
+ )
+example.com. SVCB 16 foo.example.org. alpn="f\\\\oo\\,bar,h2"
+example.com. SVCB 16 foo.example.org. alpn=f\\\092oo\092,bar,h2
+
+; KO
+@ SVCB
+@ SVCB ; Empty rdata
+@ SVCB \# 0 ; Hex empty rdata
+@ SVCB 65536 . ; Priority overflow
+@ SVCB 0 ; Missing item
+@ SVCB 1 . bogus ; Unknown parameter
+@ SVCB 1 . PORT=0 ; Capital letter in parameter name
+@ SVCB 1 . mandatory
+@ SVCB 1 . mandatory=
+@ SVCB 1 . mandatory=a,
+@ SVCB 1 . mandatory=a,,b
+@ SVCB 1 . mandatory=mandatory
+@ SVCB 1 . mandatory=bogus
+@ SVCB 1 . alpn
+@ SVCB 1 . alpn=
+@ SVCB 1 . alpn=a,
+@ SVCB 1 . alpn=a,,b
+@ SVCB 1 . alpn="abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" ; Text string overflow (256 chars)
+@ SVCB 1 . no-default-alpn=
+@ SVCB 1 . no-default-alpn=h1
+@ SVCB 1 . no-default-alpn="h1"
+@ SVCB 1 . port
+@ SVCB 1 . port=
+@ SVCB 1 . port=65536
+@ SVCB 1 . port=1,2
+@ SVCB 1 . ipv4hint
+@ SVCB 1 . ipv4hint=
+@ SVCB 1 . ipv4hint=1.2.3
+@ SVCB 1 . ipv4hint=1.2.3.4,
+@ SVCB 1 . ipv4hint=1.2.3.4,,2.3.4.5
+@ SVCB 1 . ech
+@ SVCB 1 . ech=
+@ SVCB 1 . ech=AA==,AA==
+@ SVCB 1 . ech=W
+@ SVCB 1 . ipv6hint
+@ SVCB 1 . ipv6hint=
+@ SVCB 1 . ipv6hint=::1,,::2
+@ SVCB 1 . ipv6hint=::W
+
+; RFC KO examples
+example.com. SVCB 1 foo.example.com. (
+ key123=abc key123=def
+ )
+example.com. SVCB 1 foo.example.com. mandatory
+example.com. SVCB 1 foo.example.com. alpn
+example.com. SVCB 1 foo.example.com. port
+example.com. SVCB 1 foo.example.com. ipv4hint
+example.com. SVCB 1 foo.example.com. ipv6hint
+example.com. SVCB 1 foo.example.com. no-default-alpn=abc
+example.com. SVCB 1 foo.example.com. mandatory=key123
+example.com. SVCB 1 foo.example.com. mandatory=mandatory
+example.com. SVCB 1 foo.example.com. (
+ mandatory=key123,key123 key123=abc
+ )
diff --git a/tests/libzscanner/data/57_SVCB.out b/tests/libzscanner/data/57_SVCB.out
new file mode 100644
index 0000000..a799eb7
--- /dev/null
+++ b/tests/libzscanner/data/57_SVCB.out
@@ -0,0 +1,306 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=FFFF0000000002000100010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000000002000100010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000010100FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E3132333435
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000100190570617274310570617274320C70617274332C70617274345C
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000100190570617274310570617274320C70617274332C70617274345C
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000300020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000030002FFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000004000400000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000040004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000005000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000050004666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000006001000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000006001000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000070000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000000004000300070003000200040007000003E8000162FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000003666F6F076578616D706C6503636F6D00
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C6503636F6D00000300020035
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D00029B000568656C6C6F
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D00029B000968656C6C6FD2716F6F
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D000006002020010DB800000000000000000000000120010DB8000000000000000000530001
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001076578616D706C6503636F6D000006001000000000000000000000FFFFC6336464
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000000000400010004000100090268320568332D313900040004C0000201
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000001000C08665C6F6F2C626172026832
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000001000C08665C6F6F2C626172026832
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_EMPTY_LIST_ITEM
+------
+WARNG=ZS_EMPTY_LIST_ITEM
+------
+WARNG=ZS_ITEM_OVERFLOW
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_DUPLICATE_SVCB_KEY
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_MISSING_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_DUPLICATE_SVCB_MANDATORY
+------
diff --git a/tests/libzscanner/data/58_HTTPS.in b/tests/libzscanner/data/58_HTTPS.in
new file mode 100644
index 0000000..674f865
--- /dev/null
+++ b/tests/libzscanner/data/58_HTTPS.in
@@ -0,0 +1,19 @@
+$ORIGIN .
+$TTL 1
+
+; HTTPS is the same as SVCB, so there are the differences and basics only.
+
+; OK
+@ HTTPS 0 .
+@ HTTPS 65535 @ ; Comment
+@ HTTPS 1 . key65535="a" key1000=b port=4 key7 mandatory=key7,port
+@ TYPE65 \# 3 000000
+@ TYPE65 0 .
+@ https 0 .
+
+; KO
+@ HTTPS
+@ HTTPS ; Empty rdata
+@ HTTPS \# 0 ; Hex empty rdata
+@ HTTPS 65536 . ; Priority overflow
+@ HTTPS 0 ; Missing item
diff --git a/tests/libzscanner/data/58_HTTPS.out b/tests/libzscanner/data/58_HTTPS.out
new file mode 100644
index 0000000..8ec9a35
--- /dev/null
+++ b/tests/libzscanner/data/58_HTTPS.out
@@ -0,0 +1,46 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=00010000000004000300070003000200040007000003E8000162FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
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..22066dd
--- /dev/null
+++ b/tests/libzscanner/processing.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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);
+
+ int block = *((int *)(s->process.data));
+ for (i = 0; i < s->r_data_length; i++) {
+ if (block > 0 && i > 0 && (i % block) == 0) {
+ printf(" ");
+ }
+ printf("%02X", (s->r_data)[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+void debug_process_comment(zs_scanner_t *s)
+{
+ printf("LINE(%03"PRIu64") COMMENT(%.*s)\n", s->line_counter,
+ (int)s->buffer_length, s->buffer);
+ 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("22251231235959", "%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 Wuninitialized
+ date_to_timestamp(buffer, &test_timestamp);
+
+ // Some continuous logging.
+ if (ref_timestamp % 10000000 == 0) {
+ val1 = ref_timestamp;
+ printf("%s = %"PRIu32"\n", buffer, (uint32_t)val1);
+ }
+
+ // Comparing results.
+ if ((uint32_t)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..47cf03b
--- /dev/null
+++ b/tests/libzscanner/processing.h
@@ -0,0 +1,31 @@
+/* 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 <https://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 debug_process_comment(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..10d2b5c
--- /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 86
+
+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..d5f514d
--- /dev/null
+++ b/tests/libzscanner/zscanner-tool.c
@@ -0,0 +1,257 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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"
+ " -b <num> Divide hex string to blocks of length <num>.\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, block = 0, state = 0, test = 0;
+
+ // Command line long options.
+ struct option opts[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "block", required_argument, NULL, 'b' },
+ { "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:b:sth", opts, &li)) != -1) {
+ switch (opt) {
+ case 'm':
+ mode = atoi(optarg);
+ break;
+ case 'b':
+ block = 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, &block);
+ ret += zs_set_processing_comment(s, debug_process_comment);
+ 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..fbddb6e
--- /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 <https://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..6a5210f
--- /dev/null
+++ b/tests/modules/test_rrl.c
@@ -0,0 +1,178 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://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"
+#include "stdio.h"
+
+/* 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.wire = 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");
+
+ /* 2. 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");
+
+ /* 3. Endian-independent hash input buffer. */
+ uint8_t buf[RRL_CLSBLK_MAXLEN];
+ // CLS_LARGE + remote + dname wire.
+ uint8_t expectedv4[] = "\x10\x01\x02\x03\x00\x00\x00\x00\x00\x04""beef";
+ rrl_classify(buf, sizeof(buf), &addr, &rq, qname);
+ is_int(0, memcmp(buf, expectedv4, sizeof(expectedv4)), "rrl: IPv4 hash input buffer");
+ uint8_t expectedv6[] = "\x10\x11\x22\x33\x44\x55\x66\x77\x00\x04""beef";
+ rrl_classify(buf, sizeof(buf), &addr6, &rq, qname);
+ is_int(0, memcmp(buf, expectedv6, sizeof(expectedv6)), "rrl: IPv6 hash input buffer");
+
+#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..f979d07
--- /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 <https://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..04c9956
--- /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 <https://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..bd9d106
--- /dev/null
+++ b/tests/tap/runtests.c
@@ -0,0 +1,1401 @@
+/*
+ * 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. */
+ unsigned int killed; /* Whether the set was killed. */
+ 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 output (stdout and stderr) to a pipe on our
+ * end, and storing the file descriptor to read from in the second argument.
+ * Returns the PID of the new process. Errors are fatal.
+ */
+static pid_t
+test_start(const char *path, int *fd)
+{
+ int fds[2];
+ 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. */
+ close(fds[0]);
+ if (dup2(fds[1], STDOUT_FILENO) == -1)
+ _exit(CHILDERR_DUP);
+ if (dup2(fds[1], STDERR_FILENO) == -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;
+ ts->killed = 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));
+ ts->killed = 1;
+ 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 if (ts->killed) {
+ printf("KILL ");
+ } 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;
+ unsigned long killed = 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;
+ killed += ts->killed;
+ 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 (killed != 0) {
+ printf("Killed %lu test set%s, passed %lu/%lu tests",
+ killed, killed == 1 ? "" : "s", passed, total);
+ } else 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 && killed == 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_lookup.c b/tests/utils/test_lookup.c
new file mode 100644
index 0000000..ddb0f9d
--- /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 <https://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;
+}