From 040eee1aa49b49df4698d83a05af57c220127fd1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 13:36:04 +0200 Subject: Adding upstream version 2.2.0. Signed-off-by: Daniel Baumann --- src/lib/dns/Makefile.am | 226 + src/lib/dns/Makefile.in | 1497 +++++ src/lib/dns/dns_fwd.h | 56 + src/lib/dns/edns.cc | 178 + src/lib/dns/edns.h | 437 ++ src/lib/dns/exceptions.cc | 26 + src/lib/dns/exceptions.h | 76 + src/lib/dns/gen-rdatacode.py.in | 391 ++ src/lib/dns/labelsequence.cc | 472 ++ src/lib/dns/labelsequence.h | 456 ++ src/lib/dns/master_lexer.cc | 614 ++ src/lib/dns/master_lexer.h | 678 ++ src/lib/dns/master_lexer_inputsource.cc | 221 + src/lib/dns/master_lexer_inputsource.h | 185 + src/lib/dns/master_lexer_state.h | 138 + src/lib/dns/master_loader.cc | 1073 +++ src/lib/dns/master_loader.h | 187 + src/lib/dns/master_loader_callbacks.cc | 28 + src/lib/dns/master_loader_callbacks.h | 134 + src/lib/dns/masterload.cc | 100 + src/lib/dns/masterload.h | 175 + src/lib/dns/message.cc | 1163 ++++ src/lib/dns/message.h | 682 ++ src/lib/dns/messagerenderer.cc | 397 ++ src/lib/dns/messagerenderer.h | 395 ++ src/lib/dns/name.cc | 724 ++ src/lib/dns/name.h | 766 +++ src/lib/dns/name_internal.h | 35 + src/lib/dns/nsec3hash.cc | 268 + src/lib/dns/nsec3hash.h | 290 + src/lib/dns/opcode.cc | 62 + src/lib/dns/opcode.h | 282 + src/lib/dns/qid_gen.cc | 42 + src/lib/dns/qid_gen.h | 54 + src/lib/dns/question.cc | 81 + src/lib/dns/question.h | 291 + src/lib/dns/rcode.cc | 95 + src/lib/dns/rcode.h | 338 + src/lib/dns/rdata.cc | 408 ++ src/lib/dns/rdata.h | 582 ++ src/lib/dns/rdata/any_255/tsig_250.cc | 567 ++ src/lib/dns/rdata/any_255/tsig_250.h | 148 + src/lib/dns/rdata/ch_3/a_1.cc | 64 + src/lib/dns/rdata/ch_3/a_1.h | 32 + src/lib/dns/rdata/generic/afsdb_18.cc | 201 + src/lib/dns/rdata/generic/afsdb_18.h | 68 + src/lib/dns/rdata/generic/caa_257.cc | 298 + src/lib/dns/rdata/generic/caa_257.h | 64 + src/lib/dns/rdata/generic/cname_5.cc | 125 + src/lib/dns/rdata/generic/cname_5.h | 39 + src/lib/dns/rdata/generic/detail/char_string.cc | 264 + src/lib/dns/rdata/generic/detail/char_string.h | 140 + src/lib/dns/rdata/generic/detail/ds_like.h | 277 + src/lib/dns/rdata/generic/detail/lexer_util.h | 62 + .../dns/rdata/generic/detail/nsec3param_common.cc | 115 + .../dns/rdata/generic/detail/nsec3param_common.h | 123 + src/lib/dns/rdata/generic/detail/nsec_bitmap.cc | 169 + src/lib/dns/rdata/generic/detail/nsec_bitmap.h | 103 + src/lib/dns/rdata/generic/detail/txt_like.h | 237 + src/lib/dns/rdata/generic/dlv_32769.cc | 120 + src/lib/dns/rdata/generic/dlv_32769.h | 69 + src/lib/dns/rdata/generic/dname_39.cc | 127 + src/lib/dns/rdata/generic/dname_39.h | 39 + src/lib/dns/rdata/generic/dnskey_48.cc | 316 + src/lib/dns/rdata/generic/dnskey_48.h | 60 + src/lib/dns/rdata/generic/ds_43.cc | 90 + src/lib/dns/rdata/generic/ds_43.h | 69 + src/lib/dns/rdata/generic/hinfo_13.cc | 151 + src/lib/dns/rdata/generic/hinfo_13.h | 64 + src/lib/dns/rdata/generic/minfo_14.cc | 173 + src/lib/dns/rdata/generic/minfo_14.h | 74 + src/lib/dns/rdata/generic/mx_15.cc | 160 + src/lib/dns/rdata/generic/mx_15.h | 52 + src/lib/dns/rdata/generic/naptr_35.cc | 256 + src/lib/dns/rdata/generic/naptr_35.h | 64 + src/lib/dns/rdata/generic/ns_2.cc | 121 + src/lib/dns/rdata/generic/ns_2.h | 43 + src/lib/dns/rdata/generic/nsec3_50.cc | 343 + src/lib/dns/rdata/generic/nsec3_50.h | 54 + src/lib/dns/rdata/generic/nsec3param_51.cc | 232 + src/lib/dns/rdata/generic/nsec3param_51.h | 56 + src/lib/dns/rdata/generic/nsec_47.cc | 216 + src/lib/dns/rdata/generic/nsec_47.h | 53 + src/lib/dns/rdata/generic/opt_41.cc | 219 + src/lib/dns/rdata/generic/opt_41.h | 90 + src/lib/dns/rdata/generic/ptr_12.cc | 123 + src/lib/dns/rdata/generic/ptr_12.h | 44 + src/lib/dns/rdata/generic/rp_17.cc | 160 + src/lib/dns/rdata/generic/rp_17.h | 78 + src/lib/dns/rdata/generic/rrsig_46.cc | 334 + src/lib/dns/rdata/generic/rrsig_46.h | 54 + src/lib/dns/rdata/generic/soa_6.cc | 210 + src/lib/dns/rdata/generic/soa_6.h | 51 + src/lib/dns/rdata/generic/spf_99.cc | 139 + src/lib/dns/rdata/generic/spf_99.h | 72 + src/lib/dns/rdata/generic/sshfp_44.cc | 298 + src/lib/dns/rdata/generic/sshfp_44.h | 56 + src/lib/dns/rdata/generic/tkey_249.cc | 613 ++ src/lib/dns/rdata/generic/tkey_249.h | 142 + src/lib/dns/rdata/generic/tlsa_52.cc | 342 + src/lib/dns/rdata/generic/tlsa_52.h | 57 + src/lib/dns/rdata/generic/txt_16.cc | 95 + src/lib/dns/rdata/generic/txt_16.h | 46 + src/lib/dns/rdata/hs_4/a_1.cc | 64 + src/lib/dns/rdata/hs_4/a_1.h | 32 + src/lib/dns/rdata/in_1/a_1.cc | 174 + src/lib/dns/rdata/in_1/a_1.h | 38 + src/lib/dns/rdata/in_1/aaaa_28.cc | 153 + src/lib/dns/rdata/in_1/aaaa_28.h | 38 + src/lib/dns/rdata/in_1/dhcid_49.cc | 161 + src/lib/dns/rdata/in_1/dhcid_49.h | 53 + src/lib/dns/rdata/in_1/srv_33.cc | 298 + src/lib/dns/rdata/in_1/srv_33.h | 85 + src/lib/dns/rdata/template.cc | 67 + src/lib/dns/rdata/template.h | 54 + src/lib/dns/rdata_pimpl_holder.h | 52 + src/lib/dns/rdataclass.cc | 7078 ++++++++++++++++++++ src/lib/dns/rdataclass.h | 2746 ++++++++ src/lib/dns/rdatafields.cc | 216 + src/lib/dns/rdatafields.h | 419 ++ src/lib/dns/rrclass-placeholder.h | 312 + src/lib/dns/rrclass.cc | 74 + src/lib/dns/rrclass.h | 354 + src/lib/dns/rrcollator.cc | 105 + src/lib/dns/rrcollator.h | 125 + src/lib/dns/rrparamregistry-placeholder.cc | 556 ++ src/lib/dns/rrparamregistry.cc | 660 ++ src/lib/dns/rrparamregistry.h | 545 ++ src/lib/dns/rrset.cc | 466 ++ src/lib/dns/rrset.h | 958 +++ src/lib/dns/rrset_collection.cc | 123 + src/lib/dns/rrset_collection.h | 164 + src/lib/dns/rrset_collection_base.h | 213 + src/lib/dns/rrttl.cc | 214 + src/lib/dns/rrttl.h | 306 + src/lib/dns/rrtype-placeholder.h | 286 + src/lib/dns/rrtype.cc | 65 + src/lib/dns/rrtype.h | 748 +++ src/lib/dns/serial.cc | 70 + src/lib/dns/serial.h | 150 + src/lib/dns/tests/Makefile.am | 95 + src/lib/dns/tests/Makefile.in | 2335 +++++++ src/lib/dns/tests/dns_exceptions_unittest.cc | 65 + src/lib/dns/tests/edns_unittest.cc | 258 + src/lib/dns/tests/labelsequence_unittest.cc | 1243 ++++ .../dns/tests/master_lexer_inputsource_unittest.cc | 368 + src/lib/dns/tests/master_lexer_state_unittest.cc | 607 ++ src/lib/dns/tests/master_lexer_token_unittest.cc | 162 + src/lib/dns/tests/master_lexer_unittest.cc | 521 ++ src/lib/dns/tests/master_loader_callbacks_test.cc | 79 + src/lib/dns/tests/master_loader_unittest.cc | 1445 ++++ src/lib/dns/tests/masterload_unittest.cc | 397 ++ src/lib/dns/tests/message_unittest.cc | 1162 ++++ src/lib/dns/tests/messagerenderer_unittest.cc | 292 + src/lib/dns/tests/name_unittest.cc | 797 +++ src/lib/dns/tests/nsec3hash_unittest.cc | 269 + src/lib/dns/tests/opcode_unittest.cc | 100 + src/lib/dns/tests/qid_gen_unittest.cc | 39 + src/lib/dns/tests/question_unittest.cc | 196 + src/lib/dns/tests/rcode_unittest.cc | 126 + src/lib/dns/tests/rdata_afsdb_unittest.cc | 235 + src/lib/dns/tests/rdata_caa_unittest.cc | 322 + .../dns/tests/rdata_char_string_data_unittest.cc | 181 + src/lib/dns/tests/rdata_char_string_unittest.cc | 246 + src/lib/dns/tests/rdata_cname_unittest.cc | 135 + src/lib/dns/tests/rdata_dhcid_unittest.cc | 165 + src/lib/dns/tests/rdata_dname_unittest.cc | 137 + src/lib/dns/tests/rdata_dnskey_unittest.cc | 200 + src/lib/dns/tests/rdata_ds_like_unittest.cc | 229 + src/lib/dns/tests/rdata_hinfo_unittest.cc | 154 + src/lib/dns/tests/rdata_in_a_unittest.cc | 159 + src/lib/dns/tests/rdata_in_aaaa_unittest.cc | 151 + src/lib/dns/tests/rdata_minfo_unittest.cc | 230 + src/lib/dns/tests/rdata_mx_unittest.cc | 147 + src/lib/dns/tests/rdata_naptr_unittest.cc | 239 + src/lib/dns/tests/rdata_ns_unittest.cc | 145 + src/lib/dns/tests/rdata_nsec3_unittest.cc | 226 + .../dns/tests/rdata_nsec3param_like_unittest.cc | 272 + src/lib/dns/tests/rdata_nsec3param_unittest.cc | 209 + src/lib/dns/tests/rdata_nsec_unittest.cc | 138 + src/lib/dns/tests/rdata_nsecbitmap_unittest.cc | 269 + src/lib/dns/tests/rdata_opt_unittest.cc | 198 + src/lib/dns/tests/rdata_pimpl_holder_unittest.cc | 56 + src/lib/dns/tests/rdata_ptr_unittest.cc | 145 + src/lib/dns/tests/rdata_rp_unittest.cc | 200 + src/lib/dns/tests/rdata_rrsig_unittest.cc | 369 + src/lib/dns/tests/rdata_soa_unittest.cc | 249 + src/lib/dns/tests/rdata_srv_unittest.cc | 205 + src/lib/dns/tests/rdata_sshfp_unittest.cc | 311 + src/lib/dns/tests/rdata_tkey_unittest.cc | 450 ++ src/lib/dns/tests/rdata_tlsa_unittest.cc | 276 + src/lib/dns/tests/rdata_tsig_unittest.cc | 432 ++ src/lib/dns/tests/rdata_txt_like_unittest.cc | 397 ++ src/lib/dns/tests/rdata_unittest.cc | 466 ++ src/lib/dns/tests/rdata_unittest.h | 92 + src/lib/dns/tests/rdatafields_unittest.cc | 366 + src/lib/dns/tests/rrclass_unittest.cc | 174 + src/lib/dns/tests/rrcollator_unittest.cc | 208 + src/lib/dns/tests/rrparamregistry_unittest.cc | 185 + src/lib/dns/tests/rrset_collection_unittest.cc | 240 + src/lib/dns/tests/rrset_unittest.cc | 442 ++ src/lib/dns/tests/rrttl_unittest.cc | 279 + src/lib/dns/tests/rrtype_unittest.cc | 195 + src/lib/dns/tests/run_unittests.cc | 24 + src/lib/dns/tests/serial_unittest.cc | 173 + src/lib/dns/tests/testdata/Makefile.am | 219 + src/lib/dns/tests/testdata/Makefile.in | 716 ++ src/lib/dns/tests/testdata/broken.zone | 3 + src/lib/dns/tests/testdata/edns_toWire1.spec | 5 + src/lib/dns/tests/testdata/edns_toWire1.wire | 9 + src/lib/dns/tests/testdata/edns_toWire2.spec | 5 + src/lib/dns/tests/testdata/edns_toWire2.wire | 9 + src/lib/dns/tests/testdata/edns_toWire3.spec | 7 + src/lib/dns/tests/testdata/edns_toWire3.wire | 9 + src/lib/dns/tests/testdata/edns_toWire4.spec | 7 + src/lib/dns/tests/testdata/edns_toWire4.wire | 9 + src/lib/dns/tests/testdata/example.org | 17 + src/lib/dns/tests/testdata/masterload.txt | 5 + src/lib/dns/tests/testdata/message_fromWire1 | 22 + src/lib/dns/tests/testdata/message_fromWire10.spec | 13 + src/lib/dns/tests/testdata/message_fromWire10.wire | 19 + src/lib/dns/tests/testdata/message_fromWire11.spec | 15 + src/lib/dns/tests/testdata/message_fromWire11.wire | 19 + src/lib/dns/tests/testdata/message_fromWire12.spec | 21 + src/lib/dns/tests/testdata/message_fromWire12.wire | 24 + src/lib/dns/tests/testdata/message_fromWire13.spec | 20 + src/lib/dns/tests/testdata/message_fromWire13.wire | 35 + src/lib/dns/tests/testdata/message_fromWire14.spec | 21 + src/lib/dns/tests/testdata/message_fromWire14.wire | 24 + src/lib/dns/tests/testdata/message_fromWire15.spec | 22 + src/lib/dns/tests/testdata/message_fromWire15.wire | 30 + src/lib/dns/tests/testdata/message_fromWire16.spec | 21 + src/lib/dns/tests/testdata/message_fromWire16.wire | 24 + src/lib/dns/tests/testdata/message_fromWire17.spec | 22 + src/lib/dns/tests/testdata/message_fromWire17.wire | 24 + src/lib/dns/tests/testdata/message_fromWire18.spec | 23 + src/lib/dns/tests/testdata/message_fromWire18.wire | 24 + src/lib/dns/tests/testdata/message_fromWire19.spec | 20 + src/lib/dns/tests/testdata/message_fromWire19.wire | 28 + src/lib/dns/tests/testdata/message_fromWire2 | 22 + src/lib/dns/tests/testdata/message_fromWire20.spec | 20 + src/lib/dns/tests/testdata/message_fromWire20.wire | 28 + src/lib/dns/tests/testdata/message_fromWire21.spec | 20 + src/lib/dns/tests/testdata/message_fromWire21.wire | 28 + src/lib/dns/tests/testdata/message_fromWire22.spec | 14 + src/lib/dns/tests/testdata/message_fromWire22.wire | 20 + src/lib/dns/tests/testdata/message_fromWire3 | 22 + src/lib/dns/tests/testdata/message_fromWire4 | 23 + src/lib/dns/tests/testdata/message_fromWire5 | 33 + src/lib/dns/tests/testdata/message_fromWire6 | 23 + src/lib/dns/tests/testdata/message_fromWire7 | 27 + src/lib/dns/tests/testdata/message_fromWire8 | 22 + src/lib/dns/tests/testdata/message_fromWire9 | 22 + src/lib/dns/tests/testdata/message_toText1.spec | 24 + src/lib/dns/tests/testdata/message_toText1.txt | 14 + src/lib/dns/tests/testdata/message_toText1.wire | 28 + src/lib/dns/tests/testdata/message_toText2.spec | 14 + src/lib/dns/tests/testdata/message_toText2.txt | 8 + src/lib/dns/tests/testdata/message_toText2.wire | 19 + src/lib/dns/tests/testdata/message_toText3.spec | 31 + src/lib/dns/tests/testdata/message_toText3.txt | 17 + src/lib/dns/tests/testdata/message_toText3.wire | 39 + src/lib/dns/tests/testdata/message_toWire1 | 22 + src/lib/dns/tests/testdata/message_toWire2.spec | 21 + src/lib/dns/tests/testdata/message_toWire2.wire | 24 + src/lib/dns/tests/testdata/message_toWire3.spec | 22 + src/lib/dns/tests/testdata/message_toWire3.wire | 30 + src/lib/dns/tests/testdata/message_toWire4.spec | 27 + src/lib/dns/tests/testdata/message_toWire4.wire | 24 + src/lib/dns/tests/testdata/message_toWire5.spec | 36 + src/lib/dns/tests/testdata/message_toWire5.wire | 34 + src/lib/dns/tests/testdata/message_toWire6 | 48 + src/lib/dns/tests/testdata/message_toWire7 | 35 + src/lib/dns/tests/testdata/name_fromWire1 | 14 + src/lib/dns/tests/testdata/name_fromWire10 | 12 + src/lib/dns/tests/testdata/name_fromWire11 | 12 + src/lib/dns/tests/testdata/name_fromWire12 | 13 + src/lib/dns/tests/testdata/name_fromWire13 | 5 + src/lib/dns/tests/testdata/name_fromWire14 | 7 + src/lib/dns/tests/testdata/name_fromWire2 | 15 + src/lib/dns/tests/testdata/name_fromWire3_1 | 11 + src/lib/dns/tests/testdata/name_fromWire3_2 | 13 + src/lib/dns/tests/testdata/name_fromWire4 | 45 + src/lib/dns/tests/testdata/name_fromWire6 | 14 + src/lib/dns/tests/testdata/name_fromWire7 | 6 + src/lib/dns/tests/testdata/name_fromWire8 | 27 + src/lib/dns/tests/testdata/name_fromWire9 | 12 + src/lib/dns/tests/testdata/name_toWire1 | 12 + src/lib/dns/tests/testdata/name_toWire2 | 14 + src/lib/dns/tests/testdata/name_toWire3 | 14 + src/lib/dns/tests/testdata/name_toWire4 | 16 + src/lib/dns/tests/testdata/name_toWire5.spec | 19 + src/lib/dns/tests/testdata/name_toWire5.wire | 12 + src/lib/dns/tests/testdata/name_toWire6.spec | 19 + src/lib/dns/tests/testdata/name_toWire6.wire | 12 + src/lib/dns/tests/testdata/name_toWire7 | 10 + src/lib/dns/tests/testdata/name_toWire8 | 7 + src/lib/dns/tests/testdata/name_toWire9 | 13 + src/lib/dns/tests/testdata/omitcheck.txt | 1 + src/lib/dns/tests/testdata/origincheck.txt | 5 + src/lib/dns/tests/testdata/question_fromWire | 33 + src/lib/dns/tests/testdata/question_toWire1 | 14 + src/lib/dns/tests/testdata/question_toWire2 | 14 + .../dns/tests/testdata/rdata_afsdb_fromWire1.spec | 3 + .../dns/tests/testdata/rdata_afsdb_fromWire1.wire | 8 + .../dns/tests/testdata/rdata_afsdb_fromWire2.spec | 6 + .../dns/tests/testdata/rdata_afsdb_fromWire2.wire | 11 + .../dns/tests/testdata/rdata_afsdb_fromWire3.spec | 4 + .../dns/tests/testdata/rdata_afsdb_fromWire3.wire | 8 + .../dns/tests/testdata/rdata_afsdb_fromWire4.spec | 4 + .../dns/tests/testdata/rdata_afsdb_fromWire4.wire | 8 + .../dns/tests/testdata/rdata_afsdb_fromWire5.spec | 4 + .../dns/tests/testdata/rdata_afsdb_fromWire5.wire | 8 + .../dns/tests/testdata/rdata_afsdb_toWire1.spec | 4 + .../dns/tests/testdata/rdata_afsdb_toWire1.wire | 8 + .../dns/tests/testdata/rdata_afsdb_toWire2.spec | 8 + .../dns/tests/testdata/rdata_afsdb_toWire2.wire | 11 + .../dns/tests/testdata/rdata_caa_fromWire1.spec | 6 + .../dns/tests/testdata/rdata_caa_fromWire1.wire | 8 + .../dns/tests/testdata/rdata_caa_fromWire2.spec | 7 + .../dns/tests/testdata/rdata_caa_fromWire2.wire | 8 + .../dns/tests/testdata/rdata_caa_fromWire3.spec | 7 + .../dns/tests/testdata/rdata_caa_fromWire3.wire | 8 + .../dns/tests/testdata/rdata_caa_fromWire4.spec | 7 + .../dns/tests/testdata/rdata_caa_fromWire4.wire | 8 + src/lib/dns/tests/testdata/rdata_caa_fromWire5 | 6 + src/lib/dns/tests/testdata/rdata_caa_fromWire6 | 4 + src/lib/dns/tests/testdata/rdata_cname_fromWire | 44 + src/lib/dns/tests/testdata/rdata_dhcid_fromWire | 12 + src/lib/dns/tests/testdata/rdata_dhcid_toWire | 7 + src/lib/dns/tests/testdata/rdata_dname_fromWire | 44 + .../rdata_dnskey_empty_keydata_fromWire.spec | 7 + .../rdata_dnskey_empty_keydata_fromWire.wire | 14 + .../dns/tests/testdata/rdata_dnskey_fromWire.spec | 7 + .../dns/tests/testdata/rdata_dnskey_fromWire.wire | 14 + src/lib/dns/tests/testdata/rdata_ds_fromWire | 6 + src/lib/dns/tests/testdata/rdata_in_a_fromWire | 19 + src/lib/dns/tests/testdata/rdata_in_aaaa_fromWire | 18 + .../dns/tests/testdata/rdata_minfo_fromWire1.spec | 3 + .../dns/tests/testdata/rdata_minfo_fromWire1.wire | 8 + .../dns/tests/testdata/rdata_minfo_fromWire2.spec | 7 + .../dns/tests/testdata/rdata_minfo_fromWire2.wire | 11 + .../dns/tests/testdata/rdata_minfo_fromWire3.spec | 6 + .../dns/tests/testdata/rdata_minfo_fromWire3.wire | 8 + .../dns/tests/testdata/rdata_minfo_fromWire4.spec | 6 + .../dns/tests/testdata/rdata_minfo_fromWire4.wire | 8 + .../dns/tests/testdata/rdata_minfo_fromWire5.spec | 5 + .../dns/tests/testdata/rdata_minfo_fromWire5.wire | 8 + .../dns/tests/testdata/rdata_minfo_fromWire6.spec | 5 + .../dns/tests/testdata/rdata_minfo_fromWire6.wire | 8 + .../dns/tests/testdata/rdata_minfo_toWire1.spec | 5 + .../dns/tests/testdata/rdata_minfo_toWire1.wire | 8 + .../dns/tests/testdata/rdata_minfo_toWire2.spec | 6 + .../dns/tests/testdata/rdata_minfo_toWire2.wire | 8 + .../testdata/rdata_minfo_toWireUncompressed1.spec | 7 + .../testdata/rdata_minfo_toWireUncompressed1.wire | 8 + .../testdata/rdata_minfo_toWireUncompressed2.spec | 8 + .../testdata/rdata_minfo_toWireUncompressed2.wire | 8 + src/lib/dns/tests/testdata/rdata_mx_fromWire | 15 + src/lib/dns/tests/testdata/rdata_mx_toWire1 | 12 + src/lib/dns/tests/testdata/rdata_mx_toWire2 | 12 + src/lib/dns/tests/testdata/rdata_ns_fromWire | 44 + src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 | 7 + .../dns/tests/testdata/rdata_nsec3_fromWire1.spec | 7 + .../dns/tests/testdata/rdata_nsec3_fromWire10.spec | 8 + .../dns/tests/testdata/rdata_nsec3_fromWire10.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire11.spec | 8 + .../dns/tests/testdata/rdata_nsec3_fromWire11.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire12.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire12.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire13.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire13.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire14.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire14.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire15.spec | 10 + .../dns/tests/testdata/rdata_nsec3_fromWire15.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire16.spec | 8 + .../dns/tests/testdata/rdata_nsec3_fromWire16.wire | 12 + .../dns/tests/testdata/rdata_nsec3_fromWire17.spec | 8 + .../dns/tests/testdata/rdata_nsec3_fromWire17.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire2.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire2.wire | 14 + src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 | 6 + .../dns/tests/testdata/rdata_nsec3_fromWire4.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire4.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire5.spec | 13 + .../dns/tests/testdata/rdata_nsec3_fromWire5.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire6.spec | 11 + .../dns/tests/testdata/rdata_nsec3_fromWire6.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire7.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire7.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire8.spec | 9 + .../dns/tests/testdata/rdata_nsec3_fromWire8.wire | 14 + .../dns/tests/testdata/rdata_nsec3_fromWire9.spec | 10 + .../dns/tests/testdata/rdata_nsec3_fromWire9.wire | 16 + .../dns/tests/testdata/rdata_nsec3param_fromWire1 | 5 + .../testdata/rdata_nsec3param_fromWire11.spec | 8 + .../testdata/rdata_nsec3param_fromWire11.wire | 10 + .../testdata/rdata_nsec3param_fromWire13.spec | 9 + .../testdata/rdata_nsec3param_fromWire13.wire | 10 + .../tests/testdata/rdata_nsec3param_fromWire2.spec | 9 + .../tests/testdata/rdata_nsec3param_fromWire2.wire | 10 + src/lib/dns/tests/testdata/rdata_nsec_fromWire1 | 6 + .../dns/tests/testdata/rdata_nsec_fromWire10.spec | 8 + .../dns/tests/testdata/rdata_nsec_fromWire10.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire16.spec | 8 + .../dns/tests/testdata/rdata_nsec_fromWire16.wire | 8 + src/lib/dns/tests/testdata/rdata_nsec_fromWire2 | 10 + src/lib/dns/tests/testdata/rdata_nsec_fromWire3 | 10 + .../dns/tests/testdata/rdata_nsec_fromWire4.spec | 9 + .../dns/tests/testdata/rdata_nsec_fromWire4.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire5.spec | 13 + .../dns/tests/testdata/rdata_nsec_fromWire5.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire6.spec | 11 + .../dns/tests/testdata/rdata_nsec_fromWire6.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire7.spec | 9 + .../dns/tests/testdata/rdata_nsec_fromWire7.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire8.spec | 9 + .../dns/tests/testdata/rdata_nsec_fromWire8.wire | 10 + .../dns/tests/testdata/rdata_nsec_fromWire9.spec | 10 + .../dns/tests/testdata/rdata_nsec_fromWire9.wire | 12 + src/lib/dns/tests/testdata/rdata_opt_fromWire1 | 15 + src/lib/dns/tests/testdata/rdata_opt_fromWire2 | 4 + src/lib/dns/tests/testdata/rdata_opt_fromWire3 | 8 + src/lib/dns/tests/testdata/rdata_opt_fromWire4 | 9 + src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec | 6 + src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec | 12 + src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire | 14 + src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec | 7 + src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec | 7 + src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec | 7 + src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec | 7 + src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_toWire1.spec | 8 + src/lib/dns/tests/testdata/rdata_rp_toWire1.wire | 8 + src/lib/dns/tests/testdata/rdata_rp_toWire2.spec | 14 + src/lib/dns/tests/testdata/rdata_rp_toWire2.wire | 14 + src/lib/dns/tests/testdata/rdata_rrsig_fromWire1 | 13 + .../dns/tests/testdata/rdata_rrsig_fromWire2.spec | 8 + .../dns/tests/testdata/rdata_rrsig_fromWire2.wire | 12 + src/lib/dns/tests/testdata/rdata_soa_fromWire | 20 + .../testdata/rdata_soa_toWireUncompressed.spec | 7 + .../testdata/rdata_soa_toWireUncompressed.wire | 10 + src/lib/dns/tests/testdata/rdata_srv_fromWire | 36 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire | 4 + .../dns/tests/testdata/rdata_sshfp_fromWire1.spec | 6 + .../dns/tests/testdata/rdata_sshfp_fromWire1.wire | 8 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 | 6 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 | 4 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 | 4 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 | 4 + .../dns/tests/testdata/rdata_sshfp_fromWire2.spec | 7 + .../dns/tests/testdata/rdata_sshfp_fromWire2.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire3.spec | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire3.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire4.spec | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire4.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire5.spec | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire5.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire6.spec | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire6.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire7.spec | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire7.wire | 8 + .../dns/tests/testdata/rdata_sshfp_fromWire8.spec | 9 + .../dns/tests/testdata/rdata_sshfp_fromWire8.wire | 8 + src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 | 6 + .../dns/tests/testdata/rdata_tkey_fromWire1.spec | 6 + .../dns/tests/testdata/rdata_tkey_fromWire1.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire2.spec | 8 + .../dns/tests/testdata/rdata_tkey_fromWire2.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire3.spec | 9 + .../dns/tests/testdata/rdata_tkey_fromWire3.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire4.spec | 11 + .../dns/tests/testdata/rdata_tkey_fromWire4.wire | 17 + .../dns/tests/testdata/rdata_tkey_fromWire5.spec | 7 + .../dns/tests/testdata/rdata_tkey_fromWire5.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire6.spec | 7 + .../dns/tests/testdata/rdata_tkey_fromWire6.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire7.spec | 8 + .../dns/tests/testdata/rdata_tkey_fromWire7.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire8.spec | 8 + .../dns/tests/testdata/rdata_tkey_fromWire8.wire | 14 + .../dns/tests/testdata/rdata_tkey_fromWire9.spec | 8 + .../dns/tests/testdata/rdata_tkey_fromWire9.wire | 14 + src/lib/dns/tests/testdata/rdata_tkey_toWire1.spec | 8 + src/lib/dns/tests/testdata/rdata_tkey_toWire1.wire | 14 + src/lib/dns/tests/testdata/rdata_tkey_toWire2.spec | 11 + src/lib/dns/tests/testdata/rdata_tkey_toWire2.wire | 14 + src/lib/dns/tests/testdata/rdata_tkey_toWire3.spec | 13 + src/lib/dns/tests/testdata/rdata_tkey_toWire3.wire | 14 + src/lib/dns/tests/testdata/rdata_tkey_toWire4.spec | 10 + src/lib/dns/tests/testdata/rdata_tkey_toWire4.wire | 17 + src/lib/dns/tests/testdata/rdata_tkey_toWire5.spec | 10 + src/lib/dns/tests/testdata/rdata_tkey_toWire5.wire | 17 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire | 4 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 | 6 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 | 4 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 | 4 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 | 4 + .../dns/tests/testdata/rdata_tlsa_fromWire3.spec | 7 + .../dns/tests/testdata/rdata_tlsa_fromWire3.wire | 8 + .../dns/tests/testdata/rdata_tlsa_fromWire4.spec | 7 + .../dns/tests/testdata/rdata_tlsa_fromWire4.wire | 8 + .../dns/tests/testdata/rdata_tlsa_fromWire5.spec | 7 + .../dns/tests/testdata/rdata_tlsa_fromWire5.wire | 8 + .../dns/tests/testdata/rdata_tlsa_fromWire6.spec | 7 + .../dns/tests/testdata/rdata_tlsa_fromWire6.wire | 8 + .../dns/tests/testdata/rdata_tlsa_fromWire7.spec | 9 + .../dns/tests/testdata/rdata_tlsa_fromWire7.wire | 8 + .../dns/tests/testdata/rdata_tlsa_fromWire8.spec | 7 + .../dns/tests/testdata/rdata_tlsa_fromWire8.wire | 8 + src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 | 7 + .../dns/tests/testdata/rdata_tsig_fromWire1.spec | 6 + .../dns/tests/testdata/rdata_tsig_fromWire1.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire2.spec | 8 + .../dns/tests/testdata/rdata_tsig_fromWire2.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire3.spec | 8 + .../dns/tests/testdata/rdata_tsig_fromWire3.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire4.spec | 11 + .../dns/tests/testdata/rdata_tsig_fromWire4.wire | 17 + .../dns/tests/testdata/rdata_tsig_fromWire5.spec | 7 + .../dns/tests/testdata/rdata_tsig_fromWire5.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire6.spec | 7 + .../dns/tests/testdata/rdata_tsig_fromWire6.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire7.spec | 8 + .../dns/tests/testdata/rdata_tsig_fromWire7.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire8.spec | 8 + .../dns/tests/testdata/rdata_tsig_fromWire8.wire | 14 + .../dns/tests/testdata/rdata_tsig_fromWire9.spec | 8 + .../dns/tests/testdata/rdata_tsig_fromWire9.wire | 14 + src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec | 11 + src/lib/dns/tests/testdata/rdata_tsig_toWire1.wire | 14 + src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec | 13 + src/lib/dns/tests/testdata/rdata_tsig_toWire2.wire | 14 + src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec | 15 + src/lib/dns/tests/testdata/rdata_tsig_toWire3.wire | 14 + src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec | 13 + src/lib/dns/tests/testdata/rdata_tsig_toWire4.wire | 17 + src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec | 13 + src/lib/dns/tests/testdata/rdata_tsig_toWire5.wire | 17 + src/lib/dns/tests/testdata/rdata_txt_fromWire1 | 9 + .../dns/tests/testdata/rdata_txt_fromWire2.spec | 8 + .../dns/tests/testdata/rdata_txt_fromWire2.wire | 8 + .../dns/tests/testdata/rdata_txt_fromWire3.spec | 8 + .../dns/tests/testdata/rdata_txt_fromWire3.wire | 10 + .../dns/tests/testdata/rdata_txt_fromWire4.spec | 9 + .../dns/tests/testdata/rdata_txt_fromWire4.wire | 8 + .../dns/tests/testdata/rdata_txt_fromWire5.spec | 9 + .../dns/tests/testdata/rdata_txt_fromWire5.wire | 8 + src/lib/dns/tests/testdata/rdata_unknown_fromWire | 13 + src/lib/dns/tests/testdata/rdatafields1.spec | 10 + src/lib/dns/tests/testdata/rdatafields1.wire | 9 + src/lib/dns/tests/testdata/rdatafields2.spec | 11 + src/lib/dns/tests/testdata/rdatafields2.wire | 9 + src/lib/dns/tests/testdata/rdatafields3.spec | 11 + src/lib/dns/tests/testdata/rdatafields3.wire | 12 + src/lib/dns/tests/testdata/rdatafields4.spec | 7 + src/lib/dns/tests/testdata/rdatafields4.wire | 12 + src/lib/dns/tests/testdata/rdatafields5.spec | 12 + src/lib/dns/tests/testdata/rdatafields5.wire | 18 + src/lib/dns/tests/testdata/rdatafields6.spec | 13 + src/lib/dns/tests/testdata/rdatafields6.wire | 18 + src/lib/dns/tests/testdata/rrcode16_fromWire1 | 4 + src/lib/dns/tests/testdata/rrcode16_fromWire2 | 4 + src/lib/dns/tests/testdata/rrcode32_fromWire1 | 4 + src/lib/dns/tests/testdata/rrcode32_fromWire2 | 4 + src/lib/dns/tests/testdata/rrset_toWire1 | 23 + src/lib/dns/tests/testdata/rrset_toWire2 | 38 + src/lib/dns/tests/testdata/rrset_toWire3 | 12 + src/lib/dns/tests/testdata/rrset_toWire4 | 12 + src/lib/dns/tests/testdata/tsig_verify1.spec | 19 + src/lib/dns/tests/testdata/tsig_verify1.wire | 24 + src/lib/dns/tests/testdata/tsig_verify10.spec | 22 + src/lib/dns/tests/testdata/tsig_verify10.wire | 24 + src/lib/dns/tests/testdata/tsig_verify11.spec | 24 + src/lib/dns/tests/testdata/tsig_verify11.wire | 24 + src/lib/dns/tests/testdata/tsig_verify2.spec | 32 + src/lib/dns/tests/testdata/tsig_verify2.wire | 31 + src/lib/dns/tests/testdata/tsig_verify3.spec | 26 + src/lib/dns/tests/testdata/tsig_verify3.wire | 25 + src/lib/dns/tests/testdata/tsig_verify4.spec | 27 + src/lib/dns/tests/testdata/tsig_verify4.wire | 29 + src/lib/dns/tests/testdata/tsig_verify5.spec | 26 + src/lib/dns/tests/testdata/tsig_verify5.wire | 29 + src/lib/dns/tests/testdata/tsig_verify6.spec | 21 + src/lib/dns/tests/testdata/tsig_verify6.wire | 24 + src/lib/dns/tests/testdata/tsig_verify7.spec | 21 + src/lib/dns/tests/testdata/tsig_verify7.wire | 24 + src/lib/dns/tests/testdata/tsig_verify8.spec | 23 + src/lib/dns/tests/testdata/tsig_verify8.wire | 24 + src/lib/dns/tests/testdata/tsig_verify9.spec | 21 + src/lib/dns/tests/testdata/tsig_verify9.wire | 24 + src/lib/dns/tests/testdata/tsigrecord_toWire1.spec | 16 + src/lib/dns/tests/testdata/tsigrecord_toWire1.wire | 14 + src/lib/dns/tests/testdata/tsigrecord_toWire2.spec | 19 + src/lib/dns/tests/testdata/tsigrecord_toWire2.wire | 20 + src/lib/dns/tests/tsig_unittest.cc | 1187 ++++ src/lib/dns/tests/tsigerror_unittest.cc | 126 + src/lib/dns/tests/tsigkey_unittest.cc | 350 + src/lib/dns/tests/tsigrecord_unittest.cc | 152 + src/lib/dns/tests/unittest_util.cc | 176 + src/lib/dns/tests/unittest_util.h | 88 + src/lib/dns/tests/zone_checker_unittest.cc | 349 + src/lib/dns/tsig.cc | 581 ++ src/lib/dns/tsig.h | 445 ++ src/lib/dns/tsigerror.cc | 66 + src/lib/dns/tsigerror.h | 374 ++ src/lib/dns/tsigkey.cc | 364 + src/lib/dns/tsigkey.h | 391 ++ src/lib/dns/tsigrecord.cc | 142 + src/lib/dns/tsigrecord.h | 300 + src/lib/dns/zone_checker.cc | 191 + src/lib/dns/zone_checker.h | 153 + 617 files changed, 74281 insertions(+) create mode 100644 src/lib/dns/Makefile.am create mode 100644 src/lib/dns/Makefile.in create mode 100644 src/lib/dns/dns_fwd.h create mode 100644 src/lib/dns/edns.cc create mode 100644 src/lib/dns/edns.h create mode 100644 src/lib/dns/exceptions.cc create mode 100644 src/lib/dns/exceptions.h create mode 100644 src/lib/dns/gen-rdatacode.py.in create mode 100644 src/lib/dns/labelsequence.cc create mode 100644 src/lib/dns/labelsequence.h create mode 100644 src/lib/dns/master_lexer.cc create mode 100644 src/lib/dns/master_lexer.h create mode 100644 src/lib/dns/master_lexer_inputsource.cc create mode 100644 src/lib/dns/master_lexer_inputsource.h create mode 100644 src/lib/dns/master_lexer_state.h create mode 100644 src/lib/dns/master_loader.cc create mode 100644 src/lib/dns/master_loader.h create mode 100644 src/lib/dns/master_loader_callbacks.cc create mode 100644 src/lib/dns/master_loader_callbacks.h create mode 100644 src/lib/dns/masterload.cc create mode 100644 src/lib/dns/masterload.h create mode 100644 src/lib/dns/message.cc create mode 100644 src/lib/dns/message.h create mode 100644 src/lib/dns/messagerenderer.cc create mode 100644 src/lib/dns/messagerenderer.h create mode 100644 src/lib/dns/name.cc create mode 100644 src/lib/dns/name.h create mode 100644 src/lib/dns/name_internal.h create mode 100644 src/lib/dns/nsec3hash.cc create mode 100644 src/lib/dns/nsec3hash.h create mode 100644 src/lib/dns/opcode.cc create mode 100644 src/lib/dns/opcode.h create mode 100644 src/lib/dns/qid_gen.cc create mode 100644 src/lib/dns/qid_gen.h create mode 100644 src/lib/dns/question.cc create mode 100644 src/lib/dns/question.h create mode 100644 src/lib/dns/rcode.cc create mode 100644 src/lib/dns/rcode.h create mode 100644 src/lib/dns/rdata.cc create mode 100644 src/lib/dns/rdata.h create mode 100644 src/lib/dns/rdata/any_255/tsig_250.cc create mode 100644 src/lib/dns/rdata/any_255/tsig_250.h create mode 100644 src/lib/dns/rdata/ch_3/a_1.cc create mode 100644 src/lib/dns/rdata/ch_3/a_1.h create mode 100644 src/lib/dns/rdata/generic/afsdb_18.cc create mode 100644 src/lib/dns/rdata/generic/afsdb_18.h create mode 100644 src/lib/dns/rdata/generic/caa_257.cc create mode 100644 src/lib/dns/rdata/generic/caa_257.h create mode 100644 src/lib/dns/rdata/generic/cname_5.cc create mode 100644 src/lib/dns/rdata/generic/cname_5.h create mode 100644 src/lib/dns/rdata/generic/detail/char_string.cc create mode 100644 src/lib/dns/rdata/generic/detail/char_string.h create mode 100644 src/lib/dns/rdata/generic/detail/ds_like.h create mode 100644 src/lib/dns/rdata/generic/detail/lexer_util.h create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.cc create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.h create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.cc create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.h create mode 100644 src/lib/dns/rdata/generic/detail/txt_like.h create mode 100644 src/lib/dns/rdata/generic/dlv_32769.cc create mode 100644 src/lib/dns/rdata/generic/dlv_32769.h create mode 100644 src/lib/dns/rdata/generic/dname_39.cc create mode 100644 src/lib/dns/rdata/generic/dname_39.h create mode 100644 src/lib/dns/rdata/generic/dnskey_48.cc create mode 100644 src/lib/dns/rdata/generic/dnskey_48.h create mode 100644 src/lib/dns/rdata/generic/ds_43.cc create mode 100644 src/lib/dns/rdata/generic/ds_43.h create mode 100644 src/lib/dns/rdata/generic/hinfo_13.cc create mode 100644 src/lib/dns/rdata/generic/hinfo_13.h create mode 100644 src/lib/dns/rdata/generic/minfo_14.cc create mode 100644 src/lib/dns/rdata/generic/minfo_14.h create mode 100644 src/lib/dns/rdata/generic/mx_15.cc create mode 100644 src/lib/dns/rdata/generic/mx_15.h create mode 100644 src/lib/dns/rdata/generic/naptr_35.cc create mode 100644 src/lib/dns/rdata/generic/naptr_35.h create mode 100644 src/lib/dns/rdata/generic/ns_2.cc create mode 100644 src/lib/dns/rdata/generic/ns_2.h create mode 100644 src/lib/dns/rdata/generic/nsec3_50.cc create mode 100644 src/lib/dns/rdata/generic/nsec3_50.h create mode 100644 src/lib/dns/rdata/generic/nsec3param_51.cc create mode 100644 src/lib/dns/rdata/generic/nsec3param_51.h create mode 100644 src/lib/dns/rdata/generic/nsec_47.cc create mode 100644 src/lib/dns/rdata/generic/nsec_47.h create mode 100644 src/lib/dns/rdata/generic/opt_41.cc create mode 100644 src/lib/dns/rdata/generic/opt_41.h create mode 100644 src/lib/dns/rdata/generic/ptr_12.cc create mode 100644 src/lib/dns/rdata/generic/ptr_12.h create mode 100644 src/lib/dns/rdata/generic/rp_17.cc create mode 100644 src/lib/dns/rdata/generic/rp_17.h create mode 100644 src/lib/dns/rdata/generic/rrsig_46.cc create mode 100644 src/lib/dns/rdata/generic/rrsig_46.h create mode 100644 src/lib/dns/rdata/generic/soa_6.cc create mode 100644 src/lib/dns/rdata/generic/soa_6.h create mode 100644 src/lib/dns/rdata/generic/spf_99.cc create mode 100644 src/lib/dns/rdata/generic/spf_99.h create mode 100644 src/lib/dns/rdata/generic/sshfp_44.cc create mode 100644 src/lib/dns/rdata/generic/sshfp_44.h create mode 100644 src/lib/dns/rdata/generic/tkey_249.cc create mode 100644 src/lib/dns/rdata/generic/tkey_249.h create mode 100644 src/lib/dns/rdata/generic/tlsa_52.cc create mode 100644 src/lib/dns/rdata/generic/tlsa_52.h create mode 100644 src/lib/dns/rdata/generic/txt_16.cc create mode 100644 src/lib/dns/rdata/generic/txt_16.h create mode 100644 src/lib/dns/rdata/hs_4/a_1.cc create mode 100644 src/lib/dns/rdata/hs_4/a_1.h create mode 100644 src/lib/dns/rdata/in_1/a_1.cc create mode 100644 src/lib/dns/rdata/in_1/a_1.h create mode 100644 src/lib/dns/rdata/in_1/aaaa_28.cc create mode 100644 src/lib/dns/rdata/in_1/aaaa_28.h create mode 100644 src/lib/dns/rdata/in_1/dhcid_49.cc create mode 100644 src/lib/dns/rdata/in_1/dhcid_49.h create mode 100644 src/lib/dns/rdata/in_1/srv_33.cc create mode 100644 src/lib/dns/rdata/in_1/srv_33.h create mode 100644 src/lib/dns/rdata/template.cc create mode 100644 src/lib/dns/rdata/template.h create mode 100644 src/lib/dns/rdata_pimpl_holder.h create mode 100644 src/lib/dns/rdataclass.cc create mode 100644 src/lib/dns/rdataclass.h create mode 100644 src/lib/dns/rdatafields.cc create mode 100644 src/lib/dns/rdatafields.h create mode 100644 src/lib/dns/rrclass-placeholder.h create mode 100644 src/lib/dns/rrclass.cc create mode 100644 src/lib/dns/rrclass.h create mode 100644 src/lib/dns/rrcollator.cc create mode 100644 src/lib/dns/rrcollator.h create mode 100644 src/lib/dns/rrparamregistry-placeholder.cc create mode 100644 src/lib/dns/rrparamregistry.cc create mode 100644 src/lib/dns/rrparamregistry.h create mode 100644 src/lib/dns/rrset.cc create mode 100644 src/lib/dns/rrset.h create mode 100644 src/lib/dns/rrset_collection.cc create mode 100644 src/lib/dns/rrset_collection.h create mode 100644 src/lib/dns/rrset_collection_base.h create mode 100644 src/lib/dns/rrttl.cc create mode 100644 src/lib/dns/rrttl.h create mode 100644 src/lib/dns/rrtype-placeholder.h create mode 100644 src/lib/dns/rrtype.cc create mode 100644 src/lib/dns/rrtype.h create mode 100644 src/lib/dns/serial.cc create mode 100644 src/lib/dns/serial.h create mode 100644 src/lib/dns/tests/Makefile.am create mode 100644 src/lib/dns/tests/Makefile.in create mode 100644 src/lib/dns/tests/dns_exceptions_unittest.cc create mode 100644 src/lib/dns/tests/edns_unittest.cc create mode 100644 src/lib/dns/tests/labelsequence_unittest.cc create mode 100644 src/lib/dns/tests/master_lexer_inputsource_unittest.cc create mode 100644 src/lib/dns/tests/master_lexer_state_unittest.cc create mode 100644 src/lib/dns/tests/master_lexer_token_unittest.cc create mode 100644 src/lib/dns/tests/master_lexer_unittest.cc create mode 100644 src/lib/dns/tests/master_loader_callbacks_test.cc create mode 100644 src/lib/dns/tests/master_loader_unittest.cc create mode 100644 src/lib/dns/tests/masterload_unittest.cc create mode 100644 src/lib/dns/tests/message_unittest.cc create mode 100644 src/lib/dns/tests/messagerenderer_unittest.cc create mode 100644 src/lib/dns/tests/name_unittest.cc create mode 100644 src/lib/dns/tests/nsec3hash_unittest.cc create mode 100644 src/lib/dns/tests/opcode_unittest.cc create mode 100644 src/lib/dns/tests/qid_gen_unittest.cc create mode 100644 src/lib/dns/tests/question_unittest.cc create mode 100644 src/lib/dns/tests/rcode_unittest.cc create mode 100644 src/lib/dns/tests/rdata_afsdb_unittest.cc create mode 100644 src/lib/dns/tests/rdata_caa_unittest.cc create mode 100644 src/lib/dns/tests/rdata_char_string_data_unittest.cc create mode 100644 src/lib/dns/tests/rdata_char_string_unittest.cc create mode 100644 src/lib/dns/tests/rdata_cname_unittest.cc create mode 100644 src/lib/dns/tests/rdata_dhcid_unittest.cc create mode 100644 src/lib/dns/tests/rdata_dname_unittest.cc create mode 100644 src/lib/dns/tests/rdata_dnskey_unittest.cc create mode 100644 src/lib/dns/tests/rdata_ds_like_unittest.cc create mode 100644 src/lib/dns/tests/rdata_hinfo_unittest.cc create mode 100644 src/lib/dns/tests/rdata_in_a_unittest.cc create mode 100644 src/lib/dns/tests/rdata_in_aaaa_unittest.cc create mode 100644 src/lib/dns/tests/rdata_minfo_unittest.cc create mode 100644 src/lib/dns/tests/rdata_mx_unittest.cc create mode 100644 src/lib/dns/tests/rdata_naptr_unittest.cc create mode 100644 src/lib/dns/tests/rdata_ns_unittest.cc create mode 100644 src/lib/dns/tests/rdata_nsec3_unittest.cc create mode 100644 src/lib/dns/tests/rdata_nsec3param_like_unittest.cc create mode 100644 src/lib/dns/tests/rdata_nsec3param_unittest.cc create mode 100644 src/lib/dns/tests/rdata_nsec_unittest.cc create mode 100644 src/lib/dns/tests/rdata_nsecbitmap_unittest.cc create mode 100644 src/lib/dns/tests/rdata_opt_unittest.cc create mode 100644 src/lib/dns/tests/rdata_pimpl_holder_unittest.cc create mode 100644 src/lib/dns/tests/rdata_ptr_unittest.cc create mode 100644 src/lib/dns/tests/rdata_rp_unittest.cc create mode 100644 src/lib/dns/tests/rdata_rrsig_unittest.cc create mode 100644 src/lib/dns/tests/rdata_soa_unittest.cc create mode 100644 src/lib/dns/tests/rdata_srv_unittest.cc create mode 100644 src/lib/dns/tests/rdata_sshfp_unittest.cc create mode 100644 src/lib/dns/tests/rdata_tkey_unittest.cc create mode 100644 src/lib/dns/tests/rdata_tlsa_unittest.cc create mode 100644 src/lib/dns/tests/rdata_tsig_unittest.cc create mode 100644 src/lib/dns/tests/rdata_txt_like_unittest.cc create mode 100644 src/lib/dns/tests/rdata_unittest.cc create mode 100644 src/lib/dns/tests/rdata_unittest.h create mode 100644 src/lib/dns/tests/rdatafields_unittest.cc create mode 100644 src/lib/dns/tests/rrclass_unittest.cc create mode 100644 src/lib/dns/tests/rrcollator_unittest.cc create mode 100644 src/lib/dns/tests/rrparamregistry_unittest.cc create mode 100644 src/lib/dns/tests/rrset_collection_unittest.cc create mode 100644 src/lib/dns/tests/rrset_unittest.cc create mode 100644 src/lib/dns/tests/rrttl_unittest.cc create mode 100644 src/lib/dns/tests/rrtype_unittest.cc create mode 100644 src/lib/dns/tests/run_unittests.cc create mode 100644 src/lib/dns/tests/serial_unittest.cc create mode 100644 src/lib/dns/tests/testdata/Makefile.am create mode 100644 src/lib/dns/tests/testdata/Makefile.in create mode 100644 src/lib/dns/tests/testdata/broken.zone create mode 100644 src/lib/dns/tests/testdata/edns_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/edns_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/edns_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/edns_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/edns_toWire3.spec create mode 100644 src/lib/dns/tests/testdata/edns_toWire3.wire create mode 100644 src/lib/dns/tests/testdata/edns_toWire4.spec create mode 100644 src/lib/dns/tests/testdata/edns_toWire4.wire create mode 100644 src/lib/dns/tests/testdata/example.org create mode 100644 src/lib/dns/tests/testdata/masterload.txt create mode 100644 src/lib/dns/tests/testdata/message_fromWire1 create mode 100644 src/lib/dns/tests/testdata/message_fromWire10.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire10.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire11.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire11.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire12.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire12.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire13.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire13.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire14.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire14.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire15.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire15.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire16.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire16.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire17.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire17.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire18.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire18.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire19.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire19.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire2 create mode 100644 src/lib/dns/tests/testdata/message_fromWire20.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire20.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire21.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire21.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire22.spec create mode 100644 src/lib/dns/tests/testdata/message_fromWire22.wire create mode 100644 src/lib/dns/tests/testdata/message_fromWire3 create mode 100644 src/lib/dns/tests/testdata/message_fromWire4 create mode 100644 src/lib/dns/tests/testdata/message_fromWire5 create mode 100644 src/lib/dns/tests/testdata/message_fromWire6 create mode 100644 src/lib/dns/tests/testdata/message_fromWire7 create mode 100644 src/lib/dns/tests/testdata/message_fromWire8 create mode 100644 src/lib/dns/tests/testdata/message_fromWire9 create mode 100644 src/lib/dns/tests/testdata/message_toText1.spec create mode 100644 src/lib/dns/tests/testdata/message_toText1.txt create mode 100644 src/lib/dns/tests/testdata/message_toText1.wire create mode 100644 src/lib/dns/tests/testdata/message_toText2.spec create mode 100644 src/lib/dns/tests/testdata/message_toText2.txt create mode 100644 src/lib/dns/tests/testdata/message_toText2.wire create mode 100644 src/lib/dns/tests/testdata/message_toText3.spec create mode 100644 src/lib/dns/tests/testdata/message_toText3.txt create mode 100644 src/lib/dns/tests/testdata/message_toText3.wire create mode 100644 src/lib/dns/tests/testdata/message_toWire1 create mode 100644 src/lib/dns/tests/testdata/message_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/message_toWire3.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire3.wire create mode 100644 src/lib/dns/tests/testdata/message_toWire4.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire4.wire create mode 100644 src/lib/dns/tests/testdata/message_toWire5.spec create mode 100644 src/lib/dns/tests/testdata/message_toWire5.wire create mode 100644 src/lib/dns/tests/testdata/message_toWire6 create mode 100644 src/lib/dns/tests/testdata/message_toWire7 create mode 100644 src/lib/dns/tests/testdata/name_fromWire1 create mode 100644 src/lib/dns/tests/testdata/name_fromWire10 create mode 100644 src/lib/dns/tests/testdata/name_fromWire11 create mode 100644 src/lib/dns/tests/testdata/name_fromWire12 create mode 100644 src/lib/dns/tests/testdata/name_fromWire13 create mode 100644 src/lib/dns/tests/testdata/name_fromWire14 create mode 100644 src/lib/dns/tests/testdata/name_fromWire2 create mode 100644 src/lib/dns/tests/testdata/name_fromWire3_1 create mode 100644 src/lib/dns/tests/testdata/name_fromWire3_2 create mode 100644 src/lib/dns/tests/testdata/name_fromWire4 create mode 100644 src/lib/dns/tests/testdata/name_fromWire6 create mode 100644 src/lib/dns/tests/testdata/name_fromWire7 create mode 100644 src/lib/dns/tests/testdata/name_fromWire8 create mode 100644 src/lib/dns/tests/testdata/name_fromWire9 create mode 100644 src/lib/dns/tests/testdata/name_toWire1 create mode 100644 src/lib/dns/tests/testdata/name_toWire2 create mode 100644 src/lib/dns/tests/testdata/name_toWire3 create mode 100644 src/lib/dns/tests/testdata/name_toWire4 create mode 100644 src/lib/dns/tests/testdata/name_toWire5.spec create mode 100644 src/lib/dns/tests/testdata/name_toWire5.wire create mode 100644 src/lib/dns/tests/testdata/name_toWire6.spec create mode 100644 src/lib/dns/tests/testdata/name_toWire6.wire create mode 100644 src/lib/dns/tests/testdata/name_toWire7 create mode 100644 src/lib/dns/tests/testdata/name_toWire8 create mode 100644 src/lib/dns/tests/testdata/name_toWire9 create mode 100644 src/lib/dns/tests/testdata/omitcheck.txt create mode 100644 src/lib/dns/tests/testdata/origincheck.txt create mode 100644 src/lib/dns/tests/testdata/question_fromWire create mode 100644 src/lib/dns/tests/testdata/question_toWire1 create mode 100644 src/lib/dns/tests/testdata/question_toWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire5 create mode 100644 src/lib/dns/tests/testdata/rdata_caa_fromWire6 create mode 100644 src/lib/dns/tests/testdata/rdata_cname_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_dhcid_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_dhcid_toWire create mode 100644 src/lib/dns/tests/testdata/rdata_dname_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec create mode 100644 src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire create mode 100644 src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec create mode 100644 src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire create mode 100644 src/lib/dns/tests/testdata/rdata_ds_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_in_a_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_in_aaaa_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_mx_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_mx_toWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_mx_toWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_ns_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire3 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire create mode 100644 src/lib/dns/tests/testdata/rdata_opt_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_opt_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_opt_fromWire3 create mode 100644 src/lib/dns/tests/testdata/rdata_opt_fromWire4 create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_rrsig_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_soa_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.spec create mode 100644 src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.wire create mode 100644 src/lib/dns/tests/testdata/rdata_srv_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire9.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_fromWire9.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tkey_toWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire6.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire7.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire8.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_fromWire9.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_tsig_toWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire2.spec create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire2.wire create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire3.spec create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire3.wire create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire4.spec create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire4.wire create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire5.spec create mode 100644 src/lib/dns/tests/testdata/rdata_txt_fromWire5.wire create mode 100644 src/lib/dns/tests/testdata/rdata_unknown_fromWire create mode 100644 src/lib/dns/tests/testdata/rdatafields1.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields1.wire create mode 100644 src/lib/dns/tests/testdata/rdatafields2.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields2.wire create mode 100644 src/lib/dns/tests/testdata/rdatafields3.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields3.wire create mode 100644 src/lib/dns/tests/testdata/rdatafields4.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields4.wire create mode 100644 src/lib/dns/tests/testdata/rdatafields5.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields5.wire create mode 100644 src/lib/dns/tests/testdata/rdatafields6.spec create mode 100644 src/lib/dns/tests/testdata/rdatafields6.wire create mode 100644 src/lib/dns/tests/testdata/rrcode16_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rrcode16_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rrcode32_fromWire1 create mode 100644 src/lib/dns/tests/testdata/rrcode32_fromWire2 create mode 100644 src/lib/dns/tests/testdata/rrset_toWire1 create mode 100644 src/lib/dns/tests/testdata/rrset_toWire2 create mode 100644 src/lib/dns/tests/testdata/rrset_toWire3 create mode 100644 src/lib/dns/tests/testdata/rrset_toWire4 create mode 100644 src/lib/dns/tests/testdata/tsig_verify1.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify1.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify10.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify10.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify11.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify11.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify2.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify2.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify3.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify3.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify4.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify4.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify5.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify5.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify6.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify6.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify7.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify7.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify8.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify8.wire create mode 100644 src/lib/dns/tests/testdata/tsig_verify9.spec create mode 100644 src/lib/dns/tests/testdata/tsig_verify9.wire create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire1.spec create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire1.wire create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire2.spec create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire2.wire create mode 100644 src/lib/dns/tests/tsig_unittest.cc create mode 100644 src/lib/dns/tests/tsigerror_unittest.cc create mode 100644 src/lib/dns/tests/tsigkey_unittest.cc create mode 100644 src/lib/dns/tests/tsigrecord_unittest.cc create mode 100644 src/lib/dns/tests/unittest_util.cc create mode 100644 src/lib/dns/tests/unittest_util.h create mode 100644 src/lib/dns/tests/zone_checker_unittest.cc create mode 100644 src/lib/dns/tsig.cc create mode 100644 src/lib/dns/tsig.h create mode 100644 src/lib/dns/tsigerror.cc create mode 100644 src/lib/dns/tsigerror.h create mode 100644 src/lib/dns/tsigkey.cc create mode 100644 src/lib/dns/tsigkey.h create mode 100644 src/lib/dns/tsigrecord.cc create mode 100644 src/lib/dns/tsigrecord.h create mode 100644 src/lib/dns/zone_checker.cc create mode 100644 src/lib/dns/zone_checker.h (limited to 'src/lib/dns') diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am new file mode 100644 index 0000000..4485dcb --- /dev/null +++ b/src/lib/dns/Makefile.am @@ -0,0 +1,226 @@ +AUTOMAKE_OPTIONS = subdir-objects + +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda +CLEANFILES += s-rdatacode +# These two are created with rrtype/class.h, so not explicitly listed in +# BUILT_SOURCES. +CLEANFILES += python/rrtype_constants_inc.cc +CLEANFILES += python/rrclass_constants_inc.cc + +DISTCLEANFILES = gen-rdatacode.py + +EXTRA_DIST = rrclass-placeholder.h +EXTRA_DIST += rrparamregistry-placeholder.cc +EXTRA_DIST += rrtype-placeholder.h + +# TODO: double-check that this is the only way +# NOTE: when an rdata file is added, please also add to this list: +EXTRA_DIST += rdata/any_255/tsig_250.cc +EXTRA_DIST += rdata/any_255/tsig_250.h +EXTRA_DIST += rdata/ch_3/a_1.cc +EXTRA_DIST += rdata/ch_3/a_1.h +EXTRA_DIST += rdata/generic/cname_5.cc +EXTRA_DIST += rdata/generic/cname_5.h +EXTRA_DIST += rdata/generic/detail/char_string.cc +EXTRA_DIST += rdata/generic/detail/char_string.h +EXTRA_DIST += rdata/generic/detail/lexer_util.h +EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc +EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h +EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc +EXTRA_DIST += rdata/generic/detail/nsec3param_common.h +EXTRA_DIST += rdata/generic/detail/txt_like.h +EXTRA_DIST += rdata/generic/detail/ds_like.h +EXTRA_DIST += rdata/generic/dlv_32769.cc +EXTRA_DIST += rdata/generic/dlv_32769.h +EXTRA_DIST += rdata/generic/dname_39.cc +EXTRA_DIST += rdata/generic/dname_39.h +EXTRA_DIST += rdata/generic/dnskey_48.cc +EXTRA_DIST += rdata/generic/dnskey_48.h +EXTRA_DIST += rdata/generic/ds_43.cc +EXTRA_DIST += rdata/generic/ds_43.h +EXTRA_DIST += rdata/generic/hinfo_13.cc +EXTRA_DIST += rdata/generic/hinfo_13.h +EXTRA_DIST += rdata/generic/mx_15.cc +EXTRA_DIST += rdata/generic/mx_15.h +EXTRA_DIST += rdata/generic/naptr_35.cc +EXTRA_DIST += rdata/generic/naptr_35.h +EXTRA_DIST += rdata/generic/ns_2.cc +EXTRA_DIST += rdata/generic/ns_2.h +EXTRA_DIST += rdata/generic/nsec3_50.cc +EXTRA_DIST += rdata/generic/nsec3_50.h +EXTRA_DIST += rdata/generic/nsec3param_51.cc +EXTRA_DIST += rdata/generic/nsec3param_51.h +EXTRA_DIST += rdata/generic/nsec_47.cc +EXTRA_DIST += rdata/generic/nsec_47.h +EXTRA_DIST += rdata/generic/opt_41.cc +EXTRA_DIST += rdata/generic/opt_41.h +EXTRA_DIST += rdata/generic/ptr_12.cc +EXTRA_DIST += rdata/generic/ptr_12.h +EXTRA_DIST += rdata/generic/rp_17.cc +EXTRA_DIST += rdata/generic/rp_17.h +EXTRA_DIST += rdata/generic/rrsig_46.cc +EXTRA_DIST += rdata/generic/rrsig_46.h +EXTRA_DIST += rdata/generic/soa_6.cc +EXTRA_DIST += rdata/generic/soa_6.h +EXTRA_DIST += rdata/generic/spf_99.cc +EXTRA_DIST += rdata/generic/spf_99.h +EXTRA_DIST += rdata/generic/sshfp_44.cc +EXTRA_DIST += rdata/generic/sshfp_44.h +EXTRA_DIST += rdata/generic/tlsa_52.cc +EXTRA_DIST += rdata/generic/tlsa_52.h +EXTRA_DIST += rdata/generic/tkey_249.cc +EXTRA_DIST += rdata/generic/tkey_249.h +EXTRA_DIST += rdata/generic/txt_16.cc +EXTRA_DIST += rdata/generic/txt_16.h +EXTRA_DIST += rdata/generic/minfo_14.cc +EXTRA_DIST += rdata/generic/minfo_14.h +EXTRA_DIST += rdata/generic/afsdb_18.cc +EXTRA_DIST += rdata/generic/afsdb_18.h +EXTRA_DIST += rdata/generic/caa_257.cc +EXTRA_DIST += rdata/generic/caa_257.h +EXTRA_DIST += rdata/hs_4/a_1.cc +EXTRA_DIST += rdata/hs_4/a_1.h +EXTRA_DIST += rdata/in_1/a_1.cc +EXTRA_DIST += rdata/in_1/a_1.h +EXTRA_DIST += rdata/in_1/aaaa_28.cc +EXTRA_DIST += rdata/in_1/aaaa_28.h +EXTRA_DIST += rdata/in_1/dhcid_49.cc +EXTRA_DIST += rdata/in_1/dhcid_49.h +EXTRA_DIST += rdata/in_1/srv_33.cc +EXTRA_DIST += rdata/in_1/srv_33.h +EXTRA_DIST += rdata/template.cc +EXTRA_DIST += rdata/template.h + +noinst_SCRIPTS = gen-rdatacode.py + +# auto-generate by gen-rdatacode.py: +BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc +BUILT_SOURCES += rdataclass.h rdataclass.cc + +lib_LTLIBRARIES = libkea-dns++.la + +libkea_dns___la_LDFLAGS = -no-undefined -version-info 30:0:0 +libkea_dns___la_LDFLAGS += $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +libkea_dns___la_SOURCES = +libkea_dns___la_SOURCES += dns_fwd.h +libkea_dns___la_SOURCES += edns.h edns.cc +libkea_dns___la_SOURCES += exceptions.h exceptions.cc +libkea_dns___la_SOURCES += master_lexer_inputsource.h master_lexer_inputsource.cc +libkea_dns___la_SOURCES += labelsequence.h labelsequence.cc +libkea_dns___la_SOURCES += masterload.h masterload.cc +libkea_dns___la_SOURCES += master_lexer.h master_lexer.cc +libkea_dns___la_SOURCES += master_lexer_state.h +libkea_dns___la_SOURCES += master_loader.h master_loader.cc +libkea_dns___la_SOURCES += message.h message.cc +libkea_dns___la_SOURCES += messagerenderer.h messagerenderer.cc +libkea_dns___la_SOURCES += name.h name.cc +libkea_dns___la_SOURCES += name_internal.h +libkea_dns___la_SOURCES += nsec3hash.h nsec3hash.cc +libkea_dns___la_SOURCES += opcode.h opcode.cc +libkea_dns___la_SOURCES += rcode.h rcode.cc +libkea_dns___la_SOURCES += rdata.h rdata.cc +libkea_dns___la_SOURCES += rdatafields.h rdatafields.cc +libkea_dns___la_SOURCES += rrclass.cc +libkea_dns___la_SOURCES += rrparamregistry.h +libkea_dns___la_SOURCES += rrset.h rrset.cc +libkea_dns___la_SOURCES += rrttl.h rrttl.cc +libkea_dns___la_SOURCES += rrtype.cc +libkea_dns___la_SOURCES += rrcollator.h rrcollator.cc +libkea_dns___la_SOURCES += qid_gen.h qid_gen.cc +libkea_dns___la_SOURCES += question.h question.cc +libkea_dns___la_SOURCES += serial.h serial.cc +libkea_dns___la_SOURCES += tsig.h tsig.cc +libkea_dns___la_SOURCES += tsigerror.h tsigerror.cc +libkea_dns___la_SOURCES += tsigkey.h tsigkey.cc +libkea_dns___la_SOURCES += tsigrecord.h tsigrecord.cc +libkea_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc +libkea_dns___la_SOURCES += master_loader.h +libkea_dns___la_SOURCES += rrset_collection_base.h +libkea_dns___la_SOURCES += rrset_collection.h rrset_collection.cc +libkea_dns___la_SOURCES += zone_checker.h zone_checker.cc +libkea_dns___la_SOURCES += rdata_pimpl_holder.h +libkea_dns___la_SOURCES += rdata/generic/detail/char_string.h +libkea_dns___la_SOURCES += rdata/generic/detail/char_string.cc +libkea_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h +libkea_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc +libkea_dns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc +libkea_dns___la_SOURCES += rdata/generic/detail/nsec3param_common.h +libkea_dns___la_SOURCES += rdata/generic/detail/txt_like.h +libkea_dns___la_SOURCES += rdata/generic/detail/ds_like.h + +libkea_dns___la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_dns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libkea_dns___la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_dns___la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_dns___la_LIBADD += $(CRYPTO_LIBS) + +# The following files used to be generated, but they are now part of the git tree: +# rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc +libkea_dns___la_SOURCES += rdataclass.h rrclass.h rrtype.h +libkea_dns___la_SOURCES += rdataclass.cc rrparamregistry.cc + +rrclass.h: rrclass-placeholder.h +rrtype.h: rrtype-placeholder.h +rrparamregistry.cc: rrparamregistry-placeholder.cc + +s-rdatacode: Makefile $(EXTRA_DIST) + $(PYTHON) ./gen-rdatacode.py + touch $@ + +# In ticket #3413 we removed the whole BIND10/Bundy framework. We also want +# to not require Python3, hence instead of generating the code every time, +# we added the generated files to our repo. It is still possible to regenerate +# those files, but that step is no longer required for successful compilation. + +#rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: s-rdatacode + +libdns___includedir = $(pkgincludedir)/dns +libdns___include_HEADERS = \ + dns_fwd.h \ + edns.h \ + exceptions.h \ + labelsequence.h \ + master_lexer.h \ + master_lexer_inputsource.h \ + master_lexer_state.h \ + master_loader.h \ + master_loader_callbacks.h \ + masterload.h \ + message.h \ + messagerenderer.h \ + name.h \ + nsec3hash.h \ + opcode.h \ + qid_gen.h \ + question.h \ + rcode.h \ + rdata.h \ + rdata_pimpl_holder.h \ + rdataclass.h \ + rdatafields.h \ + rrclass.h \ + rrcollator.h \ + rrparamregistry.h \ + rrset.h \ + rrset_collection.h \ + rrset_collection_base.h \ + rrttl.h \ + rrtype.h \ + serial.h \ + tsig.h \ + tsigerror.h \ + tsigkey.h \ + tsigrecord.h \ + zone_checker.h +# Purposely not installing these headers: +# name_internal.h: used only internally, and not actually DNS specific +# rdata/*/detail/*.h: these are internal use only +# rrclass-placeholder.h +# rrtype-placeholder.h diff --git a/src/lib/dns/Makefile.in b/src/lib/dns/Makefile.in new file mode 100644 index 0000000..26465bd --- /dev/null +++ b/src/lib/dns/Makefile.in @@ -0,0 +1,1497 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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@ +subdir = src/lib/dns +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(libdns___include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = gen-rdatacode.py +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libdns___includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_dns___la_DEPENDENCIES = \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) +am__dirstamp = $(am__leading_dot)dirstamp +am_libkea_dns___la_OBJECTS = libkea_dns___la-edns.lo \ + libkea_dns___la-exceptions.lo \ + libkea_dns___la-master_lexer_inputsource.lo \ + libkea_dns___la-labelsequence.lo libkea_dns___la-masterload.lo \ + libkea_dns___la-master_lexer.lo \ + libkea_dns___la-master_loader.lo libkea_dns___la-message.lo \ + libkea_dns___la-messagerenderer.lo libkea_dns___la-name.lo \ + libkea_dns___la-nsec3hash.lo libkea_dns___la-opcode.lo \ + libkea_dns___la-rcode.lo libkea_dns___la-rdata.lo \ + libkea_dns___la-rdatafields.lo libkea_dns___la-rrclass.lo \ + libkea_dns___la-rrset.lo libkea_dns___la-rrttl.lo \ + libkea_dns___la-rrtype.lo libkea_dns___la-rrcollator.lo \ + libkea_dns___la-qid_gen.lo libkea_dns___la-question.lo \ + libkea_dns___la-serial.lo libkea_dns___la-tsig.lo \ + libkea_dns___la-tsigerror.lo libkea_dns___la-tsigkey.lo \ + libkea_dns___la-tsigrecord.lo \ + libkea_dns___la-master_loader_callbacks.lo \ + libkea_dns___la-rrset_collection.lo \ + libkea_dns___la-zone_checker.lo \ + rdata/generic/detail/libkea_dns___la-char_string.lo \ + rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo \ + rdata/generic/detail/libkea_dns___la-nsec3param_common.lo \ + libkea_dns___la-rdataclass.lo \ + libkea_dns___la-rrparamregistry.lo +libkea_dns___la_OBJECTS = $(am_libkea_dns___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 = +libkea_dns___la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libkea_dns___la_LDFLAGS) \ + $(LDFLAGS) -o $@ +SCRIPTS = $(noinst_SCRIPTS) +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) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libkea_dns___la-edns.Plo \ + ./$(DEPDIR)/libkea_dns___la-exceptions.Plo \ + ./$(DEPDIR)/libkea_dns___la-labelsequence.Plo \ + ./$(DEPDIR)/libkea_dns___la-master_lexer.Plo \ + ./$(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Plo \ + ./$(DEPDIR)/libkea_dns___la-master_loader.Plo \ + ./$(DEPDIR)/libkea_dns___la-master_loader_callbacks.Plo \ + ./$(DEPDIR)/libkea_dns___la-masterload.Plo \ + ./$(DEPDIR)/libkea_dns___la-message.Plo \ + ./$(DEPDIR)/libkea_dns___la-messagerenderer.Plo \ + ./$(DEPDIR)/libkea_dns___la-name.Plo \ + ./$(DEPDIR)/libkea_dns___la-nsec3hash.Plo \ + ./$(DEPDIR)/libkea_dns___la-opcode.Plo \ + ./$(DEPDIR)/libkea_dns___la-qid_gen.Plo \ + ./$(DEPDIR)/libkea_dns___la-question.Plo \ + ./$(DEPDIR)/libkea_dns___la-rcode.Plo \ + ./$(DEPDIR)/libkea_dns___la-rdata.Plo \ + ./$(DEPDIR)/libkea_dns___la-rdataclass.Plo \ + ./$(DEPDIR)/libkea_dns___la-rdatafields.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrclass.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrcollator.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrparamregistry.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrset.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrset_collection.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrttl.Plo \ + ./$(DEPDIR)/libkea_dns___la-rrtype.Plo \ + ./$(DEPDIR)/libkea_dns___la-serial.Plo \ + ./$(DEPDIR)/libkea_dns___la-tsig.Plo \ + ./$(DEPDIR)/libkea_dns___la-tsigerror.Plo \ + ./$(DEPDIR)/libkea_dns___la-tsigkey.Plo \ + ./$(DEPDIR)/libkea_dns___la-tsigrecord.Plo \ + ./$(DEPDIR)/libkea_dns___la-zone_checker.Plo \ + rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Plo \ + rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Plo \ + rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +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 = $(libkea_dns___la_SOURCES) +DIST_SOURCES = $(libkea_dns___la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libdns___include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/gen-rdatacode.py.in \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +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@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +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_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = subdir-objects +SUBDIRS = . tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +# These two are created with rrtype/class.h, so not explicitly listed in +# BUILT_SOURCES. +CLEANFILES = *.gcno *.gcda s-rdatacode python/rrtype_constants_inc.cc \ + python/rrclass_constants_inc.cc +DISTCLEANFILES = gen-rdatacode.py + +# TODO: double-check that this is the only way +# NOTE: when an rdata file is added, please also add to this list: +EXTRA_DIST = rrclass-placeholder.h rrparamregistry-placeholder.cc \ + rrtype-placeholder.h rdata/any_255/tsig_250.cc \ + rdata/any_255/tsig_250.h rdata/ch_3/a_1.cc rdata/ch_3/a_1.h \ + rdata/generic/cname_5.cc rdata/generic/cname_5.h \ + rdata/generic/detail/char_string.cc \ + rdata/generic/detail/char_string.h \ + rdata/generic/detail/lexer_util.h \ + rdata/generic/detail/nsec_bitmap.cc \ + rdata/generic/detail/nsec_bitmap.h \ + rdata/generic/detail/nsec3param_common.cc \ + rdata/generic/detail/nsec3param_common.h \ + rdata/generic/detail/txt_like.h rdata/generic/detail/ds_like.h \ + rdata/generic/dlv_32769.cc rdata/generic/dlv_32769.h \ + rdata/generic/dname_39.cc rdata/generic/dname_39.h \ + rdata/generic/dnskey_48.cc rdata/generic/dnskey_48.h \ + rdata/generic/ds_43.cc rdata/generic/ds_43.h \ + rdata/generic/hinfo_13.cc rdata/generic/hinfo_13.h \ + rdata/generic/mx_15.cc rdata/generic/mx_15.h \ + rdata/generic/naptr_35.cc rdata/generic/naptr_35.h \ + rdata/generic/ns_2.cc rdata/generic/ns_2.h \ + rdata/generic/nsec3_50.cc rdata/generic/nsec3_50.h \ + rdata/generic/nsec3param_51.cc rdata/generic/nsec3param_51.h \ + rdata/generic/nsec_47.cc rdata/generic/nsec_47.h \ + rdata/generic/opt_41.cc rdata/generic/opt_41.h \ + rdata/generic/ptr_12.cc rdata/generic/ptr_12.h \ + rdata/generic/rp_17.cc rdata/generic/rp_17.h \ + rdata/generic/rrsig_46.cc rdata/generic/rrsig_46.h \ + rdata/generic/soa_6.cc rdata/generic/soa_6.h \ + rdata/generic/spf_99.cc rdata/generic/spf_99.h \ + rdata/generic/sshfp_44.cc rdata/generic/sshfp_44.h \ + rdata/generic/tlsa_52.cc rdata/generic/tlsa_52.h \ + rdata/generic/tkey_249.cc rdata/generic/tkey_249.h \ + rdata/generic/txt_16.cc rdata/generic/txt_16.h \ + rdata/generic/minfo_14.cc rdata/generic/minfo_14.h \ + rdata/generic/afsdb_18.cc rdata/generic/afsdb_18.h \ + rdata/generic/caa_257.cc rdata/generic/caa_257.h \ + rdata/hs_4/a_1.cc rdata/hs_4/a_1.h rdata/in_1/a_1.cc \ + rdata/in_1/a_1.h rdata/in_1/aaaa_28.cc rdata/in_1/aaaa_28.h \ + rdata/in_1/dhcid_49.cc rdata/in_1/dhcid_49.h \ + rdata/in_1/srv_33.cc rdata/in_1/srv_33.h rdata/template.cc \ + rdata/template.h +noinst_SCRIPTS = gen-rdatacode.py + +# auto-generate by gen-rdatacode.py: +BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc rdataclass.h \ + rdataclass.cc +lib_LTLIBRARIES = libkea-dns++.la +libkea_dns___la_LDFLAGS = -no-undefined -version-info 30:0:0 \ + $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +# The following files used to be generated, but they are now part of the git tree: +# rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc +libkea_dns___la_SOURCES = dns_fwd.h edns.h edns.cc exceptions.h \ + exceptions.cc master_lexer_inputsource.h \ + master_lexer_inputsource.cc labelsequence.h labelsequence.cc \ + masterload.h masterload.cc master_lexer.h master_lexer.cc \ + master_lexer_state.h master_loader.h master_loader.cc \ + message.h message.cc messagerenderer.h messagerenderer.cc \ + name.h name.cc name_internal.h nsec3hash.h nsec3hash.cc \ + opcode.h opcode.cc rcode.h rcode.cc rdata.h rdata.cc \ + rdatafields.h rdatafields.cc rrclass.cc rrparamregistry.h \ + rrset.h rrset.cc rrttl.h rrttl.cc rrtype.cc rrcollator.h \ + rrcollator.cc qid_gen.h qid_gen.cc question.h question.cc \ + serial.h serial.cc tsig.h tsig.cc tsigerror.h tsigerror.cc \ + tsigkey.h tsigkey.cc tsigrecord.h tsigrecord.cc \ + master_loader_callbacks.h master_loader_callbacks.cc \ + master_loader.h rrset_collection_base.h rrset_collection.h \ + rrset_collection.cc zone_checker.h zone_checker.cc \ + rdata_pimpl_holder.h rdata/generic/detail/char_string.h \ + rdata/generic/detail/char_string.cc \ + rdata/generic/detail/nsec_bitmap.h \ + rdata/generic/detail/nsec_bitmap.cc \ + rdata/generic/detail/nsec3param_common.cc \ + rdata/generic/detail/nsec3param_common.h \ + rdata/generic/detail/txt_like.h rdata/generic/detail/ds_like.h \ + rdataclass.h rrclass.h rrtype.h rdataclass.cc \ + rrparamregistry.cc +libkea_dns___la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_dns___la_LIBADD = \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(CRYPTO_LIBS) + +# In ticket #3413 we removed the whole BIND10/Bundy framework. We also want +# to not require Python3, hence instead of generating the code every time, +# we added the generated files to our repo. It is still possible to regenerate +# those files, but that step is no longer required for successful compilation. + +#rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: s-rdatacode +libdns___includedir = $(pkgincludedir)/dns +libdns___include_HEADERS = \ + dns_fwd.h \ + edns.h \ + exceptions.h \ + labelsequence.h \ + master_lexer.h \ + master_lexer_inputsource.h \ + master_lexer_state.h \ + master_loader.h \ + master_loader_callbacks.h \ + masterload.h \ + message.h \ + messagerenderer.h \ + name.h \ + nsec3hash.h \ + opcode.h \ + qid_gen.h \ + question.h \ + rcode.h \ + rdata.h \ + rdata_pimpl_holder.h \ + rdataclass.h \ + rdatafields.h \ + rrclass.h \ + rrcollator.h \ + rrparamregistry.h \ + rrset.h \ + rrset_collection.h \ + rrset_collection_base.h \ + rrttl.h \ + rrtype.h \ + serial.h \ + tsig.h \ + tsigerror.h \ + tsigkey.h \ + tsigrecord.h \ + zone_checker.h + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .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 src/lib/dns/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/dns/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): +gen-rdatacode.py: $(top_builddir)/config.status $(srcdir)/gen-rdatacode.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_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}; \ + } +rdata/generic/detail/$(am__dirstamp): + @$(MKDIR_P) rdata/generic/detail + @: > rdata/generic/detail/$(am__dirstamp) +rdata/generic/detail/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) rdata/generic/detail/$(DEPDIR) + @: > rdata/generic/detail/$(DEPDIR)/$(am__dirstamp) +rdata/generic/detail/libkea_dns___la-char_string.lo: \ + rdata/generic/detail/$(am__dirstamp) \ + rdata/generic/detail/$(DEPDIR)/$(am__dirstamp) +rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo: \ + rdata/generic/detail/$(am__dirstamp) \ + rdata/generic/detail/$(DEPDIR)/$(am__dirstamp) +rdata/generic/detail/libkea_dns___la-nsec3param_common.lo: \ + rdata/generic/detail/$(am__dirstamp) \ + rdata/generic/detail/$(DEPDIR)/$(am__dirstamp) + +libkea-dns++.la: $(libkea_dns___la_OBJECTS) $(libkea_dns___la_DEPENDENCIES) $(EXTRA_libkea_dns___la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_dns___la_LINK) -rpath $(libdir) $(libkea_dns___la_OBJECTS) $(libkea_dns___la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f rdata/generic/detail/*.$(OBJEXT) + -rm -f rdata/generic/detail/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-edns.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-exceptions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-labelsequence.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-master_lexer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-master_loader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-master_loader_callbacks.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-masterload.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-message.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-messagerenderer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-name.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-nsec3hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-opcode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-qid_gen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-question.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rcode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rdata.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rdataclass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rdatafields.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrclass.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrcollator.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrparamregistry.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrset_collection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrttl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-rrtype.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-serial.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-tsig.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-tsigerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-tsigkey.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-tsigrecord.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_dns___la-zone_checker.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libkea_dns___la-edns.lo: edns.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-edns.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-edns.Tpo -c -o libkea_dns___la-edns.lo `test -f 'edns.cc' || echo '$(srcdir)/'`edns.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-edns.Tpo $(DEPDIR)/libkea_dns___la-edns.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='edns.cc' object='libkea_dns___la-edns.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-edns.lo `test -f 'edns.cc' || echo '$(srcdir)/'`edns.cc + +libkea_dns___la-exceptions.lo: exceptions.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-exceptions.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-exceptions.Tpo -c -o libkea_dns___la-exceptions.lo `test -f 'exceptions.cc' || echo '$(srcdir)/'`exceptions.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-exceptions.Tpo $(DEPDIR)/libkea_dns___la-exceptions.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='exceptions.cc' object='libkea_dns___la-exceptions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-exceptions.lo `test -f 'exceptions.cc' || echo '$(srcdir)/'`exceptions.cc + +libkea_dns___la-master_lexer_inputsource.lo: master_lexer_inputsource.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-master_lexer_inputsource.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Tpo -c -o libkea_dns___la-master_lexer_inputsource.lo `test -f 'master_lexer_inputsource.cc' || echo '$(srcdir)/'`master_lexer_inputsource.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Tpo $(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_inputsource.cc' object='libkea_dns___la-master_lexer_inputsource.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-master_lexer_inputsource.lo `test -f 'master_lexer_inputsource.cc' || echo '$(srcdir)/'`master_lexer_inputsource.cc + +libkea_dns___la-labelsequence.lo: labelsequence.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-labelsequence.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-labelsequence.Tpo -c -o libkea_dns___la-labelsequence.lo `test -f 'labelsequence.cc' || echo '$(srcdir)/'`labelsequence.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-labelsequence.Tpo $(DEPDIR)/libkea_dns___la-labelsequence.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='labelsequence.cc' object='libkea_dns___la-labelsequence.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-labelsequence.lo `test -f 'labelsequence.cc' || echo '$(srcdir)/'`labelsequence.cc + +libkea_dns___la-masterload.lo: masterload.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-masterload.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-masterload.Tpo -c -o libkea_dns___la-masterload.lo `test -f 'masterload.cc' || echo '$(srcdir)/'`masterload.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-masterload.Tpo $(DEPDIR)/libkea_dns___la-masterload.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='masterload.cc' object='libkea_dns___la-masterload.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-masterload.lo `test -f 'masterload.cc' || echo '$(srcdir)/'`masterload.cc + +libkea_dns___la-master_lexer.lo: master_lexer.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-master_lexer.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-master_lexer.Tpo -c -o libkea_dns___la-master_lexer.lo `test -f 'master_lexer.cc' || echo '$(srcdir)/'`master_lexer.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-master_lexer.Tpo $(DEPDIR)/libkea_dns___la-master_lexer.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer.cc' object='libkea_dns___la-master_lexer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-master_lexer.lo `test -f 'master_lexer.cc' || echo '$(srcdir)/'`master_lexer.cc + +libkea_dns___la-master_loader.lo: master_loader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-master_loader.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-master_loader.Tpo -c -o libkea_dns___la-master_loader.lo `test -f 'master_loader.cc' || echo '$(srcdir)/'`master_loader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-master_loader.Tpo $(DEPDIR)/libkea_dns___la-master_loader.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader.cc' object='libkea_dns___la-master_loader.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-master_loader.lo `test -f 'master_loader.cc' || echo '$(srcdir)/'`master_loader.cc + +libkea_dns___la-message.lo: message.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-message.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-message.Tpo -c -o libkea_dns___la-message.lo `test -f 'message.cc' || echo '$(srcdir)/'`message.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-message.Tpo $(DEPDIR)/libkea_dns___la-message.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='message.cc' object='libkea_dns___la-message.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-message.lo `test -f 'message.cc' || echo '$(srcdir)/'`message.cc + +libkea_dns___la-messagerenderer.lo: messagerenderer.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-messagerenderer.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-messagerenderer.Tpo -c -o libkea_dns___la-messagerenderer.lo `test -f 'messagerenderer.cc' || echo '$(srcdir)/'`messagerenderer.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-messagerenderer.Tpo $(DEPDIR)/libkea_dns___la-messagerenderer.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='messagerenderer.cc' object='libkea_dns___la-messagerenderer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-messagerenderer.lo `test -f 'messagerenderer.cc' || echo '$(srcdir)/'`messagerenderer.cc + +libkea_dns___la-name.lo: name.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-name.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-name.Tpo -c -o libkea_dns___la-name.lo `test -f 'name.cc' || echo '$(srcdir)/'`name.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-name.Tpo $(DEPDIR)/libkea_dns___la-name.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='name.cc' object='libkea_dns___la-name.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-name.lo `test -f 'name.cc' || echo '$(srcdir)/'`name.cc + +libkea_dns___la-nsec3hash.lo: nsec3hash.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-nsec3hash.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-nsec3hash.Tpo -c -o libkea_dns___la-nsec3hash.lo `test -f 'nsec3hash.cc' || echo '$(srcdir)/'`nsec3hash.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-nsec3hash.Tpo $(DEPDIR)/libkea_dns___la-nsec3hash.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nsec3hash.cc' object='libkea_dns___la-nsec3hash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-nsec3hash.lo `test -f 'nsec3hash.cc' || echo '$(srcdir)/'`nsec3hash.cc + +libkea_dns___la-opcode.lo: opcode.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-opcode.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-opcode.Tpo -c -o libkea_dns___la-opcode.lo `test -f 'opcode.cc' || echo '$(srcdir)/'`opcode.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-opcode.Tpo $(DEPDIR)/libkea_dns___la-opcode.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='opcode.cc' object='libkea_dns___la-opcode.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-opcode.lo `test -f 'opcode.cc' || echo '$(srcdir)/'`opcode.cc + +libkea_dns___la-rcode.lo: rcode.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rcode.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rcode.Tpo -c -o libkea_dns___la-rcode.lo `test -f 'rcode.cc' || echo '$(srcdir)/'`rcode.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rcode.Tpo $(DEPDIR)/libkea_dns___la-rcode.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rcode.cc' object='libkea_dns___la-rcode.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rcode.lo `test -f 'rcode.cc' || echo '$(srcdir)/'`rcode.cc + +libkea_dns___la-rdata.lo: rdata.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rdata.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rdata.Tpo -c -o libkea_dns___la-rdata.lo `test -f 'rdata.cc' || echo '$(srcdir)/'`rdata.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rdata.Tpo $(DEPDIR)/libkea_dns___la-rdata.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata.cc' object='libkea_dns___la-rdata.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rdata.lo `test -f 'rdata.cc' || echo '$(srcdir)/'`rdata.cc + +libkea_dns___la-rdatafields.lo: rdatafields.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rdatafields.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rdatafields.Tpo -c -o libkea_dns___la-rdatafields.lo `test -f 'rdatafields.cc' || echo '$(srcdir)/'`rdatafields.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rdatafields.Tpo $(DEPDIR)/libkea_dns___la-rdatafields.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdatafields.cc' object='libkea_dns___la-rdatafields.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rdatafields.lo `test -f 'rdatafields.cc' || echo '$(srcdir)/'`rdatafields.cc + +libkea_dns___la-rrclass.lo: rrclass.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrclass.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrclass.Tpo -c -o libkea_dns___la-rrclass.lo `test -f 'rrclass.cc' || echo '$(srcdir)/'`rrclass.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrclass.Tpo $(DEPDIR)/libkea_dns___la-rrclass.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrclass.cc' object='libkea_dns___la-rrclass.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrclass.lo `test -f 'rrclass.cc' || echo '$(srcdir)/'`rrclass.cc + +libkea_dns___la-rrset.lo: rrset.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrset.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrset.Tpo -c -o libkea_dns___la-rrset.lo `test -f 'rrset.cc' || echo '$(srcdir)/'`rrset.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrset.Tpo $(DEPDIR)/libkea_dns___la-rrset.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset.cc' object='libkea_dns___la-rrset.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrset.lo `test -f 'rrset.cc' || echo '$(srcdir)/'`rrset.cc + +libkea_dns___la-rrttl.lo: rrttl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrttl.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrttl.Tpo -c -o libkea_dns___la-rrttl.lo `test -f 'rrttl.cc' || echo '$(srcdir)/'`rrttl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrttl.Tpo $(DEPDIR)/libkea_dns___la-rrttl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrttl.cc' object='libkea_dns___la-rrttl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrttl.lo `test -f 'rrttl.cc' || echo '$(srcdir)/'`rrttl.cc + +libkea_dns___la-rrtype.lo: rrtype.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrtype.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrtype.Tpo -c -o libkea_dns___la-rrtype.lo `test -f 'rrtype.cc' || echo '$(srcdir)/'`rrtype.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrtype.Tpo $(DEPDIR)/libkea_dns___la-rrtype.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrtype.cc' object='libkea_dns___la-rrtype.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrtype.lo `test -f 'rrtype.cc' || echo '$(srcdir)/'`rrtype.cc + +libkea_dns___la-rrcollator.lo: rrcollator.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrcollator.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrcollator.Tpo -c -o libkea_dns___la-rrcollator.lo `test -f 'rrcollator.cc' || echo '$(srcdir)/'`rrcollator.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrcollator.Tpo $(DEPDIR)/libkea_dns___la-rrcollator.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrcollator.cc' object='libkea_dns___la-rrcollator.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrcollator.lo `test -f 'rrcollator.cc' || echo '$(srcdir)/'`rrcollator.cc + +libkea_dns___la-qid_gen.lo: qid_gen.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-qid_gen.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-qid_gen.Tpo -c -o libkea_dns___la-qid_gen.lo `test -f 'qid_gen.cc' || echo '$(srcdir)/'`qid_gen.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-qid_gen.Tpo $(DEPDIR)/libkea_dns___la-qid_gen.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='qid_gen.cc' object='libkea_dns___la-qid_gen.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-qid_gen.lo `test -f 'qid_gen.cc' || echo '$(srcdir)/'`qid_gen.cc + +libkea_dns___la-question.lo: question.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-question.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-question.Tpo -c -o libkea_dns___la-question.lo `test -f 'question.cc' || echo '$(srcdir)/'`question.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-question.Tpo $(DEPDIR)/libkea_dns___la-question.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='question.cc' object='libkea_dns___la-question.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-question.lo `test -f 'question.cc' || echo '$(srcdir)/'`question.cc + +libkea_dns___la-serial.lo: serial.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-serial.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-serial.Tpo -c -o libkea_dns___la-serial.lo `test -f 'serial.cc' || echo '$(srcdir)/'`serial.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-serial.Tpo $(DEPDIR)/libkea_dns___la-serial.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='serial.cc' object='libkea_dns___la-serial.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-serial.lo `test -f 'serial.cc' || echo '$(srcdir)/'`serial.cc + +libkea_dns___la-tsig.lo: tsig.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-tsig.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-tsig.Tpo -c -o libkea_dns___la-tsig.lo `test -f 'tsig.cc' || echo '$(srcdir)/'`tsig.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-tsig.Tpo $(DEPDIR)/libkea_dns___la-tsig.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsig.cc' object='libkea_dns___la-tsig.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-tsig.lo `test -f 'tsig.cc' || echo '$(srcdir)/'`tsig.cc + +libkea_dns___la-tsigerror.lo: tsigerror.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-tsigerror.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-tsigerror.Tpo -c -o libkea_dns___la-tsigerror.lo `test -f 'tsigerror.cc' || echo '$(srcdir)/'`tsigerror.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-tsigerror.Tpo $(DEPDIR)/libkea_dns___la-tsigerror.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigerror.cc' object='libkea_dns___la-tsigerror.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-tsigerror.lo `test -f 'tsigerror.cc' || echo '$(srcdir)/'`tsigerror.cc + +libkea_dns___la-tsigkey.lo: tsigkey.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-tsigkey.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-tsigkey.Tpo -c -o libkea_dns___la-tsigkey.lo `test -f 'tsigkey.cc' || echo '$(srcdir)/'`tsigkey.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-tsigkey.Tpo $(DEPDIR)/libkea_dns___la-tsigkey.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigkey.cc' object='libkea_dns___la-tsigkey.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-tsigkey.lo `test -f 'tsigkey.cc' || echo '$(srcdir)/'`tsigkey.cc + +libkea_dns___la-tsigrecord.lo: tsigrecord.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-tsigrecord.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-tsigrecord.Tpo -c -o libkea_dns___la-tsigrecord.lo `test -f 'tsigrecord.cc' || echo '$(srcdir)/'`tsigrecord.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-tsigrecord.Tpo $(DEPDIR)/libkea_dns___la-tsigrecord.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigrecord.cc' object='libkea_dns___la-tsigrecord.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-tsigrecord.lo `test -f 'tsigrecord.cc' || echo '$(srcdir)/'`tsigrecord.cc + +libkea_dns___la-master_loader_callbacks.lo: master_loader_callbacks.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-master_loader_callbacks.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-master_loader_callbacks.Tpo -c -o libkea_dns___la-master_loader_callbacks.lo `test -f 'master_loader_callbacks.cc' || echo '$(srcdir)/'`master_loader_callbacks.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-master_loader_callbacks.Tpo $(DEPDIR)/libkea_dns___la-master_loader_callbacks.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader_callbacks.cc' object='libkea_dns___la-master_loader_callbacks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-master_loader_callbacks.lo `test -f 'master_loader_callbacks.cc' || echo '$(srcdir)/'`master_loader_callbacks.cc + +libkea_dns___la-rrset_collection.lo: rrset_collection.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrset_collection.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrset_collection.Tpo -c -o libkea_dns___la-rrset_collection.lo `test -f 'rrset_collection.cc' || echo '$(srcdir)/'`rrset_collection.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrset_collection.Tpo $(DEPDIR)/libkea_dns___la-rrset_collection.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset_collection.cc' object='libkea_dns___la-rrset_collection.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrset_collection.lo `test -f 'rrset_collection.cc' || echo '$(srcdir)/'`rrset_collection.cc + +libkea_dns___la-zone_checker.lo: zone_checker.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-zone_checker.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-zone_checker.Tpo -c -o libkea_dns___la-zone_checker.lo `test -f 'zone_checker.cc' || echo '$(srcdir)/'`zone_checker.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-zone_checker.Tpo $(DEPDIR)/libkea_dns___la-zone_checker.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='zone_checker.cc' object='libkea_dns___la-zone_checker.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-zone_checker.lo `test -f 'zone_checker.cc' || echo '$(srcdir)/'`zone_checker.cc + +rdata/generic/detail/libkea_dns___la-char_string.lo: rdata/generic/detail/char_string.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rdata/generic/detail/libkea_dns___la-char_string.lo -MD -MP -MF rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Tpo -c -o rdata/generic/detail/libkea_dns___la-char_string.lo `test -f 'rdata/generic/detail/char_string.cc' || echo '$(srcdir)/'`rdata/generic/detail/char_string.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Tpo rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata/generic/detail/char_string.cc' object='rdata/generic/detail/libkea_dns___la-char_string.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rdata/generic/detail/libkea_dns___la-char_string.lo `test -f 'rdata/generic/detail/char_string.cc' || echo '$(srcdir)/'`rdata/generic/detail/char_string.cc + +rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo: rdata/generic/detail/nsec_bitmap.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo -MD -MP -MF rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Tpo -c -o rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo `test -f 'rdata/generic/detail/nsec_bitmap.cc' || echo '$(srcdir)/'`rdata/generic/detail/nsec_bitmap.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Tpo rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata/generic/detail/nsec_bitmap.cc' object='rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rdata/generic/detail/libkea_dns___la-nsec_bitmap.lo `test -f 'rdata/generic/detail/nsec_bitmap.cc' || echo '$(srcdir)/'`rdata/generic/detail/nsec_bitmap.cc + +rdata/generic/detail/libkea_dns___la-nsec3param_common.lo: rdata/generic/detail/nsec3param_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT rdata/generic/detail/libkea_dns___la-nsec3param_common.lo -MD -MP -MF rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Tpo -c -o rdata/generic/detail/libkea_dns___la-nsec3param_common.lo `test -f 'rdata/generic/detail/nsec3param_common.cc' || echo '$(srcdir)/'`rdata/generic/detail/nsec3param_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Tpo rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata/generic/detail/nsec3param_common.cc' object='rdata/generic/detail/libkea_dns___la-nsec3param_common.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o rdata/generic/detail/libkea_dns___la-nsec3param_common.lo `test -f 'rdata/generic/detail/nsec3param_common.cc' || echo '$(srcdir)/'`rdata/generic/detail/nsec3param_common.cc + +libkea_dns___la-rdataclass.lo: rdataclass.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rdataclass.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rdataclass.Tpo -c -o libkea_dns___la-rdataclass.lo `test -f 'rdataclass.cc' || echo '$(srcdir)/'`rdataclass.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rdataclass.Tpo $(DEPDIR)/libkea_dns___la-rdataclass.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdataclass.cc' object='libkea_dns___la-rdataclass.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rdataclass.lo `test -f 'rdataclass.cc' || echo '$(srcdir)/'`rdataclass.cc + +libkea_dns___la-rrparamregistry.lo: rrparamregistry.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libkea_dns___la-rrparamregistry.lo -MD -MP -MF $(DEPDIR)/libkea_dns___la-rrparamregistry.Tpo -c -o libkea_dns___la-rrparamregistry.lo `test -f 'rrparamregistry.cc' || echo '$(srcdir)/'`rrparamregistry.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_dns___la-rrparamregistry.Tpo $(DEPDIR)/libkea_dns___la-rrparamregistry.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrparamregistry.cc' object='libkea_dns___la-rrparamregistry.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_dns___la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libkea_dns___la-rrparamregistry.lo `test -f 'rrparamregistry.cc' || echo '$(srcdir)/'`rrparamregistry.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf rdata/generic/detail/.libs rdata/generic/detail/_libs +install-libdns___includeHEADERS: $(libdns___include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libdns___include_HEADERS)'; test -n "$(libdns___includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdns___includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdns___includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libdns___includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libdns___includedir)" || exit $$?; \ + done + +uninstall-libdns___includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libdns___include_HEADERS)'; test -n "$(libdns___includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libdns___includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libdns___includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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 rdata/generic/detail/$(DEPDIR)/$(am__dirstamp) + -rm -f rdata/generic/detail/$(am__dirstamp) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libkea_dns___la-edns.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-exceptions.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-labelsequence.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_lexer.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_loader.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_loader_callbacks.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-masterload.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-message.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-messagerenderer.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-name.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-nsec3hash.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-opcode.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-qid_gen.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-question.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rcode.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdata.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdataclass.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdatafields.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrclass.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrcollator.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrparamregistry.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrset.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrset_collection.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrttl.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrtype.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-serial.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsig.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigerror.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigkey.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigrecord.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-zone_checker.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libdns___includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libkea_dns___la-edns.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-exceptions.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-labelsequence.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_lexer.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_lexer_inputsource.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_loader.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-master_loader_callbacks.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-masterload.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-message.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-messagerenderer.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-name.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-nsec3hash.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-opcode.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-qid_gen.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-question.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rcode.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdata.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdataclass.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rdatafields.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrclass.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrcollator.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrparamregistry.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrset.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrset_collection.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrttl.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-rrtype.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-serial.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsig.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigerror.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigkey.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-tsigrecord.Plo + -rm -f ./$(DEPDIR)/libkea_dns___la-zone_checker.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-char_string.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec3param_common.Plo + -rm -f rdata/generic/detail/$(DEPDIR)/libkea_dns___la-nsec_bitmap.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libdns___includeHEADERS + +.MAKE: $(am__recursive_targets) all check install install-am \ + install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES 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-libLTLIBRARIES \ + install-libdns___includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-libdns___includeHEADERS + +.PRECIOUS: Makefile + + +rrclass.h: rrclass-placeholder.h +rrtype.h: rrtype-placeholder.h +rrparamregistry.cc: rrparamregistry-placeholder.cc + +s-rdatacode: Makefile $(EXTRA_DIST) + $(PYTHON) ./gen-rdatacode.py + touch $@ +# Purposely not installing these headers: +# name_internal.h: used only internally, and not actually DNS specific +# rdata/*/detail/*.h: these are internal use only +# rrclass-placeholder.h +# rrtype-placeholder.h + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/dns/dns_fwd.h b/src/lib/dns/dns_fwd.h new file mode 100644 index 0000000..99dac37 --- /dev/null +++ b/src/lib/dns/dns_fwd.h @@ -0,0 +1,56 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DNS_FWD_H +#define DNS_FWD_H 1 + +/// \file dns_fwd.h +/// \brief Forward declarations for definitions of libdns++ +/// +/// This file provides a set of forward declarations for definitions commonly +/// used in libdns++ to help minimize dependency when actual the definition +/// is not necessary. + +namespace isc { +namespace dns { + +class EDNS; +class Name; +class MasterLoader; +class MasterLoaderCallbacks; +class Message; +class AbstractMessageRenderer; +class MessageRenderer; +class NSEC3Hash; +class NSEC3HashCreator; +class Opcode; +class Question; +class Rcode; +namespace rdata { +class Rdata; +} +class RRCollator; +class RRClass; +class RRType; +class RRTTL; +class AbstractRRset; +class RdataIterator; +class RRsetCollectionBase; +class RRsetCollection; +class Serial; +class TSIGContext; +class TSIGError; +class TSIGKey; +class TSIGKeyRing; +class TSIGRecord; + +} // namespace dns +} // namespace isc +#endif // DNS_FWD_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc new file mode 100644 index 0000000..49253cf --- /dev/null +++ b/src/lib/dns/edns.cc @@ -0,0 +1,178 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using boost::lexical_cast; +using namespace isc::dns::rdata; +using namespace isc::util; + +namespace isc { +namespace dns { + +namespace { +// This diagram shows the wire-format representation of the TTL field of +// OPT RR and its relationship with implementation specific parameters. +// +// 0 7 15 31 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | EXTENDED-RCODE| VERSION |D| Z | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// <= VERSION_SHIFT (16 bits) +// <= EXTRCODE_SHIFT (24 bits) +//EXTFLAG_DO:0 0 0 ....................... 0 1 0 0 0 0.....................0 +//VER_MASK: 0 0 0 ........0 1 1 1 1 1 1 1 1 0 0 ..........................0 + +const unsigned int VERSION_SHIFT = 16; +const unsigned int EXTRCODE_SHIFT = 24; +const uint32_t VERSION_MASK = 0x00ff0000; +const uint32_t EXTFLAG_DO = 0x00008000; +} + +EDNS::EDNS(const uint8_t version) : + version_(version), + udp_size_(Message::DEFAULT_MAX_UDPSIZE), + dnssec_aware_(false) +{ + if (version_ > SUPPORTED_VERSION) { + isc_throw(isc::InvalidParameter, + "failed to construct EDNS: unsupported version: " << + static_cast(version_)); + } +} + +EDNS::EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const Rdata&) : + version_((ttl.getValue() & VERSION_MASK) >> VERSION_SHIFT) +{ + if (rrtype != RRType::OPT()) { + isc_throw(isc::InvalidParameter, + "EDNS is being created with incompatible RR type: " + << rrtype); + } + + if (version_ > EDNS::SUPPORTED_VERSION) { + isc_throw(DNSMessageBADVERS, "unsupported EDNS version: " << + static_cast(version_)); + } + + if (name != Name::ROOT_NAME()) { + isc_throw(DNSMessageFORMERR, "invalid owner name for EDNS OPT RR: " << + name); + } + + dnssec_aware_ = ((ttl.getValue() & EXTFLAG_DO) != 0); + udp_size_ = rrclass.getCode(); +} + +string +EDNS::toText() const { + string ret = "; EDNS: version: "; + + ret += lexical_cast(static_cast(getVersion())); + ret += ", flags:"; + if (getDNSSECAwareness()) { + ret += " do"; + } + ret += "; udp: " + lexical_cast(getUDPSize()) + "\n"; + + return (ret); +} + +namespace { +/// Helper function to define unified implementation for the public versions +/// of toWire(). +template +int +toWireCommon(Output& output, const uint8_t version, + const uint16_t udp_size, const bool dnssec_aware, + const uint8_t extended_rcode) +{ + // Render EDNS OPT RR + uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT; + extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK; + if (dnssec_aware) { + extrcode_flags |= EXTFLAG_DO; + } + + // Construct an RRset corresponding to the EDNS. + // We don't support any options for now, so the OPT RR can be empty. + RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size), + RRType::OPT(), RRTTL(extrcode_flags))); + edns_rrset->addRdata(ConstRdataPtr(new generic::OPT())); + + edns_rrset->toWire(output); + + return (1); +} +} + +unsigned int +EDNS::toWire(AbstractMessageRenderer& renderer, + const uint8_t extended_rcode) const +{ + // If adding the OPT RR would exceed the size limit, don't do it. + // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte) + // (RDATA is empty in this simple implementation) + if (renderer.getLength() + 11 > renderer.getLengthLimit()) { + return (0); + } + + return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_, + extended_rcode)); +} + +unsigned int +EDNS::toWire(isc::util::OutputBuffer& buffer, + const uint8_t extended_rcode) const +{ + return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_, + extended_rcode)); +} + +EDNS* +createEDNSFromRR(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl, + const Rdata& rdata, + uint8_t& extended_rcode) +{ + // Create a new EDNS object first for exception guarantee. + EDNS* edns = new EDNS(name, rrclass, rrtype, ttl, rdata); + + // At this point we can update extended_rcode safely. + extended_rcode = ttl.getValue() >> EXTRCODE_SHIFT; + + return (edns); +} + +ostream& +operator<<(std::ostream& os, const EDNS& edns) { + os << edns.toText(); + return (os); +} + +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h new file mode 100644 index 0000000..d5e6375 --- /dev/null +++ b/src/lib/dns/edns.h @@ -0,0 +1,437 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EDNS_H +#define EDNS_H 1 + +#include + +#include + +#include + +#include + +namespace isc { +namespace util { +class OutputBuffer; +} + +namespace dns { + +class EDNS; +class Name; +class AbstractMessageRenderer; +class RRClass; +class RRTTL; +class RRType; +class Rcode; + +/// \brief A pointer-like type pointing to an \c EDNS object. +typedef boost::shared_ptr EDNSPtr; + +/// \brief A pointer-like type pointing to an immutable \c EDNS object. +typedef boost::shared_ptr ConstEDNSPtr; + +/// The \c EDNS class represents the %EDNS OPT RR defined in RFC2671. +/// +/// This class encapsulates various optional features of %EDNS such as +/// the UDP payload size or the DNSSEC DO bit, and provides interfaces +/// to manage these features. It is also responsible for conversion +/// to and from wire-format OPT RR. +/// One important exception is about the extended RCODE: +/// The \c EDNS class is only responsible for extracting the 8-bit part +/// of the 12-bit extended RCODE from the OPT RR's TTL field of an +/// incoming message, and for setting the 8-bit part into the OPT RR TTL +/// of an outgoing message. It's not supposed to know how to construct the +/// complete RCODE, much less maintain the RCODE in it. +/// It is the caller's responsibility (typically the \c Message class). +/// +/// When converting wire-format OPT RR into an \c EDNS object, it normalizes +/// the information, i.e., unknown flags will be ignored on construction. +/// +/// This class is also supposed to support %EDNS options such as NSID, +/// but the initial implementation does not include it. This is a near term +/// TODO item. +/// +/// Notes to developers +/// +/// The rest of the description is for developers who need to or want to +/// understand the design of this API. +/// +/// Representing %EDNS is tricky. An OPT RR is no different from other RRs +/// in terms of the wire format syntax, and in that sense we could use the +/// generic \c RRset class to represent an OPT RR (BIND 9 adopts this +/// approach). But the resulting interface would be inconvenient for +/// developers. For example, the developer would need to know that the +/// UDP size is encoded in the RR Class field. It's better to provide +/// a more abstract interface along with the special semantics of OPT RR. +/// +/// Another approach would be to realize each optional feature of EDNS +/// as an attribute of the DNS message. +/// NLnet Labs' ldns takes this approach. +/// This way an operation for specifying the UDP size would be written +/// like this: +/// \code message->setUDPSize(4096); \endcode +/// which should be more intuitive. +/// A drawback of this approach is that OPT RR is itself optional and the +/// separate parameters may not necessarily indicate whether to include an +/// OPT RR per se. +/// For example, consider what should be done with this code: +/// \code message->setUDPSize(512); \endcode +/// Since the payload size of 512 is the default, it may mean the OPT RR +/// should be skipped. But it might also mean the caller intentionally +/// (for some reason) wants to insert an OPT RR specifying the default UDP +/// size explicitly. +/// +/// So, we use a separate class that encapsulates the EDNS semantics and +/// knows the mapping between the semantics and the wire format representation. +/// This way the interface can be semantics-based and is intuitive: +/// \code edns->setUDPSize(4096); \endcode +/// while we can explicitly specify whether to include an OPT RR by setting +/// (or not setting) an \c EDNS object in a message: +/// \code message->setEDNS(edns); // unless we do this OPT RR is skipped +/// \endcode +/// +/// There is still a non trivial point: How to manage extended RCODEs. +/// An OPT RR encodes the upper 8 bits of extended 12-bit RCODE. +/// In general, it would be better to provide a unified interface to get +/// access to RCODEs whether or not they are traditional 4 bit codes or +/// extended ones that have non 0 upper bits. +/// However, since an OPT RR may not appear in a message the RCODE cannot be +/// maintained in the \c EDNS class. +/// But it would not be desirable to maintain the extended RCODEs completely +/// in the \c Message class, either, because we wanted to hide the mapping +/// between %EDNS semantics and its wire format representation within the +/// \c EDNS class; if we moved the responsibility about RCODEs to the +/// \c Message class, it would have to parse and render the upper 8 bits of +/// the RCODEs, dealing with wire representation of OPT RR. +/// This is suboptimal in the sense of encapsulation. +/// +/// As a compromise, our decision is to separate the knowledge about the +/// relationship with RCODE from the knowledge about the wire format as +/// noted in the beginning of this description. +/// +/// This decoupling is based on the observation that the extended RCODE +/// is a very special case where %EDNS only has partial information. +/// If a future version of the %EDNS protocol introduces further relationship +/// between the message and the %EDNS, we might reconsider the interface, +/// probably with higher abstraction. +class EDNS { +public: + /// + /// \name Constructors and Destructor + /// + /// We use the default copy constructor, default copy assignment operator, + /// and default destructors intentionally. + /// + /// Note about copyability: This version of this class is copyable, + /// but we may want to change it once we support EDNS options, when + /// we want to revise this class using the pimpl idiom. + /// But we should be careful about that: the python binding currently + /// assumes this class is copyable. + //@{ + /// Constructor with the EDNS version. + /// An application would use this constructor to specify EDNS parameters + /// and/or options for outgoing DNS messages. + /// + /// All other parameters than the version number will be initialized to + /// reasonable defaults. + /// Specifically, the UDP payload size is set to + /// \c Message::DEFAULT_MAX_UDPSIZE, and DNSSEC is assumed to be not + /// supported. + /// These parameters can be altered via setter methods of this class. + /// Note, however, that the version number cannot be changed once + /// constructed. + /// + /// The version number parameter can be omitted, in which case the highest + /// supported version in this implementation will be assumed. + /// When specified, if it is larger than the highest supported version, + /// an exception of class \c isc::InvalidParameter will be thrown. + /// + /// This constructor throws no other exception. + /// + /// \param version The version number of the EDNS to be constructed. + explicit EDNS(const uint8_t version = SUPPORTED_VERSION); + + /// \brief Constructor from resource record (RR) parameters. + /// + /// This constructor is intended to be used to construct an EDNS object + /// from an OPT RR contained in an incoming DNS message. + /// + /// Unlike many other constructors for this purpose, this constructor + /// does not take the bare wire-format %data in the form of an + /// \c InputBuffer object. This is because parsing incoming EDNS is + /// highly context dependent and it's not feasible to handle it in a + /// completely polymorphic way. For example, a DNS message parser would + /// have to check an OPT RR appears at most once in the message, and if + /// it appears it should be in the additional section. So, the parser + /// needs to have an explicit check to see if an RR is of type OPT, and + /// then (if other conditions are met) construct a corresponding \c EDNS + /// object. At that point the parser would have already converted the + /// wire %data into corresponding objects of \c Name, \c RRClass, + /// \c RRType, etc, and it makes more sense to pass them directly to the + /// constructor. + /// + /// In practice, top level applications rarely need to use this + /// constructor directly. It should normally suffice to have a higher + /// level class such as \c Message do that job. + /// + /// This constructor checks the passed parameters to see if they are + /// valid in terms of the EDNS protocol specification. + /// \c name must be the root name ("."); otherwise, an exception of + /// class \c DNSMessageFORMERR will be thrown. + /// \c rrtype must specify the OPT RR type; otherwise, an exception of + /// class \c isc::InvalidParameter will be thrown. + /// The ENDS version number is extracted from \c rrttl. If it is larger + /// than the higher supported version, an exception of class + /// \c DNSMessageBADVERS will be thrown. Note that this is different from + /// the case of the same error in the other constructor. + /// This is intentional, so that the application can transparently convert + /// the exception to a response RCODE according to the protocol + /// specification. + /// + /// This initial implementation does not support EDNS options at all, + /// and \c rdata is simply ignored. Future versions will support + /// options, and may throw exceptions while validating the given parameter. + /// + /// \b Note: since no other type than OPT for \c rrtype is allowed, this + /// parameter could actually have been omitted. But it is intentionally + /// included as a parameter so that invalid usage of the construction + /// can be detected. As noted above the caller should normally have + /// the corresponding \c RRType object at the time of call to this + /// constructor, so the overhead of having the additional parameter + /// should be marginal. + /// + /// \param name The owner name of the OPT RR. This must be the root name. + /// \param rrclass The RR class of the OPT RR. + /// \param rrtype This must specify the OPT RR type. + /// \param ttl The TTL of the OPT RR. + /// \param rdata The RDATA of the OPT RR. + EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const rdata::Rdata& rdata); + //@} + + /// + /// \name Getter and Setter Methods + /// + //@{ + /// \brief Returns the version of EDNS. + /// + /// This method never throws an exception. + uint8_t getVersion() const { return (version_); } + + /// \brief Returns the maximum payload size of UDP messages for the sender + /// of the message containing this \c EDNS. + /// + /// This method never throws an exception. + uint16_t getUDPSize() const { return (udp_size_); } + + /// \brief Specify the maximum payload size of UDP messages that use + /// this EDNS. + /// + /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed + /// for the maximum payload size, regardless of whether EDNS OPT RR is + /// included or not. This means if an application wants to send a message + /// with an EDNS OPT RR for specifying a larger UDP size, it must + /// explicitly specify the value using this method. + /// + /// This method never throws an exception. + /// + /// \param udp_size The maximum payload size of UDP messages for the sender + /// of the message containing this \c EDNS. + void setUDPSize(const uint16_t udp_size) { udp_size_ = udp_size; } + + /// \brief Returns whether the message sender is DNSSEC aware. + /// + /// This method never throws an exception. + /// + /// \return true if DNSSEC is supported; otherwise false. + bool getDNSSECAwareness() const { return (dnssec_aware_); } + + /// \brief Specifies whether the sender of the message containing this + /// \c EDNS is DNSSEC aware. + /// + /// If the parameter is true, a subsequent call to \c toWire() will + /// set the DNSSEC DO bit on for the corresponding OPT RR. + /// + /// This method never throws an exception. + /// + /// \param is_aware \c true if DNSSEC is supported; \c false otherwise. + void setDNSSECAwareness(const bool is_aware) { dnssec_aware_ = is_aware; } + //@} + + /// + /// \name Converter Methods + /// + //@{ + /// \brief Render the \c EDNS in the wire format. + /// + /// This method renders the \c EDNS object as a form of DNS OPT RR + /// via \c renderer, which encapsulates output buffer and other rendering + /// contexts. + /// Since the \c EDNS object does not maintain the extended RCODE + /// information, a separate parameter \c extended_rcode must be passed to + /// this method. + /// + /// If by adding the OPT RR the message size would exceed the limit + /// maintained in \c renderer, this method skips rendering the RR + /// and returns 0; otherwise it returns 1, which is the number of RR + /// rendered. + /// + /// In the current implementation the return value is either 0 or 1, but + /// the return type is unsigned int to be consistent with + /// \c RRset::toWire(). In any case the caller shouldn't assume these are + /// only possible return values from this method. + /// + /// This method is mostly exception free, but it requires memory + /// allocation and if it fails a corresponding standard exception will be + /// thrown. + /// + /// In practice, top level applications rarely need to use this + /// method directly. It should normally suffice to have a higher + /// level class such as \c Message do that job. + /// + /// Note to developer: the current implementation constructs an + /// \c RRset object for the OPT RR and calls its \c toWire() method, + /// which is inefficient. In future, we may want to optimize this method + /// by caching the rendered image and having the application reuse the + /// same \c EDNS object when possible. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as + /// part of the EDNS OPT RR. + /// \return 1 if the OPT RR fits in the message size limit; otherwise 0. + unsigned int toWire(AbstractMessageRenderer& renderer, + const uint8_t extended_rcode) const; + + /// \brief Render the \c EDNS in the wire format. + /// + /// This method is same as \c toWire(MessageRenderer&,uint8_t)const + /// except it renders the OPT RR in an \c OutputBuffer and therefore + /// does not care about message size limit. + /// As a consequence it always returns 1. + unsigned int toWire(isc::util::OutputBuffer& buffer, + const uint8_t extended_rcode) const; + + /// \brief Convert the EDNS to a string. + /// + /// The format of the resulting string is as follows: + /// \code ; EDNS: version: , flags: ; udp: + /// \endcode + /// where + /// - \em version is the EDNS version number (integer). + /// - edns flags is a sequence of EDNS flag bits. The only + /// possible flag is the "DNSSEC OK", which is represented as "do". + /// - udp size is sender's UDP payload size in bytes. + /// + /// The string will be terminated with a trailing newline character. + /// + /// When EDNS options are supported the output of this method will be + /// extended. + /// + /// This method is mostly exception free, but it may require memory + /// allocation and if it fails a corresponding standard exception will be + /// thrown. + /// + /// \return A string representation of \c EDNS. See above for the format. + std::string toText() const; + //@} + + // TBD: This method is currently not implemented. We'll eventually need + // something like this. + //void addOption(); + +public: + /// \brief The highest EDNS version this implementation supports. + static const uint8_t SUPPORTED_VERSION = 0; +private: + // We may eventually want to migrate to pimpl, especially when we support + // EDNS options. In this initial implementation, we keep it simple. + const uint8_t version_; + uint16_t udp_size_; + bool dnssec_aware_; +}; + +/// \brief Create a new \c EDNS object from a set of RR parameters, also +/// providing the extended RCODE value. +/// +/// This function is similar to the EDNS class constructor +/// \c EDNS::EDNS(const Name&, const RRClass&, const RRType&, const RRTTL&, const rdata::Rdata&) +/// but is different in that +/// - It dynamically creates a new object +/// - It returns (via a reference argument) the topmost 8 bits of the extended +/// RCODE encoded in the \c ttl. +/// +/// On success, \c extended_rcode will be updated with the 8-bit part of +/// the extended RCODE encoded in the TTL of the OPT RR. +/// +/// The intended usage of this function is to parse an OPT RR of an incoming +/// DNS message, while updating the RCODE of the message. +/// One common usage pattern is as follows: +/// +/// \code Message msg; +/// ... +/// uint8_t extended_rcode; +/// ConstEDNSPtr edns = ConstEDNSPtr(createEDNSFromRR(..., extended_rcode)); +/// rcode = Rcode(msg.getRcode().getCode(), extended_rcode); +/// \endcode +/// (although, like the \c EDNS constructor, normal applications wouldn't have +/// to use this function directly). +/// +/// This function provides the strong exception guarantee: Unless an +/// exception is thrown \c extended_code won't be modified. +/// +/// This function validates the given parameters and throws exceptions on +/// failure in the same way as the \c EDNS class constructor. +/// In addition, if memory allocation for the new object fails it throws the +/// corresponding standard exception. +/// +/// Note that this function returns a bare pointer to the newly allocated +/// object, not a shared pointer object enclosing the pointer. +/// The caller is responsible for deleting the object after the use of it +/// (typically, the caller would immediately encapsulate the returned pointer +/// in a shared pointer object, \c EDNSPtr or \c ConstEDNSPtr). +/// It returns a bare pointer so that it can be used where the use of a shared +/// pointer is impossible or not desirable. +/// +/// Note to developers: there is no strong technical reason why this function +/// cannot be a constructor of the \c EDNS class or even integrated into the +/// constructor. But we decided to make it a separate free function so that +/// constructors will be free from side effects (which is in itself a matter +/// of preference). +/// +/// \param name The owner name of the OPT RR. This must be the root name. +/// \param rrclass The RR class of the OPT RR. +/// \param rrtype This must specify the OPT RR type. +/// \param ttl The TTL of the OPT RR. +/// \param rdata The RDATA of the OPT RR. +/// \param extended_rcode A placeholder to store the topmost 8 bits of the +/// extended Rcode. +/// \return A pointer to the created \c EDNS object. +EDNS* createEDNSFromRR(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl, + const rdata::Rdata& rdata, uint8_t& extended_rcode); + +/// \brief Insert the \c EDNS as a string into stream. +/// +/// This method convert \c edns into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param edns A reference to an \c EDNS object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const EDNS& edns); +} +} +#endif // EDNS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/exceptions.cc b/src/lib/dns/exceptions.cc new file mode 100644 index 0000000..e164348 --- /dev/null +++ b/src/lib/dns/exceptions.cc @@ -0,0 +1,26 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +namespace isc { +namespace dns { + +const Rcode& +DNSMessageFORMERR::getRcode() const { + return (Rcode::FORMERR()); +} + +const Rcode& +DNSMessageBADVERS::getRcode() const { + return (Rcode::BADVERS()); +} + +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/exceptions.h b/src/lib/dns/exceptions.h new file mode 100644 index 0000000..40f2cc1 --- /dev/null +++ b/src/lib/dns/exceptions.h @@ -0,0 +1,76 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// XXX: we have another exceptions.h, so we need to use a prefix "DNS_" in +// the include guard. More preferably, we should define a consistent naming +// style for the header guide (e.g. module-name_file-name_H) throughout the +// package. + +#ifndef DNS_EXCEPTIONS_H +#define DNS_EXCEPTIONS_H 1 + +#include + +namespace isc { +namespace dns { + +/// +/// \brief A standard DNS module exception ...[TBD] +/// +class Rcode; // forward declaration + +class Exception : public isc::Exception { +public: + Exception(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// +/// \brief Base class for all sorts of text parse errors. +/// +class DNSTextError : public isc::dns::Exception { +public: + DNSTextError(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// \brief Base class for name parser exceptions. +/// +class NameParserException : public DNSTextError { +public: + NameParserException(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +class DNSProtocolError : public isc::dns::Exception { +public: + DNSProtocolError(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} + virtual const Rcode& getRcode() const = 0; +}; + +class DNSMessageFORMERR : public DNSProtocolError { +public: + DNSMessageFORMERR(const char* file, size_t line, const char* what) : + DNSProtocolError(file, line, what) {} + virtual const Rcode& getRcode() const; +}; + +class DNSMessageBADVERS : public DNSProtocolError { +public: + DNSMessageBADVERS(const char* file, size_t line, const char* what) : + DNSProtocolError(file, line, what) {} + virtual const Rcode& getRcode() const; +}; + +} +} +#endif // DNS_EXCEPTIONS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in new file mode 100644 index 0000000..f39fc09 --- /dev/null +++ b/src/lib/dns/gen-rdatacode.py.in @@ -0,0 +1,391 @@ +#!@PYTHON@ + +# Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +"""\ +This is a supplemental script to (half) auto-generate DNS Rdata related +classes and constants. +""" + +# This script should be used every time existing RR data changes or when new +# RR types are added or existing are removed. Since DHCP uses only four +# (A,AAAA,PTR,DHCID) RR types, its usage is expected to be an uncommon event. +# The only envisaged use case is if/when we decide to trim down libdns++. + +import os +from os.path import getmtime +import re +import sys + +re_typecode = re.compile('([\da-z\-]+)_(\d+)') +classcode2txt = {} +typecode2txt = {} +# For meta types and types well-known but not implemented. This is a dict from +# type code values (as string) to textual mnemonic. +meta_types = { + # Real meta types. We won't have Rdata implement for them, but we need + # RRType constants. + '251': 'ixfr', '252': 'axfr', '255': 'any', + # Obsolete types. We probably won't implement Rdata for them, but it's + # better to have RRType constants. + '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt', + '38': 'a6', '254': 'maila', + # Types officially assigned but not yet supported in our implementation. + '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap', + '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px', + '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl', + '45': 'ipseckey', '55': 'hip', '103': 'unspec', + '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', + '253': 'mailb', '256': 'uri' + } +# Classes that don't have any known types. This is a dict from type code +# values (as string) to textual mnemonic. +meta_classes = {'254': 'none'} +typeandclass = [] +generic_code = 65536 # something larger than any code value +rdata_declarations = '' +class_definitions = '' +classdir_mtime = 0 +rdatadef_mtime = 0 +rdatahdr_mtime = 0 +heading_txt = '''/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +''' + +def import_classdef(class_txt, file): + content = '' + rdata_source = open(file, 'r') + for line in rdata_source.readlines(): + if re.match('// BEGIN_ISC_NAMESPACE', line): + content += 'namespace isc {\n' + content += 'namespace dns {\n' + continue + if re.match('// BEGIN_RDATA_NAMESPACE', line): + content += 'namespace rdata {\n' + content += 'namespace ' + class_txt + ' {\n' + continue + if re.match('// END_ISC_NAMESPACE', line): + content += '} // end of namespace "dns"\n' + content += '} // end of namespace "isc"\n' + continue + if re.match('// END_RDATA_NAMESPACE', line): + content += '} // end of namespace "' + class_txt +'"\n' + content += '} // end of namespace "rdata"\n' + continue + content += line + rdata_source.close() + return content + +def import_classheader(class_txt, type_txt, type_code, file): + type_utxt = type_txt.upper() + class_utxt = class_txt.upper() + + # for each CLASS_n/TYPE_m.h + rdata_header = open(file, 'r') + content = '' + guard_macro = class_txt.upper() + '_' + type_txt.upper() + guard_macro += '_' + type_code + '_H' + for line in rdata_header.readlines(): + if re.match('// BEGIN_HEADER_GUARD', line): + content += '#ifndef ' + guard_macro + '\n' + content += '#define ' + guard_macro + ' 1\n' + continue + if re.match('// END_HEADER_GUARD', line): + content += '#endif // ' + guard_macro + '\n' + continue + if re.match('// BEGIN_ISC_NAMESPACE', line): + content += 'namespace isc {\n' + content += 'namespace util {\n' + content += ''' +class InputBuffer; +class OutputBuffer;\n''' + content += '}\n\n' + content += 'namespace dns {\n' + continue + if re.match('// BEGIN_RDATA_NAMESPACE', line): + content += 'namespace rdata {\n' + content += 'namespace ' + class_txt + ' {\n' + continue + if re.match('// END_ISC_NAMESPACE', line): + content += '} // end of namespace "dns"\n' + content += '} // end of namespace "isc"\n' + continue + if re.match('// END_RDATA_NAMESPACE', line): + content += '} // end of namespace "' + class_txt +'"\n' + content += '} // end of namespace "rdata"\n' + continue + if re.match('// Local Variables:', line): + break + content += line + if re.match('// BEGIN_COMMON_DECLARATIONS', line): + content += ''' +class AbstractMessageRenderer;\n\n''' + if re.match('\s+// BEGIN_COMMON_MEMBERS$', line): + content += ''' + explicit ''' + type_utxt + '''(const std::string& type_str); + ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len); + ''' + type_utxt + '''(const ''' + type_utxt + '''& other); + ''' + type_utxt + '''( + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks); + virtual std::string toText() const; + virtual void toWire(isc::util::OutputBuffer& buffer) const; + virtual void toWire(AbstractMessageRenderer& renderer) const; + virtual int compare(const Rdata& other) const;\n\n''' + rdata_header.close() + return content + +def import_definitions(classcode2txt, typecode2txt, typeandclass): + global rdata_declarations + global class_definitions + global classdir_mtime + global rdatadef_mtime + global rdatahdr_mtime + + if classdir_mtime < getmtime('@srcdir@/rdata'): + classdir_mtime = getmtime('@srcdir@/rdata') + + # Sort directories before iterating through them so that the directory + # list is processed in the same order on all systems. The resulting + # files should compile regardless of the order in which the components + # are included but... Having a fixed order for the directories should + # eliminate system-dependent problems. (Note that the directory names + # in BIND 10 are ASCII, so the order should be locale-independent.) + dirlist = os.listdir('@srcdir@/rdata') + dirlist.sort() + for dir in dirlist: + classdir = '@srcdir@/rdata' + os.sep + dir + m = re_typecode.match(dir) + if os.path.isdir(classdir) and (m != None or dir == 'generic'): + if dir == 'generic': + class_txt = 'generic' + class_code = generic_code + else: + class_txt = m.group(1) + class_code = m.group(2) + if not class_code in classcode2txt: + classcode2txt[class_code] = class_txt + + # Same considerations as directories regarding sorted order + # also apply to files. + filelist = os.listdir(classdir) + filelist.sort() + for file in filelist: + file = classdir + os.sep + file + m = re_typecode.match(os.path.split(file)[1]) + if m != None: + type_txt = m.group(1) + type_code = m.group(2) + if not type_code in typecode2txt: + typecode2txt[type_code] = type_txt + if re.search('\.cc$', file): + if rdatadef_mtime < getmtime(file): + rdatadef_mtime = getmtime(file) + class_definitions += import_classdef(class_txt, file) + elif re.search('\.h$', file): + if rdatahdr_mtime < getmtime(file): + rdatahdr_mtime = getmtime(file) + rdata_declarations += import_classheader(class_txt, + type_txt, + type_code, + file) + typeandclass.append((type_txt, int(type_code), + (class_txt, class_txt), + int(class_code))) + if class_txt == 'generic': + typeandclass.append((type_txt, int(type_code), + (class_txt, 'in'), 1)) + +def need_generate(file, mtime): + '''Check if we need to generate the specified file. + + To avoid unnecessary compilation, we skip (re)generating the file when + the file already exists and newer than the base file. + ''' + if os.path.exists(file) and getmtime(file) > mtime: + return False + return True + +def generate_rdatadef(file, basemtime): + if not need_generate(file, basemtime): + print('skip generating ' + file); + return + rdata_deffile = open(file, 'w') + rdata_deffile.write(heading_txt) + rdata_deffile.write(class_definitions) + rdata_deffile.close() + +def generate_rdatahdr(file, heading, declarations, basemtime): + if not need_generate(file, basemtime): + print('skip generating ' + file); + return + heading += ''' +#ifndef DNS_RDATACLASS_H +#define DNS_RDATACLASS_H 1 + +#include + +namespace isc { +namespace dns { +class Name; +class MasterLexer; +class MasterLoaderCallbacks; +} +} +''' + declarations += ''' +#endif // DNS_RDATACLASS_H + +// Local Variables: +// mode: c++ +// End: +''' + rdata_header = open(file, 'w') + rdata_header.write(heading) + rdata_header.write(declarations) + rdata_header.close() + +def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class): + placeholder = '@srcdir@/' + fileprefix + '-placeholder.h' + outputfile = '@builddir@/' + fileprefix + '.h' + py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc' + upper_key = type_or_class.upper() # TYPE or CLASS + lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass + cap_key = type_or_class # Type or Class + + # We only decide whether to generate files for libdns++ files; Python + # files are generated if and only if libdns++ files are generated. + # In practice it should be sufficient. + if (not need_generate(outputfile, basemtime) and + getmtime(outputfile) > getmtime(placeholder)): + print('skip generating ' + outputfile) + return + + # Create a list of (code, code-text) pairs, where code-text is generally + # upper-cased, with applying special filters when necessary. + def convert(code_txt): + # Workaround by heuristics: there's a "NULL" RR type, but it would + # cause conflict with the C/C++ macro. We use Null as a special case. + if code_txt == 'null': + return 'Null' + # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part + # of a C/C++ variable. + if code_txt == 'nsap-ptr': + return 'NSAP_PTR' + return code_txt.upper() + codes = [ (code, convert(txt)) for code, txt in code2txt.items() ] + + # Dump source code for libdns++ + with open(placeholder, 'r') as header_temp: + with open(outputfile, 'w') as header_out: + header_out.write(heading_txt) + for line in header_temp: + header_out.write(line) + if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key + + '_DECLARATIONS$', line): + for code in codes: + header_out.write(' ' * 4 + 'static const RR' + + cap_key + '& ' + code[1] + '();\n') + if re.match('// BEGIN_WELL_KNOWN_' + upper_key + + '_DEFINITIONS$', line): + for code in codes: + header_out.write('''inline const RR''' + cap_key + + '''& +RR''' + cap_key + '''::''' + code[1] + '''() { + static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + '''); + return (''' + lower_key + '''); +}\n +''') + + # Dump source code snippet for isc.dns Python module + with open(py_outputfile, 'w') as py_out: + py_out.write(" // auto-generated by ../gen-rdatacode.py." + " Don't edit this file.\n") + py_out.write("\n") + for code in codes: + py_out.write('''\ + installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''", + createRR''' + cap_key + '''Object(RR''' + \ + cap_key + '''::''' + code[1] + '''())); +''') + +def generate_rrparam(fileprefix, basemtime): + placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc' + outputfile = '@builddir@/' + fileprefix + '.cc' + if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder): + print('skip generating ' + outputfile) + return + + # sort by class, then by type + typeandclassparams = '' + typeandclass.sort(key = lambda x: (x[3], x[1])) + for param in typeandclass: + # for rrparamregistry.cc + # each param is a tuple of (type_txt, type_code, class_tuple, + # class_code) + (type_txt, type_code, class_tuple, class_code) = param + type_utxt = type_txt.upper() + class_txt = class_tuple[0] + class_utxt = class_tuple[1].upper() + indent = ' ' * 8 + typeandclassparams += indent + + if class_tuple[1] != 'generic': + typeandclassparams += 'add("' + type_utxt + '", ' + typeandclassparams += str(type_code) + ', "' + class_utxt + typeandclassparams += '", ' + str(class_code) + typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' + typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' + else: + typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code) + typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' + typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' + + typeandclassparams += indent + '// Meta and non-implemented RR types\n' + for type_code, type_txt in meta_types.items(): + typeandclassparams += indent + \ + 'addType("' + type_txt.upper() + '", ' + type_code + ');\n' + + typeandclassparams += indent + '// Meta classes\n' + for cls_code, cls_txt in meta_classes.items(): + typeandclassparams += indent + \ + 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n' + + rrparam_temp = open(placeholder, 'r') + rrparam_out = open(outputfile, 'w') + rrparam_out.write(heading_txt) + for line in rrparam_temp.readlines(): + rrparam_out.write(line) + if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line): + rrparam_out.write(typeandclassparams) + rrparam_temp.close() + rrparam_out.close() + +if __name__ == "__main__": + try: + import_definitions(classcode2txt, typecode2txt, typeandclass) + generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime) + generate_rdatahdr('@builddir@/rdataclass.h', heading_txt, + rdata_declarations, rdatahdr_mtime) + + # merge auto-generated types/classes with meta maps and generate the + # corresponding code. + generate_typeclasscode('rrtype', rdatahdr_mtime, + dict(typecode2txt, **meta_types), 'Type') + generate_typeclasscode('rrclass', classdir_mtime, + dict(classcode2txt, **meta_classes), 'Class') + + generate_rrparam('rrparamregistry', rdatahdr_mtime) + except: + sys.stderr.write('Code generation failed due to exception: %s\n' % + sys.exc_info()[1]) + exit(1) diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc new file mode 100644 index 0000000..8e0be43 --- /dev/null +++ b/src/lib/dns/labelsequence.cc @@ -0,0 +1,472 @@ +// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include + +#include + +#include + +namespace isc { +namespace dns { + +LabelSequence::LabelSequence(const void* buf) { +#ifdef ENABLE_DEBUG + // In non-debug mode, dereferencing the NULL pointer further below + // will lead to a crash, so disabling this check is not + // unsafe. Except for a programming mistake, this case should not + // happen. + if (buf == NULL) { + isc_throw(BadValue, + "Null pointer passed to LabelSequence constructor"); + } +#endif + + const uint8_t* bp = reinterpret_cast(buf); + first_label_ = 0; + const uint8_t offsets_len = *bp++; + +#ifdef ENABLE_DEBUG + if (offsets_len == 0 || offsets_len > Name::MAX_LABELS) { + isc_throw(BadValue, + "Bad offsets len in serialized LabelSequence data: " + << static_cast(offsets_len)); + } +#endif + + last_label_ = offsets_len - 1; + offsets_ = bp; + data_ = bp + offsets_len; + +#ifdef ENABLE_DEBUG + // Check the integrity on the offsets and the name data + const uint8_t* dp = data_; + for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) { + if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) { + isc_throw(BadValue, + "Broken offset or name data in serialized " + "LabelSequence data"); + } + dp += (1 + *dp); + } +#endif +} + +LabelSequence::LabelSequence(const LabelSequence& src, + uint8_t buf[MAX_SERIALIZED_LENGTH]) +{ + size_t data_len; + const uint8_t *data = src.getData(&data_len); + std::memcpy(buf, data, data_len); + + for (size_t i = 0; i < src.getLabelCount(); ++i) { + buf[Name::MAX_WIRE + i] = src.offsets_[i + src.first_label_] - + src.offsets_[src.first_label_]; + } + + first_label_ = 0; + last_label_ = src.last_label_ - src.first_label_; + data_ = buf; + offsets_ = &buf[Name::MAX_WIRE]; +} + + +const uint8_t* +LabelSequence::getData(size_t *len) const { + *len = getDataLength(); + return (&data_[offsets_[first_label_]]); +} + +size_t +LabelSequence::getDataLength() const { + const size_t last_label_len = data_[offsets_[last_label_]] + 1; + return (offsets_[last_label_] - offsets_[first_label_] + last_label_len); +} + +size_t +LabelSequence::getSerializedLength() const { + return (1 + getLabelCount() + getDataLength()); +} + +namespace { +// Check if buf is not in the range of [bp, ep), which means +// - end of buffer is before bp, or +// - beginning of buffer is on or after ep +bool +isOutOfRange(const uint8_t* bp, const uint8_t* ep, + const uint8_t* buf, size_t buf_len) +{ + return (bp >= buf + buf_len || // end of buffer is before bp + ep <= buf); // beginning of buffer is on or after ep +} +} + +void +LabelSequence::serialize(void* buf, size_t buf_len) const { + const size_t expected_size = getSerializedLength(); + if (expected_size > buf_len) { + isc_throw(BadValue, "buffer too short for LabelSequence::serialize"); + } + + const size_t offsets_len = getLabelCount(); + isc_throw_assert(offsets_len < 256); // should be in the 8-bit range + + // Overridden check. Buffer shouldn't overwrap the offset of name data + // regions. + uint8_t* bp = reinterpret_cast(buf); + const size_t ndata_len = getDataLength(); + if (!isOutOfRange(offsets_, offsets_ + offsets_len, bp, buf_len) || + !isOutOfRange(data_, data_ + ndata_len, bp, buf_len)) { + isc_throw(BadValue, "serialize would break the source sequence"); + } + + *bp++ = offsets_len; + for (size_t i = 0; i < offsets_len; ++i) { + *bp++ = offsets_[first_label_ + i] - offsets_[first_label_]; + } + std::memcpy(bp, &data_[offsets_[first_label_]], ndata_len); + bp += ndata_len; + + isc_throw_assert(bp - reinterpret_cast(buf) == expected_size); +} + +bool +LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const { + size_t len, other_len; + const uint8_t* data = getData(&len); + const uint8_t* other_data = other.getData(&other_len); + + if (len != other_len) { + return (false); + } + if (case_sensitive) { + return (std::memcmp(data, other_data, len) == 0); + } + + // As long as the data was originally validated as (part of) a name, + // label length must never be a capital ascii character, so we can + // simply compare them after converting to lower characters. + for (size_t i = 0; i < len; ++i) { + const uint8_t ch = data[i]; + const uint8_t other_ch = other_data[i]; + if (isc::dns::name::internal::maptolower[ch] != + isc::dns::name::internal::maptolower[other_ch]) { + return (false); + } + } + return (true); +} + +NameComparisonResult +LabelSequence::compare(const LabelSequence& other, + bool case_sensitive) const +{ + // Determine the relative ordering under the DNSSEC order relation of + // 'this' and 'other', and also determine the hierarchical relationship + // of the labels. + + unsigned int nlabels = 0; + int l1 = getLabelCount(); + int l2 = other.getLabelCount(); + const int ldiff = static_cast(l1) - static_cast(l2); + unsigned int l = (ldiff < 0) ? l1 : l2; + + while (l > 0) { + --l; + --l1; + --l2; + size_t pos1 = offsets_[l1 + first_label_]; + size_t pos2 = other.offsets_[l2 + other.first_label_]; + unsigned int count1 = data_[pos1++]; + unsigned int count2 = other.data_[pos2++]; + + // We don't support any extended label types including now-obsolete + // bitstring labels. + isc_throw_assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN); + + const int cdiff = static_cast(count1) - static_cast(count2); + unsigned int count = (cdiff < 0) ? count1 : count2; + + while (count > 0) { + const uint8_t label1 = data_[pos1]; + const uint8_t label2 = other.data_[pos2]; + int chdiff; + + if (case_sensitive) { + chdiff = static_cast(label1) - static_cast(label2); + } else { + chdiff = static_cast( + isc::dns::name::internal::maptolower[label1]) - + static_cast( + isc::dns::name::internal::maptolower[label2]); + } + + if (chdiff != 0) { + return (NameComparisonResult( + chdiff, nlabels, + nlabels == 0 ? NameComparisonResult::NONE : + NameComparisonResult::COMMONANCESTOR)); + } + --count; + ++pos1; + ++pos2; + } + if (cdiff != 0) { + return (NameComparisonResult( + cdiff, nlabels, + nlabels == 0 ? NameComparisonResult::NONE : + NameComparisonResult::COMMONANCESTOR)); + } + ++nlabels; + } + + if (ldiff < 0) { + return (NameComparisonResult(ldiff, nlabels, + NameComparisonResult::SUPERDOMAIN)); + } else if (ldiff > 0) { + return (NameComparisonResult(ldiff, nlabels, + NameComparisonResult::SUBDOMAIN)); + } + + return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL)); +} + +void +LabelSequence::stripLeft(size_t i) { + if (i >= getLabelCount()) { + isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i << + " (labelcount: " << getLabelCount() << ")"); + } + first_label_ += i; +} + +void +LabelSequence::stripRight(size_t i) { + if (i >= getLabelCount()) { + isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i << + " (labelcount: " << getLabelCount() << ")"); + } + last_label_ -= i; +} + +bool +LabelSequence::isAbsolute() const { + return (data_[offsets_[last_label_]] == 0); +} + +size_t +LabelSequence::getHash(bool case_sensitive) const { + size_t length; + const uint8_t* s = getData(&length); + if (length > 16) { + length = 16; + } + + size_t hash_val = 0; + while (length > 0) { + const uint8_t c = *s++; + boost::hash_combine(hash_val, case_sensitive ? c : + isc::dns::name::internal::maptolower[c]); + --length; + } + return (hash_val); +} + +std::string +LabelSequence::toRawText(bool omit_final_dot) const { + const uint8_t* np = &data_[offsets_[first_label_]]; + const uint8_t* np_end = np + getDataLength(); + + // use for integrity check + unsigned int labels = getLabelCount(); + // init with an impossible value to catch error cases in the end: + unsigned int count = Name::MAX_LABELLEN + 1; + + // result string: it will roughly have the same length as the wire format + // label sequence data. reserve that length to minimize reallocation. + std::string result; + result.reserve(getDataLength()); + + while (np != np_end) { + labels--; + count = *np++; + + if (count == 0) { + // We've reached the "final dot". If we've not dumped any + // character, the entire label sequence is the root name. + // In that case we don't omit the final dot. + if (!omit_final_dot || result.empty()) { + result.push_back('.'); + } + break; + } + + if (count <= Name::MAX_LABELLEN) { + isc_throw_assert(np_end - np >= count); + + if (!result.empty()) { + // just after a non-empty label. add a separating dot. + result.push_back('.'); + } + + while (count-- > 0) { + const uint8_t c = *np++; + result.push_back(c); + } + } else { + isc_throw(BadLabelType, "unknown label type in name data"); + } + } + + // We should be at the end of the data and have consumed all labels. + isc_throw_assert(np == np_end); + isc_throw_assert(labels == 0); + + return (result); +} + + +std::string +LabelSequence::toText(bool omit_final_dot) const { + const uint8_t* np = &data_[offsets_[first_label_]]; + const uint8_t* np_end = np + getDataLength(); + + // use for integrity check + unsigned int labels = getLabelCount(); + // init with an impossible value to catch error cases in the end: + unsigned int count = Name::MAX_LABELLEN + 1; + + // result string: it will roughly have the same length as the wire format + // label sequence data. reserve that length to minimize reallocation. + std::string result; + result.reserve(getDataLength()); + + while (np != np_end) { + labels--; + count = *np++; + + if (count == 0) { + // We've reached the "final dot". If we've not dumped any + // character, the entire label sequence is the root name. + // In that case we don't omit the final dot. + if (!omit_final_dot || result.empty()) { + result.push_back('.'); + } + break; + } + + if (count <= Name::MAX_LABELLEN) { + isc_throw_assert(np_end - np >= count); + + if (!result.empty()) { + // just after a non-empty label. add a separating dot. + result.push_back('.'); + } + + while (count-- > 0) { + const uint8_t c = *np++; + switch (c) { + case 0x22: // '"' + case 0x28: // '(' + case 0x29: // ')' + case 0x2E: // '.' + case 0x3B: // ';' + case 0x5C: // '\\' + // Special modifiers in zone files. + case 0x40: // '@' + case 0x24: // '$' + result.push_back('\\'); + result.push_back(c); + break; + default: + if (c > 0x20 && c < 0x7f) { + // append printable characters intact + result.push_back(c); + } else { + // encode non-printable characters in the form of \DDD + result.push_back(0x5c); + result.push_back(0x30 + ((c / 100) % 10)); + result.push_back(0x30 + ((c / 10) % 10)); + result.push_back(0x30 + (c % 10)); + } + } + } + } else { + isc_throw(BadLabelType, "unknown label type in name data"); + } + } + + // We should be at the end of the data and have consumed all labels. + isc_throw_assert(np == np_end); + isc_throw_assert(labels == 0); + + return (result); +} + +std::string +LabelSequence::toText() const { + return (toText(!isAbsolute())); +} + +void +LabelSequence::extend(const LabelSequence& labels, + uint8_t buf[MAX_SERIALIZED_LENGTH]) +{ + // collect data to perform steps before anything is changed + size_t label_count = last_label_ + 1; + // Since we may have been stripped, do not use getDataLength(), but + // calculate actual data size this labelsequence currently uses + size_t data_pos = offsets_[last_label_] + data_[offsets_[last_label_]] + 1; + + // If this labelsequence is absolute, virtually strip the root label. + if (isAbsolute()) { + data_pos--; + label_count--; + } + const size_t append_label_count = labels.getLabelCount(); + size_t data_len; + const uint8_t *data = labels.getData(&data_len); + + // Sanity checks + if (data_ != buf || offsets_ != &buf[Name::MAX_WIRE]) { + isc_throw(BadValue, + "extend() called with unrelated buffer"); + } + // Check MAX_LABELS before MAX_WIRE or it will be never reached + if (label_count + append_label_count > Name::MAX_LABELS) { + isc_throw(BadValue, + "extend() would exceed maximum number of labels"); + } + if (data_pos + data_len > Name::MAX_WIRE) { + isc_throw(BadValue, + "extend() would exceed maximum wire length"); + } + + // All seems to be reasonably ok, let's proceed. + std::memmove(&buf[data_pos], data, data_len); + + for (size_t i = 0; i < append_label_count; ++i) { + buf[Name::MAX_WIRE + label_count + i] = + data_pos + + labels.offsets_[i + labels.first_label_] - + labels.offsets_[labels.first_label_]; + } + last_label_ = label_count + append_label_count - 1; +} + +std::ostream& +operator<<(std::ostream& os, const LabelSequence& label_sequence) { + os << label_sequence.toText(); + return (os); +} + +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h new file mode 100644 index 0000000..91dbe59 --- /dev/null +++ b/src/lib/dns/labelsequence.h @@ -0,0 +1,456 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef LABELSEQUENCE_H +#define LABELSEQUENCE_H 1 + +#include +#include + +namespace isc { +namespace dns { + +/// \brief Light-weight Accessor to Name data. +/// +/// The purpose of this class is to easily match Names and parts of Names, +/// without needing to copy the underlying data on each label strip. +/// +/// It can only work on existing Name objects, or data as provided by the +/// Name object or another LabelSequence, and the data or Name MUST +/// remain in scope during the entire lifetime of its associated +/// LabelSequence(s). +/// +/// Upon creation of a LabelSequence, it records the offsets of the +/// labels in the wireformat data of the Name. When stripLeft() or +/// stripRight() is called on the LabelSequence, no changes in the +/// original data occur, but the internal pointers of the +/// LabelSequence are modified. +/// +/// LabelSequences can be compared to other LabelSequences, and their +/// data can be requested (which then points to part of the original +/// data of the original Name object). +class LabelSequence { + // Name calls the private toText(bool) method of LabelSequence. + friend std::string Name::toText(bool) const; + +public: + /// \brief Max possible size of serialized image generated by \c serialize + /// + /// A fixed length buffer of this size can be always passed to + /// \c serialize() safely. (But the application shouldn't use the + /// specific size value; it must use this constant variable). + static const size_t MAX_SERIALIZED_LENGTH = + Name::MAX_WIRE + Name::MAX_LABELS + 1; + + /// + /// \name Well-known LabelSequence constants + /// + //@{ + /// Wildcard label ("*") + static const LabelSequence& WILDCARD(); + //@} + + /// \brief Constructs a LabelSequence for the given name + /// + /// \note The associated Name MUST remain in scope during the lifetime + /// of this LabelSequence, since getData() refers to data from the + /// Name object (the only data the LabelSequence stores are pointers + /// to the labels in the Name object). + /// + /// \param name The Name to construct a LabelSequence for + explicit LabelSequence(const Name& name): + data_(&name.ndata_[0]), + offsets_(&name.offsets_[0]), + first_label_(0), + last_label_(name.getLabelCount() - 1) + {} + + /// \brief Constructor from serialized image. + /// + /// This constructor restores a \c LabelSequence object from a serialized + /// binary image previously generated by \c serialize(). Any other input + /// to this constructor will result in undefined behavior. + /// + /// The binary data passed to this constructor MUST remain in scope and + /// MUST NOT be modified during the lifetime of this LabelSequence. + /// + /// As long as the data were previously generated by a call to + /// \c serialize() on a valid \c LabelSequence object, this constructor + /// should succeed. While any other case is undefined, this constructor + /// may perform some validity checks internally for safety. Nevertheless, + /// applications must not rely on such checks. + /// + /// \param buf Pointer to the serialized image generated by \c serialize(). + explicit LabelSequence(const void* buf); + + /// \brief Construct 'extendable' LabelSequence + /// + /// This form of LabelSequence copies the data from the given + /// labelsequence into the given external buffer, which is subsequently + /// extendable by calling extend() + /// + /// The data is placed into the given buffer as follows: + /// - binary sequence of name data, starting at position 0, + /// length determined by source LabelSequence + /// - offsets, starting at position Name::MAX_WIRE, length + /// determined by source LabelSequence + /// The offsets are updated to be correct for the potentially partial + /// name data (as stripLeft() and stripRight may have been called on + /// the source LabelSequence). + /// + /// \note The given buf MUST remain in scope during the lifetime of + /// the LabelSequence created here. + /// \note The buffer should never be modified except through + /// calls to extend(). + /// \note Also, only associate the buffer with at most one + /// LabelSequence. Behaviour is undefined if two LabelSequences are + /// using the same buffer. + /// + /// \param src LabelSequence to copy the initial data from + /// \param buf external buffer to store this labelsequence's data in + LabelSequence(const LabelSequence& src, uint8_t buf[MAX_SERIALIZED_LENGTH]); + + /// \brief Copy constructor. + /// + /// \note The associated data MUST remain in scope during the lifetime + /// of this LabelSequence, since only the pointers are copied. + /// + /// \note No validation is done on the given data upon construction; + /// use with care. + /// + /// \param ls The LabelSequence to construct a LabelSequence from + LabelSequence(const LabelSequence& ls): + data_(ls.data_), + offsets_(ls.offsets_), + first_label_(ls.first_label_), + last_label_(ls.last_label_) + {} + + /// \brief Assignment operator. + /// + /// \note The associated data MUST remain in scope during the lifetime + /// of this LabelSequence, since only the pointers are copied. + /// + /// \note No validation is done on the given data upon construction; + /// use with care. + /// + /// \param other The LabelSequence to assign a LabelSequence from + LabelSequence& operator=(const LabelSequence& other) { + if (this != &other) { + // Not self-assignment. + data_ = other.data_; + offsets_ = other.offsets_; + first_label_ = other.first_label_; + last_label_ = other.last_label_; + } + return (*this); + } + + /// \brief Return the wire-format data for this LabelSequence + /// + /// The data is returned as a pointer to (the part of) the original + /// wireformat data, from either the original Name object, or the + /// raw data given in the constructor, and the given len value is + /// set to the number of octets that match this labelsequence. + /// + /// \note The data pointed to is only valid if the original Name + /// object or data is still in scope + /// + /// \param len Pointer to a size_t where the length of the data + /// will be stored (in number of octets) + /// \return Pointer to the wire-format data of this label sequence + const uint8_t* getData(size_t* len) const; + + /// \brief Return the length of the wire-format data of this LabelSequence + /// + /// This method returns the number of octets for the data that would + /// be returned by the \c getData() method. + /// + /// Note that the return value of this method is always positive. + /// Note also that if the return value of this method is 1, it means the + /// sequence consists of the null label, i.e., a single "dot", and vice + /// versa. + /// + /// \note The data pointed to is only valid if the original Name + /// object or data is still in scope + /// + /// \return The length of the data of the label sequence in octets. + size_t getDataLength() const; + + /// \brief Return the size of serialized image of the \c LabelSequence. + /// + /// This method calculates the size of necessary storage to store + /// serialized image of this \c LabelSequence (which would be dumped by + /// \c serialize()) and returns it. The size is in bytes. + /// + /// \throw none. + /// + /// \return The size of serialized image of the \c LabelSequence. + size_t getSerializedLength() const; + + /// \brief Serialize the \c LabelSequence object in to a buffer. + /// + /// This method dumps a serialized image of this \c LabelSequence + /// that would be restored by the corresponding constructor into the + /// given buffer. The buffer size must be at least equal to + /// the value returned by getSerializedLength() (it can be larger than + /// that). + /// + /// Be careful about where the buffer is located; due to the nature + /// of the buffer, it's quite possible that the memory region is being used + /// to construct another active \c LabelSequence. In such a case + /// the serialization would silently break that sequence object, and + /// it will be very difficult to identify the cause. This method + /// has minimal level checks to avoid such disruption: If the serialization + /// would break "this" \c LabelSequence object, it doesn't write anything + /// to the given buffer and throw a \c isc::BadValue exception. + /// + /// In general, it should be safe to call this method on a + /// \c LabelSequence object constructed from a \c Name object or + /// a copy of such \c LabelSequence. When you construct \c LabelSequence + /// from pre-serialized data, calling this method on it can be unsafe. + /// One safe (but a bit less efficient) way in such a case is to make + /// the source \c LabelSequence temporary and immediately create a + /// local copy using an explicit buffer, and call this method on the + /// latter: + /// \code + /// // don't do this, it's not safe (and would result in exception): + /// // LabelSequence(buf).serialize(buf, buf_len); + /// + /// // The following are the safe way: + /// uint8_t ext_buf[LabelSequence::MAX_SERIALIZED_LENGTH]; + /// LabelSequence seq(LabelSequence(buf), ext_buf); + /// ... (strip the labels, etc) + /// seq.serialize(buf, buf_len); // it's safe to override buf here + /// \endcode + /// + /// The serialized image would be as follows: + /// - olen: number of offsets (1 byte) + /// - binary sequence of offsets (olen bytes, verbatim copy of offsets_ + /// of this size) + /// - binary sequence of name data (length determined by itself, verbatim + /// copy of data_ of the corresponding size) + /// + /// Applications must use the resulting image as opaque value and must not + /// use it for other purposes than input to the corresponding constructor + /// to restore it. Application behavior that assumes the specific + /// organization of the image is not guaranteed. + /// + /// \throw isc::BadValue buf_len is too short (this method never throws + /// otherwise) or the serialization would override internal data of + /// of the source LabelSequence. + /// + /// \param buf Pointer to the placeholder to dump the serialized image + /// \param buf_len The size of available region in \c buf + void serialize(void* buf, size_t buf_len) const; + + /// \brief Compares two label sequences for equality. + /// + /// Performs a (optionally case-sensitive) comparison between this + /// LabelSequence and another LabelSequence for equality. + /// + /// \param other The LabelSequence to compare with + /// \param case_sensitive If true, comparison is case-sensitive + /// \return true if The label sequences consist are the same length, + /// and contain the same data. + bool equals(const LabelSequence& other, bool case_sensitive = false) const; + + /// \brief Compares two label sequences for equality (case ignored). + /// + /// This is equivalent to this->equals(other). + /// + /// The operator version is convenient some specific cases such as in + /// unit tests. + bool operator==(const LabelSequence& other) const { + return (equals(other)); + } + + /// \brief Compares two label sequences. + /// + /// Performs a (optionally case-insensitive) comparison between this + /// LabelSequence and another LabelSequence. + /// + /// \param other The LabelSequence to compare with + /// \param case_sensitive If true, comparison is case-insensitive + /// \return a NameComparisonResult object representing the + /// comparison result. + NameComparisonResult compare(const LabelSequence& other, + bool case_sensitive = false) const; + + /// \brief Remove labels from the front of this LabelSequence + /// + /// \note No actual memory is changed, this operation merely updates the + /// internal pointers based on the offsets in the Name object. + /// + /// \exception OutOfRange if i is greater than or equal to the number + /// of labels currently pointed to by this LabelSequence + /// + /// \param i The number of labels to remove. + void stripLeft(size_t i); + + /// \brief Remove labels from the end of this LabelSequence + /// + /// \note No actual memory is changed, this operation merely updates the + /// internal pointers based on the offsets originally provided. + /// + /// \exception OutOfRange if i is greater than or equal to the number + /// of labels currently pointed to by this LabelSequence + /// + /// \param i The number of labels to remove. + void stripRight(size_t i); + + /// \brief Returns the current number of labels for this LabelSequence + /// + /// \return The number of labels + size_t getLabelCount() const { + return (last_label_ - first_label_ + 1); + } + + /// \brief Convert the LabelSequence to a string. + /// + /// This method returns a std::string object representing the + /// LabelSequence as a string. The returned string ends with a dot + /// '.' if the label sequence is absolute. + /// + /// This function assumes the underlying data is in proper + /// uncompressed wire format. If it finds an unexpected label + /// character including compression pointer, an exception of class + /// \c BadLabelType will be thrown. In addition, if resource + /// allocation for the result string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return a string representation of the LabelSequence. + std::string toText() const; + + /// \brief Convert the LabelSequence to a string without escape sequences. + /// + /// The string returned will contain a single character value for any + /// escape sequences in the label(s). + /// + /// \param omit_final_dot whether to omit the trailing dot in the output. + /// \return a string representation of the LabelSequence + /// that does not contain escape sequences. + std::string toRawText(bool omit_final_dot) const; + + /// \brief Extend this LabelSequence with the given labelsequence + /// + /// The given labels are appended to the name data, and internal + /// offset data is updated accordingly. + /// + /// The data from the given LabelSequence is copied into the buffer + /// associated with this LabelSequence; the appended LabelSequence + /// (the 'labels' argument) can be released if it is not needed for + /// other operations anymore. + /// + /// If this LabelSequence is absolute, its root label will be stripped + /// before the given LabelSequence is appended; after extend(), + /// this LabelSequence will be absolute if, and only if, the appended + /// LabelSequence was. A side-effect of this property is that adding + /// the root label to an absolute LabelSequence has no effect (the + /// root label is stripped, then added again). + /// + /// Some minimal checking is done on the data, but internal integrity + /// is not assumed. Do NOT modify the given buffer except through calls + /// to this method, and do NOT call this method if the buffer is + /// associated to another LabelSequence (behaviour of the other + /// LabelSequence is undefined in that scenario). + /// + /// \exception BadValue If the buffer does not appear to be associated + /// with this LabelSequence, or if the maximum wire length or maximum + /// number of labels would be exceeded by this operation + /// + /// \param labels The labels to append to this LabelSequence + /// \param buf The buffer associated with this LabelSequence + void extend(const LabelSequence& labels, + uint8_t buf[MAX_SERIALIZED_LENGTH]); + +private: + /// \brief Convert the LabelSequence to a string. + /// + /// This method is a version of the zero-argument toText() method, + /// that accepts a omit_final_dot argument. The + /// returned string ends with a dot '.' if + /// omit_final_dot is false. + /// + /// This method is used as a helper for Name::toText() + /// only. + /// + /// \param omit_final_dot whether to omit the trailing dot in the output. + /// \return a string representation of the LabelSequence. + std::string toText(bool omit_final_dot) const; +public: + /// \brief Calculate a simple hash for the label sequence. + /// + /// This method calculates a hash value for the label sequence as binary + /// data. If \c case_sensitive is false, it ignores the case stored in + /// the labels; specifically, it normalizes the labels by converting all + /// upper case characters to lower case ones and calculates the hash value + /// for the result. + /// + /// This method is intended to provide a lightweight way to store a + /// relatively small number of label sequences in a hash table. + /// For this reason it only takes into account data up to 16 octets + /// (16 was derived from BIND 9's implementation). Also, the function does + /// not provide any unpredictability; a specific sequence will always have + /// the same hash value. It should therefore not be used in the context + /// where an untrusted third party can mount a denial of service attack by + /// forcing the application to create a very large number of label + /// sequences that have the same hash value and expected to be stored in + /// a hash table. + /// + /// \exception None + /// + /// \param case_sensitive + /// \return A hash value for this label sequence. + size_t getHash(bool case_sensitive) const; + + /// \brief Checks whether the label sequence is absolute + /// + /// \return true if the last label is the root label + bool isAbsolute() const; + +private: + const uint8_t* data_; // wire-format name data + const uint8_t* offsets_; // an array of offsets in data_ for the labels + size_t first_label_; // index of offsets_ for the first label + size_t last_label_; // index of offsets_ for the last label. + // can be equal to first_label_, but must not + // be smaller (the class ensures that) +}; + + +/// +/// \brief Insert the label sequence as a string into stream. +/// +/// This method convert the \c label_sequence into a string and inserts +/// it into the output stream \c os. +/// +/// This function overloads the global operator<< to behave as described in +/// ostream::operator<< but applied to \c LabelSequence objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param label_sequence The \c LabelSequence object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& +operator<<(std::ostream& os, const LabelSequence& label_sequence); + +inline const LabelSequence& +LabelSequence::WILDCARD() { + static const uint8_t wildcard_buf[4] = { 0x01, 0x00, 0x01, '*' }; + static const LabelSequence wild_ls(wildcard_buf); + return (wild_ls); +} + +} // end namespace dns +} // end namespace isc + +#endif + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/master_lexer.cc b/src/lib/dns/master_lexer.cc new file mode 100644 index 0000000..0d1292e --- /dev/null +++ b/src/lib/dns/master_lexer.cc @@ -0,0 +1,614 @@ +// Copyright (C) 2012-2015,2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace isc { +namespace dns { + +// The definition of SOURCE_SIZE_UNKNOWN. Note that we initialize it using +// a method of another library. Technically, this could trigger a static +// initialization fiasco. But in this particular usage it's very unlikely +// to happen because this value is expected to be used only as a return +// value of a MasterLexer's method, and its constructor needs definitions +// here. +const size_t MasterLexer::SOURCE_SIZE_UNKNOWN = + std::numeric_limits::max(); + +namespace { +typedef boost::shared_ptr InputSourcePtr; +} // end unnamed namespace +using namespace master_lexer_internal; + + +struct MasterLexer::MasterLexerImpl { + MasterLexerImpl() : source_(NULL), token_(MasterToken::NOT_STARTED), + total_size_(0), popped_size_(0), + paren_count_(0), last_was_eol_(true), + has_previous_(false), + previous_paren_count_(0), + previous_was_eol_(false) + { + separators_.set('\r'); + separators_.set('\n'); + separators_.set(' '); + separators_.set('\t'); + separators_.set('('); + separators_.set(')'); + separators_.set('"'); + esc_separators_.set('\r'); + esc_separators_.set('\n'); + } + + // A helper method to skip possible comments toward the end of EOL or EOF. + // commonly used by state classes. It returns the corresponding "end-of" + // character in case it's a comment; otherwise it simply returns the + // current character. + int skipComment(int c, bool escaped = false) { + if (c == ';' && !escaped) { + while (true) { + c = source_->getChar(); + if (c == '\n' || c == InputSource::END_OF_STREAM) { + return (c); + } + } + } + return (c); + } + + bool isTokenEnd(int c, bool escaped) { + // Special case of EOF (end of stream); this is not in the bitmaps + if (c == InputSource::END_OF_STREAM) { + return (true); + } + // In this implementation we only ensure the behavior for unsigned + // range of characters, so we restrict the range of the values up to + // 0x7f = 127 + return (escaped ? esc_separators_.test(c & 0x7f) : + separators_.test(c & 0x7f)); + } + + void setTotalSize() { + assert(source_ != NULL); + if (total_size_ != SOURCE_SIZE_UNKNOWN) { + const size_t current_size = source_->getSize(); + if (current_size != SOURCE_SIZE_UNKNOWN) { + total_size_ += current_size; + } else { + total_size_ = SOURCE_SIZE_UNKNOWN; + } + } + } + + std::vector sources_; + InputSource* source_; // current source (NULL if sources_ is empty) + MasterToken token_; // currently recognized token (set by a state) + std::vector data_; // placeholder for string data + + // Keep track of the total size of all sources and characters that have + // been read from sources already popped. + size_t total_size_; // accumulated size (# of chars) of sources + size_t popped_size_; // total size of sources that have been popped + + // These are used in states, and defined here only as a placeholder. + // The main lexer class does not need these members. + size_t paren_count_; // nest count of the parentheses + bool last_was_eol_; // whether the lexer just passed an end-of-line + + // Bitmaps that gives whether a given (positive) character should be + // considered a separator of a string/number token. The esc_ version + // is a subset of the other, excluding characters that can be ignored + // if escaped by a backslash. See isTokenEnd() for the bitmap size. + std::bitset<128> separators_; + std::bitset<128> esc_separators_; + + // These are to allow restoring state before previous token. + bool has_previous_; + size_t previous_paren_count_; + bool previous_was_eol_; +}; + +MasterLexer::MasterLexer() : impl_(new MasterLexerImpl) { +} + +MasterLexer::~MasterLexer() { + delete impl_; +} + +bool +MasterLexer::pushSource(const char* filename, std::string* error) { + if (filename == NULL) { + isc_throw(InvalidParameter, + "NULL filename for MasterLexer::pushSource"); + } + try { + impl_->sources_.push_back(InputSourcePtr(new InputSource(filename))); + } catch (const InputSource::OpenError& ex) { + if (error != NULL) { + *error = ex.what(); + } + return (false); + } + + impl_->source_ = impl_->sources_.back().get(); + impl_->has_previous_ = false; + impl_->last_was_eol_ = true; + impl_->setTotalSize(); + return (true); +} + +void +MasterLexer::pushSource(std::istream& input) { + try { + impl_->sources_.push_back(InputSourcePtr(new InputSource(input))); + } catch (const InputSource::OpenError& ex) { + // Convert the "internal" exception to public one. + isc_throw(Unexpected, "Failed to push a stream to lexer: " << + ex.what()); + } + impl_->source_ = impl_->sources_.back().get(); + impl_->has_previous_ = false; + impl_->last_was_eol_ = true; + impl_->setTotalSize(); +} + +void +MasterLexer::popSource() { + if (impl_->sources_.empty()) { + isc_throw(InvalidOperation, + "MasterLexer::popSource on an empty source"); + } + impl_->popped_size_ += impl_->source_->getPosition(); + impl_->sources_.pop_back(); + impl_->source_ = impl_->sources_.empty() ? NULL : + impl_->sources_.back().get(); + impl_->has_previous_ = false; +} + +size_t +MasterLexer::getSourceCount() const { + return (impl_->sources_.size()); +} + +std::string +MasterLexer::getSourceName() const { + if (impl_->sources_.empty()) { + return (std::string()); + } + return (impl_->sources_.back()->getName()); +} + +size_t +MasterLexer::getSourceLine() const { + if (impl_->sources_.empty()) { + return (0); + } + return (impl_->sources_.back()->getCurrentLine()); +} + +size_t +MasterLexer::getTotalSourceSize() const { + return (impl_->total_size_); +} + +size_t +MasterLexer::getPosition() const { + size_t position = impl_->popped_size_; + BOOST_FOREACH(InputSourcePtr& src, impl_->sources_) { + position += src->getPosition(); + } + return (position); +} + +const MasterToken& +MasterLexer::getNextToken(Options options) { + if (impl_->source_ == NULL) { + isc_throw(isc::InvalidOperation, "No source to read tokens from"); + } + // Store the current state so we can restore it in ungetToken + impl_->previous_paren_count_ = impl_->paren_count_; + impl_->previous_was_eol_ = impl_->last_was_eol_; + impl_->source_->mark(); + impl_->has_previous_ = true; + // Reset the token now. This is to check a token was actually produced. + // This is debugging aid. + impl_->token_ = MasterToken(MasterToken::NO_TOKEN_PRODUCED); + // And get the token + + // This actually handles EOF internally too. + const State* state = State::start(*this, options); + if (state != NULL) { + state->handle(*this); + } + // Make sure a token was produced. Since this Can Not Happen, we assert + // here instead of throwing. + assert(impl_->token_.getType() != MasterToken::ERROR || + impl_->token_.getErrorCode() != MasterToken::NO_TOKEN_PRODUCED); + return (impl_->token_); +} + +namespace { +inline MasterLexer::Options +optionsForTokenType(MasterToken::Type expect) { + switch (expect) { + case MasterToken::STRING: + return (MasterLexer::NONE); + case MasterToken::QSTRING: + return (MasterLexer::QSTRING); + case MasterToken::NUMBER: + return (MasterLexer::NUMBER); + default: + isc_throw(InvalidParameter, + "expected type for getNextToken not supported: " << expect); + } +} +} + +const MasterToken& +MasterLexer::getNextToken(MasterToken::Type expect, bool eol_ok) { + // Get the next token, specifying an appropriate option corresponding to + // the expected type. The result should be set in impl_->token_. + getNextToken(optionsForTokenType(expect)); + + if (impl_->token_.getType() == MasterToken::ERROR) { + if (impl_->token_.getErrorCode() == MasterToken::NUMBER_OUT_OF_RANGE) { + ungetToken(); + } + throw LexerError(__FILE__, __LINE__, impl_->token_); + } + + const bool is_eol_like = + (impl_->token_.getType() == MasterToken::END_OF_LINE || + impl_->token_.getType() == MasterToken::END_OF_FILE); + if (eol_ok && is_eol_like) { + return (impl_->token_); + } + if (impl_->token_.getType() == MasterToken::STRING && + expect == MasterToken::QSTRING) { + return (impl_->token_); + } + if (impl_->token_.getType() != expect) { + ungetToken(); + if (is_eol_like) { + throw LexerError(__FILE__, __LINE__, + MasterToken(MasterToken::UNEXPECTED_END)); + } + assert(expect == MasterToken::NUMBER); + throw LexerError(__FILE__, __LINE__, + MasterToken(MasterToken::BAD_NUMBER)); + } + + return (impl_->token_); +} + +void +MasterLexer::ungetToken() { + if (impl_->has_previous_) { + impl_->has_previous_ = false; + impl_->source_->ungetAll(); + impl_->last_was_eol_ = impl_->previous_was_eol_; + impl_->paren_count_ = impl_->previous_paren_count_; + } else { + isc_throw(isc::InvalidOperation, "No token to unget ready"); + } +} + +namespace { +const char* const error_text[] = { + "lexer not started", // NOT_STARTED + "unbalanced parentheses", // UNBALANCED_PAREN + "unexpected end of input", // UNEXPECTED_END + "unbalanced quotes", // UNBALANCED_QUOTES + "no token produced", // NO_TOKEN_PRODUCED + "number out of range", // NUMBER_OUT_OF_RANGE + "not a valid number", // BAD_NUMBER + "unexpected quotes" // UNEXPECTED_QUOTES +}; +const size_t error_text_max_count = sizeof(error_text) / sizeof(error_text[0]); +} // end unnamed namespace + +std::string +MasterToken::getErrorText() const { + if (type_ != ERROR) { + isc_throw(InvalidOperation, + "MasterToken::getErrorText() for non error type"); + } + + // The class integrity ensures the following: + assert(val_.error_code_ < error_text_max_count); + return (error_text[val_.error_code_]); +} + +namespace master_lexer_internal { +// Below we implement state classes for state transitions of MasterLexer. +// Note that these need to be defined here so that they can refer to +// the details of MasterLexerImpl. + +bool +State::wasLastEOL(const MasterLexer& lexer) const { + return (lexer.impl_->last_was_eol_); +} + +const MasterToken& +State::getToken(const MasterLexer& lexer) const { + return (lexer.impl_->token_); +} + +size_t +State::getParenCount(const MasterLexer& lexer) const { + return (lexer.impl_->paren_count_); +} + +namespace { +class CRLF : public State { +public: + CRLF() {} + virtual ~CRLF() {} // see the base class for the destructor + virtual void handle(MasterLexer& lexer) const { + // We've just seen '\r'. If this is part of a sequence of '\r\n', + // we combine them as a single END-OF-LINE. Otherwise we treat the + // single '\r' as an EOL and continue tokenization from the character + // immediately after '\r'. One tricky case is that there's a comment + // between '\r' and '\n'. This implementation combines these + // characters and treats them as a single EOL (the behavior derived + // from BIND 9). Technically this may not be correct, but in practice + // the caller wouldn't distinguish this case from the case it has + // two EOLs, so we simplify the process. + const int c = getLexerImpl(lexer)->skipComment( + getLexerImpl(lexer)->source_->getChar()); + if (c != '\n') { + getLexerImpl(lexer)->source_->ungetChar(); + } + getLexerImpl(lexer)->token_ = MasterToken(MasterToken::END_OF_LINE); + getLexerImpl(lexer)->last_was_eol_ = true; + } +}; + +class String : public State { +public: + String() {} + virtual ~String() {} // see the base class for the destructor + virtual void handle(MasterLexer& lexer) const; +}; + +class QString : public State { +public: + QString() {} + virtual ~QString() {} // see the base class for the destructor + virtual void handle(MasterLexer& lexer) const; +}; + +class Number : public State { +public: + Number() {} + virtual ~Number() {} + virtual void handle(MasterLexer& lexer) const; +}; + +// We use a common instance of a each state in a singleton-like way to save +// construction overhead. They are not singletons in its strict sense as +// we don't prohibit direct construction of these objects. But that doesn't +// matter much anyway, because the definitions are completely hidden within +// this file. +const CRLF CRLF_STATE; +const String STRING_STATE; +const QString QSTRING_STATE; +const Number NUMBER_STATE; +} // end unnamed namespace + +const State& +State::getInstance(ID state_id) { + switch (state_id) { + case CRLF: + return (CRLF_STATE); + case String: + return (STRING_STATE); + case QString: + return (QSTRING_STATE); + case Number: + return (NUMBER_STATE); + } + + // This is a bug of the caller, and this method is only expected to be + // used by tests, so we just forcefully make it fail by asserting the + // condition. + assert(false); + return (STRING_STATE); // a dummy return, to silence some compilers. +} + +const State* +State::start(MasterLexer& lexer, MasterLexer::Options options) { + // define some shortcuts + MasterLexer::MasterLexerImpl& lexerimpl = *lexer.impl_; + size_t& paren_count = lexerimpl.paren_count_; + + // Note: the if-else in the loop is getting complicated. When we complete + // #2374, revisit the organization to see if we need a fundamental + // refactoring. + while (true) { + const int c = lexerimpl.skipComment(lexerimpl.source_->getChar()); + if (c == InputSource::END_OF_STREAM) { + lexerimpl.last_was_eol_ = false; + if (paren_count != 0) { + lexerimpl.token_ = MasterToken(MasterToken::UNBALANCED_PAREN); + paren_count = 0; // reset to 0; this helps in lenient mode. + return (NULL); + } + lexerimpl.token_ = MasterToken(MasterToken::END_OF_FILE); + return (NULL); + } else if (c == ' ' || c == '\t') { + // If requested and we are not in (), recognize the initial space. + if (lexerimpl.last_was_eol_ && paren_count == 0 && + (options & MasterLexer::INITIAL_WS) != 0) { + lexerimpl.last_was_eol_ = false; + lexerimpl.token_ = MasterToken(MasterToken::INITIAL_WS); + return (NULL); + } + } else if (c == '\n') { + lexerimpl.last_was_eol_ = true; + if (paren_count == 0) { // we don't recognize EOL if we are in () + lexerimpl.token_ = MasterToken(MasterToken::END_OF_LINE); + return (NULL); + } + } else if (c == '\r') { + if (paren_count == 0) { // check if we are in () (see above) + return (&CRLF_STATE); + } + } else if (c == '"') { + if ((options & MasterLexer::QSTRING) != 0) { + lexerimpl.last_was_eol_ = false; + return (&QSTRING_STATE); + } else { + lexerimpl.token_ = MasterToken(MasterToken::UNEXPECTED_QUOTES); + return (NULL); + } + } else if (c == '(') { + lexerimpl.last_was_eol_ = false; + ++paren_count; + } else if (c == ')') { + lexerimpl.last_was_eol_ = false; + if (paren_count == 0) { + lexerimpl.token_ = MasterToken(MasterToken::UNBALANCED_PAREN); + return (NULL); + } + --paren_count; + } else if ((options & MasterLexer::NUMBER) != 0 &&isdigit(c)) { + lexerimpl.last_was_eol_ = false; + // this character will be handled in the number state + lexerimpl.source_->ungetChar(); + return (&NUMBER_STATE); + } else { + // this character will be handled in the string state + lexerimpl.source_->ungetChar(); + lexerimpl.last_was_eol_ = false; + return (&STRING_STATE); + } + // no code should be here; we just continue the loop. + } +} + +void +String::handle(MasterLexer& lexer) const { + std::vector& data = getLexerImpl(lexer)->data_; + data.clear(); + + bool escaped = false; + while (true) { + const int c = getLexerImpl(lexer)->skipComment( + getLexerImpl(lexer)->source_->getChar(), escaped); + + if (getLexerImpl(lexer)->isTokenEnd(c, escaped)) { + getLexerImpl(lexer)->source_->ungetChar(); + // make sure it nul-terminated as a c-str (excluded from token + // data). + data.push_back('\0'); + getLexerImpl(lexer)->token_ = + MasterToken(&data.at(0), data.size() - 1); + return; + } + escaped = (c == '\\' && !escaped); + data.push_back(c); + } +} + +void +QString::handle(MasterLexer& lexer) const { + MasterToken& token = getLexerImpl(lexer)->token_; + std::vector& data = getLexerImpl(lexer)->data_; + data.clear(); + + bool escaped = false; + while (true) { + const int c = getLexerImpl(lexer)->source_->getChar(); + if (c == InputSource::END_OF_STREAM) { + token = MasterToken(MasterToken::UNEXPECTED_END); + return; + } else if (c == '"') { + if (escaped) { + // found escaped '"'. overwrite the preceding backslash. + assert(!data.empty()); + escaped = false; + data.back() = '"'; + } else { + // make sure it nul-terminated as a c-str (excluded from token + // data). This also simplifies the case of an empty string. + data.push_back('\0'); + token = MasterToken(&data.at(0), data.size() - 1, true); + return; + } + } else if (c == '\n' && !escaped) { + getLexerImpl(lexer)->source_->ungetChar(); + token = MasterToken(MasterToken::UNBALANCED_QUOTES); + return; + } else { + escaped = (c == '\\' && !escaped); + data.push_back(c); + } + } +} + +void +Number::handle(MasterLexer& lexer) const { + MasterToken& token = getLexerImpl(lexer)->token_; + + // It may yet turn out to be a string, so we first + // collect all the data + bool digits_only = true; + std::vector& data = getLexerImpl(lexer)->data_; + data.clear(); + bool escaped = false; + + while (true) { + const int c = getLexerImpl(lexer)->skipComment( + getLexerImpl(lexer)->source_->getChar(), escaped); + if (getLexerImpl(lexer)->isTokenEnd(c, escaped)) { + getLexerImpl(lexer)->source_->ungetChar(); + // We need to close the string whether it's digits-only (for + // lexical_cast) or not (see String::handle()). + data.push_back('\0'); + if (digits_only) { + try { + const uint32_t number32 = + boost::lexical_cast(&data[0]); + token = MasterToken(number32); + } catch (const boost::bad_lexical_cast&) { + // Since we already know we have only digits, + // range should be the only possible problem. + token = MasterToken(MasterToken::NUMBER_OUT_OF_RANGE); + } + } else { + token = MasterToken(&data.at(0), data.size() - 1); + } + return; + } + if (!isdigit(c)) { + digits_only = false; + } + escaped = (c == '\\' && !escaped); + data.push_back(c); + } +} + +} // namespace master_lexer_internal + +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/master_lexer.h b/src/lib/dns/master_lexer.h new file mode 100644 index 0000000..9ed4e81 --- /dev/null +++ b/src/lib/dns/master_lexer.h @@ -0,0 +1,678 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MASTER_LEXER_H +#define MASTER_LEXER_H 1 + +#include + +#include +#include + +#include + +#include + +namespace isc { +namespace dns { +namespace master_lexer_internal { +class State; +} + +/// \brief Tokens for \c MasterLexer +/// +/// This is a simple value-class encapsulating a type of a lexer token and +/// (if it has a value) its value. Essentially, the class provides +/// constructors corresponding to different types of tokens, and corresponding +/// getter methods. The type and value are fixed at the time of construction +/// and will never be modified throughout the lifetime of the object. +/// The getter methods are still provided to maximize the safety; an +/// application cannot refer to a value that is invalid for the type of token. +/// +/// This class is intentionally implemented as copyable and assignable +/// (using the default version of copy constructor and assignment operator), +/// but it's mainly for internal implementation convenience. Applications will +/// simply refer to Token object as a reference via the \c MasterLexer class. +class MasterToken { +public: + /// \brief Enumeration for token types + /// + /// \note At the time of initial implementation, all numeric tokens + /// that would be extracted from \c MasterLexer should be represented + /// as an unsigned 32-bit integer. If we see the need for larger integers + /// or negative numbers, we can then extend the token types. + enum Type { + END_OF_LINE, ///< End of line detected + END_OF_FILE, ///< End of file detected + INITIAL_WS, ///< White spaces at the beginning of a line after an + ///< end of line or at the beginning of file (if asked + // for detecting it) + NOVALUE_TYPE_MAX = INITIAL_WS, ///< Max integer corresponding to + /// no-value (type only) types. + /// Mainly for internal use. + STRING, ///< A single string + QSTRING, ///< A single string quoted by double-quotes ("). + NUMBER, ///< A decimal number (unsigned 32-bit) + ERROR ///< Error detected in getting a token + }; + + /// \brief Enumeration for lexer error codes + enum ErrorCode { + NOT_STARTED, ///< The lexer is just initialized and has no token + UNBALANCED_PAREN, ///< Unbalanced parentheses detected + UNEXPECTED_END, ///< The lexer reaches the end of line or file + /// unexpectedly + UNBALANCED_QUOTES, ///< Unbalanced quotations detected + NO_TOKEN_PRODUCED, ///< No token was produced. This means programmer + /// error and should never get out of the lexer. + NUMBER_OUT_OF_RANGE, ///< Number was out of range + BAD_NUMBER, ///< Number is expected but not recognized + UNEXPECTED_QUOTES, ///< Unexpected quotes character detected + MAX_ERROR_CODE ///< Max integer corresponding to valid error codes. + /// (excluding this one). Mainly for internal use. + }; + + /// \brief A simple representation of a range of a string. + /// + /// This is a straightforward pair of the start pointer of a string + /// and its length. The \c STRING and \c QSTRING types of tokens + /// will be primarily represented in this form. + /// + /// Any character can be stored in the valid range of the region. + /// In particular, there can be a nul character (\0) in the middle of + /// the region. So the usual string manipulation API may not work + /// as expected. + /// + /// The `MasterLexer` implementation ensures that there are at least + /// len + 1 bytes of valid memory region starting from beg, and that + /// beg[len] is \0. This means the application can use the bytes as a + /// validly nul-terminated C string if there is no intermediate nul + /// character. Note also that due to this property beg is always non + /// NULL; for an empty string len will be set to 0 and beg[0] is \0. + struct StringRegion { + const char* beg; ///< The start address of the string + size_t len; ///< The length of the string in bytes + }; + + /// \brief Constructor for non-value type of token. + /// + /// \throw InvalidParameter A value type token is specified. + /// \param type The type of the token. It must indicate a non-value + /// type (not larger than \c NOVALUE_TYPE_MAX). + explicit MasterToken(Type type) : type_(type) { + if (type > NOVALUE_TYPE_MAX) { + isc_throw(InvalidParameter, "Token per-type constructor " + "called with invalid type: " << type); + } + } + + /// \brief Constructor for string and quoted-string types of token. + /// + /// The optional \c quoted parameter specifies whether it's a quoted or + /// non quoted string. + /// + /// The string is specified as a pair of a pointer to the start address + /// and its length. Any character can be contained in any position of + /// the valid range (see \c StringRegion). + /// + /// When it's a quoted string, the quotation marks must be excluded + /// from the specified range. + /// + /// \param str_beg The start address of the string + /// \param str_len The size of the string in bytes + /// \param quoted true if it's a quoted string; false otherwise. + MasterToken(const char* str_beg, size_t str_len, bool quoted = false) : + type_(quoted ? QSTRING : STRING) + { + val_.str_region_.beg = str_beg; + val_.str_region_.len = str_len; + } + + /// \brief Constructor for number type of token. + /// + /// \brief number An unsigned 32-bit integer corresponding to the token + /// value. + explicit MasterToken(uint32_t number) : type_(NUMBER) { + val_.number_ = number; + } + + /// \brief Constructor for error type of token. + /// + /// \throw InvalidParameter Invalid error code value is specified. + /// \brief error_code A pre-defined constant of \c ErrorCode. + explicit MasterToken(ErrorCode error_code) : type_(ERROR) { + if (!(error_code < MAX_ERROR_CODE)) { + isc_throw(InvalidParameter, "Invalid master lexer error code: " + << error_code); + } + val_.error_code_ = error_code; + } + + /// \brief Return the token type. + /// + /// \throw none + Type getType() const { return (type_); } + + /// \brief Return the value of a string-variant token. + /// + /// \throw InvalidOperation Called on a non string-variant types of token. + /// \return A reference to \c StringRegion corresponding to the string + /// token value. + const StringRegion& getStringRegion() const { + if (type_ != STRING && type_ != QSTRING) { + isc_throw(InvalidOperation, + "Token::getStringRegion() for non string-variant type"); + } + return (val_.str_region_); + } + + /// \brief Return the value of a string-variant token as a string object. + /// + /// Note that the underlying string may contain a nul (\0) character + /// in the middle. The returned string object will contain all characters + /// of the valid range of the underlying string. So some string + /// operations such as c_str() may not work as expected. + /// + /// \throw InvalidOperation Called on a non string-variant types of token. + /// \throw std::bad_alloc Resource allocation failure in constructing the + /// string object. + /// \return A std::string object corresponding to the string token value. + std::string getString() const { + std::string ret; + getString(ret); + return (ret); + } + + /// \brief Fill in a string with the value of a string-variant token. + /// + /// This is similar to the other version of \c getString(), but + /// the caller is supposed to pass a placeholder string object. + /// This will be more efficient if the caller uses the same + /// \c MasterLexer repeatedly and needs to get string token in the + /// form of a string object many times as this version could reuse + /// the existing internal storage of the passed string. + /// + /// Any existing content of the passed string will be removed. + /// + /// \throw InvalidOperation Called on a non string-variant types of token. + /// \throw std::bad_alloc Resource allocation failure in constructing the + /// string object. + /// + /// \param ret A string object to be filled with the token string. + void getString(std::string& ret) const { + if (type_ != STRING && type_ != QSTRING) { + isc_throw(InvalidOperation, + "Token::getString() for non string-variant type"); + } + ret.assign(val_.str_region_.beg, + val_.str_region_.beg + val_.str_region_.len); + } + + /// \brief Return the value of a string-variant token as a string object. + /// + /// \throw InvalidOperation Called on a non number type of token. + /// \return The integer corresponding to the number token value. + uint32_t getNumber() const { + if (type_ != NUMBER) { + isc_throw(InvalidOperation, + "Token::getNumber() for non number type"); + } + return (val_.number_); + } + + /// \brief Return the error code of a error type token. + /// + /// \throw InvalidOperation Called on a non error type of token. + /// \return The error code of the token. + ErrorCode getErrorCode() const { + if (type_ != ERROR) { + isc_throw(InvalidOperation, + "Token::getErrorCode() for non error type"); + } + return (val_.error_code_); + }; + + /// \brief Return a textual description of the error of a error type token. + /// + /// The returned string would be useful to produce a log message when + /// a zone file parser encounters an error. + /// + /// \throw InvalidOperation Called on a non error type of token. + /// \throw std::bad_alloc Resource allocation failure in constructing the + /// string object. + /// \return A string object that describes the meaning of the error. + std::string getErrorText() const; + +private: + Type type_; // this is not const so the class can be assignable + + // We use a union to represent different types of token values via the + // unified Token class. The class integrity should ensure valid operation + // on the union; getter methods should only refer to the member set at + // the construction. + union { + StringRegion str_region_; + uint32_t number_; + ErrorCode error_code_; + } val_; +}; + +/// \brief Tokenizer for parsing DNS master files. +/// +/// The \c MasterLexer class provides tokenize interfaces for parsing DNS +/// master files. It understands some special rules of master files as +/// defined in RFC 1035, such as comments, character escaping, and multi-line +/// data, and provides the user application with the actual data in a +/// more convenient form such as a std::string object. +/// +/// In order to support the $INCLUDE notation, this class is designed to be +/// able to operate on multiple files or input streams in the nested way. +/// The \c pushSource() and \c popSource() methods correspond to the push +/// and pop operations. +/// +/// While this class is public, it is less likely to be used by normal +/// applications; it's mainly expected to be used within this library, +/// specifically by the \c MasterLoader class and \c Rdata implementation +/// classes. +/// +/// \note The error handling policy of this class is slightly different from +/// that of other classes of this library. We generally throw an exception +/// for an invalid input, whether it's more likely to be a program error or +/// a "user error", which means an invalid input that comes from outside of +/// the library. But, this class returns an error code for some certain +/// types of user errors instead of throwing an exception. Such cases include +/// a syntax error identified by the lexer or a misspelled file name that +/// causes a system error at the time of open. This is based on the assumption +/// that the main user of this class is a parser of master files, where +/// we want to give an option to ignore some non fatal errors and continue +/// the parsing. This will be useful if it just performs overall error +/// checks on a master file. When the (immediate) caller needs to do explicit +/// error handling, exceptions are not that a useful tool for error reporting +/// because we cannot separate the normal and error cases anyway, which would +/// be one major advantage when we use exceptions. And, exceptions are +/// generally more expensive, either when it happens or just by being able +/// to handle with \c try and \c catch (depending on the underlying +/// implementation of the exception handling). For these reasons, some of +/// this class does not throw for an error that would be reported as an +/// exception in other classes. +class MasterLexer : public boost::noncopyable { + friend class master_lexer_internal::State; +public: + /// \brief Exception thrown when we fail to read from the input + /// stream or file. + class ReadError : public Unexpected { + public: + ReadError(const char* file, size_t line, const char* what) : + Unexpected(file, line, what) + {} + }; + + /// \brief Exception thrown from a wrapper version of + /// \c MasterLexer::getNextToken() for non fatal errors. + /// + /// See the method description for more details. + /// + /// The \c token_ member variable (read-only) is set to a \c MasterToken + /// object of type ERROR indicating the reason for the error. + class LexerError : public isc::dns::Exception { + public: + LexerError(const char* file, size_t line, MasterToken error_token) : + isc::dns::Exception(file, line, error_token.getErrorText().c_str()), + token_(error_token) + {} + const MasterToken token_; + }; + + /// \brief Special value for input source size meaning "unknown". + /// + /// This constant value will be used as a return value of + /// \c getTotalSourceSize() when the size of one of the pushed sources + /// is unknown. Note that this value itself is a valid integer in the + /// range of the type, so there's still a small possibility of + /// ambiguity. In practice, however, the value should be sufficiently + /// large that should eliminate the possibility. + static const size_t SOURCE_SIZE_UNKNOWN; + + /// \brief Options for getNextToken. + /// + /// A compound option, indicating multiple options are set, can be + /// specified using the logical OR operator (operator|()). + enum Options { + NONE = 0, ///< No option + INITIAL_WS = 1, ///< recognize begin-of-line spaces after an + ///< end-of-line + QSTRING = 2, ///< recognize quoted string + NUMBER = 4 ///< recognize numeric text as integer + }; + + /// \brief The constructor. + /// + /// \throw std::bad_alloc Internal resource allocation fails (rare case). + MasterLexer(); + + /// \brief The destructor. + /// + /// It internally closes any remaining input sources. + ~MasterLexer(); + + /// \brief Open a file and make it the current input source of MasterLexer. + /// + /// The opened file can be explicitly closed by the \c popSource() method; + /// if \c popSource() is not called within the lifetime of the + /// \c MasterLexer, it will be closed in the destructor. + /// + /// In the case possible system errors in opening the file (most likely + /// because of specifying a non-existent or unreadable file), it returns + /// false, and if the optional \c error parameter is non NULL, it will be + /// set to a description of the error (any existing content of the string + /// will be discarded). If opening the file succeeds, the given + /// \c error parameter will be intact. + /// + /// Note that this method has two styles of error reporting: one by + /// returning \c false (and setting \c error optionally) and the other + /// by throwing an exception. See the note for the class description + /// about the distinction. + /// + /// \throw InvalidParameter filename is NULL + /// \param filename A non NULL string specifying a master file + /// \param error If non null, a placeholder to set error description in + /// case of failure. + /// + /// \return true if pushing the file succeeds; false otherwise. + bool pushSource(const char* filename, std::string* error = NULL); + + /// \brief Make the given stream the current input source of MasterLexer. + /// + /// The caller still holds the ownership of the passed stream; it's the + /// caller's responsibility to keep it valid as long as it's used in + /// \c MasterLexer or to release any resource for the stream after that. + /// The caller can explicitly tell \c MasterLexer to stop using the + /// stream by calling the \c popSource() method. + /// + /// The data in \c input must be complete at the time of this call. + /// The behavior of the lexer is undefined if the caller builds or adds + /// data in \c input after pushing it. + /// + /// Except for rare case system errors such as memory allocation failure, + /// this method is generally expected to be exception free. However, + /// it can still throw if it encounters an unexpected failure when it + /// tries to identify the "size" of the input source (see + /// \c getTotalSourceSize()). It's an unexpected result unless the + /// caller intentionally passes a broken stream; otherwise it would mean + /// some system-dependent unexpected behavior or possibly an internal bug. + /// In these cases it throws an \c Unexpected exception. Note that + /// this version of the method doesn't return a boolean unlike the + /// other version that takes a file name; since this failure is really + /// unexpected and can be critical, it doesn't make sense to give the + /// caller an option to continue (other than by explicitly catching the + /// exception). + /// + /// \throw Unexpected An unexpected failure happens in initialization. + /// + /// \param input An input stream object that produces textual + /// representation of DNS RRs. + void pushSource(std::istream& input); + + /// \brief Stop using the most recently opened input source (file or + /// stream). + /// + /// If it's a file, the previously opened file will be closed internally. + /// If it's a stream, \c MasterLexer will simply stop using + /// the stream; the caller can assume it will be never used in + /// \c MasterLexer thereafter. + /// + /// This method must not be called when there is no source pushed for + /// \c MasterLexer. This method is otherwise exception free. + /// + /// \throw isc::InvalidOperation Called with no pushed source. + void popSource(); + + /// \brief Get number of sources inside the lexer. + /// + /// This method never throws. + size_t getSourceCount() const; + + /// \brief Return the name of the current input source name. + /// + /// If it's a file, it will be the C string given at the corresponding + /// \c pushSource() call, that is, its filename. If it's a stream, it will + /// be formatted as \c "stream-%p" where \c %p is hex representation + /// of the address of the stream object. + /// + /// If there is no opened source at the time of the call, this method + /// returns an empty string. + /// + /// \throw std::bad_alloc Resource allocation failed for string + /// construction (rare case) + /// + /// \return A string representation of the current source (see the + /// description) + std::string getSourceName() const; + + /// \brief Return the input source line number. + /// + /// If there is an opened source, the return value will be a non-0 + /// integer indicating the line number of the current source where + /// the \c MasterLexer is currently working. The expected usage of + /// this value is to print a helpful error message when parsing fails + /// by specifically identifying the position of the error. + /// + /// If there is no opened source at the time of the call, this method + /// returns 0. + /// + /// \throw None + /// + /// \return The current line number of the source (see the description) + size_t getSourceLine() const; + + /// \brief Return the total size of pushed sources. + /// + /// This method returns the sum of the size of sources that have been + /// pushed to the lexer by the time of the call. It would give the + /// caller some hint about the amount of data the lexer is working on. + /// + /// The size of a normal file is equal to the file size at the time of + /// the source is pushed. The size of other type of input stream is + /// the size of the data available in the stream at the time of the + /// source is pushed. + /// + /// In some special cases, it's possible that the size of the file or + /// stream is unknown. It happens, for example, if the standard input + /// is associated with a pipe from the output of another process and it's + /// specified as an input source. If the size of some of the pushed + /// source is unknown, this method returns SOURCE_SIZE_UNKNOWN. + /// + /// The total size won't change when a source is popped. So the return + /// values of this method will monotonically increase or + /// \c SOURCE_SIZE_UNKNOWN; once it returns \c SOURCE_SIZE_UNKNOWN, + /// any subsequent call will also result in that value, by the above + /// definition. + /// + /// Before pushing any source, it returns 0. + /// + /// \throw None + size_t getTotalSourceSize() const; + + /// \brief Return the position of lexer in the pushed sources so far. + /// + /// This method returns the position in terms of the number of recognized + /// characters from all sources that have been pushed by the time of the + /// call. Conceptually, the position in a single source is the offset + /// from the beginning of the file or stream to the current "read cursor" + /// of the lexer. The return value of this method is the sum of the + /// positions in all the pushed sources. If any of the sources has + /// already been popped, the position of the source at the time of the + /// pop operation will be used for the calculation. + /// + /// If the lexer reaches the end for each of all the pushed sources, + /// the return value should be equal to that of \c getTotalSourceSize(). + /// It's generally expected that a source is popped when the lexer + /// reaches the end of the source. So, when the application of this + /// class parses all contents of all sources, possibly with multiple + /// pushes and pops, the return value of this method and + /// \c getTotalSourceSize() should be identical (unless the latter + /// returns SOURCE_SIZE_UNKNOWN). But this is not necessarily + /// guaranteed as the application can pop a source in the middle of + /// parsing it. + /// + /// Before pushing any source, it returns 0. + /// + /// The return values of this method and \c getTotalSourceSize() would + /// give the caller an idea of the progress of the lexer at the time of + /// the call. Note, however, that since it's not predictable whether + /// more sources will be pushed after the call, the progress determined + /// this way may not make much sense; it can only give an informational + /// hint of the progress. + /// + /// Note that the conceptual "read cursor" would move backward after a + /// call to \c ungetToken(), in which case this method will return a + /// smaller value. That is, unlike \c getTotalSourceSize(), return + /// values of this method may not always monotonically increase. + /// + /// \throw None + size_t getPosition() const; + + /// \brief Parse and return another token from the input. + /// + /// It reads a bit of the last opened source and produces another token + /// found in it. + /// + /// This method does not provide the strong exception guarantee. Generally, + /// if it throws, the object should not be used any more and should be + /// discarded. It was decided all the exceptions thrown from here are + /// serious enough that aborting the loading process is the only reasonable + /// recovery anyway, so the strong exception guarantee is not needed. + /// + /// \param options The options can be used to modify the tokenization. + /// The method can be made reporting things which are usually ignored + /// by this parameter. Multiple options can be passed at once by + /// bitwise or (eg. option1 | option 2). See description of available + /// options. + /// \return Next token found in the input. Note that the token refers to + /// some internal data in the lexer. It is valid only until + /// getNextToken or ungetToken is called. Also, the token becomes + /// invalid when the lexer is destroyed. + /// \throw isc::InvalidOperation in case the source is not available. This + /// may mean the pushSource() has not been called yet, or that the + /// current source has been read past the end. + /// \throw ReadError in case there's problem reading from the underlying + /// source (eg. I/O error in the file on the disk). + /// \throw std::bad_alloc in case allocation of some internal resources + /// or the token fail. + const MasterToken& getNextToken(Options options = NONE); + + /// \brief Parse the input for the expected type of token. + /// + /// This method is a wrapper of the other version, customized for the case + /// where a particular type of token is expected as the next one. + /// More specifically, it's intended to be used to get tokens for RDATA + /// fields. Since most RDATA types of fixed format, the token type is + /// often predictable and the method interface can be simplified. + /// + /// This method basically works as follows: it gets the type of the + /// expected token, calls the other version of \c getNextToken(Options), + /// and returns the token if it's of the expected type (due to the usage + /// assumption this should be normally the case). There are some non + /// trivial details though: + /// + /// - If the expected type is MasterToken::QSTRING, both quoted and + /// unquoted strings are recognized and returned. + /// - A string with quotation marks is not recognized as a + /// - MasterToken::STRING. You have to get it as a + /// - MasterToken::QSTRING. + /// - If the optional \c eol_ok parameter is \c true (very rare case), + /// MasterToken::END_OF_LINE and MasterToken::END_OF_FILE are recognized + /// and returned if they are found instead of the expected type of + /// token. + /// - If the next token is not of the expected type (including the case + /// a number is expected but it's out of range), ungetToken() is + /// internally called so the caller can re-read that token. + /// - If other types or errors (such as unbalanced parentheses) are + /// detected, the erroneous part isn't "ungotten"; the caller can + /// continue parsing after that part. + /// + /// In some very rare cases where the RDATA has an optional trailing field, + /// the \c eol_ok parameter would be set to \c true. This way the caller + /// can handle both cases (the field does or does not exist) by a single + /// call to this method. In all other cases \c eol_ok should be set to + /// \c false, and that is the default and can be omitted. + /// + /// Unlike the other version of \c getNextToken(Options), this method + /// throws an exception of type \c LexerError for non fatal errors such as + /// broken syntax or encountering an unexpected type of token. This way + /// the caller can write RDATA parser code without bothering to handle + /// errors for each field. For example, pseudo parser code for MX RDATA + /// would look like this: + /// \code + /// const uint32_t pref = + /// lexer.getNextToken(MasterToken::NUMBER).getNumber(); + /// // check if pref is the uint16_t range; no other check is needed. + /// const Name mx(lexer.getNextToken(MasterToken::STRING).getString()); + /// \endcode + /// + /// In the case where \c LexerError exception is thrown, it's expected + /// to be handled comprehensively for the parser of the RDATA or at a + /// higher layer. The \c token_ member variable of the corresponding + /// \c LexerError exception object stores a token of type + /// \c MasterToken::ERROR that indicates the reason for the error. + /// + /// Due to the specific intended usage of this method, only a subset + /// of \c MasterToken::Type values are acceptable for the \c expect + /// parameter: \c MasterToken::STRING, \c MasterToken::QSTRING, and + /// \c MasterToken::NUMBER. Specifying other values will result in + /// an \c InvalidParameter exception. + /// + /// \throw InvalidParameter The expected token type is not allowed for + /// this method. + /// \throw LexerError The lexer finds non fatal error or it finds an + /// \throw other Anything the other version of getNextToken() can throw. + /// + /// \param expect Expected type of token. Must be either STRING, QSTRING, + /// or NUMBER. + /// \param eol_ok \c true iff END_OF_LINE or END_OF_FILE is acceptable. + /// \return The expected type of token. + const MasterToken& getNextToken(MasterToken::Type expect, + bool eol_ok = false); + + /// \brief Return the last token back to the lexer. + /// + /// The method undoes the lasts call to getNextToken(). If you call the + /// getNextToken() again with the same options, it'll return the same + /// token. If the options are different, it may return a different token, + /// but it acts as if the previous getNextToken() was never called. + /// + /// It is possible to return only one token back in time (you can't call + /// ungetToken() twice in a row without calling getNextToken() in between + /// successfully). + /// + /// It does not work after change of source (by pushSource or popSource). + /// + /// \throw isc::InvalidOperation If called second time in a row or if + /// getNextToken() was not called since the last change of the source. + void ungetToken(); + +private: + struct MasterLexerImpl; + MasterLexerImpl* impl_; +}; + +/// \brief Operator to combine \c MasterLexer options +/// +/// This is a trivial shortcut so that compound options can be specified +/// in an intuitive way. +inline MasterLexer::Options +operator|(MasterLexer::Options o1, MasterLexer::Options o2) { + return (static_cast( + static_cast(o1) | static_cast(o2))); +} + +} // namespace dns +} // namespace isc +#endif // MASTER_LEXER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/master_lexer_inputsource.cc b/src/lib/dns/master_lexer_inputsource.cc new file mode 100644 index 0000000..841fc6c --- /dev/null +++ b/src/lib/dns/master_lexer_inputsource.cc @@ -0,0 +1,221 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace isc { +namespace dns { +namespace master_lexer_internal { + +namespace { // unnamed namespace + +std::string +createStreamName(const std::istream& input_stream) { + std::stringstream ss; + ss << "stream-" << &input_stream; + return (ss.str()); +} + +size_t +getStreamSize(std::istream& is) { + errno = 0; // see below + is.seekg(0, std::ios_base::end); + if (is.bad()) { + // This means the istream has an integrity error. It doesn't make + // sense to continue from this point, so we treat it as a fatal error. + isc_throw(InputSource::OpenError, + "failed to seek end of input source"); + } else if (is.fail() || errno != 0) { + // This is an error specific to seekg(). There can be several + // reasons, but the most likely cause in this context is that the + // stream is associated with a special type of file such as a pipe. + // In this case, it's more likely that other main operations of + // the input source work fine, so we continue with just setting + // the stream size to "unknown". + // + // (At least some versions of) Solaris + SunStudio shows deviant + // behavior here: seekg() apparently calls lseek(2) internally, but + // even if it fails it doesn't set the error bits of istream. That will + // confuse the rest of this function, so, as a heuristic workaround + // we check errno and handle any non 0 value as fail(). + is.clear(); // clear this error not to confuse later ops. + return (MasterLexer::SOURCE_SIZE_UNKNOWN); + } + const std::streampos len = is.tellg(); + size_t ret = len; + if (len == static_cast(-1)) { // cast for some compilers + if (!is.fail()) { + // tellg() returns -1 if istream::fail() would be true, but it's + // not guaranteed that it shouldn't be returned in other cases. + // In fact, with the combination of SunStudio and stlport, + // a stringstream created by the default constructor showed that + // behavior. We treat such cases as an unknown size. + ret = MasterLexer::SOURCE_SIZE_UNKNOWN; + } else { + isc_throw(InputSource::OpenError, "failed to get input size"); + } + } + is.seekg(0, std::ios::beg); + if (is.fail()) { + isc_throw(InputSource::OpenError, + "failed to seek beginning of input source"); + } + assert(len >= 0 || ret == MasterLexer::SOURCE_SIZE_UNKNOWN); + return (ret); +} + +} // end of unnamed namespace + +// Explicit definition of class static constant. The value is given in the +// declaration so it's not needed here. +const int InputSource::END_OF_STREAM; + +InputSource::InputSource(std::istream& input_stream) : + at_eof_(false), + line_(1), + saved_line_(line_), + buffer_pos_(0), + total_pos_(0), + name_(createStreamName(input_stream)), + input_(input_stream), + input_size_(getStreamSize(input_)) +{} + +namespace { +// A helper to initialize InputSource::input_ in the member initialization +// list. +std::istream& +openFileStream(std::ifstream& file_stream, const char* filename) { + errno = 0; + file_stream.open(filename); + if (file_stream.fail()) { + std::string error_txt("Error opening the input source file: "); + error_txt += filename; + if (errno != 0) { + error_txt += "; possible cause: "; + error_txt += std::strerror(errno); + } + isc_throw(InputSource::OpenError, error_txt); + } + + return (file_stream); +} +} + +InputSource::InputSource(const char* filename) : + at_eof_(false), + line_(1), + saved_line_(line_), + buffer_pos_(0), + total_pos_(0), + name_(filename), + input_(openFileStream(file_stream_, filename)), + input_size_(getStreamSize(input_)) +{} + +InputSource::~InputSource() +{ + if (file_stream_.is_open()) { + file_stream_.close(); + } +} + +int +InputSource::getChar() { + if (buffer_pos_ == buffer_.size()) { + // We may have reached EOF at the last call to + // getChar(). at_eof_ will be set then. We then simply return + // early. + if (at_eof_) { + return (END_OF_STREAM); + } + // We are not yet at EOF. Read from the stream. + const int c = input_.get(); + // Have we reached EOF now? If so, set at_eof_ and return early, + // but don't modify buffer_pos_ (which should still be equal to + // the size of buffer_). + if (input_.eof()) { + at_eof_ = true; + return (END_OF_STREAM); + } + // This has to come after the .eof() check as some + // implementations seem to check the eofbit also in .fail(). + if (input_.fail()) { + isc_throw(MasterLexer::ReadError, + "Error reading from the input stream: " << getName()); + } + buffer_.push_back(c); + } + + const int c = buffer_[buffer_pos_]; + ++buffer_pos_; + ++total_pos_; + if (c == '\n') { + ++line_; + } + + return (c); +} + +void +InputSource::ungetChar() { + if (at_eof_) { + at_eof_ = false; + } else if (buffer_pos_ == 0) { + isc_throw(UngetBeforeBeginning, + "Cannot skip before the start of buffer"); + } else { + --buffer_pos_; + --total_pos_; + if (buffer_[buffer_pos_] == '\n') { + --line_; + } + } +} + +void +InputSource::ungetAll() { + assert(total_pos_ >= buffer_pos_); + total_pos_ -= buffer_pos_; + buffer_pos_ = 0; + line_ = saved_line_; + at_eof_ = false; +} + +void +InputSource::saveLine() { + saved_line_ = line_; +} + +void +InputSource::compact() { + if (buffer_pos_ == buffer_.size()) { + buffer_.clear(); + } else { + buffer_.erase(buffer_.begin(), buffer_.begin() + buffer_pos_); + } + + buffer_pos_ = 0; +} + +void +InputSource::mark() { + saveLine(); + compact(); +} + +} // namespace master_lexer_internal +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/master_lexer_inputsource.h b/src/lib/dns/master_lexer_inputsource.h new file mode 100644 index 0000000..d830239 --- /dev/null +++ b/src/lib/dns/master_lexer_inputsource.h @@ -0,0 +1,185 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DNS_INPUTSOURCE_H +#define DNS_INPUTSOURCE_H 1 + +#include + +#include + +#include +#include +#include +#include + +namespace isc { +namespace dns { +namespace master_lexer_internal { + +/// \brief An input source that is used internally by MasterLexer. +/// +/// This is a helper internal class for MasterLexer, and represents +/// state of a single source of the entire zone data to be +/// parsed. Normally this means the master zone file, but MasterLexer +/// can have multiple InputSources if $INCLUDE is used. The source can +/// also be generic input stream (std::istream). +/// +/// This class is not meant for public use. We also enforce that +/// instances are non-copyable. +class InputSource : boost::noncopyable { +public: + /// \brief Returned by getChar() when end of stream is reached. + /// + /// \note C++ allows a static const class member of an integral type to + /// be used without explicit definition as long as its address isn't + /// required. But, since this is a public member variable and we cannot + /// assume how it's used, we give a definition in the implementation. + static const int END_OF_STREAM = -1; + + /// \brief Exception thrown when ungetChar() is made to go before + /// the start of buffer. + struct UngetBeforeBeginning : public OutOfRange { + UngetBeforeBeginning(const char* file, size_t line, const char* what) : + OutOfRange(file, line, what) + {} + }; + + /// \brief Exception thrown when we fail to open the input file. + struct OpenError : public Unexpected { + OpenError(const char* file, size_t line, const char* what) : + Unexpected(file, line, what) + {} + }; + + /// \brief Constructor which takes an input stream. The stream is + /// read-from, but it is not closed. + /// + /// \throws OpenError If the data size of the input stream cannot be + /// detected. + explicit InputSource(std::istream& input_stream); + + /// \brief Constructor which takes a filename to read from. The + /// associated file stream is managed internally. + /// + /// \throws OpenError when opening the input file fails or the size of + /// the file cannot be detected. + explicit InputSource(const char* filename); + + /// \brief Destructor + ~InputSource(); + + /// \brief Returns a name for the InputSource. Typically this is the + /// filename, but if the InputSource was constructed for an + /// \c std::istream, it returns a name in the format "stream-%p". + const std::string& getName() const { + return (name_); + } + + /// \brief Returns the size of the input source in bytes. + /// + /// If the size is unknown, it returns \c MasterLexer::SOURCE_SIZE_UNKNOWN. + /// + /// See \c MasterLexer::getTotalSourceSize() for the definition of + /// the size of sources and for when the size can be unknown. + /// + /// \throw None + size_t getSize() const { return (input_size_); } + + /// \brief Returns the current read position in the input source. + /// + /// This method returns the position of the character that was last + /// retrieved from the source. Unless some characters have been + /// "ungotten" by \c ungetChar() or \c ungetAll(), this value is equal + /// to the number of calls to \c getChar() until it reaches the + /// END_OF_STREAM. Note that the position of the first character in + /// the source is 1. At the point of the last character, the return value + /// of this method should be equal to that of \c getSize(), and + /// recognizing END_OF_STREAM doesn't increase the position. + /// + /// If \c ungetChar() or \c ungetAll() is called, the position is + /// decreased by the number of "ungotten" characters. So the return + /// values may not always monotonically increase. + /// + /// \throw None + size_t getPosition() const { return (total_pos_); } + + /// \brief Returns if the input source is at end of file. + bool atEOF() const { + return (at_eof_); + } + + /// \brief Returns the current line number being read. + size_t getCurrentLine() const { + return (line_); + } + + /// \brief Saves the current line being read. Later, when + /// \c ungetAll() is called, it skips back to the last-saved line. + /// + /// TODO: Please make this method private if it is unused after the + /// MasterLexer implementation is complete (and only \c mark() is + /// used instead). + void saveLine(); + + /// Removes buffered content before the current location in the + /// \c InputSource. It's not possible to \c ungetChar() after this, + /// unless we read more data using \c getChar(). + /// + /// TODO: Please make this method private if it is unused after the + /// MasterLexer implementation is complete (and only \c mark() is + /// used instead). + void compact(); + + /// Calls \c saveLine() and \c compact() in sequence. + void mark(); + + /// \brief Returns a single character from the input source. If end + /// of file is reached, \c END_OF_STREAM is returned. + /// + /// \throws MasterLexer::ReadError when reading from the input stream or + /// file fails. + int getChar(); + + /// \brief Skips backward a single character in the input + /// source. The last-read character is unget. + /// + /// \throws UngetBeforeBeginning if we go backwards past the start + /// of reading, or backwards past the last time compact() was + /// called. + void ungetChar(); + + /// Forgets what was read, and skips back to the position where + /// \c compact() was last called. If \c compact() was not called, it + /// skips back to where reading started. If \c saveLine() was called + /// previously, it sets the current line number to the line number + /// saved. + void ungetAll(); + +private: + bool at_eof_; + size_t line_; + size_t saved_line_; + + std::vector buffer_; + size_t buffer_pos_; + size_t total_pos_; + + const std::string name_; + std::ifstream file_stream_; + std::istream& input_; + const size_t input_size_; +}; + +} // namespace master_lexer_internal +} // namespace dns +} // namespace isc + +#endif // DNS_INPUTSOURCE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/master_lexer_state.h b/src/lib/dns/master_lexer_state.h new file mode 100644 index 0000000..d328a70 --- /dev/null +++ b/src/lib/dns/master_lexer_state.h @@ -0,0 +1,138 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MASTER_LEXER_STATE_H +#define MASTER_LEXER_STATE_H 1 + +#include + +namespace isc { +namespace dns { + +namespace master_lexer_internal { + +/// \brief Tokenization state for \c MasterLexer. +/// +/// This is a base class of classes that represent various states of a single +/// tokenization session of \c MasterLexer, i.e., the states used for a +/// single call to \c MasterLexer::getNextToken(). +/// +/// It follows the convention of the state design pattern: each derived class +/// corresponds to a specific state, and the state transition takes place +/// through the virtual method named \c handle(). The \c handle() method +/// takes the main \c MasterLexer object that holds all necessary internal +/// context, and updates it as necessary; each \c State derived class is +/// completely stateless. +/// +/// The initial transition takes place in a static method of the base class, +/// \c start(). This is mainly for implementation convenience; we need to +/// pass options given to \c MasterLexer::getNextToken() for the initial +/// state, so it makes more sense to separate the interface for the transition +/// from the initial state. +/// +/// If the whole lexer transition is completed within start(), it sets the +/// identified token and returns NULL; otherwise it returns a pointer to +/// an object of a specific state class that completes the session +/// on the call of handle(). +/// +/// As is usual in the state design pattern, the \c State class is made +/// a friend class of \c MasterLexer and can refer to its internal details. +/// This is intentional; essentially its a part of \c MasterLexer and +/// is defined as a separate class only for implementation clarity and better +/// testability. It's defined in a publicly visible header, but that's only +/// for testing purposes. No normal application or even no other classes of +/// this library are expected to use this class. +class State { +public: + /// \brief Virtual destructor. + /// + /// In our usage this actually doesn't matter, but some compilers complain + /// about it and we need to silence them. + virtual ~State() {} + + /// \brief Begin state transitions to get the next token. + /// + /// This is the first method that \c MasterLexer needs to call for a + /// tokenization session. The lexer passes a reference to itself + /// and options given in \c getNextToken(). + /// + /// \throw MasterLexer::ReadError Unexpected I/O error + /// \throw std::bad_alloc Internal resource allocation failure + /// + /// \param lexer The lexer object that holds the main context. + /// \param options The options passed to getNextToken(). + /// \return A pointer to the next state object or NULL if the transition + /// is completed. + static const State* start(MasterLexer& lexer, + MasterLexer::Options options); + + /// \brief Handle the process of one specific state. + /// + /// This method is expected to be called on the object returned by + /// start(). In the usual state transition design pattern, it would + /// return the next state. But as we noticed, we never have another + /// state, so we simplify it by not returning anything instead of + /// returning NULL every time. + /// + /// \throw MasterLexer::ReadError Unexpected I/O error + /// \throw std::bad_alloc Internal resource allocation failure + /// + /// \param lexer The lexer object that holds the main context. + virtual void handle(MasterLexer& lexer) const = 0; + + /// \brief Types of states. + /// + /// Specific states are basically hidden within the implementation, + /// but we'd like to allow tests to examine them, so we provide + /// a way to get an instance of a specific state. + enum ID { + CRLF, ///< Just seen a carriage-return character + String, ///< Handling a string token + QString, ///< Handling a quoted string token + Number ///< Handling a number + }; + + /// \brief Returns a \c State instance of the given state. + /// + /// This is provided only for testing purposes so tests can check + /// the behavior of each state separately. \c MasterLexer shouldn't + /// need this method. + static const State& getInstance(ID state_id); + + /// \name Read-only accessors for testing purposes. + /// + /// These allow tests to inspect some selected portion of the internal + /// states of \c MasterLexer. These shouldn't be used except for testing + /// purposes. + ///@{ + bool wasLastEOL(const MasterLexer& lexer) const; + const MasterToken& getToken(const MasterLexer& lexer) const; + size_t getParenCount(const MasterLexer& lexer) const; + ///@} + +protected: + /// \brief An accessor to the internal implementation class of + /// \c MasterLexer. + /// + /// This is provided for specific derived classes as they are not direct + /// friends of \c MasterLexer. + /// + /// \param lexer The lexer object that holds the main context. + /// \return A pointer to the implementation class object of the given + /// lexer. This is never NULL. + MasterLexer::MasterLexerImpl* getLexerImpl(MasterLexer& lexer) const { + return (lexer.impl_); + } +}; + +} // namespace master_lexer_internal +} // namespace dns +} // namespace isc +#endif // MASTER_LEXER_STATE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc new file mode 100644 index 0000000..568fdea --- /dev/null +++ b/src/lib/dns/master_loader.cc @@ -0,0 +1,1073 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // for iequals +#include +#include + +#include +#include +#include + +#include // for sscanf() + +using std::string; +using std::unique_ptr; +using std::vector; +using std::pair; +using boost::algorithm::iequals; +using boost::shared_ptr; + +namespace isc { +namespace dns { + +namespace { + +// An internal exception, used to control the code flow in case of errors. +// It is thrown during the loading and caught later, not to be propagated +// outside of the file. +class InternalException : public isc::Exception { +public: + InternalException(const char* filename, size_t line, const char* what) : + Exception(filename, line, what) + {} +}; + +} // end unnamed namespace + +/// \brief Private implementation class for the \c MasterLoader +/// +/// This class is used internally by the \c MasterLoader and is not +/// publicly visible. It is present to avoid polluting the public API +/// with internal implementation details of the \c MasterLoader. +// cppcheck-suppress noConstructor +class MasterLoader::MasterLoaderImpl { +public: + /// \brief Constructor. + /// + /// \param master_file Path to the file to load. + /// \param zone_origin The origin of zone to be expected inside + /// the master file. Currently unused, but it is expected to + /// be used for some validation. + /// \param zone_class The class of zone to be expected inside the + /// master file. + /// \param callbacks The callbacks by which it should report problems. + /// Usually, the callback carries a filename and line number of the + /// input where the problem happens. There's a special case of empty + /// filename and zero line in case the opening of the top-level master + /// file fails. + /// \param add_callback The callback which would be called with each + /// loaded RR. + /// \param options Options for the parsing, which is bitwise-or of + /// the Options values or DEFAULT. If the MANY_ERRORS option is + /// included, the parser tries to continue past errors. If it + /// is not included, it stops at first encountered error. + /// \throw std::bad_alloc when there's not enough memory. + MasterLoaderImpl(const char* master_file, + const Name& zone_origin, + const RRClass& zone_class, + const MasterLoaderCallbacks& callbacks, + const AddRRCallback& add_callback, + MasterLoader::Options options) : + lexer_(), + zone_origin_(zone_origin), + active_origin_(zone_origin), + zone_class_(zone_class), + callbacks_(callbacks), + add_callback_(add_callback), + options_(options), + master_file_(master_file), + initialized_(false), + ok_(true), + many_errors_((options & MANY_ERRORS) != 0), + previous_name_(false), + complete_(false), + seen_error_(false), + warn_rfc1035_ttl_(true), + rr_count_(0) + {} + + /// \brief Wrapper around \c MasterLexer::pushSource() (file version) + /// + /// This method is used as a wrapper around the lexer's + /// \c pushSource() to also save the current origin and the last + /// seen name (to be restored upon \c popSource()). It also calls + /// \c pushSource(). See \c doInclude() implementation for more + /// details. + /// + /// \param filename Path to the file to push as a new source. + /// \param current_origin The current origin name to save. + void pushSource(const std::string& filename, const Name& current_origin) { + std::string error; + if (!lexer_.pushSource(filename.c_str(), &error)) { + if (initialized_) { + isc_throw(InternalException, error.c_str()); + } else { + // Top-level file + reportError("", 0, error); + ok_ = false; + } + } + // Store the current status, so we can recover it upon popSource + include_info_.push_back(IncludeInfo(current_origin, last_name_)); + initialized_ = true; + previous_name_ = false; + } + + /// \brief Wrapper around \c MasterLexer::pushSource() (stream version) + /// + /// Similar to \c pushSource(). This method need not save the + /// current origin as it is not used with $INCLUDE processing. + /// + /// \param stream The input stream to use as a new source. + void pushStreamSource(std::istream& stream) { + lexer_.pushSource(stream); + initialized_ = true; + } + + /// \brief Implementation of \c MasterLoader::loadIncremental() + /// + /// See \c MasterLoader::loadIncremental() for details. + bool loadIncremental(size_t count_limit); + + /// \brief Return the total size of the input sources pushed so + /// far. See \c MasterLexer::getTotalSourceSize(). + size_t getSize() const { return (lexer_.getTotalSourceSize()); } + + /// \brief Return the line number being parsed in the pushed input + /// sources. See \c MasterLexer::getPosition(). + size_t getPosition() const { return (lexer_.getPosition()); } + +private: + /// \brief Report an error using the callbacks that were supplied + /// during \c MasterLoader construction. Note that this method also + /// throws \c MasterLoaderError exception if necessary, so the + /// caller need not throw it. + void reportError(const std::string& filename, size_t line, + const std::string& reason) + { + seen_error_ = true; + callbacks_.error(filename, line, reason); + if (!many_errors_) { + // In case we don't have the lenient mode, every error is fatal + // and we throw + ok_ = false; + complete_ = true; + isc_throw(MasterLoaderError, reason.c_str()); + } + } + + /// \brief Wrapper around \c MasterLexer::popSource() + /// + /// This method is used as a wrapper around the lexer's + /// \c popSource() to also restore the current origin and the last + /// seen name (at time of push). It also calls \c popSource(). See + /// \c doInclude() implementation for more details. + bool popSource() { + if (lexer_.getSourceCount() == 1) { + return (false); + } + lexer_.popSource(); + // Restore original origin and last seen name + + // We move in tandem, there's an extra item included during the + // initialization, so we can never run out of them + assert(!include_info_.empty()); + const IncludeInfo& info(include_info_.back()); + active_origin_ = info.first; + last_name_ = info.second; + include_info_.pop_back(); + previous_name_ = false; + return (true); + } + + /// \brief Get a string token. Handle it as error if it is not string. + const string getString() { + lexer_.getNextToken(MasterToken::STRING).getString(string_token_); + return (string_token_); + } + + /// \brief Parse the initial token at the beginning of a line in a + /// master file (or stream). + /// + /// A helper method of \c loadIncremental(), parsing the first token + /// of a new line. If it looks like an RR, detect its owner name + /// and return a string token for the next field of the RR. + /// + /// Otherwise, return either \c END_OF_LINE or \c END_OF_FILE token + /// depending on whether the loader continues to the next line or + /// completes the load, respectively. Other corner cases including + /// $-directive handling is done here. + /// + /// For unexpected errors, it throws an exception, which will be + /// handled in \c loadIncremental(). + MasterToken handleInitialToken(); + + /// \brief Helper method for \c doGenerate(). + /// + /// This is a helper method for \c doGenerate() that processes the + /// LHS or RHS for a single iteration in the range that is requested + /// by the $GENERATE directive and returns a generated string (that + /// is used to build a name (LHS) or RDATA (RHS) for an RR). See the + /// commented implementation for details. + std::string generateForIter(const std::string& str, const int it); + + /// \brief Process the $GENERATE directive. + /// + /// See the commented implementation for details. + void doGenerate(); + + /// \brief Process the $ORIGIN directive. + void doOrigin(bool is_optional) { + // Parse and create the new origin. It is relative to the previous + // one. + const MasterToken& + name_tok(lexer_.getNextToken(MasterToken::QSTRING, is_optional)); + + if (name_tok.getType() == MasterToken::QSTRING || + name_tok.getType() == MasterToken::STRING) { + + const MasterToken::StringRegion& + name_string(name_tok.getStringRegion()); + active_origin_ = Name(name_string.beg, name_string.len, + &active_origin_); + if (name_string.len > 0 && + name_string.beg[name_string.len - 1] != '.') { + callbacks_.warning(lexer_.getSourceName(), + lexer_.getSourceLine(), + "The new origin is relative, did you really" + " mean " + active_origin_.toText() + "?"); + } + } else { + // If it is not optional, we must not get anything but + // a string token. + assert(is_optional); + + // We return the newline there. This is because we want to + // behave the same if there is or isn't the name, leaving the + // newline there. + lexer_.ungetToken(); + } + } + + /// \brief Process the $INCLUDE directive. + void doInclude() { + // First, get the filename to include + const string + filename(lexer_.getNextToken(MasterToken::QSTRING).getString()); + + // There optionally can be an origin, that applies before the include. + // We need to save the currently active origin before calling + // doOrigin(), because it would update active_origin_ while we need + // to pass the active origin before recognizing the new origin to + // pushSource. Note: RFC 1035 is not really clear on this: it reads + // "regardless of changes... within the included file", but the new + // origin is not really specified "within the included file". + // Nevertheless, this behavior is probably more likely to be the + // intent of the RFC, and it's compatible with BIND 9. + const Name current_origin = active_origin_; + doOrigin(true); + + pushSource(filename, current_origin); + } + + /// \brief Parse RR fields (TTL, CLASS and TYPE). + /// + /// A helper method for \c loadIncremental(). It parses part of an + /// RR until it finds the RR type field. If TTL or RR class is + /// specified before the RR type, it also recognizes and validates + /// them. + /// + /// \param explicit_ttl will be set to true if this method finds a + /// valid TTL field. + /// \param rrparam_token Pass the current (parsed) token here. + RRType parseRRParams(bool& explicit_ttl, MasterToken rrparam_token) { + // Find TTL, class and type. Both TTL and class are + // optional and may occur in any order if they exist. TTL + // and class come before type which must exist. + // + // [] [] + // [] [] + + // named-signzone outputs TTL first, so try parsing it in order + // first. + if (setCurrentTTL(rrparam_token.getString())) { + explicit_ttl = true; + rrparam_token = lexer_.getNextToken(MasterToken::STRING); + } else { + // If it's not a TTL here, continue and try again + // after the RR class below. + } + + boost::scoped_ptr rrclass + (RRClass::createFromText(rrparam_token.getString())); + if (rrclass) { + if (*rrclass != zone_class_) { + isc_throw(InternalException, "Class mismatch: " << *rrclass << + " vs. " << zone_class_); + } + rrparam_token = lexer_.getNextToken(MasterToken::STRING); + } + + // If we couldn't parse TTL earlier in the stream (above), try + // again at current location. + if (!explicit_ttl && setCurrentTTL(rrparam_token.getString())) { + explicit_ttl = true; + rrparam_token = lexer_.getNextToken(MasterToken::STRING); + } + + // Return the current string token's value as the RRType. + return (RRType(rrparam_token.getString())); + } + + /// \brief Check and limit TTL to maximum value. + /// + /// Upper limit check when recognizing a specific TTL value from the + /// zone file ($TTL, the RR's TTL field, or the SOA minimum). RFC2181 + /// Section 8 limits the range of TTL values to 2^31-1 (0x7fffffff), + /// and prohibits transmitting a TTL field exceeding this range. We + /// guarantee that by limiting the value at the time of zone + /// parsing/loading, following what BIND 9 does. Resetting it to 0 + /// at this point may not be exactly what the RFC states (depending on + /// the meaning of 'received'), but the end result would be the same (i.e., + /// the guarantee on transmission). Again, we follow the BIND 9's behavior + /// here. + /// + /// \param ttl the TTL to check. If it is larger than the maximum + /// allowed, it is set to 0. + /// \param post_parsing should be true iff this method is called + /// after parsing the entire RR and the lexer is positioned at the + /// next line. It's just for calculating the accurate source line + /// when callback is necessary. + void limitTTL(RRTTL& ttl, bool post_parsing) { + if (ttl > RRTTL::MAX_TTL()) { + const size_t src_line = lexer_.getSourceLine() - + (post_parsing ? 1 : 0); + callbacks_.warning(lexer_.getSourceName(), src_line, + "TTL " + ttl.toText() + " > MAXTTL, " + "setting to 0 per RFC2181"); + ttl = RRTTL(0); + } + } + + /// \brief Set/reset the default TTL. + /// + /// This should be from either $TTL or SOA minimum TTL (it's the + /// caller's responsibility; this method doesn't care about where it + /// comes from). See \c limitTTL() for parameter post_parsing. + void setDefaultTTL(const RRTTL& ttl, bool post_parsing) { + assignTTL(default_ttl_, ttl); + limitTTL(*default_ttl_, post_parsing); + } + + /// \brief Try to set/reset the current TTL from candidate TTL text. + /// + /// It's possible it that the text does not actually represent a TTL + /// (which is not immediately considered an error). Returns \c true + /// iff it's recognized as a valid TTL (and only in which case the + /// current TTL is set). + /// + /// \param ttl_txt The text to parse as a TTL. + /// \return true if a TTL was parsed (and set as the current TTL). + bool setCurrentTTL(const string& ttl_txt) { + // We use the factory version instead of RRTTL constructor as we + // need to expect cases where ttl_txt does not actually represent a TTL + // but an RR class or type. + RRTTL* rrttl = RRTTL::createFromText(ttl_txt); + if (rrttl) { + current_ttl_.reset(rrttl); + limitTTL(*current_ttl_, false); + return (true); + } + return (false); + } + + /// \brief Determine the TTL of the current RR based on the given + /// parsing context. + /// + /// \c explicit_ttl is true iff the TTL is explicitly specified for that RR + /// (in which case current_ttl_ is set to that TTL). + /// \c rrtype is the type of the current RR, and \c rdata is its RDATA. They + /// only matter if the type is SOA and no available TTL is known. In this + /// case the minimum TTL of the SOA will be used as the TTL of that SOA + /// and the default TTL for subsequent RRs. + const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype, + const rdata::ConstRdataPtr& rdata) { + // We've completed parsing the full of RR, and the lexer is already + // positioned at the next line. If we need to call callback, + // we need to adjust the line number. + const size_t current_line = lexer_.getSourceLine() - 1; + + if (!current_ttl_ && !default_ttl_) { + if (rrtype == RRType::SOA()) { + callbacks_.warning(lexer_.getSourceName(), current_line, + "no TTL specified; " + "using SOA MINTTL instead"); + const uint32_t ttl_val = + dynamic_cast(*rdata). + getMinimum(); + setDefaultTTL(RRTTL(ttl_val), true); + assignTTL(current_ttl_, *default_ttl_); + } else { + // On catching the exception we'll try to reach EOL again, + // so we need to unget it now. + lexer_.ungetToken(); + throw InternalException(__FILE__, __LINE__, + "no TTL specified; load rejected"); + } + } else if (!explicit_ttl && default_ttl_) { + assignTTL(current_ttl_, *default_ttl_); + } else if (!explicit_ttl && warn_rfc1035_ttl_) { + // Omitted (class and) TTL values are default to the last + // explicitly stated values (RFC 1035, Sec. 5.1). + callbacks_.warning(lexer_.getSourceName(), current_line, + "using RFC1035 TTL semantics; default to the " + "last explicitly stated TTL"); + warn_rfc1035_ttl_ = false; // we only warn about this once + } + assert(current_ttl_); + return (*current_ttl_); + } + + /// \brief Handle a $DIRECTIVE + /// + /// This method is called when a $DIRECTIVE is encountered in the + /// input stream. + void handleDirective(const char* directive, size_t length) { + if (iequals(directive, "INCLUDE")) { + doInclude(); + } else if (iequals(directive, "ORIGIN")) { + doOrigin(false); + eatUntilEOL(true); + } else if (iequals(directive, "GENERATE")) { + doGenerate(); + eatUntilEOL(true); + } else if (iequals(directive, "TTL")) { + setDefaultTTL(RRTTL(getString()), false); + eatUntilEOL(true); + } else { + isc_throw(InternalException, "Unknown directive '" << + string(directive, directive + length) << "'"); + } + } + + /// \brief Skip tokens until end-of-line. + void eatUntilEOL(bool reportExtra) { + // We want to continue. Try to read until the end of line + for (;;) { + const MasterToken& token(lexer_.getNextToken()); + switch (token.getType()) { + case MasterToken::END_OF_FILE: + callbacks_.warning(lexer_.getSourceName(), + lexer_.getSourceLine(), + "File does not end with newline"); + // We don't pop here. The End of file will stay there, + // and we'll handle it in the next iteration of + // loadIncremental properly. + return; + case MasterToken::END_OF_LINE: + // Found the end of the line. Good. + return; + default: + // Some other type of token. + if (reportExtra) { + reportExtra = false; + reportError(lexer_.getSourceName(), + lexer_.getSourceLine(), + "Extra tokens at the end of line"); + } + break; + } + } + } + + /// \brief Assign the right RRTTL's value to the left RRTTL. If one + /// doesn't exist in the scoped_ptr, make a new RRTTL copy of the + /// right argument. + static void assignTTL(boost::scoped_ptr& left, const RRTTL& right) { + if (!left) { + left.reset(new RRTTL(right)); + } else { + *left = right; + } + } + +private: + MasterLexer lexer_; + const Name zone_origin_; + Name active_origin_; // The origin used during parsing + // (modifiable by $ORIGIN) + shared_ptr last_name_; // Last seen name (for INITIAL_WS handling) + const RRClass zone_class_; + MasterLoaderCallbacks callbacks_; + const AddRRCallback add_callback_; + boost::scoped_ptr default_ttl_; // Default TTL of RRs used when + // unspecified. If NULL no default + // is known. + boost::scoped_ptr current_ttl_; // The TTL used most recently. + // Initially unset. Once set + // always stores a valid + // RRTTL. + const MasterLoader::Options options_; + const std::string master_file_; + std::string string_token_; + bool initialized_; + bool ok_; // Is it OK to continue loading? + const bool many_errors_; // Are many errors allowed (or should we abort + // on the first) + // Some info about the outer files from which we include. + // The first one is current origin, the second is the last seen name + // in that file. + typedef pair > IncludeInfo; + vector include_info_; + bool previous_name_; // True if there was a previous name in this file + // (false at the beginning or after an $INCLUDE line) + +public: + bool complete_; // All work done. + bool seen_error_; // Was there at least one error during the + // load? + bool warn_rfc1035_ttl_; // should warn if implicit TTL determination + // from the previous RR is used. + size_t rr_count_; // number of RRs successfully loaded +}; + +namespace { // begin unnamed namespace + +/// \brief Generate a dotted nibble sequence. +/// +/// This method generates a dotted nibble sequence and returns it as a +/// string. The nibbles are appended from the least significant digit +/// (in hex representation of \c num) to the most significant digit with +/// dots ('.') to separate the digits. If \c width is non-zero and the +/// dotted nibble sequence has not filled the requested width, the rest +/// of the width is filled with a dotted nibble sequence of 0 nibbles. +/// +/// Some sample representations: +/// +/// num = 0x1234, width = 0 +/// "4.3.2.1" +/// +/// num = 0x1234, width = 1 +/// "4.3.2.1" +/// +/// num = 0x1234, width = 8 +/// "4.3.2.1" +/// +/// num = 0x1234, width = 9 +/// "4.3.2.1." +/// +/// num = 0x1234, width = 10 +/// "4.3.2.1.0" +/// +/// num = 0x1234, width = 11 +/// "4.3.2.1.0." +/// +/// num = 0xabcd, width = 0, uppercase = true +/// "D.C.B.A" +/// +/// num = 0, width = 0 +/// "0" +/// +/// num = 0, width = 1 +/// "0" +/// +/// num = 0, width = 2 +/// "0." +/// +/// num = 0, width = 3 +/// "0.0" +/// +/// \param num The number for which the dotted nibble sequence should be +/// generated. +/// \param width The width of the generated string. This is only +/// meaningful when it is larger than the dotted nibble sequence +/// representation of \c num. +/// \param uppercase Whether to use uppercase characters in nibble +/// sequence. +/// \return A string containing the dotted nibble sequence. +std::string +genNibbles(int num, unsigned int width, bool uppercase) { + static const char *hex = "0123456789abcdef0123456789ABCDEF"; + std::string rstr; + + do { + char ch = hex[(num & 0x0f) + (uppercase ? 16 : 0)]; + num >>= 4; + rstr.push_back(ch); + + if (width > 0) { + --width; + } + + // If width is non zero then we need to add a label separator. + // If value is non zero then we need to add another label and + // that requires a label separator. + if (width > 0 || num != 0) { + rstr.push_back('.'); + + if (width > 0) { + --width; + } + } + } while ((num != 0) || (width > 0)); + + return (rstr); +} + +} // end unnamed namespace + +std::string +MasterLoader::MasterLoaderImpl::generateForIter(const std::string& str, + const int num) +{ + std::string rstr; + + for (std::string::const_iterator it = str.begin(); it != str.end();) { + switch (*it) { + case '$': + // This is the case when the '$' character is encountered in + // the LHS or RHS. A computed value is added in its place in + // the generated string. + ++it; + if ((it != str.end()) && (*it == '$')) { + rstr.push_back('$'); + ++it; + continue; + } + + // The str.end() check is required. + if ((it == str.end()) || (*it != '{')) { + // There is no modifier (between {}), so just copy the + // passed number into the generated string. + rstr += boost::str(boost::format("%d") % num); + } else { + // There is a modifier (between {}). Parse it and handle + // the various cases below. + const char* scan_str = + str.c_str() + std::distance(str.begin(), it); + int offset = 0; + unsigned int width; + char base[2] = {'d', 0}; // char plus null byte + // cppcheck-suppress invalidscanf_libc + const int n = sscanf(scan_str, "{%d,%u,%1[doxXnN]}", + &offset, &width, base); + switch (n) { + case 1: + // Only 1 item was matched (the offset). Copy (num + + // offset) into the generated string. + rstr += boost::str(boost::format("%d") % (num + offset)); + break; + + case 2: { + // 2 items were matched (the offset and width). Copy + // (num + offset) and format it according to the width + // into the generated string. + const std::string fmt = + boost::str(boost::format("%%0%ud") % width); + rstr += boost::str(boost::format(fmt) % (num + offset)); + break; + } + + case 3: + // 3 items were matched (offset, width and base). + if ((base[0] == 'n') || (base[0] == 'N')) { + // The base is requesting nibbles. Format it + // specially (see genNibbles() documentation). + rstr += genNibbles(num + offset, width, (base[0] == 'N')); + } else { + // The base is not requesting nibbles. Copy (num + + // offset) and format it according to the width + // and base into the generated string. + const std::string fmt = + boost::str(boost::format("%%0%u%c") % width % base[0]); + rstr += boost::str(boost::format(fmt) % (num + offset)); + } + break; + + default: + // Any other case in the modifiers is an error. + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "Invalid $GENERATE format modifiers"); + return (""); + } + + // Find the closing brace. Careful that 'it' can be equal + // to str.end() here. + while ((it != str.end()) && (*it != '}')) { + ++it; + } + // Skip past the closing brace (if there is one). + if (it != str.end()) { + ++it; + } + } + break; + + case '\\': + // This is the case when the '\' character is encountered in + // the LHS or RHS. The '\' and the following character are + // copied as-is into the generated string. This is usually + // used for escaping the $ character. + rstr.push_back(*it); + ++it; + if (it == str.end()) { + continue; + } + rstr.push_back(*it); + ++it; + break; + + default: + // This is the default case that handles all other + // characters. They are copied as-is into the generated + // string. + rstr.push_back(*it); + ++it; + break; + } + } + + return (rstr); +} + +void +MasterLoader::MasterLoaderImpl::doGenerate() { + // Parse the range token + const MasterToken& range_token = lexer_.getNextToken(MasterToken::STRING); + if (range_token.getType() != MasterToken::STRING) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "Invalid $GENERATE syntax"); + return; + } + const std::string range = range_token.getString(); + + // Parse the LHS token + const MasterToken& lhs_token = lexer_.getNextToken(MasterToken::STRING); + if (lhs_token.getType() != MasterToken::STRING) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "Invalid $GENERATE syntax"); + return; + } + const std::string lhs = lhs_token.getString(); + + // Parse the TTL, RR class and RR type tokens. Note that TTL and RR + // class may come in any order, or may be missing (either or + // both). If TTL is missing, we expect that it was either specified + // explicitly using $TTL, or is implicitly known from a previous RR, + // or that this is the SOA RR from which the MINIMUM field is + // used. It's unlikely that $GENERATE will be used with an SOA RR, + // but it's possible. The parsing happens within the parseRRParams() + // helper method which is called below. + const MasterToken& param_token = lexer_.getNextToken(MasterToken::STRING); + if (param_token.getType() != MasterToken::STRING) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "Invalid $GENERATE syntax"); + return; + } + + bool explicit_ttl = false; + const RRType rrtype = parseRRParams(explicit_ttl, param_token); + + // Parse the RHS token. It can be a quoted string. + const MasterToken& rhs_token = lexer_.getNextToken(MasterToken::QSTRING); + if ((rhs_token.getType() != MasterToken::QSTRING) && + (rhs_token.getType() != MasterToken::STRING)) + { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "Invalid $GENERATE syntax"); + return; + } + const std::string rhs = rhs_token.getString(); + + // Range can be one of two forms: start-stop or start-stop/step. If + // the first form is used, then step is set to 1. All of start, stop + // and step must be positive. + unsigned int start; + unsigned int stop; + unsigned int step; + // cppcheck-suppress invalidscanf_libc + const int n = sscanf(range.c_str(), "%u-%u/%u", &start, &stop, &step); + if ((n < 2) || (stop < start)) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "$GENERATE: invalid range: " + range); + return; + } + + if (n == 2) { + step = 1; + } + + // Generate and add the records. + for (unsigned int i = start; i <= stop; i += step) { + // Get generated strings for LHS and RHS. LHS goes to form the + // name, RHS goes to form the RDATA of the RR. + const std::string generated_name = generateForIter(lhs, i); + const std::string generated_rdata = generateForIter(rhs, i); + if (generated_name.empty() || generated_rdata.empty()) { + // The error should have been sent to the callbacks already + // by generateForIter(). + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + "$GENERATE error"); + return; + } + + // generateForIter() can return a string with a trailing '.' in + // case of a nibble representation. So we cannot use the + // relative Name constructor. We use concatenate() which is + // expensive, but keeps the generated LHS-based Name within the + // active origin. + last_name_.reset + (new Name(Name(generated_name).concatenate(active_origin_))); + previous_name_ = true; + + const rdata::RdataPtr rdata = + rdata::createRdata(rrtype, zone_class_, generated_rdata); + // In case we get NULL, it means there was error creating the + // Rdata. The errors should have been reported by callbacks_ + // already. We need to decide if we want to continue or not. + if (rdata) { + add_callback_(*last_name_, zone_class_, rrtype, + getCurrentTTL(explicit_ttl, rrtype, rdata), + rdata); + // Good, we added another one + ++rr_count_; + } else { + seen_error_ = true; + if (!many_errors_) { + ok_ = false; + complete_ = true; + // We don't have the exact error here, but it was + // reported by the error callback. + isc_throw(MasterLoaderError, "Invalid RR data"); + } + } + } +} + +MasterToken +MasterLoader::MasterLoaderImpl::handleInitialToken() { + const MasterToken& initial_token = + lexer_.getNextToken(MasterLexer::QSTRING | MasterLexer::INITIAL_WS); + + // The most likely case is INITIAL_WS, and then string/qstring. We + // handle them first. + if (initial_token.getType() == MasterToken::INITIAL_WS) { + const MasterToken& next_token = lexer_.getNextToken(); + if (next_token.getType() == MasterToken::END_OF_LINE) { + return (next_token); // blank line + } else if (next_token.getType() == MasterToken::END_OF_FILE) { + lexer_.ungetToken(); // handle it in the next iteration. + eatUntilEOL(true); // effectively warn about the unexpected EOF. + return (MasterToken(MasterToken::END_OF_LINE)); + } + + // This means the same name as previous. + if (last_name_.get() == NULL) { + isc_throw(InternalException, "No previous name to use in " + "place of initial whitespace"); + } else if (!previous_name_) { + callbacks_.warning(lexer_.getSourceName(), lexer_.getSourceLine(), + "Owner name omitted around $INCLUDE, the result " + "might not be as expected"); + } + return (next_token); + } else if (initial_token.getType() == MasterToken::STRING || + initial_token.getType() == MasterToken::QSTRING) { + // If it is name (or directive), handle it. + const MasterToken::StringRegion& + name_string(initial_token.getStringRegion()); + + if (name_string.len > 0 && name_string.beg[0] == '$') { + // This should have either thrown (and the error handler + // will read up until the end of line) or read until the + // end of line. + + // Exclude the $ from the string on this point. + handleDirective(name_string.beg + 1, name_string.len - 1); + // So, get to the next line, there's nothing more interesting + // in this one. + return (MasterToken(MasterToken::END_OF_LINE)); + } + + // This should be an RR, starting with an owner name. Construct the + // name, and some string token should follow. + last_name_.reset(new Name(name_string.beg, name_string.len, + &active_origin_)); + previous_name_ = true; + return (lexer_.getNextToken(MasterToken::STRING)); + } + + switch (initial_token.getType()) { // handle less common cases + case MasterToken::END_OF_FILE: + if (!popSource()) { + return (initial_token); + } else { + // We try to read a token from the popped source + // So continue to the next line of that source, but first, make + // sure the source is at EOL + eatUntilEOL(true); + return (MasterToken(MasterToken::END_OF_LINE)); + } + case MasterToken::END_OF_LINE: + return (initial_token); // empty line + case MasterToken::ERROR: + // Error token here. + isc_throw(InternalException, initial_token.getErrorText()); + default: + // Some other token (what could that be?) + isc_throw(InternalException, "Parser got confused (unexpected " + "token " << initial_token.getType() << ")"); + } +} + +bool +MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) { + if (count_limit == 0) { + isc_throw(isc::InvalidParameter, "Count limit set to 0"); + } + if (complete_) { + isc_throw(isc::InvalidOperation, + "Trying to load when already loaded"); + } + if (!initialized_) { + pushSource(master_file_, active_origin_); + } + size_t count = 0; + while (ok_ && count < count_limit) { + try { + const MasterToken next_token = handleInitialToken(); + if (next_token.getType() == MasterToken::END_OF_FILE) { + return (true); // we are done + } else if (next_token.getType() == MasterToken::END_OF_LINE) { + continue; // nothing more to do in this line + } + // We are going to parse an RR, have known the owner name, + // and are now seeing the next string token in the rest of the RR. + assert(next_token.getType() == MasterToken::STRING); + + bool explicit_ttl = false; + const RRType rrtype = parseRRParams(explicit_ttl, next_token); + // TODO: Check if it is SOA, it should be at the origin. + + const rdata::RdataPtr rdata = + rdata::createRdata(rrtype, zone_class_, lexer_, + &active_origin_, options_, callbacks_); + + // In case we get NULL, it means there was error creating + // the Rdata. The errors should have been reported by + // callbacks_ already. We need to decide if we want to continue + // or not. + if (rdata) { + add_callback_(*last_name_, zone_class_, rrtype, + getCurrentTTL(explicit_ttl, rrtype, rdata), + rdata); + // Good, we loaded another one + ++count; + ++rr_count_; + } else { + seen_error_ = true; + if (!many_errors_) { + ok_ = false; + complete_ = true; + // We don't have the exact error here, but it was reported + // by the error callback. + isc_throw(MasterLoaderError, "Invalid RR data"); + } + } + } catch (const isc::dns::DNSTextError& e) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + e.what()); + eatUntilEOL(false); + } catch (const MasterLexer::ReadError& e) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + e.what()); + eatUntilEOL(false); + } catch (const MasterLexer::LexerError& e) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + e.what()); + eatUntilEOL(false); + } catch (const InternalException& e) { + reportError(lexer_.getSourceName(), lexer_.getSourceLine(), + e.what()); + eatUntilEOL(false); + } + } + // When there was a fatal error and ok is false, we say we are done. + return (!ok_); +} + +MasterLoader::MasterLoader(const char* master_file, + const Name& zone_origin, + const RRClass& zone_class, + const MasterLoaderCallbacks& callbacks, + const AddRRCallback& add_callback, + Options options) +{ + if (!add_callback) { + isc_throw(isc::InvalidParameter, "Empty add RR callback"); + } + impl_ = new MasterLoaderImpl(master_file, zone_origin, + zone_class, callbacks, add_callback, options); +} + +MasterLoader::MasterLoader(std::istream& stream, + const Name& zone_origin, + const RRClass& zone_class, + const MasterLoaderCallbacks& callbacks, + const AddRRCallback& add_callback, + Options options) +{ + if (!add_callback) { + isc_throw(isc::InvalidParameter, "Empty add RR callback"); + } + unique_ptr + impl(new MasterLoaderImpl("", zone_origin, zone_class, + callbacks, add_callback, options)); + impl->pushStreamSource(stream); + impl_ = impl.release(); +} + +MasterLoader::~MasterLoader() { + delete impl_; +} + +bool +MasterLoader::loadIncremental(size_t count_limit) { + const bool result = impl_->loadIncremental(count_limit); + impl_->complete_ = result; + return (result); +} + +bool +MasterLoader::loadedSuccessfully() const { + return (impl_->complete_ && !impl_->seen_error_); +} + +size_t +MasterLoader::getSize() const { + return (impl_->getSize()); +} + +size_t +MasterLoader::getPosition() const { + return (impl_->getPosition()); +} + +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/master_loader.h b/src/lib/dns/master_loader.h new file mode 100644 index 0000000..b385806 --- /dev/null +++ b/src/lib/dns/master_loader.h @@ -0,0 +1,187 @@ +// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MASTER_LOADER_H +#define MASTER_LOADER_H + +#include + +#include + +namespace isc { +namespace dns { + +class Name; +class RRClass; + +/// \brief Error while loading by MasterLoader without specifying the +/// MANY_ERRORS option. +class MasterLoaderError : public isc::Exception { +public: + MasterLoaderError(const char* file, size_t line, const char* what) : + Exception(file, line, what) + {} +}; + +/// \brief A class able to load DNS master files +/// +/// This class is able to produce a stream of RRs from a master file. +/// It is able to load all of the master file at once, or by blocks +/// incrementally. +/// +/// It reports the loaded RRs and encountered errors by callbacks. +class MasterLoader : boost::noncopyable { +public: + /// \brief Options how the parsing should work. + enum Options { + DEFAULT = 0, ///< Nothing special. + MANY_ERRORS = 1 ///< Lenient mode (see documentation of MasterLoader + /// constructor). + }; + + /// \brief Constructor + /// + /// This creates a master loader and provides it with all + /// relevant information. + /// + /// Except for the exceptions listed below, the constructor doesn't + /// throw. Most errors (like non-existent master file) are reported + /// by the callbacks during load() or loadIncremental(). + /// + /// \param master_file Path to the file to load. + /// \param zone_origin The origin of zone to be expected inside + /// the master file. Currently unused, but it is expected to + /// be used for some validation. + /// \param zone_class The class of zone to be expected inside the + /// master file. + /// \param callbacks The callbacks by which it should report problems. + /// Usually, the callback carries a filename and line number of the + /// input where the problem happens. There's a special case of empty + /// filename and zero line in case the opening of the top-level master + /// file fails. + /// \param add_callback The callback which would be called with each + /// loaded RR. + /// \param options Options for the parsing, which is bitwise-or of + /// the Options values or DEFAULT. If the MANY_ERRORS option is + /// included, the parser tries to continue past errors. If it + /// is not included, it stops at first encountered error. + /// \throw std::bad_alloc when there's not enough memory. + /// \throw isc::InvalidParameter if add_callback is empty. + MasterLoader(const char* master_file, + const Name& zone_origin, + const RRClass& zone_class, + const MasterLoaderCallbacks& callbacks, + const AddRRCallback& add_callback, + Options options = DEFAULT); + + /// \brief Constructor from a stream + /// + /// This is a constructor very similar to the previous one. The only + /// difference is it doesn't take a filename, but an input stream + /// to read the data from. It is expected to be mostly used in tests, + /// but it is public as it may possibly be useful for other currently + /// unknown purposes. + MasterLoader(std::istream& input, + const Name& zone_origin, + const RRClass& zone_class, + const MasterLoaderCallbacks& callbacks, + const AddRRCallback& add_callback, + Options options = DEFAULT); + + /// \brief Destructor + ~MasterLoader(); + + /// \brief Load some RRs + /// + /// This method loads at most count_limit RRs and reports them. In case + /// an error (either fatal or without MANY_ERRORS) or end of file is + /// encountered, they may be less. + /// + /// \param count_limit Upper limit on the number of RRs loaded. + /// \return In case it stops because of the count limit, it returns false. + /// It returns true if the loading is done. + /// \throw isc::InvalidOperation when called after loading was done + /// already. + /// \throw MasterLoaderError when there's an error in the input master + /// file and the MANY_ERRORS is not specified. It never throws this + /// in case MANY_ERRORS is specified. + bool loadIncremental(size_t count_limit); + + /// \brief Load everything + /// + /// This simply calls loadIncremental until the loading is done. + /// \throw isc::InvalidOperation when called after loading was done + /// already. + /// \throw MasterLoaderError when there's an error in the input master + /// file and the MANY_ERRORS is not specified. It never throws this + /// in case MANY_ERRORS is specified. + void load() { + while (!loadIncremental(1000)) { // 1000 = arbitrary largish number + // Body intentionally left blank + } + } + + /// \brief Was the loading successful? + /// + /// \return true if and only if the loading was complete (after a call of + /// load or after loadIncremental returned true) and there was no + /// error. In other cases, return false. + /// \note While this method works even before the loading is complete (by + /// returning false in that case), it is meant to be called only after + /// finishing the load. + bool loadedSuccessfully() const; + + /// \brief Return the total size of the zone files and streams. + /// + /// This method returns the size of the source of the zone to be loaded + /// (master zone files or streams) that is known at the time of the call. + /// For a zone file, it's the size of the file; for a stream, it's the + /// size of the data (in bytes) available at the start of the load. + /// If separate zone files are included via the $INCLUDE directive, the + /// sum of the sizes of these files are added. + /// + /// If the loader is constructed with a stream, the size can be + /// "unknown" as described for \c MasterLexer::getTotalSourceSize(). + /// In this case this method always returns + /// \c MasterLexer::SOURCE_SIZE_UNKNOWN. + /// + /// If the loader is constructed with a zone file, this method + /// initially returns 0. So until either \c load() or \c loadIncremental() + /// is called, the value is meaningless. + /// + /// Note that when the source includes separate files, this method + /// cannot take into account the included files that the loader has not + /// recognized at the time of call. So it's possible that this method + /// returns different values at different times of call. + /// + /// \throw None + size_t getSize() const; + + /// \brief Return the position of the loader in zone. + /// + /// This method returns a conceptual "position" of the loader in the + /// zone to be loaded. Specifically, it returns the total number of + /// characters contained in the zone files and streams and recognized + /// by the loader. Before starting the load it returns 0; on successful + /// completion it will be equal to the return value of \c getSize() + /// (unless the latter returns \c MasterLexer::SOURCE_SIZE_UNKNOWN). + /// + /// \throw None + size_t getPosition() const; + +private: + class MasterLoaderImpl; + MasterLoaderImpl* impl_; +}; + +} // end namespace dns +} // end namespace isc + +#endif // MASTER_LOADER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/master_loader_callbacks.cc b/src/lib/dns/master_loader_callbacks.cc new file mode 100644 index 0000000..088a223 --- /dev/null +++ b/src/lib/dns/master_loader_callbacks.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include + +namespace isc { +namespace dns { + +namespace { +void +nullCallback(const std::string&, size_t, const std::string&) { +} +} + +MasterLoaderCallbacks +MasterLoaderCallbacks::getNullCallbacks() { + return (MasterLoaderCallbacks(nullCallback, nullCallback)); +} + +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/master_loader_callbacks.h b/src/lib/dns/master_loader_callbacks.h new file mode 100644 index 0000000..a731685 --- /dev/null +++ b/src/lib/dns/master_loader_callbacks.h @@ -0,0 +1,134 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MASTER_LOADER_CALLBACKS_H +#define MASTER_LOADER_CALLBACKS_H + +#include + +#include +#include +#include + +namespace isc { +namespace dns { +class Name; +class RRClass; +class RRType; +class RRTTL; +namespace rdata { +class Rdata; +typedef boost::shared_ptr RdataPtr; +} + +/// \brief Type of callback to add a RR. +/// +/// This type of callback is used by the loader to report another loaded +/// RR. The Rdata is no longer preserved by the loader and is fully +/// owned by the callback. +/// +/// \param name The domain name where the RR belongs. +/// \param rrclass The class of the RR. +/// \param rrtype Type of the RR. +/// \param rrttl Time to live of the RR. +/// \param rdata The actual carried data of the RR. +typedef std::function + AddRRCallback; + +/// \brief Set of issue callbacks for a loader. +/// +/// This holds a set of callbacks by which a loader (such as MasterLoader) +/// can report loaded RRsets, errors and other unusual conditions. +/// +/// All the callbacks must be set. +class MasterLoaderCallbacks { +public: + /// \brief Type of one callback to report problems. + /// + /// This is the type of one callback used to report an unusual + /// condition or error. + /// + /// \param source_name The name of the source where the problem happened. + /// This is usually a file name. + /// \param source_line Position of the problem, counted in lines from the + /// beginning of the source. + /// \param reason Human readable description of what happened. + typedef std::function IssueCallback; + + /// \brief Constructor + /// + /// Initializes the callbacks. + /// + /// \param error The error callback to use. + /// \param warning The warning callback to use. + /// \throw isc::InvalidParameter if any of the callbacks is empty. + MasterLoaderCallbacks(const IssueCallback& error, + const IssueCallback& warning) : + error_(error), + warning_(warning) + { + if (!error_ || !warning_) { + isc_throw(isc::InvalidParameter, + "Empty function passed as callback"); + } + } + + /// \brief Call callback for serious errors + /// + /// This is called whenever there's a serious problem which makes the data + /// being loaded unusable. Further processing may or may not happen after + /// this (for example to detect further errors), but the data should not + /// be used. + /// + /// It calls whatever was passed to the error parameter to the constructor. + /// + /// If the caller of the loader wants to abort, it is possible to throw + /// from the callback, which aborts the load. + void error(const std::string& source_name, size_t source_line, + const std::string& reason) const + { + error_(source_name, source_line, reason); + } + + /// \brief Call callback for potential problems + /// + /// This is called whenever a minor problem is discovered. This might mean + /// the data is completely OK, it just looks suspicious. + /// + /// It calls whatever was passed to the warn parameter to the constructor. + /// + /// The loading will continue after the callback. If the caller wants to + /// abort (which is probably not a very good idea, since warnings + /// may be false positives), it is possible to throw from inside the + /// callback. + void warning(const std::string& source_name, size_t source_line, + const std::string& reason) const + { + warning_(source_name, source_line, reason); + } + + /// \brief Return a callbacks instance with null callbacks + /// + /// This is a convenience wrapper to generate a + /// \c MasterLoaderCallbacks object with both callbacks being nothing. + /// This will be useful for applications that only need to run + /// \c MasterLoader and get the end result. + /// + /// \throw None + static MasterLoaderCallbacks getNullCallbacks(); + +private: + const IssueCallback error_, warning_; +}; + +} +} + +#endif // MASTER_LOADER_CALLBACKS_H diff --git a/src/lib/dns/masterload.cc b/src/lib/dns/masterload.cc new file mode 100644 index 0000000..94638c1 --- /dev/null +++ b/src/lib/dns/masterload.cc @@ -0,0 +1,100 @@ +// Copyright (C) 2010-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::dns::rdata; + +using namespace std; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { +namespace { +void +callbackWrapper(const RRsetPtr& rrset, MasterLoadCallback callback, + const Name* origin) +{ + // Origin related validation: + // - reject out-of-zone data + // - reject SOA whose owner is not at the top of zone + const NameComparisonResult cmp_result = + rrset->getName().compare(*origin); + if (cmp_result.getRelation() != NameComparisonResult::EQUAL && + cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) { + isc_throw(MasterLoadError, "Out-of-zone data for " << *origin + << "/" << rrset->getClass() << ": " << rrset->getName()); + } + if (rrset->getType() == RRType::SOA() && + cmp_result.getRelation() != NameComparisonResult::EQUAL) { + isc_throw(MasterLoadError, "SOA not at top of zone: " + << *rrset); + } + + callback(rrset); +} + +template +void +loadHelper(InputType input, const Name& origin, + const RRClass& zone_class, MasterLoadCallback callback) +{ + RRCollator rr_collator(std::bind(callbackWrapper, ph::_1, callback, + &origin)); + MasterLoader loader(input, origin, zone_class, + MasterLoaderCallbacks::getNullCallbacks(), + rr_collator.getCallback()); + try { + loader.load(); + } catch (const MasterLoaderError& ex) { + isc_throw(MasterLoadError, ex.what()); + } + rr_collator.flush(); +} +} + +void +masterLoad(const char* const filename, const Name& origin, + const RRClass& zone_class, MasterLoadCallback callback) +{ + if ((filename == NULL) || (*filename == '\0')) { + isc_throw(MasterLoadError, "Name of master file must not be null"); + } + + loadHelper(filename, origin, zone_class, callback); +} + +void +masterLoad(istream& input, const Name& origin, const RRClass& zone_class, + MasterLoadCallback callback, const char*) +{ + loadHelper(input, origin, zone_class, callback); +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/masterload.h b/src/lib/dns/masterload.h new file mode 100644 index 0000000..3762d54 --- /dev/null +++ b/src/lib/dns/masterload.h @@ -0,0 +1,175 @@ +// Copyright (C) 2010-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MASTERLOAD_H +#define MASTERLOAD_H 1 + +#include +#include + +#include +#include + +namespace isc { +namespace dns { +class Name; +class RRClass; + +/// \brief An exception that is thrown if an error occurs while loading a +/// master zone data. +class MasterLoadError : public isc::Exception { +public: + MasterLoadError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// The type of the \c callback parameter of \c masterLoad(). +/// +/// This represents a functor object or a function that takes one parameter +/// of type \c RRsetPtr and returns nothing. +typedef std::function MasterLoadCallback; + +/// +/// \name Master zone file loader functions. +/// +//@{ +/// Master zone file loader from a file. +/// +/// This function parses a given file as a master DNS zone file for +/// the given origin name and RR class, constructs a sequence of \c RRset +/// from the RRs containing in the file, and calls the given \c callback +/// functor object or function with each \c RRset. +/// +/// The \c callback parameter is a functor object or a function that +/// takes one parameter of type \c RRsetPtr and returns nothing, +/// i.e. \c void (see below for specific examples). +/// More precisely, it can be anything that this form of std::function +/// can represent, but the caller normally doesn't have to care about +/// that level of details. +/// +/// The ownership of constructed RRsets is transferred to the callback +/// and this function never uses it once it is called. +/// The callback can freely modify the passed \c RRset. +/// +/// This function internally uses the MasterLoader class, and basically +/// accepts and rejects input that MasterLoader accepts and rejects, +/// accordingly. In addition, this function performs the following validation: +/// if an SOA RR is included, its owner name must be the origin name. +/// +/// It does not perform other semantical checks, however. For example, +/// it doesn't check if an NS RR of the origin name is included or if +/// there is more than one SOA RR. Such further checks are the caller's +/// (or the callback's) responsibility. +/// +/// Exceptions +/// +/// This function throws an exception of class \c MasterLoadError in the +/// following cases: +/// - Any of the validation checks fails (see above). +/// - The input data has a syntax error. +/// - The specified file cannot be opened for loading. +/// - An I/O error occurs during the loading. +/// +/// In addition, this function requires resource allocation for parsing and +/// constructing RRsets. If it fails, the corresponding standard exception +/// will be thrown. +/// +/// The callback may throw its own function. This function doesn't catch it +/// and will simply propagate it towards the caller. +/// +/// Usage Examples +/// +/// A simplest example usage of this function would be to parse a zone +/// file and (after validation) dump the content to the standard output. +/// This is an example functor object and a call to \c masterLoad +/// that implements this scenario: +/// \code struct ZoneDumper { +/// void operator()(ConstRRsetPtr rrset) const { +/// std::cout << *rrset; +/// } +/// }; +/// ... +/// masterLoad(zone_file, Name("example.com"), RRClass::IN(), ZoneDumper()); +/// \endcode +/// Alternatively, you can use a normal function instead of a functor: +/// \code void zoneDumper(ConstRRsetPtr rrset) { +/// std::cout << *rrset; +/// } +/// ... +/// masterLoad(zone_file, Name("example.com"), RRClass::IN(), zoneDumper); +/// \endcode +/// Or, if you want to use it with a member function of some other class, +/// wrapping things with \c std::bind would be handy: +/// \code class ZoneDumper { +/// public: +/// void dump(ConstRRsetPtr rrset) const { +/// std::cout << *rrset; +/// } +/// }; +/// ... +/// ZoneDumper dumper; +/// masterLoad(rr_stream, Name("example.com"), RRClass::IN(), +/// std::bind(&ZoneDumper::dump, &dumper, _1)); +/// \endcode +/// You can find a bit more complicated examples in the unit tests code for +/// this function. +/// +/// Implementation Notes +/// +/// The current implementation is in a preliminary level and needs further +/// extensions. Some design decisions may also have to be reconsidered as +/// we gain experiences. Those include: +/// - We may want to allow optional conditions. For example, we may want to +/// be generous about some validation failures and be able to continue +/// parsing. +/// - Especially if we allow to be generous, we may also want to support +/// returning an error code instead of throwing an exception when we +/// encounter validation failure. +/// - RRSIGs are handled as separate RRsets, i.e. they are not included in +/// the RRset they cover. +/// +/// \param filename A path to a master zone file to be loaded. +/// \param origin The origin name of the zone. +/// \param zone_class The RR class of the zone. +/// \param callback A callback functor or function that is to be called +/// for each RRset. +void masterLoad(const char* const filename, const Name& origin, + const RRClass& zone_class, MasterLoadCallback callback); + +/// Master zone file loader from input stream. +/// +/// This function is same as the other version +/// (\c masterLoad(const char* const, const Name&, const RRClass&, MasterLoadCallback)) +/// except that it takes a \c std::istream instead of a file. +/// It extracts lines from the stream and handles each line just as a line +/// of a file for the other version of function. +/// All descriptions of the other version apply to this version except those +/// specific to file I/O. +/// +/// Note: The 'source' parameter is now ignored, but it was only used in +/// exception messages on some error. So the compatibility effect should be +/// minimal. +/// +/// \param input An input stream object that is to emit zone's RRs. +/// \param origin The origin name of the zone. +/// \param zone_class The RR class of the zone. +/// \param callback A callback functor or function that is to be called for +/// each RRset. +/// \param source This parameter is now ignored but left for compatibility. +void masterLoad(std::istream& input, const Name& origin, + const RRClass& zone_class, MasterLoadCallback callback, + const char* source = NULL); +} + + +//@} +} + +#endif // MASTERLOAD_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc new file mode 100644 index 0000000..e46c68f --- /dev/null +++ b/src/lib/dns/message.cc @@ -0,0 +1,1163 @@ +// Copyright (C) 2009-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using boost::lexical_cast; +using namespace isc::dns::rdata; +using namespace isc::util; + +namespace isc { +namespace dns { + +namespace { +// protocol constants +const size_t HEADERLEN = 12; + +const unsigned int OPCODE_MASK = 0x7800; +const unsigned int OPCODE_SHIFT = 11; +const unsigned int RCODE_MASK = 0x000f; + +// This diagram shows the wire-format representation of the 2nd 16 bits of +// the DNS header section, which contain all defined flag bits. +// +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |QR| Opcode |AA|TC|RD|RA| |AD|CD| RCODE | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// 1 0 0 0| 0 1 1 1| 1 0 1 1| 0 0 0 0| +// 0x8 0x7 0xb 0x0 +// +// This mask covers all the flag bits, and those bits only. +// Note: we reject a "flag" the is not covered by this mask in some of the +// public methods. This means our current definition is not fully extendable; +// applications cannot introduce a new flag bit temporarily without modifying +// the source code. +const unsigned int HEADERFLAG_MASK = 0x87b0; + +// This is a set of flag bits that should be preserved when building a reply +// from a request. +// Note: we assume the specific definition of HEADERFLAG_xx. We may change +// the definition in future, in which case we need to adjust this definition, +// too (see also the description about the Message::HeaderFlag type). +const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD | + Message::HEADERFLAG_CD); + +const char* const sectiontext[] = { + "QUESTION", + "ANSWER", + "AUTHORITY", + "ADDITIONAL" +}; +} + +class MessageImpl { +public: + MessageImpl(Message::Mode mode); + // Open issues: should we rather have a header in wire-format + // for efficiency? + Message::Mode mode_; + qid_t qid_; + + // We want to use NULL for [op,r]code_ to mean the code being not + // correctly parsed or set. We store the real code object in + // xxcode_placeholder_ and have xxcode_ refer to it when the object + // is valid. + const Rcode* rcode_; + Rcode rcode_placeholder_; + const Opcode* opcode_; + Opcode opcode_placeholder_; + + uint16_t flags_; // wire-format representation of header flags. + + bool header_parsed_; + static const unsigned int NUM_SECTIONS = 4; // TODO: revisit this design + int counts_[NUM_SECTIONS]; // TODO: revisit this definition + vector questions_; + vector rrsets_[NUM_SECTIONS]; + ConstEDNSPtr edns_; + ConstTSIGRecordPtr tsig_rr_; + + // RRsetsSorter* sorter_; : TODO + + void init(); + void setOpcode(const Opcode& opcode); + void setRcode(const Rcode& rcode); + int parseQuestion(InputBuffer& buffer); + int parseSection(const Message::Section section, InputBuffer& buffer, + Message::ParseOptions options); + void addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, ConstRdataPtr rdata, + Message::ParseOptions options); + // There are also times where an RR needs to be added that + // represents an empty RRset. There is no Rdata in that case + void addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, Message::ParseOptions options); + void addEDNS(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const Rdata& rdata); + void addTSIG(Message::Section section, unsigned int count, + const InputBuffer& buffer, size_t start_position, + const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const Rdata& rdata); + void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx); +}; + +MessageImpl::MessageImpl(Message::Mode mode) : + mode_(mode), + rcode_placeholder_(Rcode(0)), // as a placeholder the value doesn't matter + opcode_placeholder_(Opcode(0)) // ditto +{ + init(); +} + +void +MessageImpl::init() { + flags_ = 0; + qid_ = 0; + rcode_ = NULL; + opcode_ = NULL; + edns_ = EDNSPtr(); + tsig_rr_ = ConstTSIGRecordPtr(); + + for (int i = 0; i < NUM_SECTIONS; ++i) { + counts_[i] = 0; + } + + header_parsed_ = false; + questions_.clear(); + rrsets_[Message::SECTION_ANSWER].clear(); + rrsets_[Message::SECTION_AUTHORITY].clear(); + rrsets_[Message::SECTION_ADDITIONAL].clear(); +} + +void +MessageImpl::setOpcode(const Opcode& opcode) { + opcode_placeholder_ = opcode; + opcode_ = &opcode_placeholder_; +} + +void +MessageImpl::setRcode(const Rcode& rcode) { + rcode_placeholder_ = rcode; + rcode_ = &rcode_placeholder_; +} + +namespace { +// This helper class is used by MessageImpl::toWire() to render a set of +// RRsets of a specific section of message to a given MessageRenderer. +// +// A RenderSection object is expected to be used with a QuestionIterator or +// SectionIterator. Its operator() is called for each RRset as the iterator +// iterates over the corresponding section, and it renders the RRset to +// the given MessageRenderer, while counting the number of RRs (note: not +// RRsets) successfully rendered. If the MessageRenderer reports the need +// for truncation (via its isTruncated() method), the RenderSection object +// stops rendering further RRsets. In addition, unless partial_ok (given on +// construction) is true, it removes any RRs that are partially rendered +// from the MessageRenderer. +// +// On the completion of rendering the entire section, the owner of the +// RenderSection object can get the number of rendered RRs via the +// getTotalCount() method. +template +struct RenderSection { + RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) : + counter_(0), renderer_(renderer), partial_ok_(partial_ok), + truncated_(false) + {} + void operator()(const T& entry) { + // If it's already truncated, ignore the rest of the section. + if (truncated_) { + return; + } + const size_t pos0 = renderer_.getLength(); + counter_ += entry->toWire(renderer_); + if (renderer_.isTruncated()) { + truncated_ = true; + if (!partial_ok_) { + // roll back to the end of the previous RRset. + renderer_.trim(renderer_.getLength() - pos0); + } + } + } + unsigned int getTotalCount() { return (counter_); } + unsigned int counter_; + AbstractMessageRenderer& renderer_; + const bool partial_ok_; + bool truncated_; +}; +} + +void +MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { + if (mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted in non render mode"); + } + if (rcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted without Rcode set"); + } + if (opcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted without Opcode set"); + } + + // Reserve the space for TSIG (if needed) so that we can handle truncation + // case correctly later when that happens. orig_xxx variables remember + // some configured parameters of renderer in case they are needed in + // truncation processing below. + const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0; + const size_t orig_msg_len_limit = renderer.getLengthLimit(); + const AbstractMessageRenderer::CompressMode orig_compress_mode = + renderer.getCompressMode(); + + // We are going to skip soon, so we need to clear the renderer + // But we'll leave the length limit and the compress mode intact + // (or shortened in case of TSIG) + renderer.clear(); + renderer.setCompressMode(orig_compress_mode); + + if (tsig_len > 0) { + if (tsig_len > orig_msg_len_limit) { + isc_throw(InvalidParameter, "Failed to render DNS message: " + "too small limit for a TSIG (" << + orig_msg_len_limit << ")"); + } + renderer.setLengthLimit(orig_msg_len_limit - tsig_len); + } else { + renderer.setLengthLimit(orig_msg_len_limit); + } + + // reserve room for the header + if (renderer.getLengthLimit() < HEADERLEN) { + isc_throw(InvalidParameter, "Failed to render DNS message: " + "too small limit for a Header"); + } + renderer.skip(HEADERLEN); + + uint16_t qdcount = + for_each(questions_.begin(), questions_.end(), + RenderSection(renderer, false)).getTotalCount(); + + // TODO: sort RRsets in each section based on configuration policy. + uint16_t ancount = 0; + if (!renderer.isTruncated()) { + ancount = + for_each(rrsets_[Message::SECTION_ANSWER].begin(), + rrsets_[Message::SECTION_ANSWER].end(), + RenderSection(renderer, true)).getTotalCount(); + } + uint16_t nscount = 0; + if (!renderer.isTruncated()) { + nscount = + for_each(rrsets_[Message::SECTION_AUTHORITY].begin(), + rrsets_[Message::SECTION_AUTHORITY].end(), + RenderSection(renderer, true)).getTotalCount(); + } + uint16_t arcount = 0; + if (renderer.isTruncated()) { + flags_ |= Message::HEADERFLAG_TC; + } else { + arcount = + for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(), + rrsets_[Message::SECTION_ADDITIONAL].end(), + RenderSection(renderer, false)).getTotalCount(); + } + + // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS + // has been explicitly set. However, if the RCODE would require it and + // no EDNS has been set we generate a temporary local EDNS and use it. + if (!renderer.isTruncated()) { + ConstEDNSPtr local_edns = edns_; + if (!local_edns && rcode_->getExtendedCode() != 0) { + local_edns = ConstEDNSPtr(new EDNS()); + } + if (local_edns) { + arcount += local_edns->toWire(renderer, rcode_->getExtendedCode()); + } + } + + // If we're adding a TSIG to a truncated message, clear all RRsets + // from the message except for the question before adding the TSIG. + // If even (some of) the question doesn't fit, don't include it. + if (tsig_ctx != NULL && renderer.isTruncated()) { + renderer.clear(); + renderer.setLengthLimit(orig_msg_len_limit - tsig_len); + renderer.setCompressMode(orig_compress_mode); + renderer.skip(HEADERLEN); + qdcount = for_each(questions_.begin(), questions_.end(), + RenderSection(renderer, + false)).getTotalCount(); + ancount = 0; + nscount = 0; + arcount = 0; + } + + // Adjust the counter buffer. + // XXX: these may not be equal to the number of corresponding entries + // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR + // was inserted. This is not good, and we should revisit the entire + // design. + counts_[Message::SECTION_QUESTION] = qdcount; + counts_[Message::SECTION_ANSWER] = ancount; + counts_[Message::SECTION_AUTHORITY] = nscount; + counts_[Message::SECTION_ADDITIONAL] = arcount; + + // fill in the header + size_t header_pos = 0; + renderer.writeUint16At(qid_, header_pos); + header_pos += sizeof(uint16_t); + + uint16_t codes_and_flags = + (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK; + codes_and_flags |= (rcode_->getCode() & RCODE_MASK); + codes_and_flags |= (flags_ & HEADERFLAG_MASK); + renderer.writeUint16At(codes_and_flags, header_pos); + header_pos += sizeof(uint16_t); + // TODO: should avoid repeated pattern + renderer.writeUint16At(qdcount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(ancount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(nscount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(arcount, header_pos); + + // Add TSIG, if necessary, at the end of the message. + if (tsig_ctx != NULL) { + // Release the reserved space in the renderer. + renderer.setLengthLimit(orig_msg_len_limit); + + const int tsig_count = + tsig_ctx->sign(qid_, renderer.getData(), + renderer.getLength())->toWire(renderer); + if (tsig_count != 1) { + isc_throw(Unexpected, "Failed to render a TSIG RR"); + } + + // update the ARCOUNT for the TSIG RR. Note that for a sane DNS + // message arcount should never overflow to 0. + renderer.writeUint16At(++arcount, header_pos); + } +} + +Message::Message(Mode mode) : + impl_(new MessageImpl(mode)) +{} + +Message::~Message() { + delete impl_; +} + +bool +Message::getHeaderFlag(const HeaderFlag flag) const { + if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) { + isc_throw(InvalidParameter, + "Message::getHeaderFlag:: Invalid flag is specified: " << + "0x" << std::hex << flag); + } + return ((impl_->flags_ & flag) != 0); +} + +void +Message::setHeaderFlag(const HeaderFlag flag, const bool on) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "setHeaderFlag performed in non-render mode"); + } + if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) { + isc_throw(InvalidParameter, + "Message::getHeaderFlag:: Invalid flag is specified: " << + "0x" << std::hex << flag); + } + if (on) { + impl_->flags_ |= flag; + } else { + impl_->flags_ &= ~flag; + } +} + +qid_t +Message::getQid() const { + return (impl_->qid_); +} + +void +Message::setQid(qid_t qid) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "setQid performed in non-render mode"); + } + impl_->qid_ = qid; +} + +const Rcode& +Message::getRcode() const { + if (impl_->rcode_ == NULL) { + isc_throw(InvalidMessageOperation, "getRcode attempted before set"); + } + return (*impl_->rcode_); +} + +void +Message::setRcode(const Rcode& rcode) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "setRcode performed in non-render mode"); + } + impl_->setRcode(rcode); +} + +const Opcode& +Message::getOpcode() const { + if (impl_->opcode_ == NULL) { + isc_throw(InvalidMessageOperation, "getOpcode attempted before set"); + } + return (*impl_->opcode_); +} + +void +Message::setOpcode(const Opcode& opcode) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "setOpcode performed in non-render mode"); + } + impl_->setOpcode(opcode); +} + +ConstEDNSPtr +Message::getEDNS() const { + return (impl_->edns_); +} + +void +Message::setEDNS(ConstEDNSPtr edns) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "setEDNS performed in non-render mode"); + } + impl_->edns_ = edns; +} + +const TSIGRecord* +Message::getTSIGRecord() const { + if (impl_->mode_ != Message::PARSE) { + isc_throw(InvalidMessageOperation, + "getTSIGRecord performed in non-parse mode"); + } + + return (impl_->tsig_rr_.get()); +} + +unsigned int +Message::getRRCount(const Section section) const { + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + return (impl_->counts_[section]); +} + +void +Message::addRRset(const Section section, RRsetPtr rrset) { + if (!rrset) { + isc_throw(InvalidParameter, + "NULL RRset is given to Message::addRRset"); + } + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "addRRset performed in non-render mode"); + } + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + + impl_->rrsets_[section].push_back(rrset); + impl_->counts_[section] += rrset->getRdataCount(); + impl_->counts_[section] += rrset->getRRsigDataCount(); +} + +bool +Message::hasRRset(const Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype) const +{ + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + + BOOST_FOREACH(ConstRRsetPtr r, impl_->rrsets_[section]) { + if (r->getClass() == rrclass && + r->getType() == rrtype && + r->getName() == name) { + return (true); + } + } + + return (false); +} + +bool +Message::hasRRset(const Section section, const RRsetPtr& rrset) const { + return (hasRRset(section, rrset->getName(), + rrset->getClass(), rrset->getType())); +} + +bool +Message::removeRRset(const Section section, RRsetIterator& iterator) { + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + + bool removed = false; + for (vector::iterator i = impl_->rrsets_[section].begin(); + i != impl_->rrsets_[section].end(); ++i) { + if (((*i)->getName() == (*iterator)->getName()) && + ((*i)->getClass() == (*iterator)->getClass()) && + ((*i)->getType() == (*iterator)->getType())) { + + // Found the matching RRset so remove it & ignore rest + impl_->counts_[section] -= (*iterator)->getRdataCount(); + impl_->counts_[section] -= (*iterator)->getRRsigDataCount(); + impl_->rrsets_[section].erase(i); + removed = true; + break; + } + } + + return (removed); +} + +void +Message::clearSection(const Section section) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "clearSection performed in non-render mode"); + } + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + if (section == Message::SECTION_QUESTION) { + impl_->questions_.clear(); + } else { + impl_->rrsets_[section].clear(); + } + impl_->counts_[section] = 0; +} + +void +Message::addQuestion(const QuestionPtr question) { + if (impl_->mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "addQuestion performed in non-render mode"); + } + + impl_->questions_.push_back(question); + ++impl_->counts_[SECTION_QUESTION]; +} + +void +Message::addQuestion(const Question& question) { + addQuestion(QuestionPtr(new Question(question))); +} + +void +Message::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { + impl_->toWire(renderer, tsig_ctx); +} + +void +Message::parseHeader(InputBuffer& buffer) { + if (impl_->mode_ != Message::PARSE) { + isc_throw(InvalidMessageOperation, + "Message parse attempted in non parse mode"); + } + + if (impl_->header_parsed_) { + return; + } + + if ((buffer.getLength() - buffer.getPosition()) < HEADERLEN) { + isc_throw(MessageTooShort, "Malformed DNS message (short length): " + << buffer.getLength() - buffer.getPosition()); + } + + impl_->qid_ = buffer.readUint16(); + const uint16_t codes_and_flags = buffer.readUint16(); + impl_->setOpcode(Opcode((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT)); + impl_->setRcode(Rcode(codes_and_flags & RCODE_MASK)); + impl_->flags_ = (codes_and_flags & HEADERFLAG_MASK); + impl_->counts_[SECTION_QUESTION] = buffer.readUint16(); + impl_->counts_[SECTION_ANSWER] = buffer.readUint16(); + impl_->counts_[SECTION_AUTHORITY] = buffer.readUint16(); + impl_->counts_[SECTION_ADDITIONAL] = buffer.readUint16(); + + impl_->header_parsed_ = true; +} + +void +Message::fromWire(InputBuffer& buffer, ParseOptions options) { + if (impl_->mode_ != Message::PARSE) { + isc_throw(InvalidMessageOperation, + "Message parse attempted in non parse mode"); + } + + // Clear any old parsed data + clear(Message::PARSE); + + buffer.setPosition(0); + parseHeader(buffer); + + impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer); + impl_->counts_[SECTION_ANSWER] = + impl_->parseSection(SECTION_ANSWER, buffer, options); + impl_->counts_[SECTION_AUTHORITY] = + impl_->parseSection(SECTION_AUTHORITY, buffer, options); + impl_->counts_[SECTION_ADDITIONAL] = + impl_->parseSection(SECTION_ADDITIONAL, buffer, options); +} + +int +MessageImpl::parseQuestion(InputBuffer& buffer) { + unsigned int added = 0; + + for (unsigned int count = 0; + count < counts_[Message::SECTION_QUESTION]; + ++count) { + const Name name(buffer); + + if ((buffer.getLength() - buffer.getPosition()) < + 2 * sizeof(uint16_t)) { + isc_throw(DNSMessageFORMERR, "Question section too short: " << + (buffer.getLength() - buffer.getPosition()) << " bytes"); + } + const RRType rrtype(buffer.readUint16()); + const RRClass rrclass(buffer.readUint16()); + + // XXX: need a duplicate check. We might also want to have an + // optimized algorithm that requires the question section contain + // exactly one RR. + + questions_.push_back(QuestionPtr(new Question(name, rrclass, rrtype))); + ++added; + } + + return (added); +} + +namespace { +struct MatchRR { + MatchRR(const Name& name, const RRType& rrtype, const RRClass& rrclass) : + name_(name), rrtype_(rrtype), rrclass_(rrclass) {} + bool operator()(const RRsetPtr& rrset) const { + return (rrset->getType() == rrtype_ && + rrset->getClass() == rrclass_ && + rrset->getName() == name_); + } + const Name& name_; + const RRType& rrtype_; + const RRClass& rrclass_; +}; +} + +// Note about design decision: +// we need some type specific processing here, including EDNS and TSIG. +// how much we should generalize/hardcode the special logic is subject +// to discussion. In terms of modularity it would be ideal to introduce +// an abstract class (say "MessageAttribute") and let other such +// concrete notions as EDNS or TSIG inherit from it. Then we would +// just do: +// message->addAttribute(rrtype, rrclass, buffer); +// to create and attach type-specific concrete object to the message. +// +// A major downside of this approach is, as usual, complexity due to +// indirection and performance penalty. Also, it may not be so easy +// to separate the processing logic because in many cases we'll need +// parse context for which the message class is responsible (e.g. +// to check the EDNS OPT RR only appears in the additional section, +// and appears only once). +// +// Another point to consider is that we may not need so many special +// types other than EDNS and TSIG (and when and if we implement it, +// SIG(0)); newer optional attributes of the message would more likely +// be standardized as new flags or options of EDNS. If that's the case, +// introducing an abstract class with all the overhead and complexity +// may not make much sense. +// +// Conclusion: don't over-generalize type-specific logic for now. +// introduce separate concrete classes, and move context-independent +// logic to that class; processing logic dependent on parse context +// is hardcoded here. +int +MessageImpl::parseSection(const Message::Section section, + InputBuffer& buffer, Message::ParseOptions options) +{ + assert(static_cast(section) < MessageImpl::NUM_SECTIONS); + + unsigned int added = 0; + + for (unsigned int count = 0; count < counts_[section]; ++count) { + // We need to remember the start position for TSIG processing + const size_t start_position = buffer.getPosition(); + + const Name name(buffer); + + // buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN. + if ((buffer.getLength() - buffer.getPosition()) < + 3 * sizeof(uint16_t) + sizeof(uint32_t)) { + isc_throw(DNSMessageFORMERR, sectiontext[section] << + " section too short: " << + (buffer.getLength() - buffer.getPosition()) << " bytes"); + } + + const RRType rrtype(buffer.readUint16()); + const RRClass rrclass(buffer.readUint16()); + const RRTTL ttl(buffer.readUint32()); + const size_t rdlen = buffer.readUint16(); + + // If class is ANY or NONE, rdlength may be zero, to signal + // an empty RRset. + // (the class check must be done to differentiate from RRTypes + // that can have zero length rdata + if ((rrclass == RRClass::ANY() || rrclass == RRClass::NONE()) && + rdlen == 0) { + addRR(section, name, rrclass, rrtype, ttl, options); + ++added; + continue; + } + ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen); + + if (rrtype == RRType::OPT()) { + addEDNS(section, name, rrclass, rrtype, ttl, *rdata); + } else if (rrtype == RRType::TSIG()) { + addTSIG(section, count, buffer, start_position, name, rrclass, ttl, + *rdata); + } else { + addRR(section, name, rrclass, rrtype, ttl, rdata, options); + ++added; + } + } + + return (added); +} + +void +MessageImpl::addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, ConstRdataPtr rdata, + Message::ParseOptions options) +{ + if ((options & Message::PRESERVE_ORDER) == 0) { + vector::iterator it = + find_if(rrsets_[section].begin(), rrsets_[section].end(), + MatchRR(name, rrtype, rrclass)); + if (it != rrsets_[section].end()) { + (*it)->setTTL(min((*it)->getTTL(), ttl)); + (*it)->addRdata(rdata); + return; + } + } + RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl)); + rrset->addRdata(rdata); + rrsets_[section].push_back(rrset); +} + +void +MessageImpl::addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, Message::ParseOptions options) +{ + if ((options & Message::PRESERVE_ORDER) == 0) { + vector::iterator it = + find_if(rrsets_[section].begin(), rrsets_[section].end(), + MatchRR(name, rrtype, rrclass)); + if (it != rrsets_[section].end()) { + (*it)->setTTL(min((*it)->getTTL(), ttl)); + return; + } + } + RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl)); + rrsets_[section].push_back(rrset); +} + +void +MessageImpl::addEDNS(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const Rdata& rdata) +{ + if (section != Message::SECTION_ADDITIONAL) { + isc_throw(DNSMessageFORMERR, + "EDNS OPT RR found in an invalid section"); + } + if (edns_) { + isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found"); + } + + uint8_t extended_rcode; + edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata, + extended_rcode)); + setRcode(Rcode(rcode_->getCode(), extended_rcode)); +} + +void +MessageImpl::addTSIG(Message::Section section, unsigned int count, + const InputBuffer& buffer, size_t start_position, + const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const Rdata& rdata) +{ + if (section != Message::SECTION_ADDITIONAL) { + isc_throw(DNSMessageFORMERR, + "TSIG RR found in an invalid section"); + } + if (count != counts_[section] - 1) { + isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record"); + } + // This check will never fail as the multiple TSIG RR case is + // caught before by the not the last record check... + if (tsig_rr_) { + isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found"); + } + tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass, + ttl, rdata, + buffer.getPosition() - + start_position)); +} + +namespace { +template +struct SectionFormatter { + SectionFormatter(const Message::Section section, string& output) : + section_(section), output_(output) {} + void operator()(const T& entry) { + if (section_ == Message::SECTION_QUESTION) { + output_ += ";"; + output_ += entry->toText(); + output_ += "\n"; + } else { + output_ += entry->toText(); + } + } + const Message::Section section_; + string& output_; +}; +} + +string +Message::toText() const { + if (impl_->rcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message::toText() attempted without Rcode set"); + } + if (impl_->opcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message::toText() attempted without Opcode set"); + } + + string s; + + s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText(); + // for simplicity we don't consider extended rcode (unlike BIND9) + s += ", status: " + impl_->rcode_->toText(); + s += ", id: " + boost::lexical_cast(impl_->qid_); + s += "\n;; flags:"; + if (getHeaderFlag(HEADERFLAG_QR)) { + s += " qr"; + } + if (getHeaderFlag(HEADERFLAG_AA)) { + s += " aa"; + } + if (getHeaderFlag(HEADERFLAG_TC)) { + s += " tc"; + } + if (getHeaderFlag(HEADERFLAG_RD)) { + s += " rd"; + } + if (getHeaderFlag(HEADERFLAG_RA)) { + s += " ra"; + } + if (getHeaderFlag(HEADERFLAG_AD)) { + s += " ad"; + } + if (getHeaderFlag(HEADERFLAG_CD)) { + s += " cd"; + } + + // for simplicity, don't consider the update case for now + s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig + lexical_cast(impl_->counts_[SECTION_QUESTION]); + s += ", ANSWER: " + + lexical_cast(impl_->counts_[SECTION_ANSWER]); + s += ", AUTHORITY: " + + lexical_cast(impl_->counts_[SECTION_AUTHORITY]); + + unsigned int arcount = impl_->counts_[SECTION_ADDITIONAL]; + if (impl_->edns_ != NULL) { + ++arcount; + } + if (impl_->tsig_rr_ != NULL) { + ++arcount; + } + s += ", ADDITIONAL: " + lexical_cast(arcount) + "\n"; + + if (impl_->edns_ != NULL) { + s += "\n;; OPT PSEUDOSECTION:\n"; + s += impl_->edns_->toText(); + } + + if (!impl_->questions_.empty()) { + s += "\n;; " + + string(sectiontext[SECTION_QUESTION]) + " SECTION:\n"; + for_each(impl_->questions_.begin(), impl_->questions_.end(), + SectionFormatter(SECTION_QUESTION, s)); + } + if (!impl_->rrsets_[SECTION_ANSWER].empty()) { + s += "\n;; " + + string(sectiontext[SECTION_ANSWER]) + " SECTION:\n"; + for_each(impl_->rrsets_[SECTION_ANSWER].begin(), + impl_->rrsets_[SECTION_ANSWER].end(), + SectionFormatter(SECTION_ANSWER, s)); + } + if (!impl_->rrsets_[SECTION_AUTHORITY].empty()) { + s += "\n;; " + + string(sectiontext[SECTION_AUTHORITY]) + " SECTION:\n"; + for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(), + impl_->rrsets_[SECTION_AUTHORITY].end(), + SectionFormatter(SECTION_AUTHORITY, s)); + } + if (!impl_->rrsets_[SECTION_ADDITIONAL].empty()) { + s += "\n;; " + + string(sectiontext[SECTION_ADDITIONAL]) + + " SECTION:\n"; + for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(), + impl_->rrsets_[SECTION_ADDITIONAL].end(), + SectionFormatter(SECTION_ADDITIONAL, s)); + } + + if (impl_->tsig_rr_ != NULL) { + s += "\n;; TSIG PSEUDOSECTION:\n"; + s += impl_->tsig_rr_->toText(); + } + + return (s); +} + +void +Message::clear(Mode mode) { + impl_->init(); + impl_->mode_ = mode; +} + +void +Message::appendSection(const Section section, const Message& source) { + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + + if (section == SECTION_QUESTION) { + for (QuestionIterator qi = source.beginQuestion(); + qi != source.endQuestion(); + ++qi) { + addQuestion(*qi); + } + } else { + for (RRsetIterator rrsi = source.beginSection(section); + rrsi != source.endSection(section); + ++rrsi) { + addRRset(section, *rrsi); + } + } +} + +void +Message::makeResponse() { + if (impl_->mode_ != Message::PARSE) { + isc_throw(InvalidMessageOperation, + "makeResponse() is performed in non-parse mode"); + } + + impl_->mode_ = Message::RENDER; + + impl_->edns_ = EDNSPtr(); + impl_->flags_ &= MESSAGE_REPLYPRESERVE; + setHeaderFlag(HEADERFLAG_QR, true); + + impl_->rrsets_[SECTION_ANSWER].clear(); + impl_->counts_[SECTION_ANSWER] = 0; + impl_->rrsets_[SECTION_AUTHORITY].clear(); + impl_->counts_[SECTION_AUTHORITY] = 0; + impl_->rrsets_[SECTION_ADDITIONAL].clear(); + impl_->counts_[SECTION_ADDITIONAL] = 0; +} + +/// +/// Template version of Section Iterator +/// +template +struct SectionIteratorImpl { + SectionIteratorImpl(const typename vector::const_iterator& it) : + it_(it) {} + typename vector::const_iterator it_; +}; + +template +SectionIterator::SectionIterator(const SectionIteratorImpl& impl) { + impl_ = new SectionIteratorImpl(impl.it_); +} + +template +SectionIterator::~SectionIterator() { + delete impl_; +} + +template +SectionIterator::SectionIterator(const SectionIterator& source) : + impl_(new SectionIteratorImpl(source.impl_->it_)) +{} + +template +void +SectionIterator::operator=(const SectionIterator& source) { + if (impl_ == source.impl_) { + return; + } + SectionIteratorImpl* newimpl = + new SectionIteratorImpl(source.impl_->it_); + delete impl_; + impl_ = newimpl; +} + +template +SectionIterator& +SectionIterator::operator++() { + ++(impl_->it_); + return (*this); +} + +template +SectionIterator +SectionIterator::operator++(int) { + SectionIterator tmp(*this); + ++(*this); + return (tmp); +} + +template +const T& +SectionIterator::operator*() const { + return (*(impl_->it_)); +} + +template +const T* +SectionIterator::operator->() const { + return (&(operator*())); +} + +template +bool +SectionIterator::operator==(const SectionIterator& other) const { + return (impl_->it_ == other.impl_->it_); +} + +template +bool +SectionIterator::operator!=(const SectionIterator& other) const { + return (impl_->it_ != other.impl_->it_); +} + +/// +/// We need to explicitly instantiate these template classes because these +/// are public classes but defined in this implementation file. +/// +template class SectionIterator; +template class SectionIterator; + +namespace { +typedef SectionIteratorImpl QuestionIteratorImpl; +typedef SectionIteratorImpl RRsetIteratorImpl; +} + +/// +/// Question iterator +/// +const QuestionIterator +Message::beginQuestion() const { + return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.begin()))); +} + +const QuestionIterator +Message::endQuestion() const { + return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.end()))); +} + +/// +/// RRsets iterators +/// +const SectionIterator +Message::beginSection(const Section section) const { + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + if (section == SECTION_QUESTION) { + isc_throw(InvalidMessageSection, + "RRset iterator is requested for question"); + } + + return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].begin()))); +} + +const SectionIterator +Message::endSection(const Section section) const { + if (static_cast(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + if (section == SECTION_QUESTION) { + isc_throw(InvalidMessageSection, + "RRset iterator is requested for question"); + } + + return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].end()))); +} + +ostream& +operator<<(ostream& os, const Message& message) { + return (os << message.toText()); +} +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h new file mode 100644 index 0000000..da8acfe --- /dev/null +++ b/src/lib/dns/message.h @@ -0,0 +1,682 @@ +// Copyright (C) 2009-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MESSAGE_H +#define MESSAGE_H 1 + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace isc { +namespace util { +class InputBuffer; +} + +namespace dns { +class TSIGContext; +class TSIGRecord; + +/// +/// \brief A standard DNS module exception that is thrown if a wire format +/// message parser encounters a short length of data that don't even contain +/// the full header section. +/// +class MessageTooShort : public isc::dns::Exception { +public: + MessageTooShort(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if a section iterator +/// is being constructed for an incompatible section. Specifically, this +/// happens RRset iterator is being constructed for a Question section. +/// +class InvalidMessageSection : public isc::dns::Exception { +public: + InvalidMessageSection(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if a \c Message +/// class method is called that is prohibited for the current mode of +/// the message. +/// +class InvalidMessageOperation : public isc::dns::Exception { +public: + InvalidMessageOperation(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if a UDP buffer size +/// smaller than the standard default maximum (DEFAULT_MAX_UDPSIZE) is +/// being specified for the message. +/// +class InvalidMessageUDPSize : public isc::dns::Exception { +public: + InvalidMessageUDPSize(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +typedef uint16_t qid_t; + +class AbstractMessageRenderer; +class Message; +class MessageImpl; +class Opcode; +class Rcode; + +template +struct SectionIteratorImpl; + +/// \c SectionIterator is a templated class to provide standard-compatible +/// iterators for Questions and RRsets for a given DNS message section. +/// The template parameter is either \c QuestionPtr (for the question section) +/// or \c RRsetPtr (for the answer, authority, or additional section). +template +class SectionIterator : public std::iterator { +public: + SectionIterator() : impl_(NULL) {} + SectionIterator(const SectionIteratorImpl& impl); + ~SectionIterator(); + SectionIterator(const SectionIterator& source); + void operator=(const SectionIterator& source); + SectionIterator& operator++(); + SectionIterator operator++(int); + const T& operator*() const; + const T* operator->() const; + bool operator==(const SectionIterator& other) const; + bool operator!=(const SectionIterator& other) const; +private: + SectionIteratorImpl* impl_; +}; + +typedef SectionIterator QuestionIterator; +typedef SectionIterator RRsetIterator; + +/// \brief The \c Message class encapsulates a standard DNS message. +/// +/// Details of the design and interfaces of this class are still in flux. +/// Here are some notes about the current design. +/// +/// Since many realistic DNS applications deal with messages, message objects +/// will be frequently used, and can be performance sensitive. To minimize +/// the performance overhead of constructing and destructing the objects, +/// this class is designed to be reusable. The \c clear() method is provided +/// for this purpose. +/// +/// A \c Message class object is in either the \c PARSE or the \c RENDER mode. +/// A \c PARSE mode object is intended to be used to convert wire-format +/// message data into a complete \c Message object. +/// A \c RENDER mode object is intended to be used to convert a \c Message +/// object into wire-format data. +/// Some of the method functions of this class are limited to a specific mode. +/// In general, "set" type operations are only allowed for \c RENDER mode +/// objects. +/// The initial mode must be specified on construction, and can be changed +/// through some method functions. +/// +/// This class uses the "pimpl" idiom, and hides detailed implementation +/// through the \c impl_ pointer. Since a \c Message object is expected to +/// be reused, the construction overhead of this approach should be acceptable. +/// +/// Open issues (among other things): +/// - We may want to provide an "iterator" for all RRsets/RRs for convenience. +/// This will be for applications that do not care about performance much, +/// so the implementation can only be moderately efficient. +/// - We may want to provide a "find" method for a specified type +/// of RR in the message. +class Message { +public: + /// Constants to specify the operation mode of the \c Message. + enum Mode { + PARSE = 0, ///< Parse mode (handling an incoming message) + RENDER = 1 ///< Render mode (building an outgoing message) + }; + + /// \brief Constants for flag bit fields of a DNS message header. + /// + /// Only the defined constants are valid where a header flag is required + /// in this library (e.g., in \c Message::setHeaderFlag()). + /// Since these are enum constants, however, an invalid value could be + /// passed via casting without an error at compilation time. + /// It is generally the callee's responsibility to check and reject invalid + /// values. + /// Of course, applications shouldn't pass invalid values even if the + /// callee does not perform proper validation; the result in such usage + /// is undefined. + /// + /// In the current implementation, the defined values happen to be + /// a 16-bit integer with one bit being set corresponding to the + /// specified flag in the second 16 bits of the DNS Header section + /// in order to make the internal implementation simpler. + /// For example, \c HEADERFLAG_QR is defined to be 0x8000 as the QR + /// bit is the most significant bit of the second 16 bits of the header. + /// However, applications should not assume this coincidence and + /// must solely use the enum representations. + /// Any usage based on the assumption of the underlying values is invalid + /// and the result is undefined. + /// + /// Likewise, bit wise operations such as AND or OR on the flag values + /// are invalid and are not guaranteed to work, even if it could compile + /// with casting. + /// For example, the following code will compile: + /// \code const uint16_t combined_flags = + /// static_cast(Message::HEADERFLAG_AA) | + /// static_cast(Message::HEADERFLAG_CD); + /// message->setHeaderFlag(static_cast(combined_flags)); + /// \endcode + /// and (with the current definition) happens to work as if it were + /// validly written as follows: + /// \code message->setHeaderFlag(Message::HEADERFLAG_AA); + /// message->setHeaderFlag(Message::HEADERFLAG_CD); + /// \endcode + /// But the former notation is invalid and may not work in future versions. + /// We did not try to prohibit such usage at compilation time, e.g., by + /// introducing a separately defined class considering the balance + /// between the complexity and advantage, but hopefully the cast notation + /// is sufficiently ugly to prevent proliferation of the usage. + enum HeaderFlag { + HEADERFLAG_QR = 0x8000, ///< Query (if cleared) or response (if set) + HEADERFLAG_AA = 0x0400, ///< Authoritative answer + HEADERFLAG_TC = 0x0200, ///< Truncation + HEADERFLAG_RD = 0x0100, ///< Recursion desired + HEADERFLAG_RA = 0x0080, ///< Recursion available + HEADERFLAG_AD = 0x0020, ///< Authentic %data (RFC4035) + HEADERFLAG_CD = 0x0010 ///< DNSSEC checking disabled (RFC4035) + }; + + /// \brief Constants to specify sections of a DNS message. + /// + /// The sections are those defined in RFC 1035 excluding the Header + /// section; the fields of the Header section are accessed via specific + /// methods of the \c Message class (e.g., \c getQid()). + /// + /// Open Design Issue: + /// In the current implementation the values for the constants are + /// sorted in the order of appearance in DNS messages, i.e., + /// from %Question to Additional. + /// So, for example, + /// code section >= Message::SECTION_AUTHORITY can be + /// used to do something in or after the Authority section. + /// This would be convenient, but it is not clear if it's really a good + /// idea to rely on relationship between the underlying values of enum + /// constants. At the moment, applications are discouraged to rely on + /// this implementation detail. We will see if such usage is sufficiently + /// common to officially support it. + /// + /// Note also that since we don't define \c operator++ for this enum, + /// the following code intending to iterate over all sections will + /// \b not compile: + /// \code for (Section s; s <= SECTION_ADDITIONAL; ++s) { // ++s undefined + /// // do something + /// } \endcode + /// This is intentional at this moment, and we'll see if we need to allow + /// that as we have more experiences with this library. + /// + /// Future Extension: We'll probably also define constants for + /// the section names used in dynamic updates in future versions. + enum Section { + SECTION_QUESTION = 0, ///< %Question section + SECTION_ANSWER = 1, ///< Answer section + SECTION_AUTHORITY = 2, ///< Authority section + SECTION_ADDITIONAL = 3 ///< Additional section + }; + + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private. + /// The intended use case wouldn't require copies of a \c Message object; + /// once created, it would normally be expected to be reused, changing the + /// mode from \c PARSE to \c RENDER, and vice versa. + //@{ +public: + /// \brief The constructor. + /// The mode of the message is specified by the \c mode parameter. + Message(Mode mode); + /// \brief The destructor. + ~Message(); +private: + Message(const Message& source); + Message& operator=(const Message& source); + //@} +public: + /// \brief Return whether the specified header flag bit is set in the + /// header section. + /// + /// This method is basically exception free, but if + /// \c flag is not a valid constant of the \c HeaderFlag type, + /// an exception of class \c InvalidParameter will be thrown. + /// + /// \param flag The header flag constant to test. + /// \return \c true if the specified flag is set; otherwise \c false. + bool getHeaderFlag(const HeaderFlag flag) const; + + /// \brief Set or clear the specified header flag bit in the header + /// section. + /// + /// The optional parameter \c on indicates the operation mode, + /// set or clear; if it's \c true the corresponding flag will be set; + /// otherwise the flag will be cleared. + /// In either case the original state of the flag does not affect the + /// operation; for example, if a flag is already set and the "set" + /// operation is attempted, it effectively results in no operation. + /// + /// The parameter \c on can be omitted, in which case a value of \c true + /// (i.e., set operation) will be assumed. + /// This is based on the observation that the flag would have to be set + /// in the vast majority of the cases where an application needs to + /// use this method. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + /// + /// If \c flag is not a valid constant of the \c HeaderFlag type, + /// an exception of class \c InvalidParameter will be thrown. + /// + /// \param flag The header flag constant to set or clear. + /// \param on If \c true the flag will be set; otherwise the flag will be + /// cleared. + void setHeaderFlag(const HeaderFlag flag, const bool on = true); + + /// \brief Return the query ID given in the header section of the message. + qid_t getQid() const; + + /// \brief Set the query ID of the header section of the message. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + void setQid(qid_t qid); + + /// \brief Return the Response Code of the message. + /// + /// This includes extended codes specified by an EDNS OPT RR (when + /// included). In the \c PARSE mode, if the received message contains + /// an EDNS OPT RR, the corresponding extended code is identified and + /// returned. + /// + /// The message must have been properly parsed (in the case of the + /// \c PARSE mode) or an \c Rcode has been set (in the case of the + /// \c RENDER mode) beforehand. Otherwise, an exception of class + /// \c InvalidMessageOperation will be thrown. + const Rcode& getRcode() const; + + /// \brief Set the Response Code of the message. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + /// + /// If the specified code is an EDNS extended RCODE, an EDNS OPT RR will be + /// included in the message. + void setRcode(const Rcode& rcode); + + /// \brief Return the OPCODE given in the header section of the message. + /// + /// The message must have been properly parsed (in the case of the + /// \c PARSE mode) or an \c Opcode has been set (in the case of the + /// \c RENDER mode) beforehand. Otherwise, an exception of class + /// \c InvalidMessageOperation will be thrown. + const Opcode& getOpcode() const; + + /// \brief Set the OPCODE of the header section of the message. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + void setOpcode(const Opcode& opcode); + + /// \brief Return, if any, the EDNS associated with the message. + /// + /// This method never throws an exception. + /// + /// \return A shared pointer to the EDNS. This will be a null shared + /// pointer if the message is not associated with EDNS. + ConstEDNSPtr getEDNS() const; + + /// \brief Set EDNS for the message. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + /// + /// \param edns A shared pointer to an \c EDNS object to be set in + /// \c Message. + void setEDNS(ConstEDNSPtr edns); + + /// \brief Return, if any, the TSIG record contained in the received + /// message. + /// + /// Currently, this method is only intended to return a TSIG record + /// for an incoming message built via the \c fromWire() method in the + /// PARSE mode. A call to this method in the RENDER mode is invalid and + /// result in an exception. Also, calling this method is meaningless + /// unless \c fromWire() is performed. + /// + /// The returned pointer is valid only during the lifetime of the + /// \c Message object and until \c clear() is called. The \c Message + /// object retains the ownership of \c TSIGRecord; the caller must not + /// try to delete it. + /// + /// \exception InvalidMessageOperation Message is not in the PARSE mode. + /// + /// \return A pointer to the stored \c TSIGRecord or \c NULL. + const TSIGRecord* getTSIGRecord() const; + + /// \brief Returns the number of RRs contained in the given section. + /// + /// In the \c PARSE mode, the returned value may not be identical to + /// the actual number of RRs of the incoming message that is parsed. + /// The \c Message class handles some "meta" RRs such as EDNS OPT RR + /// separately. This method doesn't include such RRs. + /// Also, a future version of the parser will detect and unify duplicate + /// RRs (which should be rare in practice though), in which case + /// the stored RRs in the \c Message object will be fewer than the RRs + /// originally contained in the incoming message. + /// + /// Likewise, in the \c RENDER mode, even if \c EDNS is set in the + /// \c Message, this method doesn't count the corresponding OPT RR + /// in the Additional section. + /// + /// This method is basically exception free, but if + /// \c section is not a valid constant of the \c Section type, + /// an exception of class \c OutOfRange will be thrown. + /// + /// \param section The section in the message where RRs should be + /// counted. + /// \return The number of RRs stored in the specified section of the + /// message. + unsigned int getRRCount(const Section section) const; + + /// \brief Return an iterator corresponding to the beginning of the + /// Question section of the message. + const QuestionIterator beginQuestion() const; + + /// \brief Return an iterator corresponding to the end of the + /// Question section of the message. + const QuestionIterator endQuestion() const; + + /// \brief Return an iterator corresponding to the beginning of the + /// given section (other than Question) of the message. + /// + /// \c section must be a valid constant of the \c Section type; + /// otherwise, an exception of class \c OutOfRange will be thrown. + const RRsetIterator beginSection(const Section section) const; + + /// \brief Return an iterator corresponding to the end of the + /// given section (other than Question) of the message. + /// + /// \c section must be a valid constant of the \c Section type; + /// otherwise, an exception of class \c OutOfRange will be thrown. + const RRsetIterator endSection(const Section section) const; + + /// \brief Add a (pointer like object of) Question to the message. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + void addQuestion(QuestionPtr question); + + /// \brief Add a (pointer like object of) Question to the message. + /// + /// This version internally creates a \c QuestionPtr object from the + /// given \c question and calls the other version of this method. + /// So this is inherently less efficient, but is provided because this + /// form may be more intuitive and may make more sense for performance + /// insensitive applications. + /// + /// This method is only allowed in the \c RENDER mode; + /// if the \c Message is in other mode, an exception of class + /// InvalidMessageOperation will be thrown. + void addQuestion(const Question& question); + + /// \brief Add a (pointer like object of) RRset to the given section + /// of the message. + /// + /// Note that \c addRRset() does not currently check for duplicate + /// data before inserting RRsets. The caller is responsible for + /// checking for these (see \c hasRRset() below). + /// + /// \throw InvalidParameter rrset is NULL + /// \throw InvalidMessageOperation The message is not in the \c RENDER + /// mode. + /// \throw OutOfRange \c section doesn't specify a valid \c Section value. + /// + /// \param section The message section to which the rrset is to be added + /// \param rrset The rrset to be added. Must not be NULL. + void addRRset(const Section section, RRsetPtr rrset); + + /// \brief Determine whether the given section already has an RRset + /// matching the given name, RR class and RR type. + /// + /// \c section must be a valid constant of the \c Section type; + /// otherwise, an exception of class \c OutOfRange will be thrown. + /// + /// This should probably be extended to be a "find" method that returns + /// a matching RRset if found. + bool hasRRset(const Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype) const; + + /// \brief Determine whether the given section already has an RRset + /// matching the one pointed to by the argument + /// + /// \c section must be a valid constant of the \c Section type; + /// otherwise, an exception of class \c OutOfRange will be thrown. + bool hasRRset(const Section section, const RRsetPtr& rrset) const; + + /// \brief Remove RRSet from Message + /// + /// Removes the RRset identified by the section iterator from the message. + /// Note: if,.for some reason, the RRset is duplicated in the section, only + /// one occurrence is removed. + /// + /// If the operation is successful, all iterators into the section are + /// invalidated. + /// + /// \param section Section to which the iterator belongs + /// \param iterator Iterator pointing to the element to be removed + /// + /// \return true if the element was removed, false if the iterator was not + /// found in the specified section. + bool removeRRset(const Section section, RRsetIterator& iterator); + + /// \brief Remove all RRSets from the given Section + /// + /// This method is only allowed in the \c RENDER mode, and the given + /// section must be valid. + /// + /// \throw InvalidMessageOperation Message is not in the \c RENDER mode + /// \throw OutOfRange The specified section is not valid + /// + /// \param section Section to remove all rrsets from + void clearSection(const Section section); + + // The following methods are not currently implemented. + //void removeQuestion(QuestionPtr question); + // notyet: + //void addRR(const Section section, const RR& rr); + //void removeRR(const Section section, const RR& rr); + + /// \brief Clear the message content (if any) and reinitialize it in the + /// specified mode. + void clear(Mode mode); + + /// \brief Adds all rrsets from the source the given section in the + /// source message to the same section of this message + /// + /// \param section the section to append + /// \param source The source Message + void appendSection(const Section section, const Message& source); + + /// \brief Prepare for making a response from a request. + /// + /// This will clear the DNS header except those fields that should be kept + /// for the response, and clear answer and the following sections. + /// See also dns_message_reply() of BIND9. + void makeResponse(); + + /// \brief Convert the Message to a string. + /// + /// At least \c Opcode and \c Rcode must be validly set in the \c Message + /// (as a result of parse in the \c PARSE mode or by explicitly setting + /// in the \c RENDER mode); otherwise, an exception of + /// class \c InvalidMessageOperation will be thrown. + std::string toText() const; + + /// \brief Render the message in wire formant into a message renderer + /// object with (or without) TSIG. + /// + /// This \c Message must be in the \c RENDER mode and both \c Opcode and + /// \c Rcode must have been set beforehand; otherwise, an exception of + /// class \c InvalidMessageOperation will be thrown. + /// + /// If a non-NULL \c tsig_ctx is passed, it will also add a TSIG RR + /// with (in many cases) the TSIG MAC for the message along with the + /// given TSIG context (\c tsig_ctx). The TSIG RR will be placed at + /// the end of \c renderer. The \c TSIGContext at \c tsig_ctx will + /// be updated based on the fact it was used for signing and with + /// the latest MAC. + /// + /// \exception InvalidMessageOperation The message is not in the Render + /// mode, or either Rcode or Opcode is not set. + /// \exception InvalidParameter The allowable limit of \c renderer is too + /// small for a TSIG or the Header section. Note that this shouldn't + /// happen with parameters as defined in the standard protocols, + /// so it's more likely a program bug. + /// \exception Unexpected Rendering the TSIG RR fails. The implementation + /// internally makes sure this doesn't happen, so if that ever occurs + /// it should mean a bug either in the TSIG context or in the renderer + /// implementation. + /// + /// \note The renderer's internal buffers and data are automatically + /// cleared, keeping the length limit and the compression mode intact. + /// In case truncation is triggered, the renderer is cleared completely. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// \param tsig_ctx A TSIG context that is to be used for signing the + /// message + void toWire(AbstractMessageRenderer& renderer, + TSIGContext* tsig_ctx = NULL); + + /// Parse options. + /// + /// describe PRESERVE_ORDER: note doesn't affect EDNS or TSIG. + /// + /// The option values are used as a parameter for \c fromWire(). + /// These are values of a bitmask type. Bitwise operations can be + /// performed on these values to express compound options. + enum ParseOptions { + PARSE_DEFAULT = 0, ///< The default options + PRESERVE_ORDER = 1 ///< Preserve RR order and don't combine them + }; + + /// \brief Parse the header section of the \c Message. + /// + /// NOTE: If the header has already been parsed by a previous call + /// to this method, this method simply returns (i.e., it does not + /// read from the \c buffer). + void parseHeader(isc::util::InputBuffer& buffer); + + /// \brief (Re)build a \c Message object from wire-format data. + /// + /// This method parses the given wire format data to build a + /// complete Message object. On success, the values of the header section + /// fields can be accessible via corresponding get methods, and the + /// question and following sections can be accessible via the + /// corresponding iterators. If the message contains an EDNS or TSIG, + /// they can be accessible via \c getEDNS() and \c getTSIGRecord(), + /// respectively. + /// + /// This \c Message must be in the \c PARSE mode. + /// + /// This method performs strict validation on the given message based + /// on the DNS protocol specifications. If the given message data is + /// invalid, this method throws an exception (see the exception list). + /// + /// By default, this method combines RRs of the same name, RR type and + /// RR class in a section into a single RRset, even if they are interleaved + /// with a different type of RR (though it would be a rare case in + /// practice). If the \c PRESERVE_ORDER option is specified, it handles + /// each RR separately, in the appearing order, and converts it to a + /// separate RRset (so this RRset should contain exactly one Rdata). + /// This mode will be necessary when the higher level protocol is + /// ordering conscious. For example, in AXFR and IXFR, the position of + /// the SOA RRs are crucial. + /// + /// \exception InvalidMessageOperation \c Message is in the RENDER mode + /// \exception DNSMessageFORMERR The given message data is syntactically + /// \exception MessageTooShort The given data is shorter than a valid + /// header section + /// \exception std::bad_alloc Memory allocation failure + /// \exception Others \c Name, \c Rdata, and \c EDNS classes can also throw + /// + /// \param buffer A input buffer object that stores the wire + /// data. This method reads from position 0 in the passed buffer. + /// \param options Parse options + void fromWire(isc::util::InputBuffer& buffer, ParseOptions options + = PARSE_DEFAULT); + + /// + /// \name Protocol constants + /// + //@{ + /// \brief The default maximum size of UDP DNS messages that don't cause + /// truncation. + /// + /// With EDNS the maximum size can be increased per message. + static const uint16_t DEFAULT_MAX_UDPSIZE = 512; + + /// \brief The default maximum size of UDP DNS messages we can handle + static const uint16_t DEFAULT_MAX_EDNS0_UDPSIZE = 4096; + //@} + +private: + MessageImpl* impl_; +}; + +/// \brief Pointer-like type pointing to a \c Message +/// +/// This type is expected to be used as an argument in asynchronous +/// callback functions. The internal reference-counting will ensure that +/// that ongoing state information will not be lost if the object +/// that originated the asynchronous call falls out of scope. +typedef boost::shared_ptr MessagePtr; +typedef boost::shared_ptr ConstMessagePtr; + +/// Insert the \c Message as a string into stream. +/// +/// This method convert \c message into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param message A \c Message object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const Message& message); +} +} +#endif // MESSAGE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc new file mode 100644 index 0000000..81b2c92 --- /dev/null +++ b/src/lib/dns/messagerenderer.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2009-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace isc::util; +using isc::dns::name::internal::maptolower; + +namespace isc { +namespace dns { + +namespace { // hide internal-only names from the public namespaces +/// +/// \brief The \c OffsetItem class represents a pointer to a name +/// rendered in the internal buffer for the \c MessageRendererImpl object. +/// +/// A \c MessageRendererImpl object maintains a set of \c OffsetItem +/// objects in a hash table, and searches the table for the position of the +/// longest match (ancestor) name against each new name to be rendered into +/// the buffer. +struct OffsetItem { + OffsetItem(size_t hash, size_t pos, size_t len) : + hash_(hash), pos_(pos), len_(len) + {} + + /// The hash value for the stored name calculated by LabelSequence.getHash. + /// This will help make name comparison in \c NameCompare more efficient. + size_t hash_; + + /// The position (offset from the beginning) in the buffer where the + /// name starts. + uint16_t pos_; + + /// The length of the corresponding sequence (which is a domain name). + uint16_t len_; +}; + +/// \brief The \c NameCompare class is a functor that checks equality +/// between the name corresponding to an \c OffsetItem object and the name +/// consists of labels represented by a \c LabelSequence object. +/// +/// Template parameter CASE_SENSITIVE determines whether to ignore the case +/// of the names. This policy doesn't change throughout the lifetime of +/// this object, so we separate these using template to avoid unnecessary +/// condition check. +template +struct NameCompare { + /// \brief Constructor + /// + /// \param buffer The buffer for rendering used in the caller renderer + /// \param name_buf An input buffer storing the wire-format data of the + /// name to be newly rendered (and only that data). + /// \param hash The hash value for the name. + NameCompare(const OutputBuffer& buffer, InputBuffer& name_buf, + size_t hash) : + buffer_(&buffer), name_buf_(&name_buf), hash_(hash) + {} + + bool operator()(const OffsetItem& item) const { + // Trivial inequality check. If either the hash or the total length + // doesn't match, the names are obviously different. + if (item.hash_ != hash_ || item.len_ != name_buf_->getLength()) { + return (false); + } + + // Compare the name data, character-by-character. + // item_pos keeps track of the position in the buffer corresponding to + // the character to compare. item_label_len is the number of + // characters in the labels where the character pointed by item_pos + // belongs. When it reaches zero, nextPosition() identifies the + // position for the subsequent label, taking into account name + // compression, and resets item_label_len to the length of the new + // label. + name_buf_->setPosition(0); // buffer can be reused, so reset position + uint16_t item_pos = item.pos_; + uint16_t item_label_len = 0; + for (size_t i = 0; i < item.len_; ++i, ++item_pos) { + item_pos = nextPosition(*buffer_, item_pos, item_label_len); + const uint8_t ch1 = (*buffer_)[item_pos]; + const uint8_t ch2 = name_buf_->readUint8(); + if (CASE_SENSITIVE) { + if (ch1 != ch2) { + return (false); + } + } else { + if (maptolower[ch1] != maptolower[ch2]) { + return (false); + } + } + } + + return (true); + } + +private: + static uint16_t nextPosition(const OutputBuffer& buffer, + uint16_t pos, uint16_t& llen) + { + if (llen == 0) { + size_t i = 0; + + while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) == + Name::COMPRESS_POINTER_MARK8) { + pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) * + 256 + buffer[pos + 1]; + + // This loop should stop as long as the buffer has been + // constructed validly and the search/insert argument is based + // on a valid name, which is an assumption for this class. + // But we'll abort if a bug could cause an infinite loop. + i += 2; + assert(i < Name::MAX_WIRE); + } + llen = buffer[pos]; + } else { + --llen; + } + return (pos); + } + + const OutputBuffer* buffer_; + InputBuffer* name_buf_; + const size_t hash_; +}; +} + +/// +/// \brief The \c MessageRendererImpl class is the actual implementation of +/// \c MessageRenderer. +/// +/// The implementation is hidden from applications. We can refer to specific +/// members of this class only within the implementation source file. +/// +/// It internally holds a hash table for OffsetItem objects corresponding +/// to portions of names rendered in this renderer. The offset information +/// is used to compress subsequent names to be rendered. +struct MessageRenderer::MessageRendererImpl { + // The size of hash buckets and number of hash entries per bucket for + // which space is preallocated and kept reserved for subsequent rendering + // to provide better performance. These values are derived from the + // BIND 9 implementation that uses a similar hash table. + static const size_t BUCKETS = 64; + static const size_t RESERVED_ITEMS = 16; + static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found' + + /// \brief Constructor + MessageRendererImpl() : + msglength_limit_(512), truncated_(false), + compress_mode_(MessageRenderer::CASE_INSENSITIVE) + { + // Reserve some spaces for hash table items. + for (size_t i = 0; i < BUCKETS; ++i) { + table_[i].reserve(RESERVED_ITEMS); + } + } + + uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf, + size_t hash, bool case_sensitive) const + { + // Find a matching entry, if any. We use some heuristics here: often + // the same name appears consecutively (like repeating the same owner + // name for a single RRset), so in case there's a collision in the + // bucket it will be more likely to find it in the tail side of the + // bucket. + const size_t bucket_id = hash % BUCKETS; + vector::const_reverse_iterator found; + if (case_sensitive) { + found = find_if(table_[bucket_id].rbegin(), + table_[bucket_id].rend(), + NameCompare(buffer, name_buf, hash)); + } else { + found = find_if(table_[bucket_id].rbegin(), + table_[bucket_id].rend(), + NameCompare(buffer, name_buf, hash)); + } + if (found != table_[bucket_id].rend()) { + return (found->pos_); + } + return (NO_OFFSET); + } + + void addOffset(size_t hash, size_t offset, size_t len) { + table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len)); + } + + // The hash table for the (offset + position in the buffer) entries + vector table_[BUCKETS]; + /// The maximum length of rendered data that can fit without + /// truncation. + uint16_t msglength_limit_; + /// A boolean flag that indicates truncation has occurred while rendering + /// the data. + bool truncated_; + /// The name compression mode. + CompressMode compress_mode_; + + // Placeholder for hash values as they are calculated in writeName(). + // Note: we may want to make it a local variable of writeName() if it + // works more efficiently. + boost::array seq_hashes_; +}; + +MessageRenderer::MessageRenderer() : + AbstractMessageRenderer(), + impl_(new MessageRendererImpl) +{} + +MessageRenderer::~MessageRenderer() { + delete impl_; +} + +void +MessageRenderer::clear() { + AbstractMessageRenderer::clear(); + impl_->msglength_limit_ = 512; + impl_->truncated_ = false; + impl_->compress_mode_ = CASE_INSENSITIVE; + + // Clear the hash table. We reserve the minimum space for possible + // subsequent use of the renderer. + for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) { + if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) { + // Trim excessive capacity: swap ensures the new capacity is only + // reasonably large for the reserved space. + vector new_table; + new_table.reserve(MessageRendererImpl::RESERVED_ITEMS); + new_table.swap(impl_->table_[i]); + } + impl_->table_[i].clear(); + } +} + +size_t +MessageRenderer::getLengthLimit() const { + return (impl_->msglength_limit_); +} + +void +MessageRenderer::setLengthLimit(const size_t len) { + impl_->msglength_limit_ = len; +} + +bool +MessageRenderer::isTruncated() const { + return (impl_->truncated_); +} + +void +MessageRenderer::setTruncated() { + impl_->truncated_ = true; +} + +MessageRenderer::CompressMode +MessageRenderer::getCompressMode() const { + return (impl_->compress_mode_); +} + +void +MessageRenderer::setCompressMode(const CompressMode mode) { + if (getLength() != 0) { + isc_throw(isc::InvalidParameter, + "compress mode cannot be changed during rendering"); + } + impl_->compress_mode_ = mode; +} + +void +MessageRenderer::writeName(const LabelSequence& ls, const bool compress) { + LabelSequence sequence(ls); + const size_t nlabels = sequence.getLabelCount(); + size_t data_len; + const uint8_t* data; + + // Find the offset in the offset table whose name gives the longest + // match against the name to be rendered. + size_t nlabels_uncomp; + uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET; + const bool case_sensitive = (impl_->compress_mode_ == + MessageRenderer::CASE_SENSITIVE); + for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) { + if (nlabels_uncomp > 0) { + sequence.stripLeft(1); + } + + data = sequence.getData(&data_len); + if (data_len == 1) { // trailing dot. + ++nlabels_uncomp; + break; + } + // write with range check for safety + impl_->seq_hashes_.at(nlabels_uncomp) = + sequence.getHash(impl_->compress_mode_); + InputBuffer name_buf(data, data_len); + ptr_offset = impl_->findOffset(getBuffer(), name_buf, + impl_->seq_hashes_[nlabels_uncomp], + case_sensitive); + if (ptr_offset != MessageRendererImpl::NO_OFFSET) { + break; + } + } + + // Record the current offset before updating the offset table + size_t offset = getLength(); + // Write uncompress part: + if (nlabels_uncomp > 0 || !compress) { + LabelSequence uncomp_sequence(ls); + if (compress && nlabels > nlabels_uncomp) { + // If there's compressed part, strip off that part. + uncomp_sequence.stripRight(nlabels - nlabels_uncomp); + } + data = uncomp_sequence.getData(&data_len); + writeData(data, data_len); + } + // And write compression pointer if available: + if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) { + ptr_offset |= Name::COMPRESS_POINTER_MARK16; + writeUint16(ptr_offset); + } + + // Finally, record the offset and length for each uncompressed sequence + // in the hash table. The renderer's buffer has just stored the + // corresponding data, so we use the rendered data to get the length + // of each label of the names. + size_t seqlen = ls.getDataLength(); + for (size_t i = 0; i < nlabels_uncomp; ++i) { + const uint8_t label_len = getBuffer()[offset]; + if (label_len == 0) { // offset for root doesn't need to be stored. + break; + } + if (offset > Name::MAX_COMPRESS_POINTER) { + break; + } + // Store the tuple of to the table. Note that we + // already know the hash value for each name. + impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen); + offset += (label_len + 1); + seqlen -= (label_len + 1); + } +} + +void +MessageRenderer::writeName(const Name& name, const bool compress) { + const LabelSequence ls(name); + writeName(ls, compress); +} + +AbstractMessageRenderer::AbstractMessageRenderer() : + local_buffer_(0), buffer_(&local_buffer_) +{ +} + +void +AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) { + if (buffer != NULL && buffer_->getLength() != 0) { + isc_throw(isc::InvalidParameter, + "MessageRenderer buffer cannot be set when in use"); + } + if (buffer == NULL && buffer_ == &local_buffer_) { + isc_throw(isc::InvalidParameter, + "Default MessageRenderer buffer cannot be reset"); + } + + if (buffer == NULL) { + // Reset to the default buffer, then clear other internal resources. + // The order is important; we need to keep the used buffer intact. + buffer_ = &local_buffer_; + clear(); + } else { + buffer_ = buffer; + } +} + +void +AbstractMessageRenderer::clear() { + buffer_->clear(); +} + +} +} diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h new file mode 100644 index 0000000..1b8b9c0 --- /dev/null +++ b/src/lib/dns/messagerenderer.h @@ -0,0 +1,395 @@ +// Copyright (C) 2009-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MESSAGERENDERER_H +#define MESSAGERENDERER_H 1 + +#include + +#include + +namespace isc { + +namespace dns { +// forward declarations +class Name; +class LabelSequence; + +/// \brief The \c AbstractMessageRenderer class is an abstract base class +/// that provides common interfaces for rendering a DNS message into a buffer +/// in wire format. +/// +/// A specific derived class of \c AbstractMessageRenderer (we call it +/// a renderer class hereafter) is simply responsible for name compression at +/// least in the current design. A renderer class object (conceptually) +/// manages the positions of names rendered in some sort of buffer and uses +/// that information to render subsequent names with compression. +/// +/// A renderer class is mainly intended to be used as a helper for a more +/// comprehensive \c Message class internally; normal applications won't have +/// to care about details of this class. +/// +/// By default any (derived) renderer class object is associated with +/// an internal buffer, and subsequent write operations will be performed +/// on that buffer. The rendering result can be retrieved via the +/// \c getData() method. +/// +/// If an application wants a separate buffer can be (normally temporarily) +/// set for rendering operations via the \c setBuffer() method. In that case, +/// it is generally expected that all rendering operations are performed via +/// that object. If the application modifies the buffer in +/// parallel with the renderer, the result will be undefined. +/// +/// Note to developers: we introduced a separate class for name compression +/// because previous benchmark with BIND9 showed compression affects overall +/// response performance very much. By having a separate class dedicated for +/// this purpose, we'll be able to change the internal implementation of name +/// compression in the future without affecting other part of the API and +/// implementation. +/// +/// In addition, by introducing a class hierarchy from +/// \c AbstractMessageRenderer, we allow an application to use a customized +/// renderer class for specific purposes. For example, a high performance +/// DNS server may want to use an optimized renderer class assuming some +/// specific underlying data representation. +/// +/// \note Some functions (like writeUint8) are not virtual. It is because +/// it is hard to imagine any version of message renderer that would +/// do anything else than just putting the data into a buffer, so we +/// provide a default implementation and having them virtual would only +/// hurt the performance with no real gain. If it would happen a different +/// implementation is really needed, we can make them virtual in future. +/// The only one that is virtual is writeName and it's because this +/// function is much more complicated, therefore there's a lot of space +/// for different implementations or different behavior. +class AbstractMessageRenderer { +public: + /// \brief Compression mode constants. + /// + /// The \c CompressMode enum type represents the name compression mode + /// for renderer classes. + /// \c CASE_INSENSITIVE means compress names in case-insensitive manner; + /// \c CASE_SENSITIVE means compress names in case-sensitive manner. + /// By default, a renderer compresses names in case-insensitive + /// manner. + /// Compression mode can be dynamically modified by the + /// \c setCompressMode() method. + /// The mode can be changed even in the middle of rendering, although this + /// is not an intended usage. In this case the names already compressed + /// are intact; only names being compressed after the mode change are + /// affected by the change. + /// If a renderer class object is reinitialized by the \c clear() + /// method, the compression mode will be reset to the default, which is + /// \c CASE_INSENSITIVE + /// + /// One specific case where case-sensitive compression is required is + /// AXFR as described in draft-ietf-dnsext-axfr-clarify. A primary + /// authoritative DNS server implementation using this API would specify + /// \c CASE_SENSITIVE before rendering outgoing AXFR messages. + /// + enum CompressMode { + CASE_INSENSITIVE, //!< Compress names case-insensitive manner (default) + CASE_SENSITIVE //!< Compress names case-sensitive manner + }; +protected: + /// + /// \name Constructors and Destructor + //@{ + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class should + /// never be instantiated (except as part of a derived class). + AbstractMessageRenderer(); + +public: + /// \brief The destructor. + virtual ~AbstractMessageRenderer() {} + //@} +protected: + /// \brief Return the output buffer we render into. + const isc::util::OutputBuffer& getBuffer() const { return (*buffer_); } + isc::util::OutputBuffer& getBuffer() { return (*buffer_); } +private: + /// \brief Local (default) buffer to store data. + isc::util::OutputBuffer local_buffer_; + + /// \brief Buffer to store data. + /// + /// Note that the class interface ensures this pointer is never NULL; + /// it either refers to \c local_buffer_ or to an application-supplied + /// buffer by \c setBuffer(). + /// + /// It was decided that there's no need to have this in every subclass, + /// at least not now, and this reduces code size and gives compiler a + /// better chance to optimize. + isc::util::OutputBuffer* buffer_; +public: + /// + /// \name Getter Methods + /// + //@{ + /// \brief Return a pointer to the head of the data stored in the internal + /// buffer. + /// + /// This method works exactly same as the same method of the \c OutputBuffer + /// class; all notes for \c OutputBuffer apply. + const void* getData() const { + return (buffer_->getData()); + } + + /// \brief Return the length of data written in the internal buffer. + size_t getLength() const { + return (buffer_->getLength()); + } + + /// \brief Return whether truncation has occurred while rendering. + /// + /// Once the return value of this method is \c true, it doesn't make sense + /// to try rendering more data, although this class itself doesn't reject + /// the attempt. + /// + /// This method never throws an exception. + /// + /// \return true if truncation has occurred; otherwise \c false. + virtual bool isTruncated() const = 0; + + /// \brief Return the maximum length of rendered data that can fit in the + /// corresponding DNS message without truncation. + /// + /// This method never throws an exception. + /// + /// \return The maximum length in bytes. + virtual size_t getLengthLimit() const = 0; + + /// \brief Return the compression mode of the renderer class object. + /// + /// This method never throws an exception. + /// + /// \return The current compression mode. + virtual CompressMode getCompressMode() const = 0; + //@} + + /// + /// \name Setter Methods + /// + //@{ + /// \brief Set or reset a temporary output buffer. + /// + /// This method can be used for an application that manages an output + /// buffer separately from the message renderer and wants to keep reusing + /// the renderer. When the renderer is associated with the default buffer + /// and the given pointer is non NULL, the given buffer will be + /// (temporarily) used for subsequent message rendering; if the renderer + /// is associated with a temporary buffer and the given pointer is NULL, + /// the renderer will be reset with the default buffer. In the latter + /// case any additional resources (possibly specific to a derived renderer + /// class) will be cleared, but the temporary buffer is kept as the latest + /// state (which would normally store the rendering result). + /// + /// This method imposes some restrictions to prevent accidental misuse + /// that could cause disruption such as dereferencing an invalid object. + /// First, a temporary buffer must not be set when the associated buffer + /// is in use, that is, any data are stored in the buffer. Also, the + /// default buffer cannot be "reset"; when NULL is specified a temporary + /// buffer must have been set beforehand. If these conditions aren't met + /// an isc::InvalidParameter exception will be thrown. This method is + /// exception free otherwise. + /// + /// \throw isc::InvalidParameter A restrictions of the method usage isn't + /// met. + /// + /// \param buffer A pointer to a temporary output buffer or NULL for reset + /// it. + void setBuffer(isc::util::OutputBuffer* buffer); + + /// \brief Mark the renderer to indicate truncation has occurred while + /// rendering. + /// + /// This method never throws an exception. + virtual void setTruncated() = 0; + + /// \brief Set the maximum length of rendered data that can fit in the + /// corresponding DNS message without truncation. + /// + /// This method never throws an exception. + /// + /// \param len The maximum length in bytes. + virtual void setLengthLimit(size_t len) = 0; + + /// \brief Set the compression mode of the renderer class object. + /// + /// This method never throws an exception. + /// + /// \param mode A \c CompressMode value representing the compression mode. + virtual void setCompressMode(CompressMode mode) = 0; + //@} + + /// + /// \name Methods for writing data into the internal buffer. + /// + //@{ + /// \brief Insert a specified length of gap at the end of the buffer. + /// + /// The caller should not assume any particular value to be inserted. + /// This method is provided as a shortcut to make a hole in the buffer + /// that is to be filled in later, e.g, by \ref writeUint16At(). + /// + /// \param len The length of the gap to be inserted in bytes. + void skip(size_t len) { + buffer_->skip(len); + } + + /// \brief Trim the specified length of data from the end of the internal + /// buffer. + /// + /// This method is provided for such cases as DNS message truncation. + /// + /// The specified length must not exceed the current data size of the + /// buffer; otherwise an exception of class \c isc::OutOfRange will + /// be thrown. + /// + /// \param len The length of data that should be trimmed. + void trim(size_t len) { + buffer_->trim(len); + } + + /// \brief Clear the internal buffer and other internal resources. + /// + /// This method can be used to re-initialize and reuse the renderer + /// without constructing a new one. + virtual void clear(); + + /// \brief Write an unsigned 8-bit integer into the internal buffer. + /// + /// \param data The 8-bit integer to be written into the internal buffer. + void writeUint8(const uint8_t data) { + buffer_->writeUint8(data); + } + + /// \brief Write an unsigned 16-bit integer in host byte order into the + /// internal buffer in network byte order. + /// + /// \param data The 16-bit integer to be written into the buffer. + void writeUint16(uint16_t data) { + buffer_->writeUint16(data); + } + + /// \brief Write an unsigned 16-bit integer in host byte order at the + /// specified position of the internal buffer in network byte order. + /// + /// The buffer must have a sufficient room to store the given data at the + /// given position, that is, pos + 2 < getLength(); + /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will + /// be thrown. + /// Note also that this method never extends the internal buffer. + /// + /// \param data The 16-bit integer to be written into the internal buffer. + /// \param pos The beginning position in the buffer to write the data. + void writeUint16At(uint16_t data, size_t pos) { + buffer_->writeUint16At(data, pos); + } + + /// \brief Write an unsigned 32-bit integer in host byte order into the + /// internal buffer in network byte order. + /// + /// \param data The 32-bit integer to be written into the buffer. + void writeUint32(uint32_t data) { + buffer_->writeUint32(data); + } + + /// \brief Copy an arbitrary length of data into the internal buffer + /// of the renderer object. + /// + /// No conversion on the copied data is performed. + /// + /// \param data A pointer to the data to be copied into the internal buffer. + /// \param len The length of the data in bytes. + void writeData(const void *data, size_t len) { + buffer_->writeData(data, len); + } + + /// \brief Write a \c Name object into the internal buffer in wire format, + /// with or without name compression. + /// + /// If the optional parameter \c compress is \c true, this method tries to + /// compress the \c name if possible, searching the entire message that has + /// been rendered. Otherwise name compression is omitted. Its default + /// value is \c true. + /// + /// Note: even if \c compress is \c true, the position of the \c name (and + /// possibly its ancestor names) in the message is recorded and may be used + /// for compressing subsequent names. + /// + /// \param name A \c Name object to be written. + /// \param compress A boolean indicating whether to enable name + /// compression. + virtual void writeName(const Name& name, bool compress = true) = 0; + + /// \brief Write a \c LabelSequence object into the internal buffer + /// in wire format, with or without name compression. + /// + /// This is the same as the other version, which takes \c Name instead + /// of \c LabelSequence, except for the parameter type. The passed + /// \c LabelSequence must be absolute. + /// + /// \param ls A \c LabelSequence object to be written. + /// \param compress A boolean indicating whether to enable name + /// compression. + virtual void writeName(const LabelSequence& ls, bool compress = true) = 0; + //@} +}; + +/// The \c MessageRenderer is a concrete derived class of +/// \c AbstractMessageRenderer as a general purpose implementation of the +/// renderer interfaces. +/// +/// A \c MessageRenderer object is constructed with a \c OutputBuffer +/// object, which is the buffer into which the rendered %data will be written. +/// Normally the buffer is expected to be empty on construction, but it doesn't +/// have to be so; the renderer object will start rendering from the +/// end of the buffer at the time of construction. However, if the +/// pre-existing portion of the buffer contains DNS names, these names won't +/// be considered for name compression. +class MessageRenderer : public AbstractMessageRenderer, + public boost::noncopyable { // Can crash if copied +public: + using AbstractMessageRenderer::CASE_INSENSITIVE; + using AbstractMessageRenderer::CASE_SENSITIVE; + + MessageRenderer(); + + virtual ~MessageRenderer(); + virtual bool isTruncated() const; + virtual size_t getLengthLimit() const; + virtual CompressMode getCompressMode() const; + virtual void setTruncated(); + virtual void setLengthLimit(size_t len); + + /// This implementation does not allow this call in the middle of + /// rendering (i.e. after at least one name is rendered) due to + /// restriction specific to the internal implementation. Such attempts + /// will result in an \c isc::InvalidParameter exception. + /// + /// This shouldn't be too restrictive in practice; there's no known + /// practical case for such a mixed compression policy in a single + /// message. + virtual void setCompressMode(CompressMode mode); + + virtual void clear(); + virtual void writeName(const Name& name, bool compress = true); + virtual void writeName(const LabelSequence& ls, bool compress = true); + +private: + struct MessageRendererImpl; + MessageRendererImpl* impl_; +}; +} +} +#endif // MESSAGERENDERER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc new file mode 100644 index 0000000..3f75fa0 --- /dev/null +++ b/src/lib/dns/name.cc @@ -0,0 +1,724 @@ +// Copyright (C) 2009-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; +using isc::dns::NameComparisonResult; +using namespace isc::dns::name::internal; + +namespace isc { +namespace dns { + +namespace { +/// +/// These are shortcut arrays for efficient character conversion. +/// digitvalue converts a digit character to the corresponding integer. +/// maptolower convert uppercase alphabets to their lowercase counterparts. +/// We once used a helper non-local static object to avoid hardcoding the +/// array members, but we then realized it's susceptible to static +/// initialization order fiasco: Since these constants are used in a Name +/// constructor, a non-local static Name object defined in another translation +/// unit than this file may not be initialized correctly. +/// There are several ways to address this issue, but in this specific case +/// we chose the naive but simple hardcoding approach. +/// +/// These definitions are derived from BIND 9's libdns module. +/// Note: we could use the standard tolower() function instead of the +/// maptolower array, but a benchmark indicated that the private array could +/// improve the performance of message rendering (which internally uses the +/// array heavily) about 27%. Since we want to achieve very good performance +/// for message rendering in some cases, we'll keep using it. +const signed char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 64 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 256 +}; +} + +namespace name { +namespace internal { +const uint8_t maptolower[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // ..., 'A' - 'G' + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, // 'H' - 'O' + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // 'P' - 'W' + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, // 'X' - 'Z', ... + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; +} // end of internal +} // end of name + +namespace { +/// +/// Textual name parser states. +/// +typedef enum { + ft_init = 0, // begin of the name + ft_start, // begin of a label + ft_ordinary, // parsing an ordinary label + ft_initialescape, // just found '\' + ft_escape, // begin of handling a '\'-escaped sequence + ft_escdecimal // parsing a '\DDD' octet. +} ft_state; + +// The parser of name from a string. It is a template, because +// some parameters are used with two different types, while others +// are private type aliases. +template +void +stringParse(Iterator s, Iterator send, bool downcase, Offsets& offsets, + Data& ndata) +{ + const Iterator orig_s(s); + // + // Initialize things to make the compiler happy; they're not required. + // + unsigned int digits = 0; + unsigned int value = 0; + unsigned int count = 0; + + // + // Set up the state machine. + // + bool done = false; + bool is_root = false; + const bool empty = s == send; + ft_state state = ft_init; + + // Prepare the output buffers. + offsets.reserve(Name::MAX_LABELS); + offsets.push_back(0); + ndata.reserve(Name::MAX_WIRE); + + // should we refactor this code using, e.g, the state pattern? Probably + // not at this point, as this is based on proved code (derived from BIND9) + // and it's less likely that we'll have more variations in the domain name + // syntax. If this ever happens next time, we should consider refactor + // the code, rather than adding more states and cases below. + while (ndata.size() < Name::MAX_WIRE && s != send && !done) { + unsigned char c = *s++; + + switch (state) { + case ft_init: + // + // Is this the root name? + // + if (c == '.') { + if (s != send) { + isc_throw(EmptyLabel, + "non terminating empty label in " << + string(orig_s, send)); + } + is_root = true; + } else if (c == '@' && s == send) { + // handle a single '@' as the root name. + is_root = true; + } + + if (is_root) { + ndata.push_back(0); + done = true; + break; + } + + // FALLTHROUGH + case ft_start: + ndata.push_back(0); // placeholder for the label length field + count = 0; + if (c == '\\') { + state = ft_initialescape; + break; + } + state = ft_ordinary; + isc_throw_assert(ndata.size() < Name::MAX_WIRE); + // FALLTHROUGH + case ft_ordinary: + if (c == '.') { + if (count == 0) { + isc_throw(EmptyLabel, + "duplicate period in " << string(orig_s, send)); + } + ndata.at(offsets.back()) = count; + offsets.push_back(ndata.size()); + if (s == send) { + ndata.push_back(0); + done = true; + } + state = ft_start; + } else if (c == '\\') { + state = ft_escape; + } else { + if (++count > Name::MAX_LABELLEN) { + isc_throw(TooLongLabel, + "label is too long in " << string(orig_s, send)); + } + ndata.push_back(downcase ? maptolower[c] : c); + } + break; + case ft_initialescape: + if (c == '[') { + // This looks like a bitstring label, which was deprecated. + // Intentionally drop it. + isc_throw(BadLabelType, + "invalid label type in " << string(orig_s, send)); + } + // FALLTHROUGH + case ft_escape: + if (!isdigit(c & 0xff)) { + if (++count > Name::MAX_LABELLEN) { + isc_throw(TooLongLabel, + "label is too long in " << string(orig_s, send)); + } + ndata.push_back(downcase ? maptolower[c] : c); + state = ft_ordinary; + break; + } + digits = 0; + value = 0; + state = ft_escdecimal; + // FALLTHROUGH + case ft_escdecimal: + if (!isdigit(c & 0xff)) { + isc_throw(BadEscape, + "mixture of escaped digit and non-digit in " + << string(orig_s, send)); + } + value *= 10; + value += digitvalue[c]; + digits++; + if (digits == 3) { + if (value > 255) { + isc_throw(BadEscape, + "escaped decimal is too large in " + << string(orig_s, send)); + } + if (++count > Name::MAX_LABELLEN) { + isc_throw(TooLongLabel, + "label is too long in " << string(orig_s, send)); + } + ndata.push_back(downcase ? maptolower[value] : value); + state = ft_ordinary; + } + break; + default: + // impossible case + isc_throw_assert(false); + } + } + + if (!done) { // no trailing '.' was found. + if (ndata.size() == Name::MAX_WIRE) { + isc_throw(TooLongName, + "name is too long for termination in " << + string(orig_s, send)); + } + isc_throw_assert(s == send); + if (state != ft_ordinary) { + isc_throw(IncompleteName, + "incomplete textual name in " << + (empty ? "" : string(orig_s, send))); + } + if (state == ft_ordinary) { + isc_throw_assert(count != 0); + ndata.at(offsets.back()) = count; + + offsets.push_back(ndata.size()); + // add a trailing \0 + ndata.push_back('\0'); + } + } +} + +} + +Name::Name(const std::string &namestring, bool downcase) { + // Prepare inputs for the parser + const std::string::const_iterator s = namestring.begin(); + const std::string::const_iterator send = namestring.end(); + + // Prepare outputs + NameOffsets offsets; + NameString ndata; + + // To the parsing + stringParse(s, send, downcase, offsets, ndata); + + // And get the output + labelcount_ = offsets.size(); + isc_throw_assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS); + ndata_.assign(ndata.data(), ndata.size()); + length_ = ndata_.size(); + offsets_.assign(offsets.begin(), offsets.end()); +} + +Name::Name(const char* namedata, size_t data_len, const Name* origin, + bool downcase) +{ + // Check validity of data + if (namedata == NULL || data_len == 0) { + isc_throw(isc::InvalidParameter, + "No data provided to Name constructor"); + } + // If the last character is not a dot, it is a relative to origin. + // It is safe to check now, we know there's at least one character. + const bool absolute = (namedata[data_len - 1] == '.'); + // If we are not absolute, we need the origin to complete the name. + if (!absolute && origin == NULL) { + isc_throw(MissingNameOrigin, + "No origin available and name is relative"); + } + // Prepare inputs for the parser + const char* end = namedata + data_len; + + // Prepare outputs + NameOffsets offsets; + NameString ndata; + + // Do the actual parsing + stringParse(namedata, end, downcase, offsets, ndata); + + // Get the output + labelcount_ = offsets.size(); + isc_throw_assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS); + ndata_.assign(ndata.data(), ndata.size()); + length_ = ndata_.size(); + offsets_.assign(offsets.begin(), offsets.end()); + + if (!absolute) { + // Now, extend the data with the ones from origin. But eat the + // last label (the empty one). + + // Drop the last character of the data (the \0) and append a copy of + // the origin's data + ndata_.erase(ndata_.end() - 1); + ndata_.append(origin->ndata_); + + // Do a similar thing with offsets. However, we need to move them + // so they point after the prefix we parsed before. + size_t offset = offsets_.back(); + offsets_.pop_back(); + size_t offset_count = offsets_.size(); + offsets_.insert(offsets_.end(), origin->offsets_.begin(), + origin->offsets_.end()); + for (NameOffsets::iterator it(offsets_.begin() + offset_count); + it != offsets_.end(); ++it) { + *it += offset; + } + + // Adjust sizes. + length_ = ndata_.size(); + labelcount_ = offsets_.size(); + + // And check the sizes are OK. + if (labelcount_ > Name::MAX_LABELS || length_ > Name::MAX_WIRE) { + isc_throw(TooLongName, "Combined name is too long"); + } + } +} + +namespace { +/// +/// Wire-format name parser states. +/// +typedef enum { + fw_start = 0, // beginning of a label + fw_ordinary, // inside an ordinary (non compressed) label + fw_newcurrent // beginning of a compression pointer +} fw_state; +} + +Name::Name(InputBuffer& buffer, bool downcase) { + NameOffsets offsets; + offsets.reserve(Name::MAX_LABELS); + + /* + * Initialize things to make the compiler happy; they're not required. + */ + unsigned int n = 0; + + // + // Set up. + // + bool done = false; + unsigned int nused = 0; + bool seen_pointer = false; + fw_state state = fw_start; + + unsigned int cused = 0; // Bytes of compressed name data used + unsigned int current = buffer.getPosition(); + unsigned int pos_begin = current; + unsigned int biggest_pointer = current; + + // Make the compiler happy; this is not required. + // XXX: bad style in that we initialize it with a dummy value and define + // it far from where it's used. But alternatives seemed even worse. + unsigned int new_current = 0; + + // + // Note: The following code is not optimized for speed, but + // rather for correctness. Speed will be addressed in the future. + // + while (current < buffer.getLength() && !done) { + unsigned int c = buffer.readUint8(); + current++; + if (!seen_pointer) { + cused++; + } + + switch (state) { + case fw_start: + if (c <= MAX_LABELLEN) { + offsets.push_back(nused); + if (nused + c + 1 > Name::MAX_WIRE) { + isc_throw(DNSMessageFORMERR, "wire name is too long: " + << nused + c + 1 << " bytes"); + } + nused += c + 1; + ndata_.push_back(c); + if (c == 0) { + done = true; + } + n = c; + state = fw_ordinary; + } else if ((c & COMPRESS_POINTER_MARK8) == COMPRESS_POINTER_MARK8) { + // + // Ordinary 14-bit pointer. + // + new_current = c & ~COMPRESS_POINTER_MARK8; + n = 1; + state = fw_newcurrent; + } else { + // this case includes local compression pointer, which hasn't + // been standardized. + isc_throw(DNSMessageFORMERR, "unknown label character: " << c); + } + break; + case fw_ordinary: + if (downcase) { + c = maptolower[c]; + } + ndata_.push_back(c); + if (--n == 0) { + state = fw_start; + } + break; + case fw_newcurrent: + new_current *= 256; + new_current += c; + if (--n != 0) { + break; + } + if (new_current >= biggest_pointer) { + isc_throw(DNSMessageFORMERR, + "bad compression pointer (out of range): " << + new_current); + } + biggest_pointer = new_current; + current = new_current; + buffer.setPosition(current); + seen_pointer = true; + state = fw_start; + break; + default: + isc_throw_assert(false); + } + } + + if (!done) { + isc_throw(DNSMessageFORMERR, "incomplete wire-format name"); + } + + labelcount_ = offsets.size(); + length_ = nused; + offsets_.assign(offsets.begin(), offsets.end()); + buffer.setPosition(pos_begin + cused); +} + +void +Name::toWire(OutputBuffer& buffer) const { + buffer.writeData(ndata_.data(), ndata_.size()); +} + +void +Name::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(*this); +} + +std::string +Name::toText(bool omit_final_dot) const { + LabelSequence ls(*this); + return (ls.toText(omit_final_dot)); +} + +std::string +Name::toRawText(bool omit_final_dot) const { + LabelSequence ls(*this); + return (ls.toRawText(omit_final_dot)); +} + +NameComparisonResult +Name::compare(const Name& other) const { + const LabelSequence ls1(*this); + const LabelSequence ls2(other); + return (ls1.compare(ls2)); +} + +bool +Name::equals(const Name& other) const { + if (length_ != other.length_ || labelcount_ != other.labelcount_) { + return (false); + } + + for (unsigned int l = labelcount_, pos = 0; l > 0; --l) { + uint8_t count = ndata_[pos]; + if (count != other.ndata_[pos]) { + return (false); + } + ++pos; + + while (count-- > 0) { + uint8_t label1 = ndata_[pos]; + uint8_t label2 = other.ndata_[pos]; + + if (maptolower[label1] != maptolower[label2]) { + return (false); + } + ++pos; + } + } + + return (true); +} + +bool +Name::leq(const Name& other) const { + return (compare(other).getOrder() <= 0); +} + +bool +Name::geq(const Name& other) const { + return (compare(other).getOrder() >= 0); +} + +bool +Name::lthan(const Name& other) const { + return (compare(other).getOrder() < 0); +} + +bool +Name::gthan(const Name& other) const { + return (compare(other).getOrder() > 0); +} + +bool +Name::isWildcard() const { + return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*'); +} + +Name +Name::concatenate(const Name& suffix) const { + isc_throw_assert(length_ > 0 && suffix.length_ > 0); + isc_throw_assert(labelcount_ > 0 && suffix.labelcount_ > 0); + + unsigned int length = length_ + suffix.length_ - 1; + if (length > Name::MAX_WIRE) { + isc_throw(TooLongName, "names are too long to concatenate"); + } + + Name retname; + retname.ndata_.reserve(length); + retname.ndata_.assign(ndata_, 0, length_ - 1); + retname.ndata_.insert(retname.ndata_.end(), + suffix.ndata_.begin(), suffix.ndata_.end()); + isc_throw_assert(retname.ndata_.size() == length); + retname.length_ = length; + + // + // Setup the offsets vector. Copy the offsets of this (prefix) name, + // excluding that for the trailing dot, and append the offsets of the + // suffix name with the additional offset of the length of the prefix. + // + unsigned int labels = labelcount_ + suffix.labelcount_ - 1; + isc_throw_assert(labels <= Name::MAX_LABELS); + retname.offsets_.reserve(labels); + retname.offsets_.assign(&offsets_[0], &offsets_[0] + labelcount_ - 1); + transform(suffix.offsets_.begin(), suffix.offsets_.end(), + back_inserter(retname.offsets_), + [this] (char x) { return (x + length_ - 1); }); + isc_throw_assert(retname.offsets_.size() == labels); + retname.labelcount_ = labels; + + return (retname); +} + +Name +Name::reverse() const { + Name retname; + // + // Set up offsets: The size of the string and number of labels will + // be the same in as in the original. + // + retname.offsets_.reserve(labelcount_); + retname.ndata_.reserve(length_); + + // Copy the original name, label by label, from tail to head. + NameOffsets::const_reverse_iterator rit0 = offsets_.rbegin(); + NameOffsets::const_reverse_iterator rit1 = rit0 + 1; + NameString::const_iterator n0 = ndata_.begin(); + retname.offsets_.push_back(0); + while (rit1 != offsets_.rend()) { + retname.ndata_.append(n0 + *rit1, n0 + *rit0); + retname.offsets_.push_back(retname.ndata_.size()); + ++rit0; + ++rit1; + } + retname.ndata_.push_back(0); + + retname.labelcount_ = labelcount_; + retname.length_ = length_; + + return (retname); +} + +Name +Name::split(const unsigned int first, const unsigned int n) const { + if (n == 0 || n > labelcount_ || first > labelcount_ - n) { + isc_throw(OutOfRange, "Name::split: invalid split range"); + } + + Name retname; + // If the specified range doesn't include the trailing dot, we need one + // more label for that. + unsigned int newlabels = (first + n == labelcount_) ? n : n + 1; + + // + // Set up offsets: copy the corresponding range of the original offsets + // with subtracting an offset of the prefix length. + // + retname.offsets_.reserve(newlabels); + transform(offsets_.begin() + first, offsets_.begin() + first + newlabels, + back_inserter(retname.offsets_), + [&](char x) { return (x - offsets_[first]); }); + + // + // Set up the new name. At this point the tail of the new offsets specifies + // the position of the trailing dot, which should be equal to the length of + // the extracted portion excluding the dot. First copy that part from the + // original name, and append the trailing dot explicitly. + // + retname.ndata_.reserve(retname.offsets_.back() + 1); + retname.ndata_.assign(ndata_, offsets_[first], retname.offsets_.back()); + retname.ndata_.push_back(0); + + retname.length_ = retname.ndata_.size(); + retname.labelcount_ = retname.offsets_.size(); + isc_throw_assert(retname.labelcount_ == newlabels); + + return (retname); +} + +Name +Name::split(const unsigned int level) const { + if (level >= getLabelCount()) { + isc_throw(OutOfRange, "invalid level for name split (" << level + << ") for name " << *this); + } + + return (split(level, getLabelCount() - level)); +} + +Name& +Name::downcase() { + unsigned int nlen = length_; + unsigned int labels = labelcount_; + unsigned int pos = 0; + + while (labels > 0 && nlen > 0) { + --labels; + --nlen; + + // we assume a valid name, and do abort() if the assumption fails + // rather than throwing an exception. + unsigned int count = ndata_.at(pos++); + isc_throw_assert(count <= MAX_LABELLEN); + isc_throw_assert(nlen >= count); + + while (count > 0) { + ndata_.at(pos) = + maptolower[ndata_.at(pos)]; + ++pos; + --nlen; + --count; + } + } + + return (*this); +} + +std::ostream& +operator<<(std::ostream& os, const Name& name) { + os << name.toText(); + return (os); +} + +} +} diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h new file mode 100644 index 0000000..0720684 --- /dev/null +++ b/src/lib/dns/name.h @@ -0,0 +1,766 @@ +// Copyright (C) 2009-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef NAME_H +#define NAME_H 1 + +#include + +#include +#include + +#include + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// encounters an empty label in the middle of a name. +/// +class EmptyLabel : public NameParserException { +public: + EmptyLabel(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// encounters too long a name. +/// +class TooLongName : public NameParserException { +public: + TooLongName(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// encounters too long a label. +/// +class TooLongLabel : public NameParserException { +public: + TooLongLabel(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// encounters an obsolete or incomplete label type. In effect "obsolete" only +/// applies to bitstring labels, which would begin with "\[". Incomplete cases +/// include an incomplete escaped sequence such as "\12". +/// +class BadLabelType : public NameParserException { +public: + BadLabelType(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// fails to decode a back-slash escaped sequence. +/// +class BadEscape : public NameParserException { +public: + BadEscape(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if the name parser +/// finds the input (string or wire-format %data) is incomplete. +/// +/// An attempt of constructing a name from an empty string will trigger this +/// exception. +/// +class IncompleteName : public NameParserException { +public: + IncompleteName(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// \brief Thrown when origin is NULL and is needed. +/// +/// The exception is thrown when the Name constructor for master file +/// is used, the provided data is relative and the origin parameter is +/// set to NULL. +class MissingNameOrigin : public NameParserException { +public: + MissingNameOrigin(const char* file, size_t line, const char* what) : + NameParserException(file, line, what) {} +}; + +/// +/// This is a supplemental class used only as a return value of +/// Name::compare() and LabelSequence::compare(). +/// It encapsulate a tuple of the comparison: ordering, number of common +/// labels, and relationship as follows: +/// - ordering: relative ordering under the DNSSEC order relation +/// - labels: the number of common significant labels of the two names or +/// two label sequences being compared +/// - relationship: see NameComparisonResult::NameRelation +/// +/// Note that the ordering is defined for two label sequences that have no +/// hierarchical relationship (in which case the relationship will be NONE). +/// For example, two non absolute (or "relative") sequences "example.com" and +/// "example.org" have no hierarchical relationship, and the former should be +/// sorted before (i.e. "smaller") than the latter. +class NameComparisonResult { +public: + /// The relation of two names under comparison. + /// Its semantics for the case of + /// name1->compare(name2) (where name1 and name2 are instances + /// of the \c Name or \c LabelSequence class) is as follows: + /// - SUPERDOMAIN: name1 properly contains name2; name2 is a proper + /// subdomain of name1 + /// - SUBDOMAIN: name1 is a proper subdomain of name2 + /// - EQUAL: name1 and name2 are equal + /// - COMMONANCESTOR: name1 and name2 share a common ancestor + /// - NONE: There's no hierarchical relationship between name1 and name2 + /// + /// Note that there's always a hierarchical relationship between any two + /// names since all names (not generic label sequences) are absolute and + /// they at least share the trailing empty label. + /// So, for example, the relationship between "com." and "net." is + /// "commonancestor". The relationship of "NONE" can only happen for + /// comparison between two label sequences (\c LabelSequence objects); + /// usually only SUPERDOMAIN, SUBDOMAIN or EQUAL are important relationship + /// between two names. + /// + /// When two \c LabelSequence objects are compared, it's generally expected + /// they are either both absolute or both non absolute; if one is absolute + /// and the other is not, the resulting relationship will be NONE. + enum NameRelation { + SUPERDOMAIN = 0, + SUBDOMAIN = 1, + EQUAL = 2, + COMMONANCESTOR = 3, + NONE = 4 + }; + + /// + /// \name Constructors and Destructor + /// + //@{ + /// \brief Constructor from a comparison tuple + /// + /// This constructor simply initializes the object in the straightforward + /// way. + NameComparisonResult(int order, unsigned int nlabels, + NameRelation relation) : + order_(order), nlabels_(nlabels), relation_(relation) {} + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// Returns the ordering of the comparison result + int getOrder() const { return (order_); } + /// Returns the number of common labels of the comparison result + unsigned int getCommonLabels() const { return (nlabels_); } + /// Returns the NameRelation of the comparison result + NameRelation getRelation() const { return (relation_); } + //@} +private: + int order_; + unsigned int nlabels_; + NameRelation relation_; +}; + +/// +/// The \c Name class encapsulates DNS names. +/// +/// It provides interfaces to construct a name from string or wire-format %data, +/// transform a name into a string or wire-format %data, compare two names, get +/// access to various properties of a name, etc. +/// +/// Notes to developers: Internally, a name object maintains the name %data +/// in wire format as an instance of \c std::string. Since many string +/// implementations adopt copy-on-write %data sharing, we expect this approach +/// will make copying a name less expensive in typical cases. If this is +/// found to be a significant performance bottleneck later, we may reconsider +/// the internal representation or perhaps the API. +/// +/// A name object also maintains a vector of offsets (\c offsets_ member), +/// each of which is the offset to a label of the name: The n-th element of +/// the vector specifies the offset to the n-th label. For example, if the +/// object represents "www.example.com", the elements of the offsets vector +/// are 0, 4, 12, and 16. Note that the offset to the trailing dot (16) is +/// included. In the BIND9 DNS library from which this implementation is +/// derived, the offsets are optional, probably due to performance +/// considerations (in fact, offsets can always be calculated from the name +/// %data, and in that sense are redundant). In our implementation, however, +/// we always build and maintain the offsets. We believe we need more low +/// level, specialized %data structure and interface where we really need to +/// pursue performance, and would rather keep this generic API and +/// implementation simpler. +/// +/// While many other DNS APIs introduce an "absolute or relative" +/// attribute of names as defined in RFC1035, names are always "absolute" in +/// the initial design of this API. +/// In fact, separating absolute and relative would confuse API users +/// unnecessarily. For example, it's not so intuitive to consider the +/// comparison result of an absolute name with a relative name. +/// We've looked into how the concept of absolute names is used in BIND9, +/// and found that in many cases names are generally absolute. +/// The only reasonable case of separating absolute and relative is in a master +/// file parser, where a relative name must be a complete name with an "origin" +/// name, which must be absolute. So, in this initial design, we chose a +/// simpler approach: the API generally handles names as absolute; when we +/// introduce a parser of master files, we'll introduce the notion of relative +/// names as a special case. +/// +class Name { + // LabelSequences use knowledge about the internal data structure + // of this class for efficiency (they use the offsets_ vector and + // the ndata_ string) + friend class LabelSequence; + + /// + /// \name Constructors and Destructor + /// + //@{ +private: + /// \brief Name data string + typedef std::basic_string NameString; + /// \brief Name offsets type + typedef std::vector NameOffsets; + + /// The default constructor + /// + /// This is used internally in the class implementation, but at least at + /// the moment defined as private because it will construct an incomplete + /// object in that it doesn't have any labels. We may reconsider this + /// design choice as we see more applications of the class. + Name() : length_(0), labelcount_(0) {} +public: + /// Constructor from a string + /// + /// If the given string does not represent a valid DNS name, an exception + /// of class \c EmptyLabel, \c TooLongLabel, \c BadLabelType, \c BadEscape, + /// \c TooLongName, or \c IncompleteName will be thrown. + /// In addition, if resource allocation for the new name fails, a + /// corresponding standard exception will be thrown. + /// + /// \param namestr A string representation of the name to be constructed. + /// \param downcase Whether to convert upper case alphabets to lower case. + explicit Name(const std::string& namestr, bool downcase = false); + + /// \brief Constructor for master file parser + /// + /// This acts similar to the above. But the data is passed as raw C-string + /// instead of wrapped-up C++ std::string. + /// + /// Also, when the origin is non-NULL and the name_data is not ending with + /// a dot, it is considered relative and the origin is appended to it. + /// + /// If the name_data is equal to "@", the content of origin is copied. + /// + /// \param name_data The raw data of the name. + /// \param data_len How many bytes in name_data is valid and considered + /// part of the name. + /// \param origin If non-NULL, it is taken as the origin to complete + /// relative names. + /// \param downcase Whether to convert upper case letters to lower case. + /// \throw NameParserException or any of its descendants in case the + /// input data is invalid. + /// \throw isc::InvalidParameter In case name_data is NULL or data_len is + /// 0. + /// \throw std::bad_alloc In case allocation fails. + /// \note This constructor is specially designed for the use of master + /// file parser. It mimics the behaviour of names in the master file + /// and accepts raw data. It is not recommended to be used by anything + /// else. + /// \todo Should we make it private and the parser a friend, to hide the + /// constructor? + Name(const char* name_data, size_t data_len, const Name* origin, + bool downcase = false); + + /// Constructor from wire-format %data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the name to be constructed. The current read position of + /// the buffer points to the head of the name. + /// + /// The input %data may or may not be compressed; if it's compressed, this + /// method will automatically decompress it. + /// + /// If the given %data does not represent a valid DNS name, an exception + /// of class \c DNSMessageFORMERR will be thrown. + /// In addition, if resource allocation for the new name fails, a + /// corresponding standard exception will be thrown. + /// + /// \param buffer A buffer storing the wire format %data. + /// \param downcase Whether to convert upper case alphabets to lower case. + explicit Name(isc::util::InputBuffer& buffer, bool downcase = false); + /// + /// We use the default copy constructor intentionally. + //@} + /// We use the default copy assignment operator intentionally. + /// + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Provides one-byte name %data in wire format at the specified + /// position. + /// + /// This method returns the unsigned 8-bit value of wire-format \c Name + /// %data at the given position from the head. + /// + /// For example, if \c n is a \c Name object for "example.com", + /// \c n.at(3) would return \c 'a', and \c n.at(7) would return \c 'e'. + /// Note that \c n.at(0) would be 7 (decimal), the label length of + /// "example", instead of \c 'e', because it returns a %data portion + /// in wire-format. Likewise, \c n.at(8) would return 3 (decimal) + /// instead of '.' + /// + /// This method would be useful for an application to examine the + /// wire-format name %data without dumping the %data into a buffer, + /// which would involve %data copies and would be less efficient. + /// One common usage of this method would be something like this: + /// \code for (size_t i = 0; i < name.getLength(); ++i) { + /// uint8_t c = name.at(i); + /// // do something with c + /// } \endcode + /// + /// Parameter \c pos must be in the valid range of the name %data, that is, + /// must be less than \c Name.getLength(). Otherwise, an exception of + /// class \c OutOfRange will be thrown. + /// This method never throws an exception in other ways. + /// + /// \param pos The position in the wire format name %data to be returned. + /// \return An unsigned 8-bit integer corresponding to the name %data + /// at the position of \c pos. + uint8_t at(size_t pos) const + { + if (pos >= length_) { + isc_throw(OutOfRange, "Out of range access in Name::at()"); + } + return (ndata_[pos]); + } + + /// \brief Gets the length of the Name in its wire format. + /// + /// This method never throws an exception. + /// + /// \return the length (the number of octets in wire format) of the + /// Name + size_t getLength() const { return (length_); } + + /// \brief Returns the number of labels contained in the Name. + /// + /// Note that an empty label (corresponding to a trailing '.') is counted + /// as a single label, so the return value of this method must be >0. + /// + /// This method never throws an exception. + /// + /// \return the number of labels + unsigned int getLabelCount() const { return (labelcount_); } + //@} + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the Name to a string. + /// + /// This method returns a std::string object representing the + /// Name as a string. Unless omit_final_dot is + /// true, the returned string ends with a dot '.'; the default + /// is false. The default value of this parameter is + /// true; converted names will have a trailing dot by default. + /// + /// This function assumes the name is in proper uncompressed wire format. + /// If it finds an unexpected label character including compression pointer, + /// an exception of class \c BadLabelType will be thrown. + /// In addition, if resource allocation for the result string fails, a + /// corresponding standard exception will be thrown. + // + /// \param omit_final_dot whether to omit the trailing dot in the output. + /// \return a string representation of the Name. + std::string toText(bool omit_final_dot = false) const; + + /// \brief Convert the LabelSequence to a string without escape sequences. + /// + /// The string returned will contain a single character value for any + /// escape sequences in the label(s). + /// + /// \param omit_final_dot whether to omit the trailing dot in the output. + /// \return a string representation of the LabelSequence + /// that does not contain escape sequences. Default value is false. + std::string toRawText(bool omit_final_dot = false) const; + + /// \brief Render the Name in the wire format with compression. + /// + /// This method dumps the Name in wire format with help of \c renderer, + /// which encapsulates output buffer and name compression algorithm to + /// render the name. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + void toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the Name in the wire format without + /// compression. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. This can be avoided by preallocating + /// a sufficient size of \c buffer. Specifically, if + /// buffer.getCapacity() - buffer.getLength() >= Name::MAX_WIRE + /// then this method should not throw an exception. + /// + /// \param buffer An output buffer to store the wire %data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Compare two Names. + /// + /// This method compares the Name and other and + /// returns the result in the form of a NameComparisonResult + /// object. + /// + /// Note that this is case-insensitive comparison. + /// + /// This method never throws an exception. + /// + /// \param other the right-hand operand to compare against. + /// \return a NameComparisonResult object representing the + /// comparison result. + NameComparisonResult compare(const Name& other) const; + +public: + /// \brief Return true iff two names are equal. + /// + /// Semantically this could be implemented based on the result of the + /// \c compare() method, but the actual implementation uses different code + /// that simply performs character-by-character comparison (case + /// insensitive for the name label parts) on the two names. This is because + /// it would be much faster and the simple equality check would be pretty + /// common. + /// + /// This method never throws an exception. + /// + /// \param other the Name object to compare against. + /// \return true if the two names are equal; otherwise false. + bool equals(const Name& other) const; + + /// Same as equals() + bool operator==(const Name& other) const { return (equals(other)); } + + /// \brief Return true iff two names are not equal. + /// + /// This method simply negates the result of \c equal() method, and in that + /// sense it's redundant. The separate method is provided just for + /// convenience. + bool nequals(const Name& other) const { return (!(equals(other))); } + + /// Same as nequals() + bool operator!=(const Name& other) const { return (nequals(other)); } + + /// \brief Less-than or equal comparison for Name against other + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the Name object to compare against. + /// \return true if compare(other).getOrder() <= 0; + /// otherwise false. + bool leq(const Name& other) const; + + /// Same as leq() + bool operator<=(const Name& other) const { return (leq(other)); } + + /// \brief Greater-than or equal comparison for Name against + /// other + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the Name object to compare against. + /// \return true if compare(other).getOrder() >= 0; + /// otherwise false. + bool geq(const Name& other) const; + + /// Same as geq() + bool operator>=(const Name& other) const { return (geq(other)); } + + /// \brief Less-than comparison for Name against other + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the Name object to compare against. + /// \return true if compare(other).getOrder() < 0; + /// otherwise false. + bool lthan(const Name& other) const; + + /// Same as lthan() + bool operator<(const Name& other) const { return (lthan(other)); } + + /// \brief Greater-than comparison for Name against other + /// + /// The comparison is based on the result of the \c compare() method. + //// + /// This method never throws an exception. + /// + /// \param other the Name object to compare against. + /// \return true if compare(other).getOrder() > 0; + /// otherwise false. + bool gthan(const Name& other) const; + + /// Same as gthan() + bool operator>(const Name& other) const { return (gthan(other)); } + //@} + + /// + /// \name Transformer methods + /// + //@{ + /// \brief Extract a specified subpart of Name. + /// + /// name.split(first, n) constructs a new name starting from + /// the first-th label of the \c name, and subsequent \c n + /// labels including the \c first one. Since names in this current + /// implementation are always "absolute", if the specified range doesn't + /// contain the trailing dot of the original \c name, then a dot will be + /// appended to the resulting name. As a result, the number of labels + /// will be n + 1, rather than \c n. For example, + /// when \c n is Name("www.example.com"), + /// both n.split(1, 2) and n.split(1, 3) + /// will produce a name corresponding to "example.com.", which has 3 labels. + /// Note also that labels are counted from 0, and so first = 1 + /// in this example specified the label "example", not "www". + /// + /// Parameter \c n must be larger than 0, and the range specified by + /// \c first and \c n must not exceed the valid range of the original name; + /// otherwise, an exception of class \c OutOfRange will be thrown. + /// + /// Note to developers: we may want to have different versions (signatures) + /// of this method. For example, we want to split the Name based on a given + /// suffix name. + /// + /// \param first The start position (in labels) of the extracted name + /// \param n Number of labels of the extracted name + /// \return A new Name object based on the Name containing n + /// labels including and following the first label. + Name split(unsigned int first, unsigned int n) const; + + /// \brief Extract a specified super domain name of Name. + /// + /// This function constructs a new \c Name object that is a super domain + /// of \c this name. + /// The new name is \c level labels upper than \c this name. + /// For example, when \c name is www.example.com, + /// name.split(1) will return a \c Name object for example.com. + /// \c level can be 0, in which case this method returns a copy of + /// \c this name. + /// The possible maximum value for \c level is + /// this->getLabelCount()-1, in which case this method + /// returns a root name. + /// + /// One common expected usage of this method is to iterate over super + /// domains of a given name, label by label, as shown in the following + /// sample code: + /// \code // if name is www.example.com... + /// for (int i = 0; i < name.getLabelCount(); ++i) { + /// Name upper_name(name.split(i)); + /// // upper_name'll be www.example.com., example.com., com., and then . + /// } + /// \endcode + /// + /// \c level must be smaller than the number of labels of \c this name; + /// otherwise an exception of class \c OutOfRange will be thrown. + /// In addition, if resource allocation for the new name fails, a + /// corresponding standard exception will be thrown. + /// + /// Note to developers: probably as easily imagined, this method is a + /// simple wrapper to one usage of the other + /// split(unsigned int, unsigned int) const method and is + /// redundant in some sense. + /// We provide the "redundant" method for convenience, however, because + /// the expected usage shown above seems to be common, and the parameters + /// to the other \c split(unsigned int, unsigned int) const to implement + /// it may not be very intuitive. + /// + /// We are also aware that it is generally discouraged to add a public + /// member function that could be implemented using other member functions. + /// We considered making it a non member function, but we could not come + /// up with an intuitive function name to represent the specific service. + /// Some other developers argued, probably partly because of the + /// counter intuitive function name, a different signature of \c split + /// would be better to improve code readability. + /// While that may be a matter of personal preference, we accepted the + /// argument. One major goal of public APIs like this is wider acceptance + /// from internal/external developers, so unless there is a clear advantage + /// it would be better to respect the preference of the API users. + /// + /// Since this method doesn't have to be a member function in other way, + /// it is intentionally implemented only using public interfaces of the + /// \c Name class; it doesn't refer to private members of the class even if + /// it could. + /// This way we hope we can avoid damaging the class encapsulation, + /// which is a major drawback of public member functions. + /// As such if and when this "method" has to be extended, it should be + /// implemented without the privilege of being a member function unless + /// there is a very strong reason to do so. In particular a minor + /// performance advantage shouldn't justify that approach. + /// + /// \param level The number of labels to be removed from \c this name to + /// create the super domain name. + /// (0 <= \c level < this->getLabelCount()) + /// \return A new \c Name object to be created. + Name split(unsigned int level) const; + + /// \brief Reverse the labels of a name + /// + /// This method reverses the labels of a name. For example, if + /// \c this is "www.example.com.", this method will return + /// "com.example.www." (This is useful because DNSSEC sort order + /// is equivalent to a lexical sort of label-reversed names.) + Name reverse() const; + + /// \brief Concatenate two names. + /// + /// This method appends \c suffix to \c this Name. The trailing dot of + /// \c this Name will be removed. For example, if \c this is "www." + /// and \c suffix is "example.com.", a successful return of this method + /// will be a name of "www.example.com." + /// + ///The resulting length of the concatenated name must not exceed + /// \c Name::MAX_WIRE; otherwise an exception of class + /// \c TooLongName will be thrown. + /// + /// \param suffix a Name object to be appended to the Name. + /// \return a new Name object concatenating \c suffix to \c this Name. + Name concatenate(const Name& suffix) const; + + /// \brief Downcase all upper case alphabet characters in the name. + /// + /// This method modifies the calling object so that it can perform the + /// conversion as fast as possible and can be exception free. + /// + /// The return value of this version of \c downcase() is a reference to + /// the calling object (i.e., \c *this) so that the caller can use the + /// result of downcasing in a single line. For example, if variable + /// \c n is a \c Name class object possibly containing upper case + /// characters, and \c b is an \c OutputBuffer class object, then the + /// following code will dump the name in wire format to \c b with + /// downcasing upper case characters: + /// + /// \code n.downcase().toWire(b); \endcode + /// + /// Since this method modifies the calling object, a \c const name object + /// cannot call it. If \c n is a \c const Name class object, it must first + /// be copied to a different object and the latter must be used for the + /// downcase modification. + /// + /// \return A reference to the calling object with being downcased. + Name& downcase(); + //@} + + /// + /// \name Testing methods + /// + //@{ + /// \brief Test if this is a wildcard name. + /// + /// \return \c true if the least significant label of this Name is + /// '*'; otherwise \c false. + bool isWildcard() const; + //@} + + /// + /// \name Protocol constants + /// + //@{ + /// \brief Max allowable length of domain names. + static const size_t MAX_WIRE = 255; + + /// \brief Max allowable labels of domain names. + /// + /// This is ceil(MAX_WIRE / 2), and is equal to the number of + /// labels of name "a.a.a.a....a." (127 "a"'s and trailing dot). + static const size_t MAX_LABELS = 128; + + /// \brief Max allowable length of labels of a domain name. + static const size_t MAX_LABELLEN = 63; + + /// \brief Max possible pointer value for name compression. + /// + /// This is the highest number of 14-bit unsigned integer. Name compression + /// pointers are identified as a 2-byte value starting with the upper two + /// bit being 11. + static const uint16_t MAX_COMPRESS_POINTER = 0x3fff; + /// \brief A 8-bit masked value indicating a start of compression pointer. + static const uint16_t COMPRESS_POINTER_MARK8 = 0xc0; + /// \brief A 16-bit masked value indicating a start of compression pointer. + static const uint16_t COMPRESS_POINTER_MARK16 = 0xc000; + //@} + + /// + /// \name Well-known name constants + /// + //@{ + /// \brief Root name (i.e. "."). + static const Name& ROOT_NAME(); + //@} + +private: + NameString ndata_; + NameOffsets offsets_; + unsigned int length_; + unsigned int labelcount_; +}; + +inline const Name& +Name::ROOT_NAME() { + static Name root_name("."); + return (root_name); +} + +/// +/// \brief Insert the name as a string into stream. +/// +/// This method convert the \c name into a string and inserts it into the +/// output stream \c os. +/// +/// This function overloads the global operator<< to behave as described in +/// ostream::operator<< but applied to \c Name objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param name The \c Name object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& +operator<<(std::ostream& os, const Name& name); + +} +} +#endif // NAME_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/name_internal.h b/src/lib/dns/name_internal.h new file mode 100644 index 0000000..8be55ec --- /dev/null +++ b/src/lib/dns/name_internal.h @@ -0,0 +1,35 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef NAME_INTERNAL_H +#define NAME_INTERNAL_H 1 + +// This is effectively a "private" namespace for the Name class implementation, +// but exposed publicly so the definitions in it can be shared with other +// modules of the library (as of its introduction, used by LabelSequence and +// MessageRenderer). It's not expected to be used even by normal applications. +// This header file is therefore not expected to be installed as part of the +// library. +// +// Note: if it turns out that we need this shortcut for many other places +// we may even want to make it expose to other Kea modules, but for now +// we'll keep it semi-private (note also that except for very performance +// sensitive applications the standard std::tolower() function should be just +// sufficient). +namespace isc { +namespace dns { +namespace name { +namespace internal { +extern const uint8_t maptolower[]; +} // end of internal +} // end of name +} // end of dns +} // end of isc +#endif // NAME_INTERNAL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/nsec3hash.cc b/src/lib/dns/nsec3hash.cc new file mode 100644 index 0000000..6513f23 --- /dev/null +++ b/src/lib/dns/nsec3hash.cc @@ -0,0 +1,268 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::cryptolink; +using namespace isc::dns; +using namespace isc::dns::rdata; + +namespace { + +/// \brief A derived class of \c NSEC3Hash that implements the standard hash +/// calculation specified in RFC5155. +/// +/// Currently the only pre-defined algorithm in the RFC is SHA1. So we don't +/// over-generalize it at the moment, and rather hardcode it and assume that +/// specific algorithm. +/// +/// The implementation details are only open within this file, but to avoid +/// an accidental error in this implementation we explicitly make it non +/// copyable. +class NSEC3HashRFC5155 : boost::noncopyable, public NSEC3Hash { +private: + // This is the algorithm number for SHA1/NSEC3 as defined in RFC5155. + static const uint8_t NSEC3_HASH_SHA1 = 1; + // For digest_ allocation + static const size_t DEFAULT_DIGEST_LENGTH = 32; + +public: + NSEC3HashRFC5155(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, size_t salt_length) : + algorithm_(algorithm), iterations_(iterations), + salt_data_(NULL), salt_length_(salt_length), + digest_(DEFAULT_DIGEST_LENGTH), obuf_(Name::MAX_WIRE) + { + if (algorithm_ != NSEC3_HASH_SHA1) { + isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " << + static_cast(algorithm_)); + } + + if (salt_length > 0) { + salt_data_ = static_cast(std::malloc(salt_length)); + if (salt_data_ == NULL) { + throw std::bad_alloc(); + } + std::memcpy(salt_data_, salt_data, salt_length); + } + } + + virtual ~NSEC3HashRFC5155() { + std::free(salt_data_); + } + + virtual std::string calculate(const Name& name) const; + virtual std::string calculate(const LabelSequence& ls) const; + + virtual bool match(const generic::NSEC3& nsec3) const; + virtual bool match(const generic::NSEC3PARAM& nsec3param) const; + bool match(uint8_t algorithm, uint16_t iterations, + const vector& salt) const; + +private: + std::string calculateForWiredata(const uint8_t* data, size_t length) const; + + const uint8_t algorithm_; + const uint16_t iterations_; + uint8_t* salt_data_; + const size_t salt_length_; + + // The following members are placeholder of work place and don't hold + // any state over multiple calls so can be mutable without breaking + // constness. + mutable OutputBuffer digest_; + mutable vector vdigest_; + mutable OutputBuffer obuf_; +}; + +inline void +iterateSHA1(const uint8_t* input, size_t inlength, + const uint8_t* salt, size_t saltlen, + OutputBuffer& output) +{ + boost::scoped_ptr hash(CryptoLink::getCryptoLink().createHash(SHA1)); + hash->update(input, inlength); + hash->update(salt, saltlen); // this works whether saltlen == or > 0 + hash->final(output, hash->getOutputLength()); +} + +string +NSEC3HashRFC5155::calculateForWiredata(const uint8_t* data, + size_t length) const +{ + // We first need to normalize the name by converting all upper case + // characters in the labels to lower ones. + + uint8_t name_buf[256]; + assert(length < sizeof (name_buf)); + + const uint8_t *p1 = data; + uint8_t *p2 = name_buf; + while (*p1 != 0) { + char len = *p1; + + *p2++ = *p1++; + while (len--) { + *p2++ = isc::dns::name::internal::maptolower[*p1++]; + } + } + + *p2 = *p1; + + digest_.clear(); + iterateSHA1(name_buf, length, + salt_data_, salt_length_, digest_); + const uint8_t* dgst_data = static_cast(digest_.getData()); + size_t dgst_len = digest_.getLength(); + for (unsigned int n = 0; n < iterations_; ++n) { + digest_.clear(); + iterateSHA1(dgst_data, dgst_len, salt_data_, salt_length_, digest_); + } + + vdigest_.resize(dgst_len); + std::memcpy(&vdigest_[0], dgst_data, dgst_len); + return (encodeBase32Hex(vdigest_)); +} + +string +NSEC3HashRFC5155::calculate(const Name& name) const { + obuf_.clear(); + name.toWire(obuf_); + + return (calculateForWiredata(static_cast(obuf_.getData()), + obuf_.getLength())); +} + +string +NSEC3HashRFC5155::calculate(const LabelSequence& ls) const { + assert(ls.isAbsolute()); + + size_t length; + const uint8_t* data = ls.getData(&length); + + return (calculateForWiredata(data, length)); +} + +bool +NSEC3HashRFC5155::match(uint8_t algorithm, uint16_t iterations, + const vector& salt) const +{ + return (algorithm_ == algorithm && iterations_ == iterations && + salt_length_ == salt.size() && + ((salt_length_ == 0) || + memcmp(salt_data_, &salt[0], salt_length_) == 0)); +} + +bool +NSEC3HashRFC5155::match(const generic::NSEC3& nsec3) const { + return (match(nsec3.getHashalg(), nsec3.getIterations(), + nsec3.getSalt())); +} + +bool +NSEC3HashRFC5155::match(const generic::NSEC3PARAM& nsec3param) const { + return (match(nsec3param.getHashalg(), nsec3param.getIterations(), + nsec3param.getSalt())); +} + +// A static pointer that refers to the currently usable creator. +// Only get/setNSEC3HashCreator are expected to get access to this variable +// directly. +const NSEC3HashCreator* creator; + +// The accessor to the current creator. If it's not explicitly set or has +// been reset from a customized one, the default creator will be used. +const NSEC3HashCreator* +getNSEC3HashCreator() { + static DefaultNSEC3HashCreator default_creator; + if (creator == NULL) { + creator = &default_creator; + } + return (creator); +} + +} // end of unnamed namespace + +namespace isc { +namespace dns { + +NSEC3Hash* +NSEC3Hash::create(const generic::NSEC3PARAM& param) { + return (getNSEC3HashCreator()->create(param)); +} + +NSEC3Hash* +NSEC3Hash::create(const generic::NSEC3& nsec3) { + return (getNSEC3HashCreator()->create(nsec3)); +} + +NSEC3Hash* +NSEC3Hash::create(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, size_t salt_length) { + return (getNSEC3HashCreator()->create(algorithm, iterations, + salt_data, salt_length)); +} + +NSEC3Hash* +DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const { + const vector& salt = param.getSalt(); + return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(), + salt.empty() ? NULL : &salt[0], + salt.size())); +} + +NSEC3Hash* +DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const { + const vector& salt = nsec3.getSalt(); + return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(), + salt.empty() ? NULL : &salt[0], + salt.size())); +} + +NSEC3Hash* +DefaultNSEC3HashCreator::create(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, + size_t salt_length) const +{ + return (new NSEC3HashRFC5155(algorithm, iterations, + salt_data, salt_length)); +} + +void +setNSEC3HashCreator(const NSEC3HashCreator* new_creator) { + creator = new_creator; +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/nsec3hash.h b/src/lib/dns/nsec3hash.h new file mode 100644 index 0000000..26bb715 --- /dev/null +++ b/src/lib/dns/nsec3hash.h @@ -0,0 +1,290 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef NSEC3HASH_H +#define NSEC3HASH_H 1 + +#include +#include +#include +#include + +namespace isc { +namespace dns { +class Name; +class LabelSequence; + +namespace rdata { +namespace generic { +class NSEC3; +class NSEC3PARAM; +} +} + +/// \brief An exception that is thrown for when an \c NSEC3Hash object is +/// constructed with an unknown hash algorithm. +/// +/// A specific exception class is used so that the caller can selectively +/// catch this exception, e.g., while loading a zone, and handle it +/// accordingly. +class UnknownNSEC3HashAlgorithm : public isc::Exception { +public: + UnknownNSEC3HashAlgorithm(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) {} +}; + +/// \brief A calculator of NSEC3 hashes. +/// +/// This is an abstract base class that defines a simple interface to +/// calculating NSEC3 hash values as defined in RFC5155. +/// +/// (Derived classes of) this class is designed to be "stateless" in that it +/// basically doesn't hold mutable state once constructed, and hash +/// calculation solely depends on the parameters given on construction and +/// input to the \c calculate() method. In that sense this could be a +/// single free function rather than a class, but we decided to provide the +/// functionality as a class for two reasons: NSEC3 hash calculations would +/// often take place more than one time in a single query or validation +/// process, so it would be more efficient if we could hold some internal +/// resources used for the calculation and reuse it over multiple calls to +/// \c calculate() (a concrete implementation in this library actually does +/// this); Second, we may want to customize the hash calculation logic for +/// testing purposes or for other future extensions. For example, we may +/// want to use a fake calculator for tests that returns pre-defined hash +/// values (so a slight change to the test input wouldn't affect the test +/// result). Using classes from this base would make it possible more +/// transparently to the application. +/// +/// A specific derived class instance must be created by the factory method, +/// \c create(). +/// +/// There can be several ways to extend this class in future. Those include: +/// - Allow customizing the factory method so the application change the +/// behavior dynamically. +/// - Allow to construct the class from a tuple of parameters, that is, +/// integers for algorithm, iterations and flags, and opaque salt data. +/// For example, we might want to use that version for validators. +/// - Allow producing hash value as binary data +/// - Allow updating NSEC3 parameters of a class object so we can still reuse +/// the internal resources for different sets of parameters. +class NSEC3Hash { +protected: + /// \brief The default constructor. + /// + /// This is defined as protected to prevent this class from being directly + /// instantiated even if the class definition is modified (accidentally + /// or intentionally) to have no pure virtual methods. + NSEC3Hash() {} + +public: + /// \brief Factory method of NSECHash from NSEC3PARAM RDATA. + /// + /// The hash algorithm given via \c param must be known to the + /// implementation. Otherwise \c UnknownNSEC3HashAlgorithm exception + /// will be thrown. + /// + /// This method creates an \c NSEC3Hash object using \c new. The caller + /// is responsible for releasing it with \c delete that is compatible to + /// the one used in this library. In practice, the application would + /// generally need to store the returned pointer in some form of smart + /// pointer; otherwise the resulting code will be quite fragile against + /// exceptions (and in this case the application doesn't have to worry + /// about explicit \c delete). + /// + /// \throw UnknownNSEC3HashAlgorithm The specified algorithm in \c param + /// is unknown. + /// \throw std::bad_alloc Internal resource allocation failure. + /// + /// \param param NSEC3 parameters used for subsequent calculation. + /// \return A pointer to a concrete derived object of \c NSEC3Hash. + static NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param); + + /// \brief Factory method of NSECHash from NSEC3 RDATA. + /// + /// This is similar to the other version, but extracts the parameters + /// for hash calculation from an NSEC3 RDATA object. + static NSEC3Hash* create(const rdata::generic::NSEC3& nsec3); + + /// \brief Factory method of NSECHash from args. + /// + /// \param algorithm the NSEC3 algorithm to use; currently only 1 + /// (SHA-1) is supported + /// \param iterations the number of iterations + /// \param salt_data the salt data as a byte array + /// \param salt_length the length of the salt data + static NSEC3Hash* create(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, size_t salt_length); + + /// \brief The destructor. + virtual ~NSEC3Hash() {} + + /// \brief Calculate the NSEC3 hash (Name variant). + /// + /// This method calculates the NSEC3 hash value for the given \c name + /// with the hash parameters (algorithm, iterations and salt) given at + /// construction, and returns the value as a base32hex-encoded string + /// (without containing any white spaces). All US-ASCII letters in the + /// string will be lower cased. + /// + /// \param name The domain name for which the hash value is to be + /// calculated. + /// \return Base32hex-encoded string of the hash value. + virtual std::string calculate(const Name& name) const = 0; + + /// \brief Calculate the NSEC3 hash (LabelSequence variant). + /// + /// This method calculates the NSEC3 hash value for the given + /// absolute LabelSequence \c ls with the hash parameters + /// (algorithm, iterations and salt) given at construction, and + /// returns the value as a base32hex-encoded string (without + /// containing any white spaces). All US-ASCII letters in the + /// string will be lower cased. + /// + /// \param ls The absolute label sequence for which the hash value + /// is to be calculated. + /// \return Base32hex-encoded string of the hash value. + virtual std::string calculate(const LabelSequence& ls) const = 0; + + /// \brief Match given NSEC3 parameters with that of the hash. + /// + /// This method compares NSEC3 parameters used for hash calculation + /// in the object with those in the given NSEC3 RDATA, and return + /// true iff they completely match. In the current implementation + /// only the algorithm, iterations and salt are compared; the flags + /// are ignored (as they don't affect hash calculation per RFC5155). + /// + /// \throw None + /// + /// \param nsec3 An NSEC3 RDATA object whose hash parameters are to be + /// matched + /// \return true If the given parameters match the local ones; false + /// otherwise. + virtual bool match(const rdata::generic::NSEC3& nsec3) const = 0; + + /// \brief Match given NSEC3PARAM parameters with that of the hash. + /// + /// This is similar to the other version, but extracts the parameters + /// to compare from an NSEC3PARAM RDATA object. + virtual bool match(const rdata::generic::NSEC3PARAM& nsec3param) const = 0; +}; + +/// \brief Factory class of NSEC3Hash. +/// +/// This class is an abstract base class that provides the creation interfaces +/// of \c NSEC3Hash objects. By defining a specific derived class of the +/// creator, normally with a different specific class of \c NSEC3Hash, +/// the application can use a customized implementation of \c NSEC3Hash +/// without changing the library itself. The intended primary application of +/// such customization is tests (it would be convenient for a test to produce +/// a faked hash value regardless of the input so it doesn't have to identify +/// a specific input value to produce a particular hash). Another possibility +/// would be an experimental extension for a newer hash algorithm or +/// implementation. +/// +/// The three main methods named \c create() correspond to the static factory +/// methods of \c NSEC3Hash of the same name. +/// +/// By default, the library uses the \c DefaultNSEC3HashCreator creator. +/// The \c setNSEC3HashCreator() function can be used to replace it with a +/// user defined version. For such customization purposes as implementing +/// experimental new hash algorithms, the application may internally want to +/// use the \c DefaultNSEC3HashCreator in general cases while creating a +/// customized type of \c NSEC3Hash object for that particular hash algorithm. +/// +/// The creator objects are generally expected to be stateless; they will +/// only encapsulate the factory logic. The \c create() methods are declared +/// as const member functions for this reason. But if we see the need for +/// having a customized creator that benefits from its own state in future, +/// this condition can be loosened. +class NSEC3HashCreator { +protected: + /// \brief The default constructor. + /// + /// Make very sure this isn't directly instantiated by making it protected + /// even if this class is modified to lose all pure virtual methods. + NSEC3HashCreator() {} + +public: + /// \brief The destructor. + /// + /// This does nothing; defined only for allowing derived classes to + /// specialize its behavior. + virtual ~NSEC3HashCreator() {} + + /// \brief Factory method of NSECHash from NSEC3PARAM RDATA. + /// + /// See + /// NSEC3Hash::create(const rdata::generic::NSEC3PARAM& param) + virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& nsec3param) + const = 0; + + /// \brief Factory method of NSECHash from NSEC3 RDATA. + /// + /// See + /// NSEC3Hash::create(const rdata::generic::NSEC3& param) + virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) + const = 0; + + /// \brief Factory method of NSECHash from args. + /// + /// See + /// NSEC3Hash::create(uint8_t algorithm, uint16_t iterations, + /// const uint8_t* salt_data, + /// size_t salt_length) + /// + /// \param algorithm the NSEC3 algorithm to use; currently only 1 + /// (SHA-1) is supported + /// \param iterations the number of iterations + /// \param salt_data the salt data as a byte array + /// \param salt_length the length of the salt data + virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, size_t salt_length) + const = 0; +}; + +/// \brief The default NSEC3Hash creator. +/// +/// This derived class implements the \c NSEC3HashCreator interfaces for +/// the standard NSEC3 hash calculator as defined in RFC5155. The library +/// will use this creator by default, so normal applications don't have to +/// be aware of this class at all. This class is publicly visible for the +/// convenience of special applications that want to customize the creator +/// behavior for a particular type of parameters while preserving the default +/// behavior for others. +class DefaultNSEC3HashCreator : public NSEC3HashCreator { +public: + virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const; + virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const; + virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations, + const uint8_t* salt_data, + size_t salt_length) const; +}; + +/// \brief The registrar of \c NSEC3HashCreator. +/// +/// This function sets or resets the system-wide \c NSEC3HashCreator that +/// is used by \c NSEC3Hash::create(). +/// +/// If \c new_creator is non NULL, the given creator object will replace +/// any existing creator. If it's NULL, the default builtin creator will be +/// used again from that point. +/// +/// When \c new_creator is non NULL, the caller is responsible for keeping +/// the referenced object valid as long as it can be used via +/// \c NSEC3Hash::create(). +/// +/// \exception None +/// \param new_creator A pointer to the new creator object or NULL. +void setNSEC3HashCreator(const NSEC3HashCreator* new_creator); + +} +} +#endif // NSEC3HASH_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/opcode.cc b/src/lib/dns/opcode.cc new file mode 100644 index 0000000..c6e051a --- /dev/null +++ b/src/lib/dns/opcode.cc @@ -0,0 +1,62 @@ +// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +#include + +#include + +using namespace std; + +namespace isc { +namespace dns { +namespace { +const char *opcodetext[] = { + "QUERY", + "IQUERY", + "STATUS", + "RESERVED3", + "NOTIFY", + "UPDATE", + "RESERVED6", + "RESERVED7", + "RESERVED8", + "RESERVED9", + "RESERVED10", + "RESERVED11", + "RESERVED12", + "RESERVED13", + "RESERVED14", + "RESERVED15" +}; + +// OPCODEs are 4-bit values. So 15 is the highest code. +const uint8_t MAX_OPCODE = 15; +} + +Opcode::Opcode(const uint8_t code) : code_(static_cast(code)) { + if (code > MAX_OPCODE) { + isc_throw(OutOfRange, + "DNS Opcode is too large to construct: " + << static_cast(code)); + } +} + +string +Opcode::toText() const { + return (opcodetext[code_]); +} + +ostream& +operator<<(std::ostream& os, const Opcode& opcode) { + return (os << opcode.toText()); +} +} +} diff --git a/src/lib/dns/opcode.h b/src/lib/dns/opcode.h new file mode 100644 index 0000000..67dd579 --- /dev/null +++ b/src/lib/dns/opcode.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include + +#ifndef OPCODE_H +#define OPCODE_H 1 + +namespace isc { +namespace dns { + +/// \brief The \c Opcode class objects represent standard OPCODEs +/// of the header section of DNS messages as defined in RFC1035. +/// +/// This is a straightforward value class encapsulating the OPCODE code +/// values. Since OPCODEs are 4-bit integers that are used in limited +/// places and it's unlikely that new code values will be assigned, we could +/// represent them as simple integers (via constant variables or enums). +/// However, we define a separate class so that we can benefit from C++ +/// type safety as much as possible. For convenience we also provide +/// an enum type for standard OPCDE values, but it is generally advisable +/// to handle OPCODEs through this class. In fact, public interfaces of +/// this library uses this class to pass or return OPCODEs instead of the +/// bare code values. +class Opcode { +public: + /// Constants for standard OPCODE values. + enum CodeValue { + QUERY_CODE = 0, ///< 0: Standard query (RFC1035) + IQUERY_CODE = 1, ///< 1: Inverse query (RFC1035) + STATUS_CODE = 2, ///< 2: Server status request (RFC1035) + RESERVED3_CODE = 3, ///< 3: Reserved for future use (RFC1035) + NOTIFY_CODE = 4, ///< 4: Notify (RFC1996) + UPDATE_CODE = 5, ///< 5: Dynamic update (RFC2136) + RESERVED6_CODE = 6, ///< 6: Reserved for future use (RFC1035) + RESERVED7_CODE = 7, ///< 7: Reserved for future use (RFC1035) + RESERVED8_CODE = 8, ///< 8: Reserved for future use (RFC1035) + RESERVED9_CODE = 9, ///< 9: Reserved for future use (RFC1035) + RESERVED10_CODE = 10, ///< 10: Reserved for future use (RFC1035) + RESERVED11_CODE = 11, ///< 11: Reserved for future use (RFC1035) + RESERVED12_CODE = 12, ///< 12: Reserved for future use (RFC1035) + RESERVED13_CODE = 13, ///< 13: Reserved for future use (RFC1035) + RESERVED14_CODE = 14, ///< 14: Reserved for future use (RFC1035) + RESERVED15_CODE = 15 ///< 15: Reserved for future use (RFC1035) + }; + + /// \name Constructors and Destructor + /// + /// We use the default versions of destructor, copy constructor, + /// and assignment operator. + /// + /// The default constructor is hidden as a result of defining the other + /// constructors. This is intentional; we don't want to allow an + /// \c Opcode object to be constructed with an invalid state. + //@{ + /// \brief Constructor from the code value. + /// + /// Since OPCODEs are 4-bit values, parameters larger than 15 are invalid. + /// If \c code is larger than 15 an exception of class \c isc::OutOfRange + /// will be thrown. + /// + /// \param code The underlying code value of the \c Opcode. + explicit Opcode(const uint8_t code); + //@} + + /// \brief Returns the \c Opcode code value. + /// + /// This method never throws an exception. + /// + /// \return The underlying code value corresponding to the \c Opcode. + CodeValue getCode() const { return (code_); } + + /// \brief Return true iff two Opcodes are equal. + /// + /// Two Opcodes are equal iff their type codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c Opcode object to compare against. + /// \return true if the two Opcodes are equal; otherwise false. + bool equals(const Opcode& other) const + { return (code_ == other.code_); } + + /// \brief Same as \c equals(). + bool operator==(const Opcode& other) const { return (equals(other)); } + + /// \brief Return true iff two Opcodes are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c Opcode object to compare against. + /// \return true if the two Opcodes are not equal; otherwise false. + bool nequals(const Opcode& other) const + { return (code_ != other.code_); } + + /// \brief Same as \c nequals(). + bool operator!=(const Opcode& other) const { return (nequals(other)); } + + /// \brief Convert the \c Opcode to a string. + /// + /// This method returns a string representation of the "mnemonic' used + /// for the enum and constant objects. For example, the string for + /// code value 0 is "QUERY", etc. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c Opcode. + std::string toText() const; + + /// A constant object for the QUERY Opcode. + static const Opcode& QUERY(); + + /// A constant object for the IQUERY Opcode. + static const Opcode& IQUERY(); + + /// A constant object for the STATUS Opcode. + static const Opcode& STATUS(); + + /// A constant object for a reserved (code 3) Opcode. + static const Opcode& RESERVED3(); + + /// A constant object for the NOTIFY Opcode. + static const Opcode& NOTIFY(); + + /// A constant object for the UPDATE Opcode. + static const Opcode& UPDATE(); + + /// A constant object for a reserved (code 6) Opcode. + static const Opcode& RESERVED6(); + + /// A constant object for a reserved (code 7) Opcode. + static const Opcode& RESERVED7(); + + /// A constant object for a reserved (code 8) Opcode. + static const Opcode& RESERVED8(); + + /// A constant object for a reserved (code 9) Opcode. + static const Opcode& RESERVED9(); + + /// A constant object for a reserved (code 10) Opcode. + static const Opcode& RESERVED10(); + + /// A constant object for a reserved (code 11) Opcode. + static const Opcode& RESERVED11(); + + /// A constant object for a reserved (code 12) Opcode. + static const Opcode& RESERVED12(); + + /// A constant object for a reserved (code 13) Opcode. + static const Opcode& RESERVED13(); + + /// A constant object for a reserved (code 14) Opcode. + static const Opcode& RESERVED14(); + + /// A constant object for a reserved (code 15) Opcode. + static const Opcode& RESERVED15(); +private: + CodeValue code_; +}; + +inline const Opcode& +Opcode::QUERY() { + static Opcode c(0); + return (c); +} + +inline const Opcode& +Opcode::IQUERY() { + static Opcode c(1); + return (c); +} + +inline const Opcode& +Opcode::STATUS() { + static Opcode c(2); + return (c); +} + +inline const Opcode& +Opcode::RESERVED3() { + static Opcode c(3); + return (c); +} + +inline const Opcode& +Opcode::NOTIFY() { + static Opcode c(4); + return (c); +} + +inline const Opcode& +Opcode::UPDATE() { + static Opcode c(5); + return (c); +} + +inline const Opcode& +Opcode::RESERVED6() { + static Opcode c(6); + return (c); +} + +inline const Opcode& +Opcode::RESERVED7() { + static Opcode c(7); + return (c); +} + +inline const Opcode& +Opcode::RESERVED8() { + static Opcode c(8); + return (c); +} + +inline const Opcode& +Opcode::RESERVED9() { + static Opcode c(9); + return (c); +} + +inline const Opcode& +Opcode::RESERVED10() { + static Opcode c(10); + return (c); +} + +inline const Opcode& +Opcode::RESERVED11() { + static Opcode c(11); + return (c); +} + +inline const Opcode& +Opcode::RESERVED12() { + static Opcode c(12); + return (c); +} + +inline const Opcode& +Opcode::RESERVED13() { + static Opcode c(13); + return (c); +} + +inline const Opcode& +Opcode::RESERVED14() { + static Opcode c(14); + return (c); +} + +inline const Opcode& +Opcode::RESERVED15() { + static Opcode c(15); + return (c); +} + +/// \brief Insert the \c Opcode as a string into stream. +/// +/// This method convert \c opcode into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param opcode A reference to an \c Opcode object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const Opcode& opcode); +} +} +#endif // OPCODE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/qid_gen.cc b/src/lib/dns/qid_gen.cc new file mode 100644 index 0000000..dad2b6f --- /dev/null +++ b/src/lib/dns/qid_gen.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// qid_gen defines a generator for query id's +// +// We probably want to merge this with the weighted random in the nsas +// (and other parts where we need randomness, perhaps another thing +// for a general libutil?) + +#include + +#include +#include +#include + +namespace isc { +namespace dns { + +QidGenerator qid_generator_instance; + +QidGenerator& +QidGenerator::getInstance() { + return (qid_generator_instance); +} + +QidGenerator::QidGenerator() +{ +} + +uint16_t +QidGenerator::generateQid() { + uint16_t val; + std::vector rnd = isc::cryptolink::random(sizeof(uint16_t)); + memmove(&val, &rnd[0], sizeof(uint16_t)); + return (val); +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/qid_gen.h b/src/lib/dns/qid_gen.h new file mode 100644 index 0000000..732dd9a --- /dev/null +++ b/src/lib/dns/qid_gen.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// qid_gen defines a generator for query id's +// +// We probably want to merge this with the weighted random in the nsas +// (and other parts where we need randomness, perhaps another thing +// for a general libutil?) + +#ifndef QID_GEN_H +#define QID_GEN_H + +#include +#include + +namespace isc { +namespace dns { + +/// This class generates Qids for outgoing queries +/// +/// It is implemented as a singleton; the public way to access it +/// is to call getInstance()->generateQid(). +/// +/// It automatically seeds it with the current time when it is first +/// used. +class QidGenerator { +public: + /// \brief Returns the singleton instance of the QidGenerator + /// + /// Returns a reference to the singleton instance of the generator + static QidGenerator& getInstance(); + + /// \brief Default constructor + /// + /// It is recommended that getInstance is used rather than creating + /// separate instances of this class. + /// + /// The constructor automatically seeds the generator with the + /// current time. + QidGenerator(); + + /// Generate a Qid + /// + /// \return A random Qid + uint16_t generateQid(); +}; + +} // namespace dns +} // namespace isc + +#endif // QID_GEN_H diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc new file mode 100644 index 0000000..f0170d8 --- /dev/null +++ b/src/lib/dns/question.cc @@ -0,0 +1,81 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +Question::Question(InputBuffer& buffer) : + name_(buffer), rrtype_(0), rrclass_(0) +{ + // In theory, we could perform this in the member initialization list, + // and it would be a little bit more efficient. We don't do this, however, + // because the initialization ordering is crucial (type must be first) + // and the ordering in the initialization list depends on the appearance + // order of member variables. It's fragile to rely on such an implicit + // dependency, so we make the initialization order explicit. + rrtype_ = RRType(buffer); + rrclass_ = RRClass(buffer); +} + +std::string +Question::toText(bool newline) const { + std::string r(name_.toText() + " " + rrclass_.toText() + " " + + rrtype_.toText()); + if (newline) { + r.append("\n"); + } + + return (r); +} + +unsigned int +Question::toWire(OutputBuffer& buffer) const { + name_.toWire(buffer); + rrtype_.toWire(buffer); + rrclass_.toWire(buffer); // number of "entries", which is always 1 + + return (1); +} + +unsigned int +Question::toWire(AbstractMessageRenderer& renderer) const { + const size_t pos0 = renderer.getLength(); + + renderer.writeName(name_); + rrtype_.toWire(renderer); + rrclass_.toWire(renderer); + + // Make sure the renderer has a room for the question + if (renderer.getLength() > renderer.getLengthLimit()) { + renderer.trim(renderer.getLength() - pos0); + renderer.setTruncated(); + return (0); + } + + return (1); // number of "entries" +} + +ostream& +operator<<(std::ostream& os, const Question& question) { + os << question.toText(); + return (os); +} +} +} diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h new file mode 100644 index 0000000..05f52ee --- /dev/null +++ b/src/lib/dns/question.h @@ -0,0 +1,291 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef QUESTION_H +#define QUESTION_H 1 + +#include +#include + +#include + +#include +#include +#include + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +class AbstractMessageRenderer; +class Question; + +/// \brief A pointer-like type pointing to an \c Question object. +typedef boost::shared_ptr QuestionPtr; + +/// \brief A pointer-like type pointing to an (immutable) \c Question object. +typedef boost::shared_ptr ConstQuestionPtr; + +/// \brief The \c Question class encapsulates the common search key of DNS +/// lookup, consisting of owner name, RR type and RR class. +/// +/// The primarily intended use case of this class is an entry of the question +/// section of DNS messages. +/// This could also be used as a general purpose lookup key, e.g., in a +/// custom implementation of DNS database. +/// +/// In this initial implementation, the \c Question class is defined as +/// a concrete class; it's not expected to be inherited by +/// a user-defined customized class. +/// It may be worth noting that it's different from the design of the +/// RRset classes (\c AbstractRRset and its derived classes). +/// The RRset classes form an inheritance hierarchy from the base abstract +/// class. +/// This may look odd in that an "RRset" and "Question" are similar from the +/// protocol point of view: Both are used as a semantics unit of DNS messages; +/// both share the same set of components (name, RR type and RR class). +/// +/// In fact, BIND9 didn't introduce a separate data structure for Questions, +/// and use the same \c "rdataset" structure for both RRsets and Questions. +/// We could take the same approach, but chose to adopt the different design. +/// One reason for that is because a Question and an RRset are still +/// different, and a Question might not be cleanly defined, e.g., if it were +/// a derived class of some "RRset-like" class. +/// For example, we couldn't give a reasonable semantics for \c %getTTL() or +/// \c %setTTL() methods for a Question, since it's not associated with the +/// TTL. +/// In fact, the BIND9 implementation ended up often separating the case where +/// a \c "rdataset" is from the Question section of a DNS message and the +/// case where it comes from other sections. +/// If we cannot treat them completely transparently anyway, separating the +/// class (type) would make more sense because we can exploit compilation +/// time type checks. +/// +/// On the other hand, we do not expect a strong need for customizing the +/// \c Question class, unlike the RRset. +/// Handling the "Question" section of a DNS message is relatively a +/// simple work comparing to RRset-involved operations, so a unified +/// straightforward implementation should suffice for any use cases +/// including performance sensitive ones. +/// +/// We may, however, still want to have a customized version of Question +/// for, e.g, highly optimized behavior, and may revisit this design choice +/// as we have more experience with this implementation. +/// +/// One disadvantage of defining RRsets and Questions as unrelated classes +/// is that we cannot handle them in a polymorphic way. +/// For example, we might want to iterate over DNS message sections and +/// render the data in the wire format, whether it's an RRset or a Question. +/// If a \c Question were a derived class of some common RRset-like class, +/// we could do this by calling rrset_or_question->%toWire(). +/// But the actual design doesn't allow this approach, which may require +/// duplicate code for almost the same operation. +/// To mitigate this problem, we intentionally used the same names +/// with the same signature for some common methods of \c Question and +/// \c AbstractRRset such as \c %getName() or \c %toWire(). +/// So the user class may use a template function that is applicable to both +/// \c Question and \c RRset to avoid writing duplicate code logic. +class Question { + /// + /// \name Constructors and Destructor + /// + /// We use the default versions of destructor, copy constructor, + /// and assignment operator. + /// + /// The default constructor is hidden as a result of defining the other + /// constructors. This is intentional; we don't want to allow a + /// \c Question object to be constructed with an invalid state. + //@{ +public: + /// \brief Constructor from wire-format data. + /// + /// It simply constructs a set of \c Name, \c RRType, and \c RRClass + /// object from the \c buffer in this order, and constructs a + /// \c Question object in a straightforward way. + /// + /// It may throw an exception if the construction of these component + /// classes fails. + /// + /// \param buffer A buffer storing the wire format data. + Question(isc::util::InputBuffer& buffer); + + /// \brief Constructor from fixed parameters of the \c Question. + /// + /// This constructor is basically expected to be exception free, but + /// copying the name may involve resource allocation, and if it fails + /// the corresponding standard exception will be thrown. + /// + /// \param name The owner name of the \c Question. + /// \param rrclass The RR class of the \c Question. + /// \param rrtype The RR type of the \c Question. + Question(const Name& name, const RRClass& rrclass, const RRType& rrtype) : + name_(name), rrtype_(rrtype), rrclass_(rrclass) + {} + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the owner name of the \c Question. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c Name class object corresponding to the + /// \c Question owner name. + const Name& getName() const { return (name_); } + + /// \brief Returns the RR Class of the \c Question. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c RRClass class object corresponding to the + /// RR class of the \c Question. + const RRType& getType() const { return (rrtype_); } + + /// \brief Returns the RR Type of the \c Question. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c RRType class object corresponding to the + /// RR type of the \c Question. + const RRClass& getClass() const { return (rrclass_); } + //@} + + /// + /// \name Converter Methods + /// + //@{ + /// \brief Convert the Question to a string. + /// + /// When \c newline argument is \c true, this method terminates the + /// resulting string with a trailing newline character (following + /// the BIND9 convention). + /// + /// This method simply calls the \c %toText() methods of the corresponding + /// \c Name, \c RRType and \c RRClass classes for this \c Question, and + /// these methods may throw an exception. + /// In particular, if resource allocation fails, a corresponding standard + /// exception will be thrown. + /// + /// \param newline Whether to add a trailing newline. If true, a + /// trailing newline is added. If false, no trailing newline is + /// added. + /// + /// \return A string representation of the \c Question. + std::string toText(bool newline = false) const; + + /// \brief Render the Question in the wire format with name compression. + /// + /// This method simply calls the \c %toWire() methods of the corresponding + /// \c Name, \c RRType and \c RRClass classes for this \c Question, and + /// these methods may throw an exception. + /// In particular, if resource allocation fails, a corresponding standard + /// exception will be thrown. + /// + /// This method returns 1, which is the number of "questions" contained + /// in the \c Question. + /// This is a meaningless value, but is provided to be consistent with + /// the corresponding method of \c AbstractRRset (see the detailed + /// class description). + /// + /// The owner name will be compressed if possible, although it's an + /// unlikely event in practice because the Question section a DNS + /// message normally doesn't contain multiple question entries and + /// it's located right after the Header section. + /// Nevertheless, \c renderer records the information of the owner name + /// so that it can be pointed by other RRs in other sections (which is + /// more likely to happen). + /// + /// It could be possible, though very rare in practice, that + /// an attempt to render a Question may cause truncation + /// (when the Question section contains a large number of entries). + /// In such a case this method avoid the rendering and indicate the + /// truncation in the \c renderer. This method returns 0 in this case. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// + /// \return 1 on success; 0 if it causes truncation + unsigned int toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the Question in the wire format without name compression. + /// + /// This method behaves like the render version except it doesn't compress + /// the owner name. + /// See \c toWire(AbstractMessageRenderer& renderer)const. + /// + /// \param buffer An output buffer to store the wire data. + /// \return 1 + unsigned int toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Comparison Operators + /// + //@{ + /// A "less than" operator is needed for this class so it can + /// function as an index to std::map. + bool operator <(const Question& rhs) const { + return (rrclass_ < rhs.rrclass_ || + (rrclass_ == rhs.rrclass_ && + (rrtype_ < rhs.rrtype_ || + (rrtype_ == rhs.rrtype_ && (name_ < rhs.name_))))); + } + + /// Equality operator. Primarily used to compare the question section in + /// a response to that in the query. + /// + /// \param rhs Question to compare against + /// \return true if name, class and type are equal, false otherwise + bool operator==(const Question& rhs) const { + return ((rrclass_ == rhs.rrclass_) && (rrtype_ == rhs.rrtype_) && + (name_ == rhs.name_)); + } + + /// Inequality operator. Primarily used to compare the question section in + /// a response to that in the query. + /// + /// \param rhs Question to compare against + /// \return true if one or more of the name, class and type do not match, + /// false otherwise. + bool operator!=(const Question& rhs) const { + return (!operator==(rhs)); + } + //@} + +private: + Name name_; + RRType rrtype_; + RRClass rrclass_; +}; + +/// \brief Insert the \c Question as a string into stream. +/// +/// This method convert the \c question into a string and inserts it into the +/// output stream \c os. +/// +/// This function overloads the global \c operator<< to behave as described in +/// \c %ostream::%operator<< but applied to Question objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param question A reference to a \c Question object output by the +/// operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const Question& question); +} // end of namespace dns +} // end of namespace isc +#endif // QUESTION_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rcode.cc b/src/lib/dns/rcode.cc new file mode 100644 index 0000000..34d93ce --- /dev/null +++ b/src/lib/dns/rcode.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include + +#include + +#include + +using namespace std; + +namespace isc { +namespace dns { +namespace { +// This diagram shows the wire-format representation of the 12-bit extended +// form RCODEs and its relationship with implementation specific parameters. +// +// 0 3 11 15 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |UNUSED | EXTENDED-RCODE | RCODE | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// <= EXTRCODE_SHIFT (4 bits) +const unsigned int EXTRCODE_SHIFT = 4; +const unsigned int RCODE_MASK = 0x000f; + + +// EDNS-extended RCODEs are 12-bit unsigned integers. 0xfff is the highest. +const uint16_t MAX_RCODE = 0xfff; + +const char* const rcodetext[] = { + "NOERROR", + "FORMERR", + "SERVFAIL", + "NXDOMAIN", + "NOTIMP", + "REFUSED", + "YXDOMAIN", + "YXRRSET", + "NXRRSET", + "NOTAUTH", + "NOTZONE", + "RESERVED11", + "RESERVED12", + "RESERVED13", + "RESERVED14", + "RESERVED15", + "BADVERS" +}; +} + +Rcode::Rcode(const uint16_t code) : code_(code) { + if (code_ > MAX_RCODE) { + isc_throw(OutOfRange, "Rcode is too large to construct"); + } +} + +Rcode::Rcode(const uint8_t code, const uint8_t extended_code) : + code_((extended_code << EXTRCODE_SHIFT) | (code & RCODE_MASK)) +{ + if (code > RCODE_MASK) { + isc_throw(OutOfRange, + "Base Rcode is too large to construct: " + << static_cast(code)); + } +} + +uint8_t +Rcode::getExtendedCode() const { + return (code_ >> EXTRCODE_SHIFT); +} + +string +Rcode::toText() const { + if (code_ < sizeof(rcodetext) / sizeof (const char*)) { + return (rcodetext[code_]); + } + + ostringstream oss; + oss << code_; + return (oss.str()); +} + +ostream& +operator<<(std::ostream& os, const Rcode& rcode) { + return (os << rcode.toText()); +} +} +} diff --git a/src/lib/dns/rcode.h b/src/lib/dns/rcode.h new file mode 100644 index 0000000..eb192cd --- /dev/null +++ b/src/lib/dns/rcode.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include + +#ifndef RCODE_H +#define RCODE_H 1 + +namespace isc { +namespace dns { + +/// \brief DNS Response Codes (RCODEs) class. +/// +/// The \c Rcode class objects represent standard Response Codes +/// (RCODEs) of the header section of DNS messages, and extended response +/// codes as defined in the EDNS specification. +/// +/// Originally RCODEs were defined as 4-bit integers (RFC1035), and then +/// extended to 12 bits as part of the %EDNS specification (RFC2671). +/// This API uses the 12-bit version of the definition from the beginning; +/// applications don't have to aware of the original definition except when +/// dealing with the wire-format representation of the %EDNS OPT RR +/// (which is rare). +/// +/// Like the \c Opcode class, Rcodes could be represented as bare integers, +/// but we define a separate class to benefit from C++ type safety. +/// +/// For convenience we also provide +/// an enum type for pre-defined RCODE values, but it is generally advisable +/// to handle RCODEs through this class. In fact, public interfaces of +/// this library uses this class to pass or return RCODEs instead of the +/// bare code values. +class Rcode { +public: + /// Constants for pre-defined RCODE values. + enum CodeValue { + NOERROR_CODE = 0, ///< 0: No error (RFC1035) + FORMERR_CODE = 1, ///< 1: Format error (RFC1035) + SERVFAIL_CODE = 2, ///< 2: Server failure (RFC1035) + NXDOMAIN_CODE = 3, ///< 3: Name Error (RFC1035) + NOTIMP_CODE = 4, ///< 4: Not Implemented (RFC1035) + REFUSED_CODE = 5, ///< 5: Refused (RFC1035) + YXDOMAIN_CODE = 6, ///< 6: Name unexpectedly exists (RFC2136) + YXRRSET_CODE = 7, ///< 7: RRset unexpectedly exists (RFC2136) + NXRRSET_CODE = 8, ///< 8: RRset should exist but not (RFC2136) + NOTAUTH_CODE = 9, ///< 9: Server isn't authoritative (RFC2136) + NOTZONE_CODE = 10, ///< 10: Name is not within the zone (RFC2136) + RESERVED11_CODE = 11, ///< 11: Reserved for future use (RFC1035) + RESERVED12_CODE = 12, ///< 12: Reserved for future use (RFC1035) + RESERVED13_CODE = 13, ///< 13: Reserved for future use (RFC1035) + RESERVED14_CODE = 14, ///< 14: Reserved for future use (RFC1035) + RESERVED15_CODE = 15, ///< 15: Reserved for future use (RFC1035) + BADVERS_CODE = 16 ///< 16: EDNS version not implemented (RFC2671) + }; + + /// \name Constructors and Destructor + /// + /// We use the default versions of destructor, copy constructor, + /// and assignment operator. + /// + /// The default constructor is hidden as a result of defining the other + /// constructors. This is intentional; we don't want to allow an + /// \c Rcode object to be constructed with an invalid state. + //@{ + /// \brief Constructor from the code value. + /// + /// Since RCODEs are 12-bit values, parameters larger than 0xfff are + /// invalid. + /// If \c code is larger than 0xfff an exception of class + /// \c isc::OutOfRange will be thrown. + /// + /// \param code The underlying 12-bit code value of the \c Rcode. + explicit Rcode(const uint16_t code); + + /// \brief Constructor from a pair of base and extended parts of code. + /// + /// This constructor takes two parameters, one for the lower 4 bits of + /// the code value, the other for the upper 8 bits, and combines them + /// to build a complete 12-bit code value. + /// + /// The first parameter, \c code, is the lower 4 bits, and therefore must + /// not exceed 15. Otherwise, an exception of class + /// \c isc::OutOfRange will be thrown. + /// + /// This version of constructor is provided specifically for constructing + /// an Rcode from a DNS header and an %EDNS OPT RR. Normal applications + /// won't have to use this constructor. + /// + /// \param code The lower 4 bits of the underlying code value. + /// \param extended_code The upper 8 bits of the underlying code value. + Rcode(const uint8_t code, const uint8_t extended_code); + //@} + + /// \brief Returns the \c Rcode code value. + /// + /// This method never throws an exception. + /// + /// \return The underlying code value corresponding to the \c Rcode. + uint16_t getCode() const { return (code_); } + + /// \brief Returns the upper 8-bit of the \c Rcode code value. + /// + /// Normal applications won't have to use this method. This is provided + /// in case the upper 8 bits are necessary for the EDNS protocol + /// processing. + /// + /// This method never throws an exception. + /// + /// \return The upper 8-bit of the underlying code value. + uint8_t getExtendedCode() const; + + /// \brief Return true iff two Rcodes are equal. + /// + /// Two Rcodes are equal iff their type codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c Rcode object to compare against. + /// \return true if the two Rcodes are equal; otherwise false. + bool equals(const Rcode& other) const + { return (code_ == other.code_); } + + /// \brief Same as \c equals(). + bool operator==(const Rcode& other) const { return (equals(other)); } + + /// \brief Return true iff two Rcodes are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c Rcode object to compare against. + /// \return true if the two Rcodes are not equal; otherwise false. + bool nequals(const Rcode& other) const + { return (code_ != other.code_); } + + /// \brief Same as \c nequals(). + bool operator!=(const Rcode& other) const { return (nequals(other)); } + + /// \brief Convert the \c Rcode to a string. + /// + /// For pre-defined code values (see Rcode::CodeValue), + /// this method returns a string representation of the "mnemonic' used + /// for the enum and constant objects. For example, the string for + /// code value 0 is "NOERROR", etc. + /// For other code values it returns a string representation of the decimal + /// number of the value, e.g. "32", "100", etc. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c Rcode. + std::string toText() const; + + /// A constant object for the NOERROR Rcode (see \c Rcode::NOERROR_CODE). + static const Rcode& NOERROR(); + + /// A constant object for the FORMERR Rcode (see \c Rcode::FORMERR_CODE). + static const Rcode& FORMERR(); + + /// A constant object for the SERVFAIL Rcode (see \c Rcode::SERVFAIL_CODE). + static const Rcode& SERVFAIL(); + + /// A constant object for the NXDOMAIN Rcode (see \c Rcode::NXDOMAIN_CODE). + static const Rcode& NXDOMAIN(); + + /// A constant object for the NOTIMP Rcode (see \c Rcode::NOTIMP_CODE). + static const Rcode& NOTIMP(); + + /// A constant object for the REFUSED Rcode (see \c Rcode::REFUSED_CODE). + static const Rcode& REFUSED(); + + /// A constant object for the YXDOMAIN Rcode (see \c Rcode::YXDOMAIN_CODE). + static const Rcode& YXDOMAIN(); + + /// A constant object for the YXRRSET Rcode (see \c Rcode::YXRRSET_CODE). + static const Rcode& YXRRSET(); + + /// A constant object for the NXRRSET Rcode (see \c Rcode::NXRRSET_CODE). + static const Rcode& NXRRSET(); + + /// A constant object for the NOTAUTH Rcode (see \c Rcode::NOTAUTH_CODE). + static const Rcode& NOTAUTH(); + + /// A constant object for the NOTZONE Rcode (see \c Rcode::NOTZONE_CODE). + static const Rcode& NOTZONE(); + + /// A constant object for a reserved (code 11) Rcode. + /// (see \c Rcode::RESERVED11_CODE). + static const Rcode& RESERVED11(); + + /// A constant object for a reserved (code 12) Rcode. + /// (see \c Rcode::RESERVED12_CODE). + static const Rcode& RESERVED12(); + + /// A constant object for a reserved (code 13) Rcode. + /// (see \c Rcode::RESERVED13_CODE). + static const Rcode& RESERVED13(); + + /// A constant object for a reserved (code 14) Rcode. + /// (see \c Rcode::RESERVED14_CODE). + static const Rcode& RESERVED14(); + + /// A constant object for a reserved (code 15) Rcode. + /// (see \c Rcode::RESERVED15_CODE). + static const Rcode& RESERVED15(); + + /// A constant object for the BADVERS Rcode (see \c Rcode::BADVERS_CODE). + static const Rcode& BADVERS(); +private: + uint16_t code_; +}; + +inline const Rcode& +Rcode::NOERROR() { + static Rcode c(0); + return (c); +} + +inline const Rcode& +Rcode::FORMERR() { + static Rcode c(1); + return (c); +} + +inline const Rcode& +Rcode::SERVFAIL() { + static Rcode c(2); + return (c); +} + +inline const Rcode& +Rcode::NXDOMAIN() { + static Rcode c(3); + return (c); +} + +inline const Rcode& +Rcode::NOTIMP() { + static Rcode c(4); + return (c); +} + +inline const Rcode& +Rcode::REFUSED() { + static Rcode c(5); + return (c); +} + +inline const Rcode& +Rcode::YXDOMAIN() { + static Rcode c(6); + return (c); +} + +inline const Rcode& +Rcode::YXRRSET() { + static Rcode c(7); + return (c); +} + +inline const Rcode& +Rcode::NXRRSET() { + static Rcode c(8); + return (c); +} + +inline const Rcode& +Rcode::NOTAUTH() { + static Rcode c(9); + return (c); +} + +inline const Rcode& +Rcode::NOTZONE() { + static Rcode c(10); + return (c); +} + +inline const Rcode& +Rcode::RESERVED11() { + static Rcode c(11); + return (c); +} + +inline const Rcode& +Rcode::RESERVED12() { + static Rcode c(12); + return (c); +} + +inline const Rcode& +Rcode::RESERVED13() { + static Rcode c(13); + return (c); +} + +inline const Rcode& +Rcode::RESERVED14() { + static Rcode c(14); + return (c); +} + +inline const Rcode& +Rcode::RESERVED15() { + static Rcode c(15); + return (c); +} + +inline const Rcode& +Rcode::BADVERS() { + static Rcode c(16); + return (c); +} + +/// \brief Insert the \c Rcode as a string into stream. +/// +/// This method convert \c rcode into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rcode A reference to an \c Rcode object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const Rcode& rcode); +} +} +#endif // RCODE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc new file mode 100644 index 0000000..357ccc7 --- /dev/null +++ b/src/lib/dns/rdata.cc @@ -0,0 +1,408 @@ +// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { + +uint16_t +Rdata::getLength() const { + OutputBuffer obuffer(0); + + toWire(obuffer); + + return (obuffer.getLength()); +} + +// XXX: we need to specify std:: for string to help doxygen match the +// function signature with that given in the header file. +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + const std::string& rdata_string) +{ + return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, + rdata_string)); +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + isc::util::InputBuffer& buffer, size_t len) +{ + if (len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large"); + } + + size_t old_pos = buffer.getPosition(); + + RdataPtr rdata = + RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, buffer, + len); + + if (buffer.getPosition() - old_pos != len) { + isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " << + buffer.getPosition() - old_pos << " != " << len); + } + + return (rdata); +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source) +{ + return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, + source)); +} + +namespace { +void +fromtextError(bool& error_issued, const MasterLexer& lexer, + MasterLoaderCallbacks& callbacks, + const MasterToken* token, const char* reason) +{ + // Don't be too noisy if there are many issues for single RDATA + if (error_issued) { + return; + } + error_issued = true; + + if (token == NULL) { + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed: " + string(reason)); + return; + } + + switch (token->getType()) { + case MasterToken::STRING: + case MasterToken::QSTRING: + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed near '" + + token->getString() + "': " + string(reason)); + break; + case MasterToken::ERROR: + callbacks.error(lexer.getSourceName(), lexer.getSourceLine(), + "createRdata from text failed: " + + token->getErrorText()); + break; + default: + // This case shouldn't happen based on how we use MasterLexer in + // createRdata(), so we could assert() that here. But since it + // depends on detailed behavior of other classes, we treat the case + // in a bit less harsh way. + isc_throw(Unexpected, "bug: createRdata() saw unexpected token type"); + } +} +} + +RdataPtr +createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) +{ + RdataPtr rdata; + + bool error_issued = false; + try { + rdata = RRParamRegistry::getRegistry().createRdata( + rrtype, rrclass, lexer, origin, options, callbacks); + } catch (const MasterLexer::LexerError& error) { + fromtextError(error_issued, lexer, callbacks, &error.token_, ""); + } catch (const Exception& ex) { + // Catching all isc::Exception is too broad, but right now we don't + // have better granularity. When we complete #2518 we can make this + // finer. + fromtextError(error_issued, lexer, callbacks, NULL, ex.what()); + } + // Other exceptions mean a serious implementation bug or fatal system + // error; it doesn't make sense to catch and try to recover from them + // here. Just propagate. + + // Consume to end of line / file. + // Call callback via fromtextError once if there was an error. + do { + const MasterToken& token = lexer.getNextToken(); + switch (token.getType()) { + case MasterToken::END_OF_LINE: + return (rdata); + case MasterToken::END_OF_FILE: + callbacks.warning(lexer.getSourceName(), lexer.getSourceLine(), + "file does not end with newline"); + return (rdata); + default: + rdata.reset(); // we'll return NULL + fromtextError(error_issued, lexer, callbacks, &token, + "extra input text"); + // Continue until we see EOL or EOF + } + } while (true); + + // We shouldn't reach here + assert(false); + return (RdataPtr()); // add explicit return to silence some compilers +} + +int +compareNames(const Name& n1, const Name& n2) { + size_t len1 = n1.getLength(); + size_t len2 = n2.getLength(); + size_t cmplen = min(len1, len2); + + for (size_t i = 0; i < cmplen; ++i) { + uint8_t c1 = tolower(n1.at(i)); + uint8_t c2 = tolower(n2.at(i)); + if (c1 < c2) { + return (-1); + } else if (c1 > c2) { + return (1); + } + } + + return ((len1 == len2) ? 0 : (len1 < len2) ? -1 : 1); +} + +namespace generic { +struct GenericImpl { + GenericImpl(const vector& data) : data_(data) {} + vector data_; +}; + +Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large"); + } + + vector data(rdata_len); + if (rdata_len > 0) { + buffer.readData(&data[0], rdata_len); + } + + impl_ = new GenericImpl(data); +} + +GenericImpl* +Generic::constructFromLexer(MasterLexer& lexer) { + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + if (token.getString() != "\\#") { + isc_throw(InvalidRdataText, + "Missing the special token (\\#) for " + "unknown RDATA encoding"); + } + + // Initialize with an absurd value. + uint32_t rdlen = 65536; + + try { + rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + } catch (const MasterLexer::LexerError&) { + isc_throw(InvalidRdataLength, + "Unknown RDATA length is invalid"); + } + + if (rdlen > 65535) { + isc_throw(InvalidRdataLength, + "Unknown RDATA length is out of range: " << rdlen); + } + + vector data; + + if (rdlen > 0) { + string hex_txt; + string hex_part; + // Whitespace is allowed within hex data, so read to the end of input. + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + // Unget the last read token as createRdata() expects us + // to leave it at the end-of-line or end-of-file when we + // return. + lexer.ungetToken(); + break; + } + token.getString(hex_part); + hex_txt.append(hex_part); + } + + try { + isc::util::encode::decodeHex(hex_txt, data); + } catch (const isc::BadValue& ex) { + isc_throw(InvalidRdataText, + "Invalid hex encoding of generic RDATA: " << ex.what()); + } + } + + if (data.size() != rdlen) { + isc_throw(InvalidRdataLength, + "Size of unknown RDATA hex data doesn't match RDLENGTH: " + << data.size() << " vs. " << rdlen); + } + + return (new GenericImpl(data)); +} + +Generic::Generic(const std::string& rdata_string) : + impl_(NULL) +{ + // We use unique_ptr here because if there is an exception in this + // constructor, the destructor is not called and there could be a + // leak of the GenericImpl that constructFromLexer() returns. + std::unique_ptr impl_ptr; + + try { + std::istringstream ss(rdata_string); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for unknown RDATA: " + << rdata_string); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct unknown RDATA " + "from '" << rdata_string << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +Generic::Generic(MasterLexer& lexer, const Name*, + MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +Generic::~Generic() { + delete impl_; +} + +Generic::Generic(const Generic& source) : + Rdata(), impl_(new GenericImpl(*source.impl_)) +{} + +Generic& +// Our check is better than the usual if (this == &source), +// but cppcheck doesn't recognize it. +// cppcheck-suppress operatorEqToSelf +Generic::operator=(const Generic& source) { + if (impl_ == source.impl_) { + return (*this); + } + + GenericImpl* newimpl = new GenericImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +namespace { +class UnknownRdataDumper { +public: + UnknownRdataDumper(ostringstream& oss) : oss_(&oss) {} + void operator()(const unsigned char d) + { + *oss_ << setw(2) << static_cast(d); + } +private: + ostringstream* oss_; +}; +} + +string +Generic::toText() const { + ostringstream oss; + + oss << "\\# " << impl_->data_.size() << " "; + oss.fill('0'); + oss << right << hex; + for_each(impl_->data_.begin(), impl_->data_.end(), UnknownRdataDumper(oss)); + + return (oss.str()); +} + +void +Generic::toWire(isc::util::OutputBuffer& buffer) const { + buffer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +void +Generic::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +namespace { +inline int +compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) { + size_t this_len = lhs.data_.size(); + size_t other_len = rhs.data_.size(); + size_t len = (this_len < other_len) ? this_len : other_len; + int cmp; + + // TODO: is there a need to check len - should we just assert? + // (Depends if it is possible for rdata to have zero length) + if ((len != 0) && + ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len)) != 0)) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } +} +} + +int +Generic::compare(const Rdata& other) const { + const Generic& other_rdata = dynamic_cast(other); + + return (compare_internal(*impl_, *other_rdata.impl_)); +} + +std::ostream& +operator<<(std::ostream& os, const Generic& rdata) { + return (os << rdata.toText()); +} +} // end of namespace generic + +} // end of namespace rdata +} +} diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h new file mode 100644 index 0000000..a6515ef --- /dev/null +++ b/src/lib/dns/rdata.h @@ -0,0 +1,582 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef RDATA_H +#define RDATA_H 1 + +#include +#include +#include + +#include + +#include + +#include + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} +namespace dns { +class AbstractMessageRenderer; +class RRType; +class RRClass; +class Name; + +namespace rdata { + +/// +/// \brief A standard DNS module exception that is thrown if RDATA parser +/// encounters an invalid or inconsistent data length. +/// +class InvalidRdataLength : public DNSTextError { +public: + InvalidRdataLength(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if RDATA parser +/// fails to recognize a given textual representation. +/// +class InvalidRdataText : public DNSTextError { +public: + InvalidRdataText(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if RDATA parser +/// encounters a character-string (as defined in RFC1035) exceeding +/// the maximum allowable length (\c MAX_CHARSTRING_LEN). +/// +class CharStringTooLong : public DNSTextError { +public: + CharStringTooLong(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +// Forward declaration to define RdataPtr. +class Rdata; + +/// +/// The \c RdataPtr type is a pointer-like type, pointing to an +/// object of some concrete derived class of \c Rdata. +/// +typedef boost::shared_ptr RdataPtr; +typedef boost::shared_ptr ConstRdataPtr; + +/// \brief Possible maximum length of RDATA, which is the maximum unsigned +/// 16 bit value. +const size_t MAX_RDLENGTH = 65535; + +/// \brief The maximum allowable length of character-string containing in +/// RDATA as defined in RFC1035, not including the 1-byte length field. +const unsigned int MAX_CHARSTRING_LEN = 255; + +/// \brief The \c Rdata class is an abstract base class that provides +/// a set of common interfaces to manipulate concrete RDATA objects. +/// +/// Generally, a separate derived class directly inherited from the base +/// \c Rdata class is defined for each well known RDATA. +/// Each of such classes will define the common logic based on the +/// corresponding protocol standard. +/// +/// Since some types of RRs are class specific and the corresponding RDATA +/// may have different semantics (e.g. type A for class IN and type A for +/// class CH have different representations and semantics), we separate +/// \c Rdata derived classes for such RR types in different namespaces. +/// The namespace of types specific to a class is named the lower-cased class +/// name; for example, RDATA of class IN-specific types are defined in the +/// \c in namespace, and RDATA of class CH-specific types are defined in +/// the \c ch namespace, and so on. +/// The derived classes are named using the RR type name (upper cased) such as +/// \c A or \c AAAA. +/// Thus RDATA of type A RR for class IN and CH are defined as \c in::A and +/// \c ch::A, respectively. +/// Many other RR types are class independent; the derived \c Rdata classes +/// for such RR types are defined in the \c generic namespace. Examples are +/// \c generic::NS and \c generic::SOA. +/// +/// If applications need to refer to these derived classes, it is generally +/// recommended to prepend at least some part of the namespace because the +/// same class name can be used in different namespaces. +/// So, instead of doing +/// \code using namespace isc::dns::rdata::in; +/// A& rdata_type_a; \endcode +/// it is advisable to prepend at least \c in from the namespace: +/// \code using namespace isc::dns::rdata; +/// in::A& rdata_type_a; \endcode +/// +/// In many cases, however, an application doesn't have to care about such +/// derived classes. +/// For instance, to parse an incoming DNS message an application wouldn't +/// have to perform type specific operation unless the application is +/// specifically concerned about a particular type. +/// So, this API generally handles \c Rdata in a polymorphic way through +/// a pointer or reference to this base abstract class. +class Rdata { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are intentionally + /// defined as private. Concrete classes should generally specialize their + /// own versions of these methods. + //@{ +protected: + /// The default constructor. + /// + /// This is intentionally defined as \c protected as this base class should + /// never be instantiated (except as part of a derived class). In many + /// cases, the derived class wouldn't define a public default constructor + /// either, because an \c Rdata object without concrete data isn't + /// meaningful. + Rdata() {} +private: + Rdata(const Rdata& source); + void operator=(const Rdata& source); +public: + /// The destructor. + virtual ~Rdata() {}; + //@} + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert an \c Rdata to a string. + /// + /// This method returns a \c std::string object representing the \c Rdata. + /// + /// This is a pure virtual method without the definition; the actual + /// representation is specific to each derived concrete class and + /// should be explicitly defined in the derived class. + /// + /// \return A string representation of \c Rdata. + virtual std::string toText() const = 0; + + /// \brief Render the \c Rdata in the wire format into a buffer. + /// + /// This is a pure virtual method without the definition; the actual + /// conversion is specific to each derived concrete class and + /// should be explicitly defined in the derived class. + /// + /// \param buffer An output buffer to store the wire data. + virtual void toWire(isc::util::OutputBuffer& buffer) const = 0; + + /// \brief Render the \c Rdata in the wire format into a + /// \c MessageRenderer object. + /// + /// This is a pure virtual method without the definition; the actual + /// conversion is specific to each derived concrete class and + /// should be explicitly defined in the derived class. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer in which the \c Rdata is to be stored. + virtual void toWire(AbstractMessageRenderer& renderer) const = 0; + //@} + + /// + /// \name Comparison method + /// + //@{ + /// \brief Compare two instances of \c Rdata. + /// + /// This method compares \c this and the \c other Rdata objects + /// in terms of the DNSSEC sorting order as defined in RFC4034, and returns + /// the result as an integer. + /// + /// This is a pure virtual method without the definition; the actual + /// comparison logic is specific to each derived concrete class and + /// should be explicitly defined in the derived class. + /// + /// Specific implementations of this method must confirm that \c this + /// and the \c other are objects of the same concrete derived class of + /// \c Rdata. This is normally done by \c dynamic_cast in the + /// implementation. It also means if the assumption isn't met + /// an exception of class \c std::bad_cast will be thrown. + /// + /// Here is an implementation choice: instead of relying on + /// \c dynamic_cast, we could first convert the data into wire-format + /// and compare the pair as opaque data. This would be more polymorphic, + /// but might involve significant overhead, especially for a large size + /// of RDATA. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting order. + /// \return > 0 if \c this would be sorted after \c other. + virtual int compare(const Rdata& other) const = 0; + //@} + + /// \brief Get the wire format length of an Rdata. + /// + /// IMPLEMENTATION NOTE: Currently this base class implementation is + /// non-optimal as it renders the wire data to a buffer and returns + /// the buffer's length. What would perform better is to add + /// implementations of \c getLength() method to every RDATA + /// type. This is why this method is virtual. Once all Rdata types + /// have \c getLength() implementations, this base class + /// implementation must be removed and the method should become a + /// pure interface. + /// + /// \return The length of the wire format representation of the + /// RDATA. + virtual uint16_t getLength() const; +}; + +namespace generic { + +/// \brief The \c GenericImpl class is the actual implementation of the +/// \c generic::Generic class. +/// +/// The implementation is hidden from applications. This approach requires +/// dynamic memory allocation on construction, copy, or assignment, but +/// we believe it should be acceptable as "unknown" RDATA should be pretty +/// rare. +struct GenericImpl; + +/// \brief The \c generic::Generic class represents generic "unknown" RDATA. +/// +/// This class is used as a placeholder for all non well-known type of RDATA. +/// By definition, the stored data is regarded as opaque binary without +/// assuming any structure. +class Generic : public Rdata { +public: + /// + /// \name Constructors, Assignment Operator and Destructor. + /// + //@{ + /// \brief Constructor from a string. + /// + /// This method constructs a \c generic::Generic object from a textual + /// representation as defined in RFC3597. + /// + /// If \c rdata_string isn't a valid textual representation of this type + /// of RDATA, an exception of class \c InvalidRdataText or + /// \c InvalidRdataLength will be thrown. + /// If resource allocation to store the data fails, a corresponding standard + /// exception will be thrown. + /// + /// \param rdata_string A string of textual representation of generic + /// RDATA. + explicit Generic(const std::string& rdata_string); + + /// + /// \brief Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the generic RDATA to be constructed. + /// The current read position of the buffer points to the head of the + /// data. + /// + /// This method reads \c rdata_len bytes from the \c buffer, and internally + /// stores the data as an opaque byte sequence. + /// + /// \c rdata_len must not exceed \c MAX_RDLENGTH; otherwise, an exception + /// of class \c InvalidRdataLength will be thrown. + /// If resource allocation to hold the data fails, a corresponding standard + /// exception will be thrown; if the \c buffer doesn't contain \c rdata_len + /// bytes of unread data, an exception of class \c InvalidBufferPosition + /// will be thrown. + /// + /// \param buffer A reference to an \c InputBuffer object storing the + /// \c Rdata to parse. + /// \param rdata_len The length in buffer of the \c Rdata. In bytes. + Generic(isc::util::InputBuffer& buffer, size_t rdata_len); + + /// \brief Constructor from master lexer. + /// + Generic(MasterLexer& lexer, const Name* name, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks); + + /// + /// \brief The destructor. + virtual ~Generic(); + /// + /// \brief The copy constructor. + /// + /// If resource allocation to copy the data fails, a corresponding standard + /// exception will be thrown. + /// + /// \param source A reference to a \c generic::Generic object to copy from. + Generic(const Generic& source); + + /// + /// \brief The assignment operator. + /// + /// If resource allocation to copy the data fails, a corresponding standard + /// exception will be thrown. + /// + /// \param source A reference to a \c generic::Generic object to copy from. + Generic& operator=(const Generic& source); + //@} + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert an \c generic::Generic object to a string. + /// + /// This method converts a generic "unknown" RDATA object into a textual + /// representation of such unknown data as defined in RFC3597. + /// + /// If resource allocation to copy the data fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of \c generic::Generic. + virtual std::string toText() const; + + /// + /// \brief Render the \c generic::Generic in the wire format into a buffer. + /// + /// This will require \c rdata_len bytes of remaining capacity in the + /// \c buffer. If this is not the case and resource allocation for the + /// necessary memory space fails, a corresponding standard exception will + /// be thrown. + /// + /// \param buffer An output buffer to store the wire data. + virtual void toWire(isc::util::OutputBuffer& buffer) const; + + /// \brief Render the \c generic::Generic in the wire format into a + /// \c MessageRenderer object. + /// + /// This will require \c rdata_len bytes of remaining capacity in the + /// \c buffer. If this is not the case and resource allocation for the + /// necessary memory space fails, a corresponding standard exception will + /// be thrown. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer in which the \c Generic object is to be stored. + virtual void toWire(AbstractMessageRenderer& renderer) const; + //@} + + /// + /// \name Comparison method + /// + //@{ + /// \brief Compare two instances of \c generic::Generic objects. + /// + /// As defined in RFC4034, this method simply compares the wire-format + /// representations of the two objects as left-justified unsigned octet + /// sequences. + /// + /// The object referenced by \c other must have been instantiated as + /// a c generic::Generic class object; otherwise, an exception of class + /// \c std::bad_cast will be thrown. + /// Note that the comparison is RR type/class agnostic: this method doesn't + /// check whether the two \c Rdata objects to compare are of the comparable + /// RR type/class. For example, \c this object may come from an \c RRset + /// of \c RRType x, and the \c other may come from a different \c RRset + /// of \c RRType y (where x != y). This situation would be considered a + /// bug, but this method cannot detect this type of error. + /// The caller must ensure this condition. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting order. + /// \return > 0 if \c this would be sorted after \c other. + virtual int compare(const Rdata& other) const; + //@} + +private: + GenericImpl* constructFromLexer(MasterLexer& lexer); + + GenericImpl* impl_; +}; + +/// +/// \brief Insert the name as a string into stream. +/// +/// This method convert the \c rdata into a string and inserts it into the +/// output stream \c os. +/// +/// This function overloads the global \c operator<< to behave as described in +/// \c ostream::operator<< but applied to \c generic::Generic Rdata objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rdata The \c Generic object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const Generic& rdata); +} // end of namespace "generic" + +// +// Non class-member functions related to Rdata +// + +/// +/// \name Parameterized Polymorphic RDATA Factories +/// +/// This set of global functions provide a unified interface to create an +/// \c Rdata object in a parameterized polymorphic way, +/// that is, these functions take a pair of \c RRType and \c RRClass +/// objects and data specific to that pair, and create an object of +/// the corresponding concrete derived class of \c Rdata. +/// +/// These will be useful when parsing/constructing a DNS message or +/// parsing a master file, where information for a specific type of RDATA +/// is given but the resulting object, once created, should better be used +/// in a polymorphic way. +/// +/// For example, if a master file parser encounters an NS RR +/// \verbatim example.com. 3600 IN NS ns.example.com.\endverbatim +/// it stores the text fragments "IN", "NS", and "ns.example.com." in +/// \c std::string objects \c class_txt, \c type_txt, and \c nsname_txt, +/// respectively, then it would create a new \c RdataPtr object as follows: +/// \code RdataPtr rdata = createRdata(RRType(type_txt), RRClass(class_txt), +/// nsname_txt); \endcode +/// On success, \c rdata will point to an object of the \c generic::NS class +/// that internally holds a domain name of "ns.example.com." +/// +/// Internally, these functions uses the corresponding +/// \c RRParamRegistry::createRdata methods of the \c RRParamRegistry. +/// See also the description on these methods for related notes. +//@{ +/// \brief Create RDATA of a given pair of RR type and class from a string. +/// +/// This method creates from a string an \c Rdata object of the given pair +/// of RR type and class. +/// +/// \param rrtype An \c RRType object specifying the type/class pair. +/// \param rrclass An \c RRClass object specifying the type/class pair. +/// \param rdata_string A string of textual representation of the \c Rdata. +/// \return An \c RdataPtr object pointing to the created \c Rdata +/// object. +RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + const std::string& rdata_string); + +/// \brief Create RDATA of a given pair of RR type and class from +/// wire-format data. +/// +/// This method creates from wire-format binary data an \c Rdata object +/// of the given pair of RR type and class. +/// +/// \c len must not exceed the protocol defined maximum value, \c MAX_RDLENGTH; +/// otherwise, an exception of class \c InvalidRdataLength will be thrown. +/// +/// In some cases, the length of the RDATA is determined without the +/// information of \c len. For example, the RDATA length of an IN/A RR +/// must always be 4. If \c len is not equal to the actual length in such +/// cases, an exception of class InvalidRdataLength will be thrown. +/// +/// \param rrtype An \c RRType object specifying the type/class pair. +/// \param rrclass An \c RRClass object specifying the type/class pair. +/// \param buffer A reference to an \c InputBuffer object storing the +/// \c Rdata to parse. +/// \param len The length in buffer of the \c Rdata. In bytes. +/// \return An \c RdataPtr object pointing to the created \c Rdata +/// object. +RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + isc::util::InputBuffer& buffer, size_t len); + +/// \brief Create RDATA of a given pair of RR type and class, copying +/// of another RDATA of same kind. +/// +/// This method creates an \c Rdata object of the given pair of +/// RR type and class, copying the content of the given \c Rdata, +/// \c source. +/// +/// \param rrtype An \c RRType object specifying the type/class pair. +/// \param rrclass An \c RRClass object specifying the type/class pair. +/// \param source A reference to an \c Rdata object whose content +/// is to be copied to the created \c Rdata object. +/// \return An \c RdataPtr object pointing to the created +/// \c Rdata object. +RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + const Rdata& source); + +/// \brief Create RDATA of a given pair of RR type and class using the +/// master lexer. +/// +/// This is a more generic form of factory from textual RDATA, and is mainly +/// intended to be used internally by the master file parser (\c MasterLoader) +/// of this library. +/// +/// The \c lexer is expected to be at the beginning of textual RDATA of the +/// specified type and class. This function (and its underlying Rdata +/// implementations) extracts necessary tokens from the lexer and constructs +/// the RDATA from them. +/// +/// Due to the intended usage of this version, this function handles error +/// cases quite differently from other versions. It internally catches +/// most of syntax and semantics errors of the input (reported as exceptions), +/// calls the corresponding callback specified by the \c callbacks parameters, +/// and returns a NULL smart pointer. If the caller rather wants to get +/// an exception in these cases, it can pass a callback that internally +/// throws on error. Some critical exceptions such as \c std::bad_alloc are +/// still propagated to the upper layer as it doesn't make sense to try +/// recovery from such a situation within this function. +/// +/// Whether or not the creation succeeds, this function updates the lexer +/// until it reaches either the end of line or file, starting from the end of +/// the RDATA text (or the point of failure if the parsing fails in the +/// middle of it). The caller can therefore assume it's ready for reading +/// the next data (which is normally a subsequent RR in the zone file) on +/// return, whether or not this function succeeds. +/// +/// \param rrtype An \c RRType object specifying the type/class pair. +/// \param rrclass An \c RRClass object specifying the type/class pair. +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of any domain name fields +/// of the RDATA that are non absolute. +/// \param options Master loader options controlling how to deal with errors +/// or non critical issues in the parsed RDATA. +/// \param callbacks Callback to be called when an error or non critical issue +/// is found. +/// \return An \c RdataPtr object pointing to the created +/// \c Rdata object. Will be NULL if parsing fails. +RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks); + +//@} + +/// +/// \brief Gives relative ordering of two names in terms of DNSSEC RDATA +/// ordering. +/// +/// This method compares two names as defined in Sections 6.2 and 6.3 of +/// RFC4034: Comparing two names in their canonical form +/// (i.e., converting upper case ASCII characters to lower ones) and +/// as a left-justified unsigned octet sequence. Note that the ordering is +/// different from that for owner names. For example, "a.example" should be +/// sorted before "example" as RDATA, but the ordering is the opposite when +/// compared as owner names. +/// +/// Normally, applications would not need this function directly. +/// This is mostly an internal helper function for \c Rdata related classes +/// to implement their \c compare() method. +/// This function is publicly open, however, for the convenience of +/// external developers who want to implement new or experimental RR types. +/// +/// This function never throws an exception as long as the given names are +/// valid \c Name objects. +/// +/// Additional note about applicability: In fact, BIND9's similar function, +/// \c dns_name_rdatacompare(), is only used in rdata implementations and +/// for testing purposes. +/// +/// \param n1,n2 \c Name class objects to compare. +/// \return -1 if \c n1 would be sorted before \c n2. +/// \return 0 if \c n1 is identical to \c n2 in terms of sorting order. +/// \return 1 if \c n1 would be sorted after \c n2. +/// +int compareNames(const Name& n1, const Name& n2); + +} // end of namespace "rdata" +} +} +#endif // RDATA_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc new file mode 100644 index 0000000..a80d742 --- /dev/null +++ b/src/lib/dns/rdata/any_255/tsig_250.cc @@ -0,0 +1,567 @@ +// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +// straightforward representation of TSIG RDATA fields +struct TSIGImpl { + TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + vector& mac, uint16_t original_id, uint16_t error, + vector& other_data) : + algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), + mac_(mac), original_id_(original_id), error_(error), + other_data_(other_data) + {} + TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + size_t macsize, const void* mac, uint16_t original_id, + uint16_t error, size_t other_len, const void* other_data) : + algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), + mac_(static_cast(mac), + static_cast(mac) + macsize), + original_id_(original_id), error_(error), + other_data_(static_cast(other_data), + static_cast(other_data) + other_len) + {} + template + void toWireCommon(Output& output) const; + + const Name algorithm_; + const uint64_t time_signed_; + const uint16_t fudge_; + const vector mac_; + const uint16_t original_id_; + const uint16_t error_; + const vector other_data_; +}; + +// helper function for string and lexer constructors +TSIGImpl* +TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const Name& algorithm = + createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + + const string& time_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint64_t time_signed; + try { + time_signed = boost::lexical_cast(time_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TSIG Time"); + } + if ((time_signed >> 48) != 0) { + isc_throw(InvalidRdataText, "TSIG Time out of range"); + } + + const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (fudge > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Fudge out of range"); + } + const uint32_t macsize = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (macsize > 0xffff) { + isc_throw(InvalidRdataText, "TSIG MAC Size out of range"); + } + + const string& mac_txt = (macsize > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector mac; + decodeBase64(mac_txt, mac); + if (mac.size() != macsize) { + isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent"); + } + + const uint32_t orig_id = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (orig_id > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Original ID out of range"); + } + + const string& error_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t error = 0; + // XXX: In the initial implementation we hardcode the mnemonics. + // We'll soon generalize this. + if (error_txt == "NOERROR") { + error = Rcode::NOERROR_CODE; + } else if (error_txt == "BADSIG") { + error = TSIGError::BAD_SIG_CODE; + } else if (error_txt == "BADKEY") { + error = TSIGError::BAD_KEY_CODE; + } else if (error_txt == "BADTIME") { + error = TSIGError::BAD_TIME_CODE; + } else if (error_txt == "BADMODE") { + error = TSIGError::BAD_MODE_CODE; + } else if (error_txt == "BADNAME") { + error = TSIGError::BAD_NAME_CODE; + } else if (error_txt == "BADALG") { + error = TSIGError::BAD_ALG_CODE; + } else if (error_txt == "BADTRUNC") { + error = TSIGError::BAD_TRUNC_CODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + error = boost::lexical_cast(error_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TSIG Error"); + } + if (error > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Error out of range"); + } + } + + const uint32_t otherlen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (otherlen > 0xffff) { + isc_throw(InvalidRdataText, "TSIG Other Len out of range"); + } + const string otherdata_txt = (otherlen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector other_data; + decodeBase64(otherdata_txt, other_data); + if (other_data.size() != otherlen) { + isc_throw(InvalidRdataText, + "TSIG Other Data length does not match Other Len"); + } + // RFC2845 says Other Data is "empty unless Error == BADTIME". + // However, we don't enforce that. + + return (new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, + orig_id, error, other_data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TSIG RDATA. There can be extra +/// space characters at the beginning or end of the text (which are simply +/// ignored), but other extra text, including a new line, will make the +/// construction fail with an exception. +/// +/// \c tsig_str must be formatted as follows: +/// \code