diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
commit | 040eee1aa49b49df4698d83a05af57c220127fd1 (patch) | |
tree | f635435954e6ccde5eee9893889e24f30ca68346 /src/lib/dns | |
parent | Initial commit. (diff) | |
download | isc-kea-040eee1aa49b49df4698d83a05af57c220127fd1.tar.xz isc-kea-040eee1aa49b49df4698d83a05af57c220127fd1.zip |
Adding upstream version 2.2.0.upstream/2.2.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/dns')
617 files changed, 74281 insertions, 0 deletions
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 <config.h> + +#include <stdint.h> + +#include <cassert> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> + +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<unsigned int>(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<unsigned int>(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<string>(static_cast<int>(getVersion())); + ret += ", flags:"; + if (getDNSSECAwareness()) { + ret += " do"; + } + ret += "; udp: " + lexical_cast<string>(getUDPSize()) + "\n"; + + return (ret); +} + +namespace { +/// Helper function to define unified implementation for the public versions +/// of toWire(). +template <typename Output> +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 <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include <ostream> + +#include <dns/rdata.h> + +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<EDNS> EDNSPtr; + +/// \brief A pointer-like type pointing to an immutable \c EDNS object. +typedef boost::shared_ptr<const EDNS> 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. +/// +/// <b>Notes to developers</b> +/// +/// 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 <code>unsigned int</code> 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. + /// + /// <b>Note to developer:</b> 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: <version>, flags: <edns flags>; udp: <udp size> + /// \endcode + /// where + /// - \em version is the EDNS version number (integer). + /// - <em>edns flags</em> is a sequence of EDNS flag bits. The only + /// possible flag is the "DNSSEC OK", which is represented as "do". + /// - <em>udp size</em> 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 <config.h> + +#include <dns/exceptions.h> +#include <dns/rcode.h> + +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 <exceptions/exceptions.h> + +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 <dns/master_loader.h> + +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 <config.h> + +#include <dns/labelsequence.h> +#include <dns/name_internal.h> +#include <exceptions/exceptions.h> +#include <exceptions/isc_assert.h> + +#include <boost/functional/hash.hpp> + +#include <cstring> + +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<const uint8_t*>(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<unsigned int>(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<uint8_t*>(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<const uint8_t*>(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<int>(l1) - static_cast<int>(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<int>(count1) - static_cast<int>(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<int>(label1) - static_cast<int>(label2); + } else { + chdiff = static_cast<int>( + isc::dns::name::internal::maptolower[label1]) - + static_cast<int>( + 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 <dns/name.h> +#include <util/buffer.h> + +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 <code>this->equals(other)</code>. + /// + /// 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 <code>NameComparisonResult</code> 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 <code>std::string</code> 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 <code>LabelSequence</code>. + 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 <code>LabelSequence</code> + /// 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 <code>omit_final_dot</code> argument. The + /// returned string ends with a dot '.' if + /// <code>omit_final_dot</code> is <code>false</code>. + /// + /// This method is used as a helper for <code>Name::toText()</code> + /// only. + /// + /// \param omit_final_dot whether to omit the trailing dot in the output. + /// \return a string representation of the <code>LabelSequence</code>. + 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/master_lexer.h> +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer_state.h> + +#include <boost/foreach.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include <bitset> +#include <cassert> +#include <limits> +#include <string> +#include <vector> + +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<size_t>::max(); + +namespace { +typedef boost::shared_ptr<master_lexer_internal::InputSource> 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<InputSourcePtr> sources_; + InputSource* source_; // current source (NULL if sources_ is empty) + MasterToken token_; // currently recognized token (set by a state) + std::vector<char> 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<char>& 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<char>& 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<char>& 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<uint32_t, const char*>(&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 <dns/exceptions.h> + +#include <istream> +#include <string> + +#include <stdint.h> + +#include <boost/noncopyable.hpp> + +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<MasterLexer::Options>( + static_cast<unsigned>(o1) | static_cast<unsigned>(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 <config.h> + +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer.h> + +#include <istream> +#include <iostream> +#include <cassert> +#include <cerrno> +#include <cstring> + +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<std::streampos>(-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 <exceptions/exceptions.h> + +#include <boost/noncopyable.hpp> + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +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<char> 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 <dns/master_lexer.h> + +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 <config.h> + +#include <dns/master_loader.h> +#include <dns/master_lexer.h> +#include <dns/name.h> +#include <dns/rdataclass.h> +#include <dns/rrttl.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> + +#include <boost/format.hpp> +#include <boost/algorithm/string/predicate.hpp> // for iequals +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> + +#include <string> +#include <memory> +#include <vector> + +#include <cstdio> // 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. + // + // [<TTL>] [<class>] <type> <RDATA> + // [<class>] [<TTL>] <type> <RDATA> + + // 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 + (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<const rdata::generic::SOA&>(*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<RRTTL>& 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<Name> last_name_; // Last seen name (for INITIAL_WS handling) + const RRClass zone_class_; + MasterLoaderCallbacks callbacks_; + const AddRRCallback add_callback_; + boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when + // unspecified. If NULL no default + // is known. + boost::scoped_ptr<RRTTL> 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<Name, shared_ptr<Name> > IncludeInfo; + vector<IncludeInfo> 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<MasterLoaderImpl> + 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 <dns/master_loader_callbacks.h> + +#include <boost/noncopyable.hpp> + +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 <config.h> + +#include <dns/master_loader_callbacks.h> + +#include <string> + +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 <exceptions/exceptions.h> + +#include <boost/shared_ptr.hpp> +#include <functional> +#include <string> + +namespace isc { +namespace dns { +class Name; +class RRClass; +class RRType; +class RRTTL; +namespace rdata { +class Rdata; +typedef boost::shared_ptr<Rdata> 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<void(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& rrttl, + const rdata::RdataPtr& rdata)> + 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<void(const std::string& source_name, + size_t source_line, + const std::string& reason)> 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 <config.h> + +#include <dns/masterload.h> +#include <dns/master_loader.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrset.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> +#include <dns/rrcollator.h> +#include <exceptions/exceptions.h> + +#include <boost/scoped_ptr.hpp> + +#include <functional> +#include <istream> +#include <fstream> +#include <sstream> +#include <string> +#include <cctype> +#include <cerrno> + +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 <typename InputType> +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<const char*>(filename, origin, zone_class, callback); +} + +void +masterLoad(istream& input, const Name& origin, const RRClass& zone_class, + MasterLoadCallback callback, const char*) +{ + loadHelper<istream&>(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 <dns/rrset.h> +#include <exceptions/exceptions.h> + +#include <functional> +#include <iosfwd> + +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<void(RRsetPtr)> 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. +/// +/// <b>Exceptions</b> +/// +/// 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. +/// +/// <b>Usage Examples</b> +/// +/// 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. +/// +/// <b>Implementation Notes</b> +/// +/// 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 <config.h> + +#include <stdint.h> + +#include <algorithm> +#include <cassert> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> + +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/question.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rrset.h> +#include <dns/tsig.h> + +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<QuestionPtr> questions_; + vector<RRsetPtr> 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 <typename T> +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<QuestionPtr>(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<RRsetPtr>(renderer, true)).getTotalCount(); + } + uint16_t nscount = 0; + if (!renderer.isTruncated()) { + nscount = + for_each(rrsets_[Message::SECTION_AUTHORITY].begin(), + rrsets_[Message::SECTION_AUTHORITY].end(), + RenderSection<RRsetPtr>(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<RRsetPtr>(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<QuestionPtr>(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<int>(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<int>(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<int>(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<int>(section) >= MessageImpl::NUM_SECTIONS) { + isc_throw(OutOfRange, "Invalid message section: " << section); + } + + bool removed = false; + for (vector<RRsetPtr>::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<int>(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<int>(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<RRsetPtr>::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<RRsetPtr>::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 <typename T> +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<string>(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<string>(impl_->counts_[SECTION_QUESTION]); + s += ", ANSWER: " + + lexical_cast<string>(impl_->counts_[SECTION_ANSWER]); + s += ", AUTHORITY: " + + lexical_cast<string>(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<string>(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<QuestionPtr>(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<RRsetPtr>(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<RRsetPtr>(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<RRsetPtr>(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<int>(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 <typename T> +struct SectionIteratorImpl { + SectionIteratorImpl(const typename vector<T>::const_iterator& it) : + it_(it) {} + typename vector<T>::const_iterator it_; +}; + +template <typename T> +SectionIterator<T>::SectionIterator(const SectionIteratorImpl<T>& impl) { + impl_ = new SectionIteratorImpl<T>(impl.it_); +} + +template <typename T> +SectionIterator<T>::~SectionIterator() { + delete impl_; +} + +template <typename T> +SectionIterator<T>::SectionIterator(const SectionIterator<T>& source) : + impl_(new SectionIteratorImpl<T>(source.impl_->it_)) +{} + +template <typename T> +void +SectionIterator<T>::operator=(const SectionIterator<T>& source) { + if (impl_ == source.impl_) { + return; + } + SectionIteratorImpl<T>* newimpl = + new SectionIteratorImpl<T>(source.impl_->it_); + delete impl_; + impl_ = newimpl; +} + +template <typename T> +SectionIterator<T>& +SectionIterator<T>::operator++() { + ++(impl_->it_); + return (*this); +} + +template <typename T> +SectionIterator<T> +SectionIterator<T>::operator++(int) { + SectionIterator<T> tmp(*this); + ++(*this); + return (tmp); +} + +template <typename T> +const T& +SectionIterator<T>::operator*() const { + return (*(impl_->it_)); +} + +template <typename T> +const T* +SectionIterator<T>::operator->() const { + return (&(operator*())); +} + +template <typename T> +bool +SectionIterator<T>::operator==(const SectionIterator<T>& other) const { + return (impl_->it_ == other.impl_->it_); +} + +template <typename T> +bool +SectionIterator<T>::operator!=(const SectionIterator<T>& 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<QuestionPtr>; +template class SectionIterator<RRsetPtr>; + +namespace { +typedef SectionIteratorImpl<QuestionPtr> QuestionIteratorImpl; +typedef SectionIteratorImpl<RRsetPtr> 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<RRsetPtr> +Message::beginSection(const Section section) const { + if (static_cast<int>(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<RRsetPtr> +Message::endSection(const Section section) const { + if (static_cast<int>(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 <stdint.h> + +#include <iterator> +#include <string> +#include <ostream> + +#include <dns/exceptions.h> + +#include <dns/edns.h> +#include <dns/question.h> +#include <dns/rrset.h> + +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 <typename T> +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 <typename T> +class SectionIterator : public std::iterator<std::input_iterator_tag, T> { +public: + SectionIterator() : impl_(NULL) {} + SectionIterator(const SectionIteratorImpl<T>& impl); + ~SectionIterator(); + SectionIterator(const SectionIterator<T>& source); + void operator=(const SectionIterator<T>& source); + SectionIterator<T>& operator++(); + SectionIterator<T> operator++(int); + const T& operator*() const; + const T* operator->() const; + bool operator==(const SectionIterator<T>& other) const; + bool operator!=(const SectionIterator<T>& other) const; +private: + SectionIteratorImpl<T>* impl_; +}; + +typedef SectionIterator<QuestionPtr> QuestionIterator; +typedef SectionIterator<RRsetPtr> 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<uint16_t>(Message::HEADERFLAG_AA) | + /// static_cast<uint16_t>(Message::HEADERFLAG_CD); + /// message->setHeaderFlag(static_cast<Message::HeaderFlag>(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()). + /// + /// <b>Open Design Issue:</b> + /// 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 <code>section >= Message::SECTION_AUTHORITY</code> 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. + /// + /// <b>Future Extension:</b> 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<Message> MessagePtr; +typedef boost::shared_ptr<const Message> 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 <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/name_internal.h> +#include <dns/labelsequence.h> +#include <dns/messagerenderer.h> + +#include <boost/array.hpp> +#include <boost/static_assert.hpp> + +#include <limits> +#include <cassert> +#include <vector> + +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 <bool CASE_SENSITIVE> +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<OffsetItem>::const_reverse_iterator found; + if (case_sensitive) { + found = find_if(table_[bucket_id].rbegin(), + table_[bucket_id].rend(), + NameCompare<true>(buffer, name_buf, hash)); + } else { + found = find_if(table_[bucket_id].rbegin(), + table_[bucket_id].rend(), + NameCompare<false>(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<OffsetItem> 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<size_t, Name::MAX_LABELS> 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<OffsetItem> 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 <hash, offset, len> 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 <util/buffer.h> + +#include <boost/noncopyable.hpp> + +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, <code>pos + 2 < getLength()</code>; + /// 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 <config.h> + +#include <cctype> +#include <iterator> +#include <functional> +#include <vector> +#include <iostream> +#include <algorithm> + +#include <exceptions/isc_assert.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/name.h> +#include <dns/name_internal.h> +#include <dns/messagerenderer.h> +#include <dns/labelsequence.h> + +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<class Iterator, class Offsets, class Data> +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 ? "<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 <stdint.h> + +#include <string> +#include <vector> + +#include <dns/exceptions.h> + +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 + /// <code>name1->compare(name2)</code> (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<uint8_t> NameString; + /// \brief Name offsets type + typedef std::vector<uint8_t> 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 <code>'.'</code> + /// + /// 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 <code>Name</code> in its wire format. + /// + /// This method never throws an exception. + /// + /// \return the length (the number of octets in wire format) of the + /// <code>Name</code> + size_t getLength() const { return (length_); } + + /// \brief Returns the number of labels contained in the <code>Name</code>. + /// + /// 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 <code>std::string</code> object representing the + /// Name as a string. Unless <code>omit_final_dot</code> is + /// <code>true</code>, the returned string ends with a dot '.'; the default + /// is <code>false</code>. The default value of this parameter is + /// <code>true</code>; 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 <code>Name</code>. + 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 <code>LabelSequence</code> + /// that does not contain escape sequences. Default value is false. + std::string toRawText(bool omit_final_dot = false) const; + + /// \brief Render the <code>Name</code> 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 <code>Name</code> 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 + /// <code>buffer.getCapacity() - buffer.getLength() >= Name::MAX_WIRE</code> + /// 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 <code>Name</code>s. + /// + /// This method compares the <code>Name</code> and <code>other</code> and + /// returns the result in the form of a <code>NameComparisonResult</code> + /// 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 <code>NameComparisonResult</code> 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 <code>Name</code> 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 <code>other</code> + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the <code>Name</code> object to compare against. + /// \return true if <code>compare(other).getOrder() <= 0</code>; + /// 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 + /// <code>other</code> + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the <code>Name</code> object to compare against. + /// \return true if <code>compare(other).getOrder() >= 0</code>; + /// 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 <code>other</code> + /// + /// The comparison is based on the result of the \c compare() method. + /// + /// This method never throws an exception. + /// + /// \param other the <code>Name</code> object to compare against. + /// \return true if <code>compare(other).getOrder() < 0</code>; + /// 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 <code>other</code> + /// + /// The comparison is based on the result of the \c compare() method. + //// + /// This method never throws an exception. + /// + /// \param other the <code>Name</code> object to compare against. + /// \return true if <code>compare(other).getOrder() > 0</code>; + /// 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. + /// + /// <code>name.split(first, n)</code> constructs a new name starting from + /// the <code>first</code>-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 <code>n + 1</code>, rather than \c n. For example, + /// when \c n is <code>Name("www.example.com")</code>, + /// both <code>n.split(1, 2)</code> and <code>n.split(1, 3)</code> + /// will produce a name corresponding to "example.com.", which has 3 labels. + /// Note also that labels are counted from 0, and so <code>first = 1</code> + /// 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 <code>n</code> + /// labels including and following the <code>first</code> 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, + /// <code>name.split(1)</code> 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 + /// <code>this->getLabelCount()-1</code>, 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 + /// <code>split(unsigned int, unsigned int) const</code> 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 < <code>this->getLabelCount()</code>) + /// \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 + /// <code>'*'</code>; 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 <code>ceil(MAX_WIRE / 2)</code>, 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 <config.h> + +#include <stdint.h> + +#include <cassert> +#include <cstring> +#include <cstdlib> +#include <string> +#include <vector> + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/base32hex.h> + +#include <cryptolink/cryptolink.h> +#include <cryptolink/crypto_hash.h> + +#include <dns/name.h> +#include <dns/labelsequence.h> +#include <dns/nsec3hash.h> +#include <dns/rdataclass.h> +#include <dns/name_internal.h> + +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<unsigned int>(algorithm_)); + } + + if (salt_length > 0) { + salt_data_ = static_cast<uint8_t*>(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<uint8_t>& 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<uint8_t> 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> 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<const uint8_t*>(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<const uint8_t*>(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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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 <string> +#include <vector> +#include <stdint.h> +#include <exceptions/exceptions.h> + +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 + /// <code>NSEC3Hash::create(const rdata::generic::NSEC3PARAM& param)</code> + virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& nsec3param) + const = 0; + + /// \brief Factory method of NSECHash from NSEC3 RDATA. + /// + /// See + /// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code> + virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) + const = 0; + + /// \brief Factory method of NSECHash from args. + /// + /// See + /// <code>NSEC3Hash::create(uint8_t algorithm, uint16_t iterations, + /// const uint8_t* salt_data, + /// size_t salt_length)</code> + /// + /// \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 <config.h> + +#include <string> +#include <ostream> + +#include <exceptions/exceptions.h> + +#include <dns/opcode.h> + +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<CodeValue>(code)) { + if (code > MAX_OPCODE) { + isc_throw(OutOfRange, + "DNS Opcode is too large to construct: " + << static_cast<unsigned>(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 <stdint.h> + +#include <ostream> + +#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 <config.h> + +#include <cryptolink/crypto_rng.h> +#include <dns/qid_gen.h> +#include <cstring> + +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<uint8_t> 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 <cryptolink/crypto_rng.h> +#include <stdint.h> + +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 <config.h> + +#include <iostream> +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/question.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +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 <iostream> +#include <string> + +#include <boost/shared_ptr.hpp> + +#include <dns/name.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +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<Question> QuestionPtr; + +/// \brief A pointer-like type pointing to an (immutable) \c Question object. +typedef boost::shared_ptr<const Question> 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 <em>concrete class</em>; 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 <code>rrset_or_question->%toWire()</code>. +/// 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 <config.h> + +#include <string> +#include <sstream> +#include <ostream> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> + +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<unsigned int>(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 <stdint.h> + +#include <ostream> + +#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 <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/rdata.h> +#include <dns/rrparamregistry.h> +#include <dns/rrtype.h> + +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> + +#include <algorithm> +#include <cctype> +#include <string> +#include <sstream> +#include <iomanip> +#include <ios> +#include <ostream> +#include <vector> + +#include <stdint.h> +#include <string.h> + +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<uint8_t>& data) : data_(data) {} + vector<uint8_t> data_; +}; + +Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large"); + } + + vector<uint8_t> 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<uint8_t> 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<GenericImpl> 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<unsigned int>(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<const Generic&>(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 <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> + +#include <dns/exceptions.h> + +#include <boost/shared_ptr.hpp> + +#include <stdint.h> + +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<Rdata> RdataPtr; +typedef boost::shared_ptr<const Rdata> 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 <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/tsigkey.h> +#include <dns/tsigerror.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +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<uint8_t>& mac, uint16_t original_id, uint16_t error, + vector<uint8_t>& 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<const uint8_t*>(mac), + static_cast<const uint8_t*>(mac) + macsize), + original_id_(original_id), error_(error), + other_data_(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + other_len) + {} + template <typename Output> + void toWireCommon(Output& output) const; + + const Name algorithm_; + const uint64_t time_signed_; + const uint16_t fudge_; + const vector<uint8_t> mac_; + const uint16_t original_id_; + const uint16_t error_; + const vector<uint8_t> 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<uint64_t>(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<uint8_t> 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<uint32_t>(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<uint8_t> 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 <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>] +/// <Original ID> <Error> <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and +/// "BADTIME" are supported (case sensitive). In future versions other +/// representations that are compatible with the DNS RCODE may be supported. +/// +/// The MAC and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tsig_str. +/// The decoded data of the MAC field is MAC Size bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2845 does not define the standard presentation format +/// of %TSIG RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if MAC or Other Data is not validly encoded in base-64. +/// +/// \param tsig_str A string containing the RDATA to be created +TSIG::TSIG(const std::string& tsig_str) : 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 TSIGImpl that constructFromLexer() returns. + std::unique_ptr<TSIGImpl> impl_ptr; + + try { + std::istringstream ss(tsig_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TSIG: " << tsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TSIG from '" << tsig_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TSIG RDATA. +/// +/// See \c TSIG::TSIG(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TSIG::TSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TSIG RR even if that +/// field is compressed. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TSIG::TSIG(InputBuffer& buffer, size_t) : + impl_(NULL) +{ + Name algorithm(buffer); + + uint8_t time_signed_buf[6]; + buffer.readData(time_signed_buf, sizeof(time_signed_buf)); + const uint64_t time_signed = + (static_cast<uint64_t>(time_signed_buf[0]) << 40 | + static_cast<uint64_t>(time_signed_buf[1]) << 32 | + static_cast<uint64_t>(time_signed_buf[2]) << 24 | + static_cast<uint64_t>(time_signed_buf[3]) << 16 | + static_cast<uint64_t>(time_signed_buf[4]) << 8 | + static_cast<uint64_t>(time_signed_buf[5])); + + const uint16_t fudge = buffer.readUint16(); + + const uint16_t mac_size = buffer.readUint16(); + vector<uint8_t> mac(mac_size); + if (mac_size > 0) { + buffer.readData(&mac[0], mac_size); + } + + const uint16_t original_id = buffer.readUint16(); + const uint16_t error = buffer.readUint16(); + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, + original_id, error, other_data); +} + +TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + uint16_t mac_size, const void* mac, uint16_t original_id, + uint16_t error, uint16_t other_len, const void* other_data) : + impl_(NULL) +{ + // Time Signed is a 48-bit value. + if ((time_signed >> 48) != 0) { + isc_throw(OutOfRange, "TSIG Time Signed is too large: " << + time_signed); + } + if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) { + isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent"); + } + if ((other_len == 0 && other_data != NULL) || + (other_len > 0 && other_data == NULL)) { + isc_throw(InvalidParameter, + "TSIG Other data length and data inconsistent"); + } + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size, + mac, original_id, error, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_)) +{} + +TSIG& +TSIG::operator=(const TSIG& source) { + if (this == &source) { + return (*this); + } + + TSIGImpl* newimpl = new TSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TSIG::~TSIG() { + delete impl_; +} + +/// \brief Convert the \c TSIG to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TSIG(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TSIG object. +std::string +TSIG::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + lexical_cast<string>(impl_->time_signed_) + " " + + lexical_cast<string>(impl_->fudge_) + " " + + lexical_cast<string>(impl_->mac_.size()) + " "; + if (!impl_->mac_.empty()) { + result += encodeBase64(impl_->mac_) + " "; + } + result += lexical_cast<string>(impl_->original_id_) + " "; + result += TSIGError(impl_->error_).toText() + " "; + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TSIGImpl::toWireCommon(Output& output) const { + output.writeUint16(time_signed_ >> 32); + output.writeUint32(time_signed_ & 0xffffffff); + output.writeUint16(fudge_); + const uint16_t mac_size = mac_.size(); + output.writeUint16(mac_size); + if (mac_size > 0) { + output.writeData(&mac_[0], mac_size); + } + output.writeUint16(original_id_); + output.writeUint16(error_); + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TSIG in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TSIG::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TSIG in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TSIG::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TSIG::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TSIG RDATA. +/// +/// This method compares \c this and the \c other \c TSIG objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TSIG object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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. +int +TSIG::compare(const Rdata& other) const { + const TSIG& other_tsig = dynamic_cast<const TSIG&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tsig.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->time_signed_ != other_tsig.impl_->time_signed_) { + return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1); + } + if (impl_->fudge_ != other_tsig.impl_->fudge_) { + return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1); + } + const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_); + if (vcmp != 0) { + return (vcmp); + } + if (impl_->original_id_ != other_tsig.impl_->original_id_) { + return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1); + } + if (impl_->error_ != other_tsig.impl_->error_) { + return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1); + } + return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_)); +} + +const Name& +TSIG::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint64_t +TSIG::getTimeSigned() const { + return (impl_->time_signed_); +} + +uint16_t +TSIG::getFudge() const { + return (impl_->fudge_); +} + +uint16_t +TSIG::getMACSize() const { + return (impl_->mac_.size()); +} + +const void* +TSIG::getMAC() const { + if (!impl_->mac_.empty()) { + return (&impl_->mac_[0]); + } else { + return (NULL); + } +} + +uint16_t +TSIG::getOriginalID() const { + return (impl_->original_id_); +} + +uint16_t +TSIG::getError() const { + return (impl_->error_); +} + +uint16_t +TSIG::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TSIG::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (NULL); + } +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/any_255/tsig_250.h b/src/lib/dns/rdata/any_255/tsig_250.h new file mode 100644 index 0000000..63c2234 --- /dev/null +++ b/src/lib/dns/rdata/any_255/tsig_250.h @@ -0,0 +1,148 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct TSIGImpl; + +/// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in +/// RFC2845. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// TSIG RDATA. +class TSIG : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %TSIG RDATA + /// fields as defined %in RFC2845, but there are some implementation + /// specific notes as follows. + /// + /// \c algorithm is a \c Name object that specifies the algorithm. + /// For example, if the algorithm is HMAC-SHA256, \c algorithm would be + /// \c Name("hmac-sha256"). + /// + /// \c time_signed corresponds to the Time Signed field, which is of + /// 48-bit unsigned integer type, and therefore cannot exceed 2^48-1; + /// otherwise, an exception of type \c OutOfRange will be thrown. + /// + /// \c mac_size and \c mac correspond to the MAC Size and MAC fields, + /// respectively. When the MAC field is empty, \c mac must be NULL. + /// \c mac_size and \c mac must be consistent %in that \c mac_size is 0 if + /// and only if \c mac is NULL; otherwise an exception of type + /// InvalidParameter will be thrown. + /// + /// The same restriction applies to \c other_len and \c other_data, + /// which correspond to the Other Len and Other Data fields, respectively. + /// + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + uint16_t mac_size, const void* mac, uint16_t original_id, + uint16_t error, uint16_t other_len, const void* other_data); + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + TSIG& operator=(const TSIG& source); + + /// \brief The destructor. + ~TSIG(); + + /// \brief Return the algorithm name. + /// + /// This method never throws an exception. + const Name& getAlgorithm() const; + + /// \brief Return the value of the Time Signed field. + /// + /// The returned value does not exceed 2^48-1. + /// + /// This method never throws an exception. + uint64_t getTimeSigned() const; + + /// \brief Return the value of the Fudge field. + /// + /// This method never throws an exception. + uint16_t getFudge() const; + + /// \brief Return the value of the MAC Size field. + /// + /// This method never throws an exception. + uint16_t getMACSize() const; + + /// \brief Return the value of the MAC field. + /// + /// If the MAC field is empty, it returns NULL. + /// Otherwise, the memory region beginning at the address returned by + /// this method is valid up to the bytes specified by the return value + /// of \c getMACSize(). + /// The memory region is only valid while the corresponding \c TSIG + /// object is valid. The caller must hold the \c TSIG object while + /// it needs to refer to the region or it must make a local copy of the + /// region. + /// + /// This method never throws an exception. + const void* getMAC() const; + + /// \brief Return the value of the Original ID field. + /// + /// This method never throws an exception. + uint16_t getOriginalID() const; + + /// \brief Return the value of the Error field. + /// + /// This method never throws an exception. + uint16_t getError() const; + + /// \brief Return the value of the Other Len field. + /// + /// This method never throws an exception. + uint16_t getOtherLen() const; + + /// \brief Return the value of the Other Data field. + /// + /// The same note as \c getMAC() applies. + /// + /// This method never throws an exception. + const void* getOtherData() const; +private: + TSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + TSIGImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/ch_3/a_1.cc b/src/lib/dns/rdata/ch_3/a_1.cc new file mode 100644 index 0000000..cd4c824 --- /dev/null +++ b/src/lib/dns/rdata/ch_3/a_1.cc @@ -0,0 +1,64 @@ +// 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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/ch_3/a_1.h b/src/lib/dns/rdata/ch_3/a_1.h new file mode 100644 index 0000000..6f319b9 --- /dev/null +++ b/src/lib/dns/rdata/ch_3/a_1.h @@ -0,0 +1,32 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc new file mode 100644 index 0000000..5e82b03 --- /dev/null +++ b/src/lib/dns/rdata/generic/afsdb_18.cc @@ -0,0 +1,201 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/lexical_cast.hpp> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// \c afsdb_str must be formatted as follows: +/// \code <subtype> <server name> +/// \endcode +/// where server name field must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "1 server.example.com." \endcode +/// +/// <b>Exceptions</b> +/// +/// \exception InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +AFSDB::AFSDB(const std::string& afsdb_str) : + subtype_(0), server_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(afsdb_str); + MasterLexer lexer; + lexer.pushSource(ss); + + createFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for AFSDB: " + << afsdb_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct AFSDB from '" << + afsdb_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an AFSDB RDATA. The SERVER field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The SUBTYPE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +AFSDB::AFSDB(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + subtype_(0), server_(".") +{ + createFromLexer(lexer, origin); +} + +void +AFSDB::createFromLexer(MasterLexer& lexer, const Name* origin) +{ + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid AFSDB subtype: " << num); + } + subtype_ = static_cast<uint16_t>(num); + + server_ = createNameFromLexer(lexer, origin); +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +AFSDB::AFSDB(InputBuffer& buffer, size_t) : + subtype_(buffer.readUint16()), server_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \exception std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +AFSDB::AFSDB(const AFSDB& other) : + Rdata(), subtype_(other.subtype_), server_(other.server_) +{} + +AFSDB& +AFSDB::operator=(const AFSDB& source) { + subtype_ = source.subtype_; + server_ = source.server_; + + return (*this); +} + +/// \brief Convert the \c AFSDB to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c AFSDB(const std::string&))). +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c AFSDB object. +string +AFSDB::toText() const { + return (lexical_cast<string>(subtype_) + " " + server_.toText()); +} + +/// \brief Render the \c AFSDB in the wire format without name compression. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +AFSDB::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(subtype_); + server_.toWire(buffer); +} + +/// \brief Render the \c AFSDB in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE AFSDB is not "well-known", the server +/// field (domain name) will not be compressed. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +AFSDB::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(subtype_); + renderer.writeName(server_, false); +} + +/// \brief Compare two instances of \c AFSDB RDATA. +/// +/// See documentation in \c Rdata. +int +AFSDB::compare(const Rdata& other) const { + const AFSDB& other_afsdb = dynamic_cast<const AFSDB&>(other); + if (subtype_ < other_afsdb.subtype_) { + return (-1); + } else if (subtype_ > other_afsdb.subtype_) { + return (1); + } + + return (compareNames(server_, other_afsdb.server_)); +} + +const Name& +AFSDB::getServer() const { + return (server_); +} + +uint16_t +AFSDB::getSubtype() const { + return (subtype_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/afsdb_18.h b/src/lib/dns/rdata/generic/afsdb_18.h new file mode 100644 index 0000000..2fd8fba --- /dev/null +++ b/src/lib/dns/rdata/generic/afsdb_18.h @@ -0,0 +1,68 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::AFSDB class represents the AFSDB RDATA as defined %in +/// RFC1183. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// AFSDB RDATA. +class AFSDB : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// This method never throws an exception. + AFSDB& operator=(const AFSDB& source); + /// + /// Specialized methods + /// + + /// \brief Return the value of the server field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal server name. + /// + /// This method never throws an exception. + const Name& getServer() const; + + /// \brief Return the value of the subtype field. + /// + /// This method never throws an exception. + uint16_t getSubtype() const; + +private: + void createFromLexer(MasterLexer& lexer, const Name* origin); + + uint16_t subtype_; + Name server_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/caa_257.cc b/src/lib/dns/rdata/generic/caa_257.cc new file mode 100644 index 0000000..7f8b455 --- /dev/null +++ b/src/lib/dns/rdata/generic/caa_257.cc @@ -0,0 +1,298 @@ +// Copyright (C) 2014-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 <config.h> + +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/char_string.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct CAAImpl { + // straightforward representation of CAA RDATA fields + CAAImpl(uint8_t flags, const std::string& tag, + const detail::CharStringData& value) : + flags_(flags), + tag_(tag), + value_(value) + { + if ((sizeof(flags) + 1 + tag_.size() + value_.size()) > 65535) { + isc_throw(InvalidRdataLength, + "CAA Value field is too large: " << value_.size()); + } + } + + uint8_t flags_; + const std::string tag_; + const detail::CharStringData value_; +}; + +// helper function for string and lexer constructors +CAAImpl* +CAA::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 255) { + isc_throw(InvalidRdataText, + "CAA flags field out of range"); + } + + // Tag field must not be empty. + const std::string tag = + lexer.getNextToken(MasterToken::STRING).getString(); + if (tag.empty()) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(InvalidRdataText, + "CAA tag field is too large: " << tag.size()); + } + + // Value field may be empty. + detail::CharStringData value; + MasterToken token = lexer.getNextToken(MasterToken::QSTRING, true); + if ((token.getType() != MasterToken::END_OF_FILE) && + (token.getType() != MasterToken::END_OF_LINE)) + { + detail::stringToCharStringData(token.getStringRegion(), value); + } + + return (new CAAImpl(flags, tag, value)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CAA 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. +/// +/// The Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, incorrect, or empty. +/// +/// \param caa_str A string containing the RDATA to be created +CAA::CAA(const string& caa_str) : + 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 CAAImpl that constructFromLexer() returns. + std::unique_ptr<CAAImpl> impl_ptr; + + try { + std::istringstream ss(caa_str); + 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 CAA: " + << caa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CAA from '" << + caa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an CAA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing +/// field. +/// \throw InvalidRdataText Fields are out of their valid ranges, +/// incorrect, or empty. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +CAA::CAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid CAA RDATA. +/// +/// The Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +CAA::CAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "CAA record too short"); + } + + const uint8_t flags = buffer.readUint8(); + const uint8_t tag_length = buffer.readUint8(); + rdata_len -= 2; + if (tag_length == 0) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } + + if (rdata_len < tag_length) { + isc_throw(InvalidRdataLength, + "RDATA is too short for CAA tag field"); + } + + std::vector<uint8_t> tag_vec(tag_length); + buffer.readData(&tag_vec[0], tag_length); + std::string tag(tag_vec.begin(), tag_vec.end()); + rdata_len -= tag_length; + + detail::CharStringData value; + value.resize(rdata_len); + if (rdata_len > 0) { + buffer.readData(&value[0], rdata_len); + } + + impl_ = new CAAImpl(flags, tag, value); +} + +CAA::CAA(uint8_t flags, const std::string& tag, const std::string& value) : + impl_(NULL) +{ + if (tag.empty()) { + isc_throw(isc::InvalidParameter, + "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(isc::InvalidParameter, + "CAA tag field is too large: " << tag.size()); + } + + MasterToken::StringRegion region; + region.beg = &value[0]; // note std ensures this works even if str is empty + region.len = value.size(); + + detail::CharStringData value_vec; + detail::stringToCharStringData(region, value_vec); + + impl_ = new CAAImpl(flags, tag, value_vec); +} + +CAA::CAA(const CAA& other) : + Rdata(), impl_(new CAAImpl(*other.impl_)) +{} + +CAA& +CAA::operator=(const CAA& source) { + if (this == &source) { + return (*this); + } + + CAAImpl* newimpl = new CAAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +CAA::~CAA() { + delete impl_; +} + +void +CAA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + buffer.writeUint8(impl_->tag_.size()); + buffer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + buffer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +void +CAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + renderer.writeUint8(impl_->tag_.size()); + renderer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + renderer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +std::string +CAA::toText() const { + std::string result; + + result = lexical_cast<std::string>(static_cast<int>(impl_->flags_)); + result += " " + impl_->tag_; + result += " \"" + detail::charStringDataToString(impl_->value_) + "\""; + + return (result); +} + +int +CAA::compare(const Rdata& other) const { + const CAA& other_caa = dynamic_cast<const CAA&>(other); + + if (impl_->flags_ < other_caa.impl_->flags_) { + return (-1); + } else if (impl_->flags_ > other_caa.impl_->flags_) { + return (1); + } + + // Do a case-insensitive compare of the tag strings. + const int result = boost::ilexicographical_compare + <std::string, std::string>(impl_->tag_, other_caa.impl_->tag_); + if (result != 0) { + return (result); + } + + return (detail::compareCharStringDatas(impl_->value_, + other_caa.impl_->value_)); +} + +uint8_t +CAA::getFlags() const { + return (impl_->flags_); +} + +const std::string& +CAA::getTag() const { + return (impl_->tag_); +} + +const std::vector<uint8_t>& +CAA::getValue() const { + return (impl_->value_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/caa_257.h b/src/lib/dns/rdata/generic/caa_257.h new file mode 100644 index 0000000..0e81e71 --- /dev/null +++ b/src/lib/dns/rdata/generic/caa_257.h @@ -0,0 +1,64 @@ +// Copyright (C) 2014-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct CAAImpl; + +class CAA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + CAA(uint8_t flags, const std::string& tag, const std::string& value); + CAA& operator=(const CAA& source); + ~CAA(); + + /// + /// Specialized methods + /// + + /// \brief Return the Flags field of the CAA RDATA. + uint8_t getFlags() const; + + /// \brief Return the Tag field of the CAA RDATA. + const std::string& getTag() const; + + /// \brief Return the Value field of the CAA RDATA. + /// + /// Note: The const reference which is returned is valid only during + /// the lifetime of this \c generic::CAA object. It should not be + /// used afterwards. + const std::vector<uint8_t>& getValue() const; + +private: + CAAImpl* constructFromLexer(MasterLexer& lexer); + + CAAImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/cname_5.cc b/src/lib/dns/rdata/generic/cname_5.cc new file mode 100644 index 0000000..71cb4dc --- /dev/null +++ b/src/lib/dns/rdata/generic/cname_5.cc @@ -0,0 +1,125 @@ +// 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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CNAME 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. +/// +/// The CNAME must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +CNAME::CNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + cname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + cname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for CNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CNAME from '" << + namestr << "': " << ex.what()); + } +} + +CNAME::CNAME(InputBuffer& buffer, size_t) : + Rdata(), cname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a CNAME RDATA. The CNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 CNAME when it +/// is non-absolute. +CNAME::CNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + cname_(createNameFromLexer(lexer, origin)) +{} + +CNAME::CNAME(const CNAME& other) : + Rdata(), cname_(other.cname_) +{} + +CNAME::CNAME(const Name& cname) : + cname_(cname) +{} + +void +CNAME::toWire(OutputBuffer& buffer) const { + cname_.toWire(buffer); +} + +void +CNAME::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(cname_); +} + +string +CNAME::toText() const { + return (cname_.toText()); +} + +int +CNAME::compare(const Rdata& other) const { + const CNAME& other_cname = dynamic_cast<const CNAME&>(other); + + return (compareNames(cname_, other_cname.cname_)); +} + +const Name& +CNAME::getCname() const { + return (cname_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/cname_5.h b/src/lib/dns/rdata/generic/cname_5.h new file mode 100644 index 0000000..2149340 --- /dev/null +++ b/src/lib/dns/rdata/generic/cname_5.h @@ -0,0 +1,39 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class CNAME : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // CNAME specific methods + CNAME(const Name& cname); + const Name& getCname() const; +private: + Name cname_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc new file mode 100644 index 0000000..8eee8c0 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/char_string.cc @@ -0,0 +1,264 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +#include <boost/lexical_cast.hpp> + +#include <cassert> +#include <cctype> +#include <cstring> +#include <vector> + +#include <stdint.h> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +namespace { +// Convert a DDD form to the corresponding integer +int +decimalToNumber(const char* s, const char* s_end) { + if (s_end - s < 3) { + isc_throw(InvalidRdataText, "Escaped digits too short"); + } + + const std::string num_str(s, s + 3); + try { + const int i = boost::lexical_cast<int>(num_str); + if (i > 255) { + isc_throw(InvalidRdataText, "Escaped digits too large: " + << num_str); + } + return (i); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, + "Invalid form for escaped digits: " << num_str); + } +} +} + +void +stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result) +{ + // make a space for the 1-byte length field; filled in at the end + result.push_back(0); + + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + assert(n >= 3); + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } + if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field + isc_throw(CharStringTooLong, "character-string is too long: " << + (result.size() - 1) << "(+1) characters"); + } + result[0] = result.size() - 1; +} + +void +stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result) +{ + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + // decimalToNumber() already throws if (s_end - s) is less + // than 3, so the following assertion is unnecessary. But we + // assert it anyway. 'n' is an unsigned type (size_t) and + // can underflow. + assert(n >= 3); + // 'n' and 's' are also updated by 1 in the for statement's + // expression, so we update them by 2 instead of 3 here. + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } +} + +std::string +charStringToString(const CharString& char_string) { + std::string s; + for (CharString::const_iterator it = char_string.begin() + 1; + it != char_string.end(); ++it) { + const uint8_t ch = *it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +std::string +charStringDataToString(const CharStringData& char_string) { + std::string s; + for (CharString::const_iterator it = char_string.begin(); + it != char_string.end(); ++it) { + const uint8_t ch = *it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +int compareCharStrings(const detail::CharString& self, + const detail::CharString& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self[0]; + const size_t other_len = other[0]; + const size_t cmp_len = std::min(self_len, other_len); + if (cmp_len == 0) { + if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } + } + const int cmp = std::memcmp(&self[1], &other[1], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +int compareCharStringDatas(const detail::CharStringData& self, + const detail::CharStringData& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self.size(); + const size_t other_len = other.size(); + const size_t cmp_len = std::min(self_len, other_len); + const int cmp = std::memcmp(&self[0], &other[0], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +size_t +bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target) { + if (rdata_len < 1 || buffer.getLength() - buffer.getPosition() < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "insufficient data to read character-string length"); + } + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "character string length is too large: " << + static_cast<int>(len)); + } + if (buffer.getLength() - buffer.getPosition() < len) { + isc_throw(isc::dns::DNSMessageFORMERR, + "not enough data in buffer to read character-string of len" + << static_cast<int>(len)); + } + + target.resize(len + 1); + target[0] = len; + buffer.readData(&target[0] + 1, len); + + return (len + 1); +} + +} // end of detail +} // end of generic +} // end of rdata +} // end of dns +} // end of isc diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h new file mode 100644 index 0000000..2ad12fb --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/char_string.h @@ -0,0 +1,140 @@ +// 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_RDATA_CHARSTRING_H +#define DNS_RDATA_CHARSTRING_H 1 + +#include <dns/master_lexer.h> + +#include <string> +#include <vector> +#include <algorithm> +#include <stdint.h> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief Type for DNS character string. +/// +/// A character string can contain any unsigned 8-bit value, so this cannot +/// be the bare char basis. +typedef std::vector<uint8_t> CharString; + +/// \brief Type for DNS character string without the length prefix. +typedef std::vector<uint8_t> CharStringData; + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This helper function takes a string object that is expected to be a +/// textual representation of a valid DNS character-string, and dumps +/// the corresponding binary sequence in the given placeholder (passed +/// via the \c result parameter). It handles escape notations of +/// character-strings with a backslash ('\'), and checks the length +/// restriction. +/// +/// \throw CharStringTooLong The resulting binary data are too large for a +/// valid character-string. +/// \throw InvalidRdataText Other syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result); + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This method functions similar to \c stringToCharString() except it +/// does not include the 1-octet length prefix in the \c result, and the +/// result is not limited to MAX_CHARSTRING_LEN octets. +/// +/// \throw InvalidRdataText Upon syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result); + +/// \brief Convert a CharString into a textual DNS character-string. +/// +/// This method converts a binary 8-bit representation of a DNS +/// character string into a textual string representation, escaping any +/// special characters in the process. For example, characters like +/// double-quotes, semi-colon and backspace are prefixed with backspace +/// character, and characters not in the printable range of [0x20, 0x7e] +/// (inclusive) are converted to the \xxx 3-digit decimal +/// representation. +/// +/// \param char_string The \c CharString to convert. +/// \return A string representation of \c char_string. +std::string charStringToString(const CharString& char_string); + +/// \brief Convert a CharStringData into a textual DNS character-string. +/// +/// Reverse of \c stringToCharStringData(). See \c stringToCharString() +/// vs. \c stringToCharStringData(). +/// +/// \param char_string The \c CharStringData to convert. +/// \return A string representation of \c char_string. +std::string charStringDataToString(const CharStringData& char_string); + +/// \brief Compare two CharString objects +/// +/// \param self The CharString field to compare +/// \param other The CharString field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStrings(const CharString& self, const CharString& other); + +/// \brief Compare two CharStringData objects +/// +/// \param self The CharStringData field to compare +/// \param other The CharStringData field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStringDatas(const CharStringData& self, + const CharStringData& other); + +/// \brief Convert a buffer containing a character-string to CharString +/// +/// This method reads one character-string from the given buffer (in wire +/// format) and places the result in the given \c CharString object. +/// Since this is expected to be used in message parsing, the exception it +/// raises is of that type. +/// +/// On success, the buffer position is advanced to the end of the char-string, +/// and the number of bytes read is returned. +/// +/// \param buffer The buffer to read from. +/// \param rdata_len The total size of the rr's rdata currently being read +/// (used for integrity checks in the wire data) +/// \param target The \c CharString where the result will be stored. Any +/// existing data in the target will be overwritten. +/// \throw DNSMessageFORMERR If the available data is not enough to read +/// the character-string, or if the character-string length is out of bounds +/// \return The number of bytes read +size_t bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target); + + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc +#endif // DNS_RDATA_CHARSTRING_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/ds_like.h b/src/lib/dns/rdata/generic/detail/ds_like.h new file mode 100644 index 0000000..4d8c2ea --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/ds_like.h @@ -0,0 +1,277 @@ +// Copyright (C) 2011-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 DS_LIKE_H +#define DS_LIKE_H 1 + +#include <stdint.h> + +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief \c rdata::DSLikeImpl class represents the DS-like RDATA for DS +/// and DLV types. +/// +/// This class implements the basic interfaces inherited by the DS and DLV +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to DS-like RDATA. +template <class Type, uint16_t typeCode> class DSLikeImpl { + // Common sequence of toWire() operations used for the two versions of + // toWire(). + template <typename Output> + void + toWireCommon(Output& output) const { + output.writeUint16(tag_); + output.writeUint8(algorithm_); + output.writeUint8(digest_type_); + output.writeData(&digest_[0], digest_.size()); + } + +public: + /// \brief Constructor from string. + /// + /// The given string must represent a valid DS-like 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. + /// + /// The tag field must be a valid decimal representation of an + /// unsigned 16-bit integer. The protocol and algorithm fields must + /// be valid decimal representations of unsigned 8-bit integers + /// respectively. The digest field may contain whitespace. + /// + /// \throw InvalidRdataText if any fields are out of their valid range. + /// + /// \param ds_str A string containing the RDATA to be created + DSLikeImpl(const std::string& ds_str) { + try { + std::istringstream ss(ds_str); + MasterLexer lexer; + lexer.pushSource(ss); + + constructFromLexer(lexer); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for " << RRType(typeCode) << ": " + << ds_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct " << RRType(typeCode) << " from '" << + ds_str << "': " << ex.what()); + } + } + + /// \brief Constructor with a context of MasterLexer. + /// + /// The \c lexer should point to the beginning of valid textual + /// representation of a DS-like RDATA. + /// + /// The tag field must be a valid decimal representation of an + /// unsigned 16-bit integer. The protocol and algorithm fields must + /// be valid decimal representations of unsigned 8-bit integers + /// respectively. + /// + /// \throw MasterLexer::LexerError General parsing error such as + /// missing field. + /// \throw InvalidRdataText if any fields are out of their valid range. + /// + /// \param lexer A \c MasterLexer object parsing a master file for the + /// RDATA to be created + DSLikeImpl(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) + { + constructFromLexer(lexer); + } + +private: + void constructFromLexer(MasterLexer& lexer) { + const uint32_t tag = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (tag > 0xffff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " tag: " << tag); + } + + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " algorithm: " + << algorithm); + } + + const uint32_t digest_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (digest_type > 0xff) { + isc_throw(InvalidRdataText, + "Invalid " << RRType(typeCode) << " digest type: " + << digest_type); + } + + std::string digest; + while (true) { + const MasterToken& token = lexer.getNextToken(); + if (token.getType() != MasterToken::STRING) { + break; + } + digest.append(token.getString()); + } + + lexer.ungetToken(); + + if (digest.size() == 0) { + isc_throw(InvalidRdataText, + "Missing " << RRType(typeCode) << " digest"); + } + + tag_ = tag; + algorithm_ = algorithm; + digest_type_ = digest_type; + decodeHex(digest, digest_); + } + +public: + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// <b>Exceptions</b> + /// + /// \c InvalidRdataLength is thrown if the input data is too short for the + /// type. + DSLikeImpl(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, RRType(typeCode) << " too short"); + } + + tag_ = buffer.readUint16(); + algorithm_ = buffer.readUint8(); + digest_type_ = buffer.readUint8(); + + rdata_len -= 4; + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); + } + + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. + DSLikeImpl(const DSLikeImpl& source) : + tag_(source.tag_), + algorithm_(source.algorithm_), + digest_type_(source.digest_type_), + digest_(source.digest_) + {} + + /// \brief Convert the DS-like data to a string. + /// + /// \return A \c string object that represents the DS-like data. + std::string + toText() const { + using namespace boost; + return (lexical_cast<string>(static_cast<int>(tag_)) + + " " + lexical_cast<string>(static_cast<int>(algorithm_)) + + " " + lexical_cast<string>(static_cast<int>(digest_type_)) + + " " + encodeHex(digest_)); + } + + /// \brief Render the DS-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. + void + toWire(OutputBuffer& buffer) const { + toWireCommon(buffer); + } + + /// \brief Render the DS-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param renderer A renderer object to send the wire data to. + void + toWire(AbstractMessageRenderer& renderer) const { + toWireCommon(renderer); + } + + /// \brief Compare two instances of DS-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c DSLikeImpl class. + /// + /// \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. + int + compare(const DSLikeImpl& other_ds) const { + if (tag_ != other_ds.tag_) { + return (tag_ < other_ds.tag_ ? -1 : 1); + } + if (algorithm_ != other_ds.algorithm_) { + return (algorithm_ < other_ds.algorithm_ ? -1 : 1); + } + if (digest_type_ != other_ds.digest_type_) { + return (digest_type_ < other_ds.digest_type_ ? -1 : 1); + } + + size_t this_len = digest_.size(); + size_t other_len = other_ds.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_ds.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) + ? 0 : (this_len < other_len) ? -1 : 1); + } + } + + /// \brief Accessors + uint16_t + getTag() const { + return (tag_); + } + +private: + // straightforward representation of DS RDATA fields + uint16_t tag_; + uint8_t algorithm_; + uint8_t digest_type_; + std::vector<uint8_t> digest_; +}; + +} +} +} +} +} +#endif // DS_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/lexer_util.h b/src/lib/dns/rdata/generic/detail/lexer_util.h new file mode 100644 index 0000000..29b6c31 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/lexer_util.h @@ -0,0 +1,62 @@ +// Copyright (C) 2013-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_RDATA_LEXER_UTIL_H +#define DNS_RDATA_LEXER_UTIL_H 1 + +#include <dns/name.h> +#include <dns/master_lexer.h> + +/// \file lexer_util.h +/// \brief Utilities for extracting RDATA fields from lexer. +/// +/// This file intends to define convenient small routines that can be +/// commonly used in the RDATA implementation to build RDATA fields from +/// a \c MasterLexer. + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief Construct a Name object using a master lexer and optional origin. +/// +/// This is a convenient shortcut of commonly used code pattern that would +/// be used to build RDATA that contain a domain name field. +/// +/// Note that this function throws an exception against invalid input. +/// The (direct or indirect) caller's responsibility needs to expect and +/// handle exceptions appropriately. +/// +/// \throw MasterLexer::LexerError The next token from lexer is not string. +/// \throw Other Exceptions from the \c Name class constructor if the next +/// string token from the lexer does not represent a valid name. +/// +/// \param lexer A \c MasterLexer object. Its next token is expected to be +/// a string that represent a domain name. +/// \param origin If non NULL, specifies the origin of the name to be +/// constructed. +/// +/// \return A new Name object that corresponds to the next string token of +/// the \c lexer. +inline Name +createNameFromLexer(MasterLexer& lexer, const Name* origin) { + const MasterToken::StringRegion& str_region = + lexer.getNextToken(MasterToken::STRING).getStringRegion(); + return (Name(str_region.beg, str_region.len, origin)); +} + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc +#endif // DNS_RDATA_LEXER_UTIL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.cc b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc new file mode 100644 index 0000000..efe488a --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc @@ -0,0 +1,115 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <util/encode/hex.h> +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <boost/lexical_cast.hpp> + +#include <sstream> +#include <vector> +#include <stdint.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec3 { + +ParseNSEC3ParamResult +parseNSEC3ParamFromLexer(const char* const rrtype_name, + MasterLexer& lexer, vector<uint8_t>& salt) +{ + const uint32_t hashalg = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (hashalg > 0xff) { + isc_throw(InvalidRdataText, rrtype_name << + " hash algorithm out of range: " << hashalg); + } + + const uint32_t flags = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 0xff) { + isc_throw(InvalidRdataText, rrtype_name << " flags out of range: " << + flags); + } + + const uint32_t iterations = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (iterations > 0xffff) { + isc_throw(InvalidRdataText, rrtype_name << + " iterations out of range: " << iterations); + } + + const string salthex = + lexer.getNextToken(MasterToken::STRING).getString(); + + // Salt is up to 255 bytes, and space is not allowed in the HEX encoding, + // so the encoded string cannot be longer than the double of max length + // of the actual salt. + if (salthex.size() > 255 * 2) { + isc_throw(InvalidRdataText, rrtype_name << " salt is too long: " + << salthex.size() << " (encoded) bytes"); + } + if (salthex != "-") { // "-" means a 0-length salt + decodeHex(salthex, salt); + } + + return (ParseNSEC3ParamResult(hashalg, flags, iterations)); +} + +ParseNSEC3ParamResult +parseNSEC3ParamWire(const char* const rrtype_name, + InputBuffer& buffer, + size_t& rdata_len, std::vector<uint8_t>& salt) +{ + // NSEC3/NSEC3PARAM RR must have at least 5 octets: + // hash algorithm(1), flags(1), iteration(2), saltlen(1) + if (rdata_len < 5) { + isc_throw(DNSMessageFORMERR, rrtype_name << " too short, length: " + << rdata_len); + } + + const uint8_t hashalg = buffer.readUint8(); + const uint8_t flags = buffer.readUint8(); + const uint16_t iterations = buffer.readUint16(); + + const uint8_t saltlen = buffer.readUint8(); + rdata_len -= 5; + if (rdata_len < saltlen) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " salt length is too large: " << + static_cast<unsigned int>(saltlen)); + } + + salt.resize(saltlen); + if (saltlen > 0) { + buffer.readData(&salt[0], saltlen); + rdata_len -= saltlen; + } + + return (ParseNSEC3ParamResult(hashalg, flags, iterations)); +} + +} // end of nsec3 +} // end of detail +} // end of generic +} // end of rdata +} // end of dns +} // end of isc diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.h b/src/lib/dns/rdata/generic/detail/nsec3param_common.h new file mode 100644 index 0000000..89b2596 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.h @@ -0,0 +1,123 @@ +// 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 NSEC3PARAM_COMMON_H +#define NSEC3PARAM_COMMON_H 1 + +#include <dns/master_lexer.h> + +#include <util/buffer.h> + +#include <stdint.h> +#include <vector> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec3 { + +/// \file +/// +/// This helper module provides some utilities that handle NSEC3 and +/// NSEC3PARAM RDATA. They share the first few fields, and some operations +/// on these fields are sufficiently complicated, so it would make sense to +/// consolidate the processing logic into a single implementation module. +/// +/// The functions defined here are essentially private and are only expected +/// to be called from the \c NSEC3 and \c NSEC3PARAM class implementations. + +/// \brief Result values of the utilities. +/// +/// This structure encapsulates a tuple of NSEC3/NSEC3PARAM algorithm, +/// flags and iterations field values. This is used as the return value +/// of the utility functions defined in this module so the caller can +/// use it for constructing the corresponding RDATA. +struct ParseNSEC3ParamResult { + ParseNSEC3ParamResult(uint8_t param_algorithm, uint8_t param_flags, + uint16_t param_iterations) : + algorithm(param_algorithm), flags(param_flags), + iterations(param_iterations) + {} + const uint8_t algorithm; + const uint8_t flags; + const uint16_t iterations; +}; + +/// \brief Convert textual representation of NSEC3 parameters. +/// +/// This function takes an input MasterLexer that points at a complete +/// textual representation of an NSEC3 or NSEC3PARAM RDATA and parses it +/// extracting the hash algorithm, flags, iterations, and salt fields. +/// +/// The first three fields are returned as the return value of this function. +/// The salt will be stored in the given vector. The vector is expected +/// to be empty, but if not, the existing content will be overridden. +/// +/// On successful return the given MasterLexer will reach the end of the +/// salt field. +/// +/// \exception isc::BadValue The salt is not a valid hex string. +/// \exception InvalidRdataText The given RDATA is otherwise invalid for +/// NSEC3 or NSEC3PARAM fields. +/// \exception MasterLexer::LexerError There was a syntax error reading +/// a field from the MasterLexer. +/// +/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of +/// exception messages. +/// \param lexer The MasterLexer to read NSEC3 parameter fields from. +/// \param salt A placeholder for the salt field value of the RDATA. +/// Expected to be empty, but it's not checked (and will be overridden). +/// +/// \return The hash algorithm, flags, iterations in the form of +/// ParseNSEC3ParamResult. +ParseNSEC3ParamResult parseNSEC3ParamFromLexer(const char* const rrtype_name, + isc::dns::MasterLexer& lexer, + std::vector<uint8_t>& salt); + +/// \brief Extract NSEC3 parameters from wire-format data. +/// +/// This function takes an input buffer that stores wire-format NSEC3 or +/// NSEC3PARAM RDATA and parses it extracting the hash algorithm, flags, +/// iterations, and salt fields. +/// +/// The first three fields are returned as the return value of this function. +/// The salt will be stored in the given vector. The vector is expected +/// to be empty, but if not, the existing content will be overridden. +/// +/// On successful return the input buffer will point to the end of the +/// salt field; rdata_len will be the length of the rest of RDATA +/// (in the case of a valid NSEC3PARAM, it should be 0). +/// +/// \exception DNSMessageFORMERR The wire data is invalid. +/// +/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of +/// exception messages. +/// \param buffer An input buffer that stores wire-format RDATA. It must +/// point to the beginning of the data. +/// \param rdata_len The total length of the RDATA. +/// \param salt A placeholder for the salt field value of the RDATA. +/// Expected to be empty, but it's not checked (and will be overridden). +/// +/// \return The hash algorithm, flags, iterations in the form of +/// ParseNSEC3ParamResult. +ParseNSEC3ParamResult parseNSEC3ParamWire(const char* const rrtype_name, + isc::util::InputBuffer& buffer, + size_t& rdata_len, + std::vector<uint8_t>& salt); +} +} +} +} +} +} + +#endif // NSEC3PARAM_COMMON_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc new file mode 100644 index 0000000..d02c11d --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc @@ -0,0 +1,169 @@ +// Copyright (C) 2011-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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rrtype.h> + +#include <cassert> +#include <sstream> +#include <vector> +#include <cstring> +#include <stdint.h> + +using namespace std; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec { +void +checkRRTypeBitmaps(const char* const rrtype_name, + const vector<uint8_t>& typebits) +{ + bool first = true; + unsigned int lastblock = 0; + const size_t total_len = typebits.size(); + size_t i = 0; + + while (i < total_len) { + if (i + 2 > total_len) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: incomplete bit map field"); + } + const unsigned int block = typebits[i]; + const size_t len = typebits[i + 1]; + // Check that bitmap window blocks are in the correct order. + if (!first && block <= lastblock) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: Disordered window blocks found: " + << lastblock << " then " << block); + } + // Check for legal length + if (len < 1 || len > 32) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: Invalid bitmap length: " << len); + } + // Check for overflow. + i += 2; + if (i + len > total_len) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: bitmap length too large: " << len); + } + // The last octet of the bitmap must be non zero. + if (typebits[i + len - 1] == 0) { + isc_throw(DNSMessageFORMERR, rrtype_name << + " RDATA from wire: bitmap ending an all-zero byte"); + } + + i += len; + lastblock = block; + first = false; + } +} + +void +buildBitmapsFromLexer(const char* const rrtype_name, + MasterLexer& lexer, vector<uint8_t>& typebits, + bool allow_empty) +{ + uint8_t bitmap[8 * 1024]; // 64k bits + memset(bitmap, 0, sizeof(bitmap)); + + bool have_rrtypes = false; + std::string type_str; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + // token is now assured to be of type STRING. + + have_rrtypes = true; + token.getString(type_str); + try { + const int code = RRType(type_str).getCode(); + bitmap[code / 8] |= (0x80 >> (code % 8)); + } catch (const InvalidRRType&) { + isc_throw(InvalidRdataText, "Invalid RRtype in " + << rrtype_name << " bitmap: " << type_str); + } + } + + lexer.ungetToken(); + + if (!have_rrtypes) { + if (allow_empty) { + return; + } + isc_throw(InvalidRdataText, + rrtype_name << + " record does not end with RR type mnemonic"); + } + + for (int window = 0; window < 256; ++window) { + int octet; + for (octet = 31; octet >= 0; octet--) { + if (bitmap[window * 32 + octet] != 0) { + break; + } + } + if (octet < 0) { + continue; + } + typebits.push_back(window); + typebits.push_back(octet + 1); + for (int i = 0; i <= octet; ++i) { + typebits.push_back(bitmap[window * 32 + i]); + } + } +} + +void +bitmapsToText(const vector<uint8_t>& typebits, ostringstream& oss) { + // In the following loop we use string::at() rather than operator[]. + // Since the index calculation is a bit complicated, it will be safer + // and easier to find a bug (if any). Note that this conversion method + // is generally not expected to be very efficient, so the slight overhead + // of at() should be acceptable. + const size_t typebits_len = typebits.size(); + size_t len = 0; + for (size_t i = 0; i < typebits_len; i += len) { + assert(i + 2 <= typebits.size()); + const unsigned int block = typebits.at(i); + len = typebits.at(i + 1); + assert(len > 0 && len <= 32); + i += 2; + for (size_t j = 0; j < len; ++j) { + if (typebits.at(i + j) == 0) { + continue; + } + for (size_t k = 0; k < 8; ++k) { + if ((typebits.at(i + j) & (0x80 >> k)) == 0) { + continue; + } + const unsigned int t = block * 256 + j * 8 + k; + assert(t < 65536); + oss << " " << RRType(t); + } + } + } +} +} +} +} +} +} +} diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h new file mode 100644 index 0000000..4e073a0 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h @@ -0,0 +1,103 @@ +// Copyright (C) 2011-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 NSECBITMAP_H +#define NSECBITMAP_H 1 + +#include <dns/master_lexer.h> + +#include <stdint.h> + +#include <sstream> +#include <vector> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { +namespace nsec { + +/// \file +/// +/// This helper module provides some utilities that handle NSEC and NSEC3 +/// type bitmaps. The format and processing of the type bitmaps are generally +/// the same for these two RRs, so it would make sense to consolidate +/// the processing logic into a single implementation module. +/// +/// The functions defined here are essentially private and are only expected +/// to be called from the \c NSEC and \c NSEC3 class implementations. + +/// \brief Check if a given "type bitmap" for NSEC/NSEC3 is valid. +/// +/// This function checks given wire format data (stored in a +/// \c std::vector) is a valid type bitmaps used for the NSEC and NSEC3 RRs +/// according to RFC4034 and RFC5155. +/// +/// \exception DNSMessageFORMERR The bitmap is not valid. +/// +/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception +/// messages. +/// \param typebits The type bitmaps in wire format. The size of vector +/// is the total length of the bitmaps. +void checkRRTypeBitmaps(const char* const rrtype_name, + const std::vector<uint8_t>& typebits); + +/// \brief Convert textual sequence of RR types read from a lexer into +/// type bitmaps. +/// +/// See the other variant above for description. If \c allow_empty is +/// true and there are no mnemonics, \c typebits is left untouched. +/// +/// \exception InvalidRdataText Data read from the given lexer does not +/// meet the assumption (e.g. including invalid form of RR type, not +/// ending with an RR type string). +/// +/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception +/// messages. +/// \param lexer MasterLexer that provides consists of a complete +/// sequence of textual lexemes of RR types for which the corresponding +/// bits are set. +/// \param typebits A placeholder for the resulting bitmaps. Expected to be +/// empty, but it's not checked. +/// \param allow_empty If true, the function simply returns if no RR +/// type mnemonics are found. Otherwise, it throws an exception if no RR +/// type mnemonics are found. +void buildBitmapsFromLexer(const char* const rrtype_name, + isc::dns::MasterLexer& lexer, + std::vector<uint8_t>& typebits, + bool allow_empty = false); + +/// \brief Convert type bitmaps to textual sequence of RR types. +/// +/// This function converts wire-format data of type bitmaps for NSEC/NSEC3 +/// into a sequence of corresponding RR type strings, and inserts them +/// into the given output stream with separating them by a single space +/// character. +/// +/// This function assumes the given bitmaps are valid in terms of RFC4034 +/// and RFC5155 (in practice, it assumes it's from a validly constructed +/// NSEC or NSEC3 object); if it detects a format error, it aborts the +/// program with assert(). +/// +/// \param typebits The type bitmaps in wire format. The size of vector +/// is the total length of the bitmaps. +/// \param oss The output stream to which the converted RR type sequence +/// are to be inserted. +void bitmapsToText(const std::vector<uint8_t>& typebits, + std::ostringstream& oss); +} +} +} +} +} +} + +#endif // NSECBITMAP_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h new file mode 100644 index 0000000..5801b09 --- /dev/null +++ b/src/lib/dns/rdata/generic/detail/txt_like.h @@ -0,0 +1,237 @@ +// Copyright (C) 2011-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/. + +#ifndef TXT_LIKE_H +#define TXT_LIKE_H 1 + +#include <dns/master_lexer.h> +#include <dns/rdata/generic/detail/char_string.h> + +#include <stdint.h> + +#include <string> +#include <sstream> +#include <vector> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT +/// and SPF types. +/// +/// This class implements the basic interfaces inherited by the TXT and SPF +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to TXT-like RDATA. +template<class Type, uint16_t typeCode>class TXTLikeImpl { +public: + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// <b>Exceptions</b> + /// + /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum. + /// \c DNSMessageFORMERR is thrown if the RR is malformed. + TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); + } + + if (rdata_len == 0) { // note that this couldn't happen in the loop. + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << " RDATA: 0-length character string"); + } + + do { + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << + " RDATA: character string length is too large: " << + static_cast<int>(len)); + } + std::vector<uint8_t> data(len + 1); + data[0] = len; + buffer.readData(&data[0] + 1, len); + string_list_.push_back(data); + + rdata_len -= (len + 1); + } while (rdata_len > 0); + } + + /// \brief Constructor from string. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + explicit TXTLikeImpl(const std::string& txtstr) { + std::istringstream ss(txtstr); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + buildFromTextHelper(lexer); + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << + "': extra new line"); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << "': " + << ex.what()); + } + } + + /// \brief Constructor using the master lexer. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + /// + /// \param lexer A \c MasterLexer object parsing a master file for this + /// RDATA. + TXTLikeImpl(MasterLexer& lexer) { + buildFromTextHelper(lexer); + } + +private: + void buildFromTextHelper(MasterLexer& lexer) { + while (true) { + const MasterToken& token = lexer.getNextToken( + MasterToken::QSTRING, true); + if (token.getType() != MasterToken::STRING && + token.getType() != MasterToken::QSTRING) { + break; + } + string_list_.push_back(std::vector<uint8_t>()); + stringToCharString(token.getStringRegion(), string_list_.back()); + } + + // Let upper layer handle eol/eof. + lexer.ungetToken(); + + if (string_list_.empty()) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA: empty input"); + } + } + +public: + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. + TXTLikeImpl(const TXTLikeImpl& other) : + string_list_(other.string_list_) + {} + + /// \brief Render the TXT-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. + void + toWire(util::OutputBuffer& buffer) const { + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + buffer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Render the TXT-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param buffer An output AbstractMessageRenderer to send the wire data + /// to. + void + toWire(AbstractMessageRenderer& renderer) const { + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + renderer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Convert the TXT-like data to a string. + /// + /// \return A \c string object that represents the TXT-like data. + std::string + toText() const { + std::string s; + + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); it != string_list_.end(); ++it) + { + if (!s.empty()) { + s.push_back(' '); + } + s.push_back('"'); + s.append(charStringToString(*it)); + s.push_back('"'); + } + + return (s); + } + + /// \brief Compare two instances of TXT-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c TXTLikeImpl class. + /// + /// \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. + int + compare(const TXTLikeImpl& other) const { + // This implementation is not efficient. Revisit this (TBD). + OutputBuffer this_buffer(0); + toWire(this_buffer); + uint8_t const* const this_data = (uint8_t const*)this_buffer.getData(); + const size_t this_len = this_buffer.getLength(); + + OutputBuffer other_buffer(0); + other.toWire(other_buffer); + uint8_t const* const other_data + = (uint8_t const*)other_buffer.getData(); + const size_t other_len = other_buffer.getLength(); + + const size_t cmplen = min(this_len, other_len); + const int cmp = memcmp(this_data, other_data, cmplen); + + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } + } + +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + std::vector<std::vector<uint8_t> > string_list_; +}; + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc + +#endif // TXT_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/dlv_32769.cc b/src/lib/dns/rdata/generic/dlv_32769.cc new file mode 100644 index 0000000..66303b7 --- /dev/null +++ b/src/lib/dns/rdata/generic/dlv_32769.cc @@ -0,0 +1,120 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const std::string& ds_str) : + impl_(new DLVImpl(ds_str)) +{} + +/// \brief Constructor from wire-format data. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(InputBuffer& buffer, size_t rdata_len) : + impl_(new DLVImpl(buffer, rdata_len)) +{} + +DLV::DLV(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DLVImpl(lexer, origin, options, callbacks)) +{} + +/// \brief Copy constructor +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const DLV& source) : + Rdata(), impl_(new DLVImpl(*source.impl_)) +{} + +/// \brief Assignment operator +/// +/// PIMPL-induced logic +DLV& +DLV::operator=(const DLV& source) { + if (this == &source) { + return (*this); + } + + DLVImpl* newimpl = new DLVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief Destructor +/// +/// Deallocates an internal resource. +DLV::~DLV() { + delete impl_; +} + +/// \brief Convert the \c DLV to a string. +/// +/// A pass-thru to the corresponding implementation method. +string +DLV::toText() const { + return (impl_->toText()); +} + +/// \brief Render the \c DLV in the wire format to a OutputBuffer object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c DLV in the wire format to a AbstractMessageRenderer +/// object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Compare two instances of \c DLV RDATA. +/// +/// The type check is performed here. Otherwise, a pass-thru to the +/// corresponding implementation method. +int +DLV::compare(const Rdata& other) const { + const DLV& other_ds = dynamic_cast<const DLV&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +/// \brief Tag accessor +uint16_t +DLV::getTag() const { + return (impl_->getTag()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dlv_32769.h b/src/lib/dns/rdata/generic/dlv_32769.h new file mode 100644 index 0000000..26523de --- /dev/null +++ b/src/lib/dns/rdata/generic/dlv_32769.h @@ -0,0 +1,69 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +namespace detail { +template <class Type, uint16_t typeCode> class DSLikeImpl; +} + +/// \brief \c rdata::generic::DLV class represents the DLV RDATA as defined in +/// RFC4431. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DLV RDATA. +class DLV : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + DLV& operator=(const DLV& source); + + /// \brief The destructor. + ~DLV(); + + /// \brief Return the value of the Tag field. + /// + /// This method never throws an exception. + uint16_t getTag() const; +private: + typedef detail::DSLikeImpl<DLV, 32769> DLVImpl; + DLVImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/dname_39.cc b/src/lib/dns/rdata/generic/dname_39.cc new file mode 100644 index 0000000..9ee669b --- /dev/null +++ b/src/lib/dns/rdata/generic/dname_39.cc @@ -0,0 +1,127 @@ +// 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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNAME 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. +/// +/// The TARGET must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +DNAME::DNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + dname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + dname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DNAME from '" << + namestr << "': " << ex.what()); + } +} + +DNAME::DNAME(InputBuffer& buffer, size_t) : + dname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a DNAME RDATA. The TARGET field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 TARGET when it +/// is non-absolute. +DNAME::DNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + dname_(createNameFromLexer(lexer, origin)) +{} + +DNAME::DNAME(const DNAME& other) : + Rdata(), dname_(other.dname_) +{} + +DNAME::DNAME(const Name& dname) : + dname_(dname) +{} + +void +DNAME::toWire(OutputBuffer& buffer) const { + dname_.toWire(buffer); +} + +void +DNAME::toWire(AbstractMessageRenderer& renderer) const { + // Type DNAME is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(dname_, false); +} + +string +DNAME::toText() const { + return (dname_.toText()); +} + +int +DNAME::compare(const Rdata& other) const { + const DNAME& other_dname = dynamic_cast<const DNAME&>(other); + + return (compareNames(dname_, other_dname.dname_)); +} + +const Name& +DNAME::getDname() const { + return (dname_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dname_39.h b/src/lib/dns/rdata/generic/dname_39.h new file mode 100644 index 0000000..998fea0 --- /dev/null +++ b/src/lib/dns/rdata/generic/dname_39.h @@ -0,0 +1,39 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class DNAME : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // DNAME specific methods + DNAME(const Name& dname); + const Name& getDname() const; +private: + Name dname_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/dnskey_48.cc b/src/lib/dns/rdata/generic/dnskey_48.cc new file mode 100644 index 0000000..7bea847 --- /dev/null +++ b/src/lib/dns/rdata/generic/dnskey_48.cc @@ -0,0 +1,316 @@ +// 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 <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> +#include <boost/foreach.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct DNSKEYImpl { + // straightforward representation of DNSKEY RDATA fields + DNSKEYImpl(uint16_t flags, uint8_t protocol, uint8_t algorithm, + const vector<uint8_t>& keydata) : + flags_(flags), protocol_(protocol), algorithm_(algorithm), + keydata_(keydata) + {} + + uint16_t flags_; + uint8_t protocol_; + uint8_t algorithm_; + const vector<uint8_t> keydata_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNSKEY 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. +/// +/// The Protocol and Algorithm fields must be within their valid +/// ranges. The Public Key field must be present and must contain a +/// Base64 encoding of the public key. Whitespace is allowed within the +/// Base64 text. +/// +/// It is okay for the key data to be missing. Note: BIND 9 also accepts +/// DNSKEY missing key data. While the RFC is silent in this case, and it +/// may be debatable what an implementation should do, but since this field +/// is algorithm dependent and this implementations doesn't reject unknown +/// algorithms, it's lenient here. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param dnskey_str A string containing the RDATA to be created +DNSKEY::DNSKEY(const std::string& dnskey_str) : + 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 DNSKEYImpl that constructFromLexer() returns. + std::unique_ptr<DNSKEYImpl> impl_ptr; + + try { + std::istringstream ss(dnskey_str); + 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 DNSKEY: " << dnskey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct DNSKEY from '" << dnskey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid DNSKEY RDATA. +/// +/// The Protocol and Algorithm fields are not checked for unknown +/// values. It is okay for the key data to be missing (see the description +/// of the constructor from string). +DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, "DNSKEY too short: " << rdata_len); + } + + const uint16_t flags = buffer.readUint16(); + const uint16_t protocol = buffer.readUint8(); + const uint16_t algorithm = buffer.readUint8(); + + rdata_len -= 4; + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (rdata_len > 0) { + keydata.resize(rdata_len); + buffer.readData(&keydata[0], rdata_len); + } + + impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an DNSKEY RDATA. +/// +/// See \c DNSKEY::DNSKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DNSKEY::DNSKEY(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +DNSKEYImpl* +DNSKEY::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 0xffff) { + isc_throw(InvalidRdataText, + "DNSKEY flags out of range: " << flags); + } + + const uint32_t protocol = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (protocol > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY protocol out of range: " << protocol); + } + + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY algorithm out of range: " << algorithm); + } + + std::string keydata_str; + std::string keydata_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + // token is now assured to be of type STRING. + + token.getString(keydata_substr); + keydata_str.append(keydata_substr); + } + + lexer.ungetToken(); + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (keydata_str.size() > 0) { + decodeBase64(keydata_str, keydata); + } + + return (new DNSKEYImpl(flags, protocol, algorithm, keydata)); +} + +DNSKEY::DNSKEY(const DNSKEY& source) : + Rdata(), impl_(new DNSKEYImpl(*source.impl_)) +{} + +DNSKEY& +DNSKEY::operator=(const DNSKEY& source) { + if (this == &source) { + return (*this); + } + + DNSKEYImpl* newimpl = new DNSKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DNSKEY::~DNSKEY() { + delete impl_; +} + +string +DNSKEY::toText() const { + return (boost::lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->protocol_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + encodeBase64(impl_->keydata_)); +} + +void +DNSKEY::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->flags_); + buffer.writeUint8(impl_->protocol_); + buffer.writeUint8(impl_->algorithm_); + buffer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +void +DNSKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->flags_); + renderer.writeUint8(impl_->protocol_); + renderer.writeUint8(impl_->algorithm_); + renderer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +int +DNSKEY::compare(const Rdata& other) const { + const DNSKEY& other_dnskey = dynamic_cast<const DNSKEY&>(other); + + if (impl_->flags_ != other_dnskey.impl_->flags_) { + return (impl_->flags_ < other_dnskey.impl_->flags_ ? -1 : 1); + } + if (impl_->protocol_ != other_dnskey.impl_->protocol_) { + return (impl_->protocol_ < other_dnskey.impl_->protocol_ ? -1 : 1); + } + if (impl_->algorithm_ != other_dnskey.impl_->algorithm_) { + return (impl_->algorithm_ < other_dnskey.impl_->algorithm_ ? -1 : 1); + } + + const size_t this_len = impl_->keydata_.size(); + const size_t other_len = other_dnskey.impl_->keydata_.size(); + const size_t cmplen = min(this_len, other_len); + if (cmplen == 0) { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } + const int cmp = memcmp(&impl_->keydata_[0], + &other_dnskey.impl_->keydata_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +uint16_t +DNSKEY::getTag() const { + if (impl_->algorithm_ == 1) { + // See RFC 4034 appendix B.1 for why the key data must contain + // at least 4 bytes with RSA/MD5: 3 trailing bytes to extract + // the tag from, and 1 byte of exponent length subfield before + // modulus. + const int len = impl_->keydata_.size(); + if (len < 4) { + isc_throw(isc::OutOfRange, + "DNSKEY keydata too short for tag extraction"); + } + + return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]); + } + + uint32_t ac = impl_->flags_; + ac += (impl_->protocol_ << 8); + ac += impl_->algorithm_; + + const size_t size = impl_->keydata_.size(); + for (size_t i = 0; i < size; i ++) { + ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8); + } + ac += (ac >> 16) & 0xffff; + return (ac & 0xffff); +} + +uint16_t +DNSKEY::getFlags() const { + return (impl_->flags_); +} + +uint8_t +DNSKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/dnskey_48.h b/src/lib/dns/rdata/generic/dnskey_48.h new file mode 100644 index 0000000..a5e9efa --- /dev/null +++ b/src/lib/dns/rdata/generic/dnskey_48.h @@ -0,0 +1,60 @@ +// 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 <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct DNSKEYImpl; + +class DNSKEY : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + DNSKEY& operator=(const DNSKEY& source); + ~DNSKEY(); + + /// + /// Specialized methods + /// + + /// \brief Returns the key tag + /// + /// \throw isc::OutOfRange if the key data for RSA/MD5 is too short + /// to support tag extraction. + uint16_t getTag() const; + + uint16_t getFlags() const; + uint8_t getAlgorithm() const; + +private: + DNSKEYImpl* constructFromLexer(isc::dns::MasterLexer& lexer); + + DNSKEYImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc new file mode 100644 index 0000000..48c421c --- /dev/null +++ b/src/lib/dns/rdata/generic/ds_43.cc @@ -0,0 +1,90 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +DS::DS(const std::string& ds_str) : + impl_(new DSImpl(ds_str)) +{} + +DS::DS(InputBuffer& buffer, size_t rdata_len) : + impl_(new DSImpl(buffer, rdata_len)) +{} + +DS::DS(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DSImpl(lexer, origin, options, callbacks)) +{} + +DS::DS(const DS& source) : + Rdata(), impl_(new DSImpl(*source.impl_)) +{} + +DS& +DS::operator=(const DS& source) { + if (this == &source) { + return (*this); + } + + DSImpl* newimpl = new DSImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DS::~DS() { + delete impl_; +} + +string +DS::toText() const { + return (impl_->toText()); +} + +void +DS::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +DS::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +int +DS::compare(const Rdata& other) const { + const DS& other_ds = dynamic_cast<const DS&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +uint16_t +DS::getTag() const { + return (impl_->getTag()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ds_43.h b/src/lib/dns/rdata/generic/ds_43.h new file mode 100644 index 0000000..a20e349 --- /dev/null +++ b/src/lib/dns/rdata/generic/ds_43.h @@ -0,0 +1,69 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +namespace detail { +template <class Type, uint16_t typeCode> class DSLikeImpl; +} + +/// \brief \c rdata::generic::DS class represents the DS RDATA as defined in +/// RFC3658. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DS RDATA. +class DS : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + DS& operator=(const DS& source); + + /// \brief The destructor. + ~DS(); + + /// \brief Return the value of the Tag field. + /// + /// This method never throws an exception. + uint16_t getTag() const; +private: + typedef detail::DSLikeImpl<DS, 43> DSImpl; + DSImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc new file mode 100644 index 0000000..3bda219 --- /dev/null +++ b/src/lib/dns/rdata/generic/hinfo_13.cc @@ -0,0 +1,151 @@ +// Copyright (C) 2011-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 <config.h> + +#include <exceptions/exceptions.h> +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +class HINFOImpl { +public: + HINFOImpl(const std::string& hinfo_str) { + std::istringstream ss(hinfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseHINFOData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid HINFO text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct HINFO RDATA from " + << hinfo_str << "': " << ex.what()); + } + } + + HINFOImpl(InputBuffer& buffer, size_t rdata_len) { + rdata_len -= detail::bufferToCharString(buffer, rdata_len, cpu); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, os); + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "HINFO RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + HINFOImpl(MasterLexer& lexer) + { + parseHINFOData(lexer); + } + +private: + void + parseHINFOData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), cpu); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), os); + } + +public: + detail::CharString cpu; + detail::CharString os; +}; + +HINFO::HINFO(const std::string& hinfo_str) : impl_(new HINFOImpl(hinfo_str)) +{} + + +HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) : + impl_(new HINFOImpl(buffer, rdata_len)) +{} + +HINFO::HINFO(const HINFO& source): + Rdata(), impl_(new HINFOImpl(*source.impl_)) +{ +} + +HINFO::HINFO(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new HINFOImpl(lexer)) +{} + +HINFO& +HINFO::operator=(const HINFO& source) +{ + impl_.reset(new HINFOImpl(*source.impl_)); + return (*this); +} + +HINFO::~HINFO() { +} + +std::string +HINFO::toText() const { + string result; + result += "\""; + result += detail::charStringToString(impl_->cpu); + result += "\" \""; + result += detail::charStringToString(impl_->os); + result += "\""; + return (result); +} + +void +HINFO::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); +} + +void +HINFO::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); +} + +int +HINFO::compare(const Rdata& other) const { + const HINFO& other_hinfo = dynamic_cast<const HINFO&>(other); + + const int cmp = compareCharStrings(impl_->cpu, other_hinfo.impl_->cpu); + if (cmp != 0) { + return (cmp); + } + return (compareCharStrings(impl_->os, other_hinfo.impl_->os)); +} + +const std::string +HINFO::getCPU() const { + return (detail::charStringToString(impl_->cpu)); +} + +const std::string +HINFO::getOS() const { + return (detail::charStringToString(impl_->os)); +} + +template <typename T> +void +HINFO::toWireHelper(T& outputer) const { + outputer.writeData(&impl_->cpu[0], impl_->cpu.size()); + outputer.writeData(&impl_->os[0], impl_->os.size()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h new file mode 100644 index 0000000..acceb14 --- /dev/null +++ b/src/lib/dns/rdata/generic/hinfo_13.h @@ -0,0 +1,64 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD +#include <stdint.h> + +#include <string> + +#include <boost/scoped_ptr.hpp> +#include <boost/noncopyable.hpp> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <util/buffer.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class HINFOImpl; + +/// \brief \c HINFO class represents the HINFO rdata defined in +/// RFC1034, RFC1035 +/// +/// This class implements the basic interfaces inherited from the +/// \c rdata::Rdata class, and provides accessors specific to the +/// HINFO rdata. +class HINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // HINFO specific methods + ~HINFO(); + + HINFO& operator=(const HINFO&); + + const std::string getCPU() const; + const std::string getOS() const; + +private: + /// Helper template function for toWire() + /// + /// \param outputer Where to write data in + template <typename T> + void toWireHelper(T& outputer) const; + + boost::scoped_ptr<HINFOImpl> impl_; +}; + + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc new file mode 100644 index 0000000..d3dfd1e --- /dev/null +++ b/src/lib/dns/rdata/generic/minfo_14.cc @@ -0,0 +1,173 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// \c minfo_str must be formatted as follows: +/// \code <rmailbox name> <emailbox name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "rmail.example.com. email.example.com." \endcode +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +MINFO::MINFO(const std::string& minfo_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(minfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + rmailbox_ = createNameFromLexer(lexer, NULL); + emailbox_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MINFO: " + << minfo_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MINFO from '" << + minfo_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MINFO RDATA. The RMAILBOX and EMAILBOX fields can be non-absolute +/// if \c origin is non-NULL, in which case \c origin is used to make them +/// absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +MINFO::MINFO(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + rmailbox_(createNameFromLexer(lexer, origin)), + emailbox_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +MINFO::MINFO(InputBuffer& buffer, size_t) : + rmailbox_(buffer), emailbox_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +MINFO::MINFO(const MINFO& other) : + Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_) +{} + +/// \brief Convert the \c MINFO to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c MINFO(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c MINFO object. +std::string +MINFO::toText() const { + return (rmailbox_.toText() + " " + emailbox_.toText()); +} + +/// \brief Render the \c MINFO in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +MINFO::toWire(OutputBuffer& buffer) const { + rmailbox_.toWire(buffer); + emailbox_.toWire(buffer); +} + +MINFO& +MINFO::operator=(const MINFO& source) { + rmailbox_ = source.rmailbox_; + emailbox_ = source.emailbox_; + + return (*this); +} + +/// \brief Render the \c MINFO in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and +/// emailbox fields (domain names) will be compressed. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +MINFO::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(rmailbox_); + renderer.writeName(emailbox_); +} + +/// \brief Compare two instances of \c MINFO RDATA. +/// +/// See documentation in \c Rdata. +int +MINFO::compare(const Rdata& other) const { + const MINFO& other_minfo = dynamic_cast<const MINFO&>(other); + + const int cmp = compareNames(rmailbox_, other_minfo.rmailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(emailbox_, other_minfo.emailbox_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/minfo_14.h b/src/lib/dns/rdata/generic/minfo_14.h new file mode 100644 index 0000000..ce9f43d --- /dev/null +++ b/src/lib/dns/rdata/generic/minfo_14.h @@ -0,0 +1,74 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::generic::MINFO class represents the MINFO RDATA as +/// defined in RFC1035. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// MINFO RDATA. +class MINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Define the assignment operator. + /// + /// \exception std::bad_alloc Memory allocation fails in copying + /// internal member variables (this should be very rare). + MINFO& operator=(const MINFO& source); + + /// \brief Return the value of the rmailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + /// + /// \note + /// Unlike the case of some other RDATA classes (such as + /// \c NS::getNSName()), this method constructs a new \c Name object + /// and returns it, instead of returning a reference to a \c Name object + /// internally maintained in the class (which is a private member). + /// This is based on the observation that this method will be rarely + /// used and even when it's used it will not be in a performance context + /// (for example, a recursive resolver won't need this field in its + /// resolution process). By returning a new object we have flexibility + /// of changing the internal representation without the risk of changing + /// the interface or method property. + /// The same note applies to the \c getEmailbox() method. + Name getRmailbox() const { return (rmailbox_); } + + /// \brief Return the value of the emailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + Name getEmailbox() const { return (emailbox_); } + +private: + Name rmailbox_; + Name emailbox_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/mx_15.cc b/src/lib/dns/rdata/generic/mx_15.cc new file mode 100644 index 0000000..bbe8abc --- /dev/null +++ b/src/lib/dns/rdata/generic/mx_15.cc @@ -0,0 +1,160 @@ +// 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 <config.h> + +#include <string> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +MX::MX(InputBuffer& buffer, size_t) : + preference_(buffer.readUint16()), mxname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid MX 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. +/// +/// The EXCHANGE name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +MX::MX(const std::string& mx_str) : + // Fill in dummy name and replace them soon below. + preference_(0), mxname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(mx_str); + MasterLexer lexer; + lexer.pushSource(ss); + + constructFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MX: " + << mx_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MX from '" << + mx_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MX RDATA. The EXCHANGE field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PREFERENCE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 EXCHANGE when it +/// is non-absolute. +MX::MX(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + preference_(0), mxname_(Name::ROOT_NAME()) +{ + constructFromLexer(lexer, origin); +} + +void +MX::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid MX preference: " << num); + } + preference_ = static_cast<uint16_t>(num); + + mxname_ = createNameFromLexer(lexer, origin); +} + +MX::MX(uint16_t preference, const Name& mxname) : + preference_(preference), mxname_(mxname) +{} + +MX::MX(const MX& other) : + Rdata(), preference_(other.preference_), mxname_(other.mxname_) +{} + +void +MX::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(preference_); + mxname_.toWire(buffer); +} + +void +MX::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(preference_); + renderer.writeName(mxname_); +} + +string +MX::toText() const { + return (lexical_cast<string>(preference_) + " " + mxname_.toText()); +} + +int +MX::compare(const Rdata& other) const { + const MX& other_mx = dynamic_cast<const MX&>(other); + + if (preference_ < other_mx.preference_) { + return (-1); + } else if (preference_ > other_mx.preference_) { + return (1); + } + + return (compareNames(mxname_, other_mx.mxname_)); +} + +const Name& +MX::getMXName() const { + return (mxname_); +} + +uint16_t +MX::getMXPref() const { + return (preference_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/mx_15.h b/src/lib/dns/rdata/generic/mx_15.h new file mode 100644 index 0000000..b688f88 --- /dev/null +++ b/src/lib/dns/rdata/generic/mx_15.h @@ -0,0 +1,52 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class MX : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + MX(uint16_t preference, const Name& mxname); + + /// + /// Specialized methods + /// + const Name& getMXName() const; + uint16_t getMXPref() const; + +private: + void constructFromLexer(isc::dns::MasterLexer& lexer, + const isc::dns::Name* origin); + + /// Note: this is a prototype version; we may reconsider + /// this representation later. + uint16_t preference_; + Name mxname_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc new file mode 100644 index 0000000..aa2d7f5 --- /dev/null +++ b/src/lib/dns/rdata/generic/naptr_35.cc @@ -0,0 +1,256 @@ +// Copyright (C) 2011-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 <config.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <exceptions/exceptions.h> + +#include <string> +#include <boost/lexical_cast.hpp> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::dns; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +class NAPTRImpl { +public: + NAPTRImpl() : order(0), preference(0), replacement(".") {} + + NAPTRImpl(InputBuffer& buffer, size_t rdata_len) : replacement(".") { + if (rdata_len < 4 || buffer.getLength() - buffer.getPosition() < 4) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: insufficient length "); + } + order = buffer.readUint16(); + preference = buffer.readUint16(); + rdata_len -= 4; + + rdata_len -= detail::bufferToCharString(buffer, rdata_len, flags); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, services); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, regexp); + replacement = Name(buffer); + if (rdata_len < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: missing replacement name"); + } + rdata_len -= replacement.getLength(); + + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "NAPTR RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + NAPTRImpl(const std::string& naptr_str) : replacement(".") { + std::istringstream ss(naptr_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseNAPTRData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NAPTR RDATA from " + << naptr_str << "': " << ex.what()); + } + } + + NAPTRImpl(MasterLexer& lexer) : replacement(".") + { + parseNAPTRData(lexer); + } + +private: + void + parseNAPTRData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: order out of range: " + << token.getNumber()); + } + order = token.getNumber(); + token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: preference out of range: " + << token.getNumber()); + } + preference = token.getNumber(); + + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), flags); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), services); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), regexp); + + token = lexer.getNextToken(MasterToken::STRING); + replacement = Name(token.getString()); + } + + +public: + uint16_t order; + uint16_t preference; + detail::CharString flags; + detail::CharString services; + detail::CharString regexp; + Name replacement; +}; + +NAPTR::NAPTR(InputBuffer& buffer, size_t rdata_len) : + impl_(new NAPTRImpl(buffer, rdata_len)) +{} + +NAPTR::NAPTR(const std::string& naptr_str) : impl_(new NAPTRImpl(naptr_str)) +{} + +NAPTR::NAPTR(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new NAPTRImpl(lexer)) +{} + +NAPTR::NAPTR(const NAPTR& naptr) : Rdata(), + impl_(new NAPTRImpl(*naptr.impl_)) +{} + +NAPTR& +NAPTR::operator=(const NAPTR& source) +{ + impl_.reset(new NAPTRImpl(*source.impl_)); + return (*this); +} + +NAPTR::~NAPTR() { +} + +void +NAPTR::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); + impl_->replacement.toWire(buffer); +} + +void +NAPTR::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); + // Type NAPTR is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->replacement, false); +} + +string +NAPTR::toText() const { + string result; + result += lexical_cast<string>(impl_->order); + result += " "; + result += lexical_cast<string>(impl_->preference); + result += " \""; + result += detail::charStringToString(impl_->flags); + result += "\" \""; + result += detail::charStringToString(impl_->services); + result += "\" \""; + result += detail::charStringToString(impl_->regexp); + result += "\" "; + result += impl_->replacement.toText(); + return (result); +} + +int +NAPTR::compare(const Rdata& other) const { + const NAPTR other_naptr = dynamic_cast<const NAPTR&>(other); + + if (impl_->order < other_naptr.impl_->order) { + return (-1); + } else if (impl_->order > other_naptr.impl_->order) { + return (1); + } + + if (impl_->preference < other_naptr.impl_->preference) { + return (-1); + } else if (impl_->preference > other_naptr.impl_->preference) { + return (1); + } + + const int fcmp = detail::compareCharStrings(impl_->flags, + other_naptr.impl_->flags); + if (fcmp != 0) { + return (fcmp); + } + + const int scmp = detail::compareCharStrings(impl_->services, + other_naptr.impl_->services); + if (scmp != 0) { + return (scmp); + } + + const int rcmp = detail::compareCharStrings(impl_->regexp, + other_naptr.impl_->regexp); + if (rcmp != 0) { + return (rcmp); + } + + return (compareNames(impl_->replacement, other_naptr.impl_->replacement)); +} + +uint16_t +NAPTR::getOrder() const { + return (impl_->order); +} + +uint16_t +NAPTR::getPreference() const { + return (impl_->preference); +} + +const std::string +NAPTR::getFlags() const { + return (detail::charStringToString(impl_->flags)); +} + +const std::string +NAPTR::getServices() const { + return (detail::charStringToString(impl_->services)); +} + +const std::string +NAPTR::getRegexp() const { + return (detail::charStringToString(impl_->regexp)); +} + +const Name& +NAPTR::getReplacement() const { + return (impl_->replacement); +} + +template <typename T> +void +NAPTR::toWireHelper(T& outputer) const { + outputer.writeUint16(impl_->order); + outputer.writeUint16(impl_->preference); + + outputer.writeData(&impl_->flags[0], impl_->flags.size()); + outputer.writeData(&impl_->services[0], impl_->services.size()); + outputer.writeData(&impl_->regexp[0], impl_->regexp.size()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/naptr_35.h b/src/lib/dns/rdata/generic/naptr_35.h new file mode 100644 index 0000000..c77b95d --- /dev/null +++ b/src/lib/dns/rdata/generic/naptr_35.h @@ -0,0 +1,64 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <boost/scoped_ptr.hpp> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <util/buffer.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class NAPTRImpl; + +/// \brief \c NAPTR class represents the NAPTR rdata defined in +/// RFC2915, RFC2168 and RFC3403 +/// +/// This class implements the basic interfaces inherited from the +/// \c rdata::Rdata class, and provides accessors specific to the +/// NAPTR rdata. +class NAPTR : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // NAPTR specific methods + ~NAPTR(); + + NAPTR& operator=(const NAPTR& source); + + uint16_t getOrder() const; + uint16_t getPreference() const; + const std::string getFlags() const; + const std::string getServices() const; + const std::string getRegexp() const; + const Name& getReplacement() const; +private: + /// Helper template function for toWire() + /// + /// \param outputer Where to write data in + template <typename T> + void toWireHelper(T& outputer) const; + + boost::scoped_ptr<NAPTRImpl> impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/ns_2.cc b/src/lib/dns/rdata/generic/ns_2.cc new file mode 100644 index 0000000..7c1fd01 --- /dev/null +++ b/src/lib/dns/rdata/generic/ns_2.cc @@ -0,0 +1,121 @@ +// 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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NS 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. +/// +/// The NSDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +NS::NS(const std::string& namestr) : + // Fill in dummy name and replace them soon below. + nsname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + nsname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for NS: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NS from '" << + namestr << "': " << ex.what()); + } +} + +NS::NS(InputBuffer& buffer, size_t) : + nsname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NS RDATA. The NSDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 NSDNAME when it +/// is non-absolute. +NS::NS(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + nsname_(createNameFromLexer(lexer, origin)) +{} + +NS::NS(const NS& other) : + Rdata(), nsname_(other.nsname_) +{} + +void +NS::toWire(OutputBuffer& buffer) const { + nsname_.toWire(buffer); +} + +void +NS::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(nsname_); +} + +string +NS::toText() const { + return (nsname_.toText()); +} + +int +NS::compare(const Rdata& other) const { + const NS& other_ns = dynamic_cast<const NS&>(other); + + return (compareNames(nsname_, other_ns.nsname_)); +} + +const Name& +NS::getNSName() const { + return (nsname_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ns_2.h b/src/lib/dns/rdata/generic/ns_2.h new file mode 100644 index 0000000..18c3cf6 --- /dev/null +++ b/src/lib/dns/rdata/generic/ns_2.h @@ -0,0 +1,43 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class NS : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + /// + /// Specialized constructor + /// + explicit NS(const Name& nsname) : nsname_(nsname) {} + /// + /// Specialized methods + /// + const Name& getNSName() const; +private: + Name nsname_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc new file mode 100644 index 0000000..e99c109 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec3_50.cc @@ -0,0 +1,343 @@ +// 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 <config.h> + +#include <iostream> +#include <iomanip> +#include <string> +#include <sstream> +#include <vector> +#include <cassert> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base32hex.h> +#include <util/encode/hex.h> +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::dns::rdata::generic::detail::nsec; +using namespace isc::dns::rdata::generic::detail::nsec3; +using namespace isc::util::encode; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct NSEC3Impl { + // straightforward representation of NSEC3 RDATA fields + NSEC3Impl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + vector<uint8_t>salt, vector<uint8_t>next, + vector<uint8_t> typebits) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), + salt_(salt), next_(next), typebits_(typebits) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; + const vector<uint8_t> next_; + const vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3 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. +/// +/// The Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3_str A string containing the RDATA to be created +NSEC3::NSEC3(const std::string& nsec3_str) : + 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 NSEC3Impl that constructFromLexer() returns. + std::unique_ptr<NSEC3Impl> impl_ptr; + + try { + std::istringstream ss(nsec3_str); + 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 NSEC3: " << nsec3_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3 from '" << nsec3_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3 RDATA. +/// +/// See \c NSEC3::NSEC3(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3::NSEC3(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3Impl* +NSEC3::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3", lexer, salt); + + const string& nexthash = + lexer.getNextToken(MasterToken::STRING).getString(); + if (*nexthash.rbegin() == '=') { + isc_throw(InvalidRdataText, "NSEC3 hash has padding: " << nexthash); + } + + vector<uint8_t> next; + decodeBase32Hex(nexthash, next); + if (next.size() > 255) { + isc_throw(InvalidRdataText, "NSEC3 hash is too long: " + << next.size() << " bytes"); + } + + vector<uint8_t> typebits; + // For NSEC3 empty bitmap is possible and allowed. + buildBitmapsFromLexer("NSEC3", lexer, typebits, true); + return (new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits)); +} + +NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3", buffer, rdata_len, salt); + + if (rdata_len < 1) { + isc_throw(DNSMessageFORMERR, "NSEC3 too short to contain hash length, " + "length: " << rdata_len + salt.size() + 5); + } + const uint8_t nextlen = buffer.readUint8(); + --rdata_len; + if (nextlen == 0 || rdata_len < nextlen) { + isc_throw(DNSMessageFORMERR, "NSEC3 invalid hash length: " << + static_cast<unsigned int>(nextlen)); + } + + vector<uint8_t> next(nextlen); + buffer.readData(&next[0], nextlen); + rdata_len -= nextlen; + + vector<uint8_t> typebits(rdata_len); + if (rdata_len > 0) { + // Read and parse the bitmaps only when they exist; empty bitmap + // is possible for NSEC3. + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC3", typebits); + } + + impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits); +} + +NSEC3::NSEC3(const NSEC3& source) : + Rdata(), impl_(new NSEC3Impl(*source.impl_)) +{} + +NSEC3& +NSEC3::operator=(const NSEC3& source) { + if (this == &source) { + return (*this); + } + + NSEC3Impl* newimpl = new NSEC3Impl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3::~NSEC3() { + delete impl_; +} + +string +NSEC3::toText() const { + ostringstream s; + bitmapsToText(impl_->typebits_, s); + + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)) + + " " + encodeBase32Hex(impl_->next_) + s.str()); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3Impl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } + assert(!impl.next_.empty()); + output.writeUint8(impl.next_.size()); + output.writeData(&impl.next_[0], impl.next_.size()); + if (!impl.typebits_.empty()) { + output.writeData(&impl.typebits_[0], impl.typebits_.size()); + } +} + +void +NSEC3::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +namespace { +// This is a helper subroutine for compare(). It compares two binary +// data stored in vector<uint8_t> objects based on the "Canonical RR Ordering" +// as defined in Section 6.3 of RFC4034, that is, the data are treated +// "as a left-justified unsigned octet sequence in which the absence of an +// octet sorts before a zero octet." +// +// If check_length_first is true, it treats the compared data as if they +// began with a single-octet "length" field whose value is the size of the +// corresponding vector. In this case, if the sizes of the two vectors are +// different the shorter one is always considered the "smaller"; the contents +// of the vector don't matter. +// +// This function returns: +// -1 if v1 is considered smaller than v2 +// 1 if v1 is considered larger than v2 +// 0 otherwise +int +compareVectors(const vector<uint8_t>& v1, const vector<uint8_t>& v2, + bool check_length_first = true) +{ + const size_t len1 = v1.size(); + const size_t len2 = v2.size(); + if (check_length_first && len1 != len2) { + return (len1 - len2); + } + const size_t cmplen = min(len1, len2); + const int cmp = cmplen == 0 ? 0 : memcmp(&v1.at(0), &v2.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (len1 - len2); + } +} +} + +int +NSEC3::compare(const Rdata& other) const { + const NSEC3& other_nsec3 = dynamic_cast<const NSEC3&>(other); + + if (impl_->hashalg_ != other_nsec3.impl_->hashalg_) { + return (impl_->hashalg_ < other_nsec3.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_nsec3.impl_->flags_) { + return (impl_->flags_ < other_nsec3.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_nsec3.impl_->iterations_) { + return (impl_->iterations_ < other_nsec3.impl_->iterations_ ? -1 : 1); + } + + int cmp = compareVectors(impl_->salt_, other_nsec3.impl_->salt_); + if (cmp != 0) { + return (cmp); + } + cmp = compareVectors(impl_->next_, other_nsec3.impl_->next_); + if (cmp != 0) { + return (cmp); + } + // Note that bitmap doesn't have a dedicated length field, so we shouldn't + // terminate the comparison just because the lengths are different. + return (compareVectors(impl_->typebits_, other_nsec3.impl_->typebits_, + false)); +} + +uint8_t +NSEC3::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3::getSalt() const { + return (impl_->salt_); +} + +const vector<uint8_t>& +NSEC3::getNext() const { + return (impl_->next_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/nsec3_50.h b/src/lib/dns/rdata/generic/nsec3_50.h new file mode 100644 index 0000000..cf73624 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec3_50.h @@ -0,0 +1,54 @@ +// 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 <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct NSEC3Impl; + +class NSEC3 : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + NSEC3& operator=(const NSEC3& source); + ~NSEC3(); + + uint8_t getHashalg() const; + uint8_t getFlags() const; + uint16_t getIterations() const; + const std::vector<uint8_t>& getSalt() const; + const std::vector<uint8_t>& getNext() const; + +private: + NSEC3Impl* constructFromLexer(isc::dns::MasterLexer& lexer); + + NSEC3Impl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc new file mode 100644 index 0000000..2d28a69 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec3param_51.cc @@ -0,0 +1,232 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <boost/lexical_cast.hpp> + +#include <memory> +#include <string> +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct NSEC3PARAMImpl { + // straightforward representation of NSEC3PARAM RDATA fields + NSEC3PARAMImpl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + const vector<uint8_t>& salt) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), salt_(salt) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3PARAM 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. +/// +/// The Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3param_str A string containing the RDATA to be created +NSEC3PARAM::NSEC3PARAM(const std::string& nsec3param_str) : + 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 NSEC3PARAMImpl that constructFromLexer() returns. + std::unique_ptr<NSEC3PARAMImpl> impl_ptr; + + try { + std::istringstream ss(nsec3param_str); + 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 NSEC3PARAM: " << nsec3param_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3PARAM from '" << nsec3param_str + << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3PARAM RDATA. +/// +/// See \c NSEC3PARAM::NSEC3PARAM(const std::string&) for description of +/// the expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3PARAM::NSEC3PARAM(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3PARAMImpl* +NSEC3PARAM::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3PARAM", lexer, salt); + + return (new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt)); +} + +NSEC3PARAM::NSEC3PARAM(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3PARAM", buffer, rdata_len, salt); + + impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt); +} + +NSEC3PARAM::NSEC3PARAM(const NSEC3PARAM& source) : + Rdata(), impl_(new NSEC3PARAMImpl(*source.impl_)) +{} + +NSEC3PARAM& +NSEC3PARAM::operator=(const NSEC3PARAM& source) { + if (this == &source) { + return (*this); + } + + NSEC3PARAMImpl* newimpl = new NSEC3PARAMImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3PARAM::~NSEC3PARAM() { + delete impl_; +} + +string +NSEC3PARAM::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_))); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3PARAMImpl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } +} + +void +NSEC3PARAM::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +int +NSEC3PARAM::compare(const Rdata& other) const { + const NSEC3PARAM& other_param = dynamic_cast<const NSEC3PARAM&>(other); + + if (impl_->hashalg_ != other_param.impl_->hashalg_) { + return (impl_->hashalg_ < other_param.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_param.impl_->flags_) { + return (impl_->flags_ < other_param.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_param.impl_->iterations_) { + return (impl_->iterations_ < other_param.impl_->iterations_ ? -1 : 1); + } + + const size_t this_len = impl_->salt_.size(); + const size_t other_len = other_param.impl_->salt_.size(); + if (this_len != other_len) { + return (this_len - other_len); + } + const size_t cmplen = min(this_len, other_len); + const int cmp = (cmplen == 0) ? 0 : + memcmp(&impl_->salt_.at(0), &other_param.impl_->salt_.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (this_len - other_len); + } +} + +uint8_t +NSEC3PARAM::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3PARAM::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3PARAM::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3PARAM::getSalt() const { + return (impl_->salt_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/nsec3param_51.h b/src/lib/dns/rdata/generic/nsec3param_51.h new file mode 100644 index 0000000..1c7bf03 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec3param_51.h @@ -0,0 +1,56 @@ +// 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 <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct NSEC3PARAMImpl; + +class NSEC3PARAM : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + NSEC3PARAM& operator=(const NSEC3PARAM& source); + ~NSEC3PARAM(); + + /// + /// Specialized methods + /// + uint8_t getHashalg() const; + uint8_t getFlags() const; + uint16_t getIterations() const; + const std::vector<uint8_t>& getSalt() const; + +private: + NSEC3PARAMImpl* constructFromLexer(isc::dns::MasterLexer& lexer); + + NSEC3PARAMImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc new file mode 100644 index 0000000..f8af0e0 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec_47.cc @@ -0,0 +1,216 @@ +// 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 <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail::nsec; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct NSECImpl { + // straightforward representation of NSEC RDATA fields + NSECImpl(const Name& next, vector<uint8_t> typebits) : + nextname_(next), typebits_(typebits) + {} + + Name nextname_; + vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC 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. +/// +/// The Next Domain Name field must be absolute since there's no +/// parameter that specifies the origin name; if it is not absolute, +/// \c MissingNameOrigin exception will be thrown. This must not be +/// represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not absolute. +/// \throw InvalidRdataText if any fields are out of their valid range. +/// +/// \param nsec_str A string containing the RDATA to be created +NSEC::NSEC(const std::string& nsec_str) : + impl_(NULL) +{ + try { + std::istringstream ss(nsec_str); + MasterLexer lexer; + lexer.pushSource(ss); + + const Name origin_name(createNameFromLexer(lexer, NULL)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(origin_name, typebits); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for NSEC: " << nsec_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC from '" << nsec_str << "': " + << ex.what()); + } +} + +NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) { + const size_t pos = buffer.getPosition(); + const Name nextname(buffer); + + // rdata_len must be sufficiently large to hold non empty bitmap. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(DNSMessageFORMERR, + "NSEC RDATA from wire too short: " << rdata_len << "bytes"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> typebits(rdata_len); + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC", typebits); + + impl_ = new NSECImpl(nextname, typebits); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC RDATA. +/// +/// The Next Domain Name field can be non-absolute if \c origin is +/// non-NULL, in which case \c origin is used to make it absolute. It +/// must not be represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not +/// absolute and \c origin is NULL. +/// \throw InvalidRdataText if any fields are out of their valid range. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin The origin to use with a relative Next Domain Name +/// field +NSEC::NSEC(MasterLexer& lexer, const Name* origin, MasterLoader::Options, + MasterLoaderCallbacks&) +{ + const Name next_name(createNameFromLexer(lexer, origin)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(next_name, typebits); +} + +NSEC::NSEC(const NSEC& source) : + Rdata(), impl_(new NSECImpl(*source.impl_)) +{} + +NSEC& +NSEC::operator=(const NSEC& source) { + if (this == &source) { + return (*this); + } + + NSECImpl* newimpl = new NSECImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC::~NSEC() { + delete impl_; +} + +string +NSEC::toText() const { + ostringstream s; + s << impl_->nextname_; + bitmapsToText(impl_->typebits_, s); + return (s.str()); +} + +void +NSEC::toWire(OutputBuffer& buffer) const { + impl_->nextname_.toWire(buffer); + buffer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +void +NSEC::toWire(AbstractMessageRenderer& renderer) const { + // Type NSEC is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->nextname_, false); + renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +const Name& +NSEC::getNextName() const { + return (impl_->nextname_); +} + +int +NSEC::compare(const Rdata& other) const { + const NSEC& other_nsec = dynamic_cast<const NSEC&>(other); + + int cmp = compareNames(impl_->nextname_, other_nsec.impl_->nextname_); + if (cmp != 0) { + return (cmp); + } + + const size_t this_len = impl_->typebits_.size(); + const size_t other_len = other_nsec.impl_->typebits_.size(); + const size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->typebits_[0], &other_nsec.impl_->typebits_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/nsec_47.h b/src/lib/dns/rdata/generic/nsec_47.h new file mode 100644 index 0000000..299d381 --- /dev/null +++ b/src/lib/dns/rdata/generic/nsec_47.h @@ -0,0 +1,53 @@ +// 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 <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct NSECImpl; + +class NSEC : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + NSEC& operator=(const NSEC& source); + ~NSEC(); + + // specialized methods + + /// Return the next domain name. + /// + /// \exception std::bad_alloc Resource allocation failure in name copy. + /// + /// \return The next domain name field in the form of \c Name object. + const Name& getNextName() const; + +private: + NSECImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc new file mode 100644 index 0000000..40cb1c7 --- /dev/null +++ b/src/lib/dns/rdata/generic/opt_41.cc @@ -0,0 +1,219 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/foreach.hpp> + +#include <string> +#include <string.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor. +OPT::PseudoRR::PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data) : + code_(code), + data_(data) +{ +} + +uint16_t +OPT::PseudoRR::getCode() const { + return (code_); +} + +const uint8_t* +OPT::PseudoRR::getData() const { + return (&(*data_)[0]); +} + +uint16_t +OPT::PseudoRR::getLength() const { + return (data_->size()); +} + +struct OPTImpl { + OPTImpl() : + rdlength_(0) + {} + + uint16_t rdlength_; + std::vector<OPT::PseudoRR> pseudo_rrs_; +}; + +/// \brief Default constructor. +OPT::OPT() : + impl_(new OPTImpl) +{ +} + +/// \brief Constructor from string. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(const std::string&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +OPT::OPT(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + std::unique_ptr<OPTImpl> impl_ptr(new OPTImpl); + + while (true) { + if (rdata_len == 0) { + break; + } + + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, + "Pseudo OPT RR record too short: " + << rdata_len << " bytes"); + } + + const uint16_t option_code = buffer.readUint16(); + const uint16_t option_length = buffer.readUint16(); + rdata_len -= 4; + + if (static_cast<uint16_t>(impl_ptr->rdlength_ + option_length) < + impl_ptr->rdlength_) + { + isc_throw(InvalidRdataText, + "Option length " << option_length + << " would overflow OPT RR RDLEN (currently " + << impl_ptr->rdlength_ << ")."); + } + + if (rdata_len < option_length) { + isc_throw(InvalidRdataLength, "Corrupt pseudo OPT RR record"); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(option_length)); + buffer.readData(&(*option_data)[0], option_length); + impl_ptr->pseudo_rrs_.push_back(PseudoRR(option_code, option_data)); + impl_ptr->rdlength_ += option_length; + rdata_len -= option_length; + } + + impl_ = impl_ptr.release(); +} + +OPT::OPT(const OPT& other) : + Rdata(), impl_(new OPTImpl(*other.impl_)) +{ +} + +OPT& +OPT::operator=(const OPT& source) { + if (this == &source) { + return (*this); + } + + OPTImpl* newimpl = new OPTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +OPT::~OPT() { + delete impl_; +} + +std::string +OPT::toText() const { + isc_throw(isc::InvalidOperation, + "OPT RRs do not have a presentation format"); +} + +void +OPT::toWire(OutputBuffer& buffer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + buffer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + buffer.writeUint16(length); + if (length > 0) { + buffer.writeData(pseudo_rr.getData(), length); + } + } +} + +void +OPT::toWire(AbstractMessageRenderer& renderer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + renderer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + renderer.writeUint16(length); + if (length > 0) { + renderer.writeData(pseudo_rr.getData(), length); + } + } +} + +int +OPT::compare(const Rdata&) const { + isc_throw(isc::InvalidOperation, + "It is meaningless to compare a set of OPT pseudo RRs; " + "they have unspecified order"); + return (0); +} + +void +OPT::appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length) { + // See if it overflows 16-bit length field. We only worry about the + // pseudo-RR length here, not the whole message length (which should + // be checked and enforced elsewhere). + if (static_cast<uint16_t>(impl_->rdlength_ + length) < + impl_->rdlength_) + { + isc_throw(isc::InvalidParameter, + "Option length " << length + << " would overflow OPT RR RDLEN (currently " + << impl_->rdlength_ << ")."); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(length)); + if (length != 0) { + std::memcpy(&(*option_data)[0], data, length); + } + impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data)); + impl_->rdlength_ += length; +} + +const std::vector<OPT::PseudoRR>& +OPT::getPseudoRRs() const { + return (impl_->pseudo_rrs_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/opt_41.h b/src/lib/dns/rdata/generic/opt_41.h new file mode 100644 index 0000000..0c00aed --- /dev/null +++ b/src/lib/dns/rdata/generic/opt_41.h @@ -0,0 +1,90 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/rdata.h> + +#include <boost/shared_ptr.hpp> + +#include <vector> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct OPTImpl; + +class OPT : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + // The default constructor makes sense for OPT as it can be empty. + OPT(); + OPT& operator=(const OPT& source); + ~OPT(); + + /// \brief A class representing a pseudo RR (or option) within an + /// OPT RR (see RFC 6891). + class PseudoRR { + public: + /// \brief Constructor. + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo + /// RR. OPTION-LENGTH is set to the length of this vector. + PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data); + + /// \brief Return the option code of this pseudo RR. + uint16_t getCode() const; + + /// \brief Return the option data of this pseudo RR. + const uint8_t* getData() const; + + /// \brief Return the length of the option data of this + /// pseudo RR. + uint16_t getLength() const; + + private: + uint16_t code_; + boost::shared_ptr<std::vector<uint8_t> > data_; + }; + + /// \brief Append a pseudo RR (option) in this OPT RR. + /// + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo RR. + /// \param length The size of the \c data argument. OPTION-LENGTH is + /// set to this size. + /// \throw isc::InvalidParameter if this pseudo RR would cause + /// the OPT RDATA to overflow its RDLENGTH. + void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length); + + /// \brief Return a vector of the pseudo RRs (options) in this + /// OPT RR. + /// + /// Note: The returned reference is only valid during the lifetime + /// of this \c generic::OPT object. It should not be used + /// afterwards. + const std::vector<PseudoRR>& getPseudoRRs() const; + +private: + OPTImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/ptr_12.cc b/src/lib/dns/rdata/generic/ptr_12.cc new file mode 100644 index 0000000..b3596e8 --- /dev/null +++ b/src/lib/dns/rdata/generic/ptr_12.cc @@ -0,0 +1,123 @@ +// 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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// The given string must represent a valid PTR 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. +/// +/// The PTRDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +PTR::PTR(const std::string& type_str) : + // Fill in dummy name and replace them soon below. + ptr_name_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(type_str); + MasterLexer lexer; + lexer.pushSource(ss); + + ptr_name_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for PTR: " + << type_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct PTR from '" << + type_str << "': " << ex.what()); + } +} + +PTR::PTR(InputBuffer& buffer, size_t) : + ptr_name_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a PTR RDATA. The PTRDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 PTRDNAME when it +/// is non-absolute. +PTR::PTR(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + ptr_name_(createNameFromLexer(lexer, origin)) +{} + +PTR::PTR(const PTR& source) : + Rdata(), ptr_name_(source.ptr_name_) +{} + +std::string +PTR::toText() const { + return (ptr_name_.toText()); +} + +void +PTR::toWire(OutputBuffer& buffer) const { + ptr_name_.toWire(buffer); +} + +void +PTR::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(ptr_name_); +} + +int +PTR::compare(const Rdata& other) const { + // The compare method normally begins with this dynamic cast. + const PTR& other_ptr = dynamic_cast<const PTR&>(other); + + return (compareNames(ptr_name_, other_ptr.ptr_name_)); + +} + +const Name& +PTR::getPTRName() const { + return (ptr_name_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ptr_12.h b/src/lib/dns/rdata/generic/ptr_12.h new file mode 100644 index 0000000..e562ef7 --- /dev/null +++ b/src/lib/dns/rdata/generic/ptr_12.h @@ -0,0 +1,44 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class PTR : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// + /// Specialized constructor + /// + explicit PTR(const Name& ptr_name) : ptr_name_(ptr_name) {} + /// + /// Specialized methods + /// + const Name& getPTRName() const; +private: + Name ptr_name_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/rp_17.cc b/src/lib/dns/rdata/generic/rp_17.cc new file mode 100644 index 0000000..43bf647 --- /dev/null +++ b/src/lib/dns/rdata/generic/rp_17.cc @@ -0,0 +1,160 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief Constructor from string. +/// +/// \c rp_str must be formatted as follows: +/// \code <mailbox name> <text name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// given name is invalid. +RP::RP(const std::string& rp_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(rp_str); + MasterLexer lexer; + lexer.pushSource(ss); + + mailbox_ = createNameFromLexer(lexer, NULL); + text_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RP: " + << rp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RP from '" << + rp_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RP RDATA. The MAILBOX and TEXT fields can be non-absolute if \c +/// origin is non-NULL, in which case \c origin is used to make them absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +RP::RP(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mailbox_(createNameFromLexer(lexer, origin)), + text_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) { +} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +RP::RP(const RP& other) : + Rdata(), mailbox_(other.mailbox_), text_(other.text_) +{} + +/// \brief Convert the \c RP to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c RP(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c RP object. +std::string +RP::toText() const { + return (mailbox_.toText() + " " + text_.toText()); +} + +/// \brief Render the \c RP in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +RP::toWire(OutputBuffer& buffer) const { + mailbox_.toWire(buffer); + text_.toWire(buffer); +} + +/// \brief Render the \c RP in the wire format with taking into account +/// compression. +/// +// Type RP is not "well-known", and name compression must be disabled +// per RFC3597. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +RP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mailbox_, false); + renderer.writeName(text_, false); +} + +/// \brief Compare two instances of \c RP RDATA. +/// +/// See documentation in \c Rdata. +int +RP::compare(const Rdata& other) const { + const RP& other_rp = dynamic_cast<const RP&>(other); + + const int cmp = compareNames(mailbox_, other_rp.mailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(text_, other_rp.text_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/rp_17.h b/src/lib/dns/rdata/generic/rp_17.h new file mode 100644 index 0000000..9536ee9 --- /dev/null +++ b/src/lib/dns/rdata/generic/rp_17.h @@ -0,0 +1,78 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::generic::RP class represents the RP RDATA as defined in +/// RFC1183. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// RP RDATA. +class RP : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// We use the default copy constructor and assignment operator. + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %RP RDATA + /// fields as defined in RFC1183. + RP(const Name& mailbox, const Name& text) : + mailbox_(mailbox), text_(text) + {} + + /// \brief Return the value of the mailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + /// + /// \note + /// Unlike the case of some other RDATA classes (such as + /// \c NS::getNSName()), this method constructs a new \c Name object + /// and returns it, instead of returning a reference to a \c Name object + /// internally maintained in the class (which is a private member). + /// This is based on the observation that this method will be rarely used + /// and even when it's used it will not be in a performance context + /// (for example, a recursive resolver won't need this field in its + /// resolution process). By returning a new object we have flexibility of + /// changing the internal representation without the risk of changing + /// the interface or method property. + /// The same note applies to the \c getText() method. + Name getMailbox() const { return (mailbox_); } + + /// \brief Return the value of the text field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + Name getText() const { return (text_); } + +private: + Name mailbox_; + Name text_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc new file mode 100644 index 0000000..de92c67 --- /dev/null +++ b/src/lib/dns/rdata/generic/rrsig_46.cc @@ -0,0 +1,334 @@ +// 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 <config.h> + +#include <string> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +namespace { +// This is the minimum necessary length of all wire-format RRSIG RDATA: +// - two 8-bit fields (algorithm and labels) +// - two 16-bit fields (covered and tag) +// - three 32-bit fields (original TTL, expire and inception) +const size_t RRSIG_MINIMUM_LEN = 2 * sizeof(uint8_t) + 2 * sizeof(uint16_t) + + 3 * sizeof(uint32_t); +} + +struct RRSIGImpl { + // straightforward representation of RRSIG RDATA fields + RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels, + uint32_t originalttl, uint32_t timeexpire, + uint32_t timeinception, uint16_t tag, const Name& signer, + const vector<uint8_t>& signature) : + covered_(covered), algorithm_(algorithm), labels_(labels), + originalttl_(originalttl), timeexpire_(timeexpire), + timeinception_(timeinception), tag_(tag), signer_(signer), + signature_(signature) + {} + + const RRType covered_; + uint8_t algorithm_; + uint8_t labels_; + uint32_t originalttl_; + uint32_t timeexpire_; + uint32_t timeinception_; + uint16_t tag_; + const Name signer_; + const vector<uint8_t> signature_; +}; + +// helper function for string and lexer constructors +RRSIGImpl* +RRSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const RRType covered(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, "RRSIG algorithm out of range"); + } + const uint32_t labels = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (labels > 0xff) { + isc_throw(InvalidRdataText, "RRSIG labels out of range"); + } + const uint32_t originalttl = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + const uint32_t timeexpire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t timeinception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t tag = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (tag > 0xffff) { + isc_throw(InvalidRdataText, "RRSIG key tag out of range"); + } + const Name& signer = createNameFromLexer(lexer, origin); + + string signature_txt; + string signature_part; + // Whitespace is allowed within base64 text, 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)) { + break; + } + token.getString(signature_part); + signature_txt.append(signature_part); + } + lexer.ungetToken(); + + vector<uint8_t> signature; + // missing signature is okay + if (signature_txt.size() > 0) { + decodeBase64(signature_txt, signature); + } + + return (new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, + static_cast<uint16_t>(tag), signer, signature)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid RRSIG 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. +/// +/// The Signer's Name must be absolute since there's no parameter that +/// specifies the origin name; if this is not absolute, \c MissingNameOrigin +/// exception will be thrown. This must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name constructor. +/// \throw InvalidRdataText Other general syntax errors. +RRSIG::RRSIG(const std::string& rrsig_str) : + 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 RRSIGImpl that constructFromLexer() returns. + std::unique_ptr<RRSIGImpl> impl_ptr; + + try { + std::istringstream iss(rrsig_str); + MasterLexer lexer; + lexer.pushSource(iss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RRSIG: " + << rrsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RRSIG from '" << + rrsig_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RRSIG RDATA. The Signer's Name fields can be non absolute if \c +/// origin is non NULL, in which case \c origin is used to make it absolute. +/// This must not be represented as a quoted string. +/// +/// The Original TTL field is a valid decimal representation of an unsigned +/// 32-bit integer. Note that alternate textual representations of \c RRTTL, +/// such as "1H" for 3600 seconds, are not allowed here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name constructor if +/// construction of textual fields as these objects fail. +/// +/// \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 Signer's Name when +/// it is non absolute. +RRSIG::RRSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +RRSIG::RRSIG(InputBuffer& buffer, size_t rdata_len) { + size_t pos = buffer.getPosition(); + + if (rdata_len < RRSIG_MINIMUM_LEN) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + + RRType covered(buffer); + uint8_t algorithm = buffer.readUint8(); + uint8_t labels = buffer.readUint8(); + uint32_t originalttl = buffer.readUint32(); + uint32_t timeexpire = buffer.readUint32(); + uint32_t timeinception = buffer.readUint32(); + uint16_t tag = buffer.readUint16(); + Name signer(buffer); + + // rdata_len must be sufficiently large to hold non empty signature data. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> signature(rdata_len); + buffer.readData(&signature[0], rdata_len); + + impl_ = new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, tag, + signer, signature); +} + +RRSIG::RRSIG(const RRSIG& source) : + Rdata(), impl_(new RRSIGImpl(*source.impl_)) +{} + +RRSIG& +RRSIG::operator=(const RRSIG& source) { + if (this == &source) { + return (*this); + } + + RRSIGImpl* newimpl = new RRSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +RRSIG::~RRSIG() { + delete impl_; +} + +string +RRSIG::toText() const { + return (impl_->covered_.toText() + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_)) + + " " + boost::lexical_cast<string>(impl_->originalttl_) + + " " + timeToText32(impl_->timeexpire_) + + " " + timeToText32(impl_->timeinception_) + + " " + boost::lexical_cast<string>(impl_->tag_) + + " " + impl_->signer_.toText() + + " " + encodeBase64(impl_->signature_)); +} + +void +RRSIG::toWire(OutputBuffer& buffer) const { + impl_->covered_.toWire(buffer); + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->labels_); + buffer.writeUint32(impl_->originalttl_); + buffer.writeUint32(impl_->timeexpire_); + buffer.writeUint32(impl_->timeinception_); + buffer.writeUint16(impl_->tag_); + impl_->signer_.toWire(buffer); + buffer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +void +RRSIG::toWire(AbstractMessageRenderer& renderer) const { + impl_->covered_.toWire(renderer); + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->labels_); + renderer.writeUint32(impl_->originalttl_); + renderer.writeUint32(impl_->timeexpire_); + renderer.writeUint32(impl_->timeinception_); + renderer.writeUint16(impl_->tag_); + renderer.writeName(impl_->signer_, false); + renderer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +int +RRSIG::compare(const Rdata& other) const { + const RRSIG& other_rrsig = dynamic_cast<const RRSIG&>(other); + + if (impl_->covered_.getCode() != other_rrsig.impl_->covered_.getCode()) { + return (impl_->covered_.getCode() < + other_rrsig.impl_->covered_.getCode() ? -1 : 1); + } + if (impl_->algorithm_ != other_rrsig.impl_->algorithm_) { + return (impl_->algorithm_ < other_rrsig.impl_->algorithm_ ? -1 : 1); + } + if (impl_->labels_ != other_rrsig.impl_->labels_) { + return (impl_->labels_ < other_rrsig.impl_->labels_ ? -1 : 1); + } + if (impl_->originalttl_ != other_rrsig.impl_->originalttl_) { + return (impl_->originalttl_ < other_rrsig.impl_->originalttl_ ? + -1 : 1); + } + if (impl_->timeexpire_ != other_rrsig.impl_->timeexpire_) { + return (impl_->timeexpire_ < other_rrsig.impl_->timeexpire_ ? + -1 : 1); + } + if (impl_->timeinception_ != other_rrsig.impl_->timeinception_) { + return (impl_->timeinception_ < other_rrsig.impl_->timeinception_ ? + -1 : 1); + } + if (impl_->tag_ != other_rrsig.impl_->tag_) { + return (impl_->tag_ < other_rrsig.impl_->tag_ ? -1 : 1); + } + + int cmp = compareNames(impl_->signer_, other_rrsig.impl_->signer_); + if (cmp != 0) { + return (cmp); + } + + size_t this_len = impl_->signature_.size(); + size_t other_len = other_rrsig.impl_->signature_.size(); + size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->signature_[0], &other_rrsig.impl_->signature_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +const RRType& +RRSIG::typeCovered() const { + return (impl_->covered_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/rrsig_46.h b/src/lib/dns/rdata/generic/rrsig_46.h new file mode 100644 index 0000000..aca26ba --- /dev/null +++ b/src/lib/dns/rdata/generic/rrsig_46.h @@ -0,0 +1,54 @@ +// 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 <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> + +// BEGIN_HEADER_GUARD + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct RRSIGImpl; + +/// \brief \c rdata::RRSIG class represents the RRSIG RDATA as defined %in +/// RFC4034. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// RRSIG RDATA. +class RRSIG : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + RRSIG& operator=(const RRSIG& source); + ~RRSIG(); + + // specialized methods + const RRType& typeCovered() const; +private: + // helper function for string and lexer constructors + RRSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + RRSIGImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc new file mode 100644 index 0000000..ee4e43d --- /dev/null +++ b/src/lib/dns/rdata/generic/soa_6.cc @@ -0,0 +1,210 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <boost/static_assert.hpp> +#include <boost/lexical_cast.hpp> + +#include <string> +#include <sstream> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +SOA::SOA(InputBuffer& buffer, size_t) : + mname_(buffer), rname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. + buffer.readData(numdata_, sizeof(numdata_)); +} + +namespace { +void +fillParameters(MasterLexer& lexer, uint8_t numdata[20]) { + // Copy serial, refresh, retry, expire, minimum. We accept the extended + // TTL-compatible style for the latter four. + OutputBuffer buffer(20); + buffer.writeUint32(lexer.getNextToken(MasterToken::NUMBER).getNumber()); + for (int i = 0; i < 4; ++i) { + buffer.writeUint32(RRTTL(lexer.getNextToken(MasterToken::STRING). + getString()).getValue()); + } + memcpy(numdata, buffer.getData(), buffer.getLength()); +} +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SOA 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. +/// +/// The MNAME and RNAME must be absolute since there's no parameter that +/// specifies the origin name; if these are not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SOA::SOA(const std::string& soastr) : + // Fill in dummy name and replace them soon below. + mname_(Name::ROOT_NAME()), rname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(soastr); + MasterLexer lexer; + lexer.pushSource(ss); + + mname_ = createNameFromLexer(lexer, NULL); + rname_ = createNameFromLexer(lexer, NULL); + fillParameters(lexer, numdata_); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SOA: " + << soastr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SOA from '" << + soastr << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SOA RDATA. The MNAME and RNAME fields can be non absolute if +/// \c origin is non NULL, in which case \c origin is used to make them +/// absolute. These must not be represented as a quoted string. +/// +/// The REFRESH, RETRY, EXPIRE, and MINIMUM fields can be either a valid +/// decimal representation of an unsigned 32-bit integer or other +/// valid textual representation of \c RRTTL such as "1H" (which means 3600). +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 MNAME and RNAME when +/// they are non absolute. +SOA::SOA(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mname_(createNameFromLexer(lexer, origin)), + rname_(createNameFromLexer(lexer, origin)) +{ + fillParameters(lexer, numdata_); +} + +SOA::SOA(const Name& mname, const Name& rname, uint32_t serial, + uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) : + mname_(mname), rname_(rname) +{ + OutputBuffer b(20); + b.writeUint32(serial); + b.writeUint32(refresh); + b.writeUint32(retry); + b.writeUint32(expire); + b.writeUint32(minimum); + assert(b.getLength() == sizeof(numdata_)); + memcpy(numdata_, b.getData(), sizeof(numdata_)); +} + +SOA::SOA(const SOA& other) : + Rdata(), mname_(other.mname_), rname_(other.rname_) +{ + memcpy(numdata_, other.numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(OutputBuffer& buffer) const { + mname_.toWire(buffer); + rname_.toWire(buffer); + buffer.writeData(numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mname_); + renderer.writeName(rname_); + renderer.writeData(numdata_, sizeof(numdata_)); +} + +Serial +SOA::getSerial() const { + InputBuffer b(numdata_, sizeof(numdata_)); + return (Serial(b.readUint32())); +} + +uint32_t +SOA::getMinimum() const { + // Make sure the buffer access is safe. + BOOST_STATIC_ASSERT(sizeof(numdata_) == + sizeof(uint32_t) * 4 + sizeof(uint32_t)); + + InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t)); + return (b.readUint32()); +} + +string +SOA::toText() const { + InputBuffer b(numdata_, sizeof(numdata_)); + uint32_t serial = b.readUint32(); + uint32_t refresh = b.readUint32(); + uint32_t retry = b.readUint32(); + uint32_t expire = b.readUint32(); + uint32_t minimum = b.readUint32(); + + return (mname_.toText() + " " + rname_.toText() + " " + + lexical_cast<string>(serial) + " " + + lexical_cast<string>(refresh) + " " + + lexical_cast<string>(retry) + " " + + lexical_cast<string>(expire) + " " + + lexical_cast<string>(minimum)); +} + +int +SOA::compare(const Rdata& other) const { + const SOA& other_soa = dynamic_cast<const SOA&>(other); + + int order = compareNames(mname_, other_soa.mname_); + if (order != 0) { + return (order); + } + + order = compareNames(rname_, other_soa.rname_); + if (order != 0) { + return (order); + } + + return (memcmp(numdata_, other_soa.numdata_, sizeof(numdata_))); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/soa_6.h b/src/lib/dns/rdata/generic/soa_6.h new file mode 100644 index 0000000..50a062e --- /dev/null +++ b/src/lib/dns/rdata/generic/soa_6.h @@ -0,0 +1,51 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/serial.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class SOA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + SOA(const Name& mname, const Name& rname, uint32_t serial, + uint32_t refresh, uint32_t retry, uint32_t expire, + uint32_t minimum); + + /// \brief Returns the serial stored in the SOA. + Serial getSerial() const; + + /// brief Returns the minimum TTL field value of the SOA. + uint32_t getMinimum() const; +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + Name mname_; + Name rname_; + /// serial, refresh, retry, expire, minimum, stored in network byte order + uint8_t numdata_[20]; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc new file mode 100644 index 0000000..f25585a --- /dev/null +++ b/src/lib/dns/rdata/generic/spf_99.cc @@ -0,0 +1,139 @@ +// 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 <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class. The semantics of the class is provided by +/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF. +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +/// \brief The assignment operator +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +SPF& +SPF::operator=(const SPF& source) { + if (this == &source) { + return (*this); + } + + SPFImpl* newimpl = new SPFImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief The destructor +SPF::~SPF() { + delete impl_; +} + +/// \brief Constructor from wire-format data. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(InputBuffer& buffer, size_t rdata_len) : + impl_(new SPFImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \throw CharStringTooLong the parameter string length exceeds maximum. +/// \throw InvalidRdataText the method cannot process the parameter data +/// +/// \param lexer A \c MasterLexer object parsing a master file for this +/// RDATA. +SPF::SPF(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new SPFImpl(lexer)) +{} + +/// \brief Constructor from string. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const std::string& txtstr) : + impl_(new SPFImpl(txtstr)) +{} + +/// \brief Copy constructor +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const SPF& other) : + Rdata(), impl_(new SPFImpl(*other.impl_)) +{} + +/// \brief Render the \c SPF in the wire format to a OutputBuffer object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c SPF in the wire format to an AbstractMessageRenderer +/// object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Convert the \c SPF to a string. +/// +/// \return is the return of the corresponding implementation method. +string +SPF::toText() const { + return (impl_->toText()); +} + +/// \brief Compare two instances of \c SPF RDATA. +/// +/// This method compares \c this and the \c other \c SPF objects. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c SPF object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// +/// \param other the right-hand operand to compare against. +/// \return is the return of the corresponding implementation method. +int +SPF::compare(const Rdata& other) const { + const SPF& other_txt = dynamic_cast<const SPF&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h new file mode 100644 index 0000000..3a84d9d --- /dev/null +++ b/src/lib/dns/rdata/generic/spf_99.h @@ -0,0 +1,72 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +namespace detail { +template<class Type, uint16_t typeCode> class TXTLikeImpl; +} + +/// \brief \c rdata::SPF class represents the SPF RDATA as defined %in +/// RFC4408. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class. The semantics of the class is provided by +/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF. +class SPF : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + SPF& operator=(const SPF& source); + + /// \brief The destructor. + ~SPF(); + + /// + /// Specialized methods + /// + + /// \brief Return a reference to the data strings + /// + /// This method never throws an exception. + const std::vector<std::vector<uint8_t> >& getString() const; + +private: + typedef isc::dns::rdata::generic::detail::TXTLikeImpl<SPF, 99> SPFImpl; + SPFImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc new file mode 100644 index 0000000..a08a17f --- /dev/null +++ b/src/lib/dns/rdata/generic/sshfp_44.cc @@ -0,0 +1,298 @@ +// Copyright (C) 2012-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 <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct SSHFPImpl { + // straightforward representation of SSHFP RDATA fields + SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type, + const vector<uint8_t>& fingerprint) : + algorithm_(algorithm), + fingerprint_type_(fingerprint_type), + fingerprint_(fingerprint) + {} + + uint8_t algorithm_; + uint8_t fingerprint_type_; + const vector<uint8_t> fingerprint_; +}; + +// helper function for string and lexer constructors +SSHFPImpl* +SSHFP::constructFromLexer(MasterLexer& lexer) { + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 255) { + isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); + } + + const uint32_t fingerprint_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (fingerprint_type > 255) { + isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); + } + + std::string fingerprint_str; + std::string fingerprint_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + token.getString(fingerprint_substr); + fingerprint_str.append(fingerprint_substr); + } + lexer.ungetToken(); + + vector<uint8_t> fingerprint; + // If fingerprint is missing, it's OK. See the API documentation of the + // constructor. + if (fingerprint_str.size() > 0) { + try { + decodeHex(fingerprint_str, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + } + + return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SSHFP 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. +/// +/// The Algorithm and Fingerprint Type fields must be within their valid +/// ranges, but are not constrained to the values defined in RFC4255. +/// +/// The Fingerprint field may be absent, but if present it must contain a +/// valid hex encoding of the fingerprint. For compatibility with BIND 9, +/// whitespace is allowed in the hex text (RFC4255 is silent on the matter). +/// +/// \throw InvalidRdataText if any fields are missing, are out of their +/// valid ranges or are incorrect, or if the fingerprint is not a valid +/// hex string. +/// +/// \param sshfp_str A string containing the RDATA to be created +SSHFP::SSHFP(const string& sshfp_str) : + 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 SSHFPImpl that constructFromLexer() returns. + std::unique_ptr<SSHFPImpl> impl_ptr; + + try { + std::istringstream ss(sshfp_str); + 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 SSHFP: " + << sshfp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" << + sshfp_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SSHFP RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range or are +/// incorrect, or if the fingerprint is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +SSHFP::SSHFP(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid SSHFP RDATA. +/// +/// The Algorithm and Fingerprint Type fields are not checked for unknown +/// values. It is okay for the fingerprint data to be missing (see the +/// description of the constructor from string). +SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "SSHFP record too short"); + } + + const uint8_t algorithm = buffer.readUint8(); + const uint8_t fingerprint_type = buffer.readUint8(); + + vector<uint8_t> fingerprint; + rdata_len -= 2; + if (rdata_len > 0) { + fingerprint.resize(rdata_len); + buffer.readData(&fingerprint[0], rdata_len); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, + const string& fingerprint_txt) : + impl_(NULL) +{ + vector<uint8_t> fingerprint; + try { + decodeHex(fingerprint_txt, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(const SSHFP& other) : + Rdata(), impl_(new SSHFPImpl(*other.impl_)) +{} + +SSHFP& +SSHFP::operator=(const SSHFP& source) { + if (this == &source) { + return (*this); + } + + SSHFPImpl* newimpl = new SSHFPImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SSHFP::~SSHFP() { + delete impl_; +} + +void +SSHFP::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + buffer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +void +SSHFP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + renderer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +string +SSHFP::toText() const { + return (lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->fingerprint_type_)) + + (impl_->fingerprint_.empty() ? "" : + " " + encodeHex(impl_->fingerprint_))); +} + +int +SSHFP::compare(const Rdata& other) const { + const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other); + + if (impl_->algorithm_ < other_sshfp.impl_->algorithm_) { + return (-1); + } else if (impl_->algorithm_ > other_sshfp.impl_->algorithm_) { + return (1); + } + + if (impl_->fingerprint_type_ < other_sshfp.impl_->fingerprint_type_) { + return (-1); + } else if (impl_->fingerprint_type_ > + other_sshfp.impl_->fingerprint_type_) { + return (1); + } + + const size_t this_len = impl_->fingerprint_.size(); + const size_t other_len = other_sshfp.impl_->fingerprint_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->fingerprint_[0], + &other_sshfp.impl_->fingerprint_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +SSHFP::getAlgorithmNumber() const { + return (impl_->algorithm_); +} + +uint8_t +SSHFP::getFingerprintType() const { + return (impl_->fingerprint_type_); +} + +const std::vector<uint8_t>& +SSHFP::getFingerprint() const { + return (impl_->fingerprint_); +} + +size_t +SSHFP::getFingerprintLength() const { + return (impl_->fingerprint_.size()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h new file mode 100644 index 0000000..4eae696 --- /dev/null +++ b/src/lib/dns/rdata/generic/sshfp_44.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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct SSHFPImpl; + +class SSHFP : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + SSHFP(uint8_t algorithm, uint8_t fingerprint_type, + const std::string& fingerprint); + SSHFP& operator=(const SSHFP& source); + ~SSHFP(); + + /// + /// Specialized methods + /// + uint8_t getAlgorithmNumber() const; + uint8_t getFingerprintType() const; + const std::vector<uint8_t>& getFingerprint() const; + size_t getFingerprintLength() const; + +private: + SSHFPImpl* constructFromLexer(MasterLexer& lexer); + + SSHFPImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/tkey_249.cc b/src/lib/dns/rdata/generic/tkey_249.cc new file mode 100644 index 0000000..435a345 --- /dev/null +++ b/src/lib/dns/rdata/generic/tkey_249.cc @@ -0,0 +1,613 @@ +// Copyright (C) 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 <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <util/time_utilities.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +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 + +const uint16_t TKEY::GSS_API_MODE = 3; + +// straightforward representation of TKEY RDATA fields +struct TKEYImpl { + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key The key (can be empty). + /// \param other_data The other data (can be and usually is empty). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, vector<uint8_t>& key, + vector<uint8_t>& other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), key_(key), other_data_(other_data) + {} + + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key_len The key length (0 means no key). + /// \param key The key (can be 0). + /// \param other_len The other data length (0 means no other data). + /// \param other_data The other data (can be and usually is 0). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, size_t key_len, + const void* key, size_t other_len, const void* other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), + key_(key_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(key), + static_cast<const uint8_t*>(key) + key_len) : + vector<uint8_t>(key_len)), + other_data_(other_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + + other_len) : + vector<uint8_t>(other_len)) + {} + + /// \brief Common part of toWire methods. + /// \tparam Output \c OutputBuffer or \c AbstractMessageRenderer. + template <typename Output> + void toWireCommon(Output& output) const; + + /// \brief The DNS name of the algorithm e.g. gss-tsig. + const Name algorithm_; + + /// \brief The inception time (in seconds since 1970). + const uint32_t inception_; + + /// \brief The expire time (in seconds since 1970). + const uint32_t expire_; + + /// \brief The mode e.g. Diffie-Hellman (2) or GSS-API (3). + const uint16_t mode_; + + /// \brief The error code (extended error space shared with TSIG). + const uint16_t error_; + + /// \brief The key (can be empty). + const vector<uint8_t> key_; + + /// \brief The other data (can be and usually is empty). + const vector<uint8_t> other_data_; +}; + +// helper function for string and lexer constructors +TKEYImpl* +TKEY::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const Name& algorithm = + createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); + + const uint32_t inception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + const uint32_t expire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + /// The mode is either a mnemonic (only one is defined: GSS-API) or + /// a number. + const string& mode_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t mode = 0; + if (mode_txt == "GSS-API") { + mode = GSS_API_MODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + mode = boost::lexical_cast<uint32_t>(mode_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Mode"); + } + if (mode > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Mode 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<uint32_t>(error_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Error"); + } + if (error > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Error out of range"); + } + } + + const uint32_t keylen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (keylen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Key Len out of range"); + } + const string keydata_txt = (keylen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> key_data; + decodeBase64(keydata_txt, key_data); + if (key_data.size() != keylen) { + isc_throw(InvalidRdataText, + "TKEY Key Data length does not match Other Len"); + } + + const uint32_t otherlen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (otherlen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Other Len out of range"); + } + const string otherdata_txt = (otherlen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> other_data; + decodeBase64(otherdata_txt, other_data); + if (other_data.size() != otherlen) { + isc_throw(InvalidRdataText, + "TKEY 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 TKEYImpl(algorithm, inception, expire, mode, error, + key_data, other_data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TKEY 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 tkey_str must be formatted as follows: +/// \code <Algorithm Name> <Inception> <Expire> <Mode> <Error> +/// <Key Len> [<Key Data>] <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Mode field is an unsigned 16-bit decimal integer as specified +/// in RFC2930 or a common mnemonic. Currently only "GSS-API" (case sensitive) +/// is supported ("Diffie-Hellman" is not). +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", +/// "BADTIME", "BADMODE", "BADNAME", and "BADALG" are supported +/// (case sensitive). In future versions other representations that +/// are compatible with the DNS RCODE may be supported. +/// +/// The Key Data and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the Key Len field is 0, the Key Data field must not appear in +/// \c tkey_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tkey_str. +/// The decoded data of the Key Data field is Key Len bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "gss-tsig. 20210501120000 20210501130000 0 3 aabbcc 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2930 does not define the standard presentation format +/// of %TKEY RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if Key Data or Other Data is not validly encoded +/// in base-64. +/// +/// \param tkey_str A string containing the RDATA to be created +TKEY::TKEY(const std::string& tkey_str) : impl_(0) { + // 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 TKEYImpl that constructFromLexer() returns. + std::unique_ptr<TKEYImpl> impl_ptr; + + try { + std::istringstream ss(tkey_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, 0)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TKEY: " << tkey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TKEY from '" << tkey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TKEY RDATA. +/// +/// See \c TKEY::TKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TKEY::TKEY(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TKEY RR even if that +/// field is compressed. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TKEY::TKEY(InputBuffer& buffer, size_t) : + impl_(0) +{ + Name algorithm(buffer); + + const uint32_t inception = buffer.readUint32(); + + const uint32_t expire = buffer.readUint32(); + + const uint16_t mode = buffer.readUint16(); + + const uint16_t error = buffer.readUint16(); + + const uint16_t key_len = buffer.readUint16(); + vector<uint8_t> key(key_len); + if (key_len > 0) { + buffer.readData(&key[0], key_len); + } + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key, other_data); +} + +TKEY::TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, uint16_t key_len, + const void* key, uint16_t other_len, const void* other_data) : + impl_(0) +{ + if ((key_len == 0 && key != 0) || (key_len > 0 && key == 0)) { + isc_throw(InvalidParameter, "TKEY Key length and data inconsistent"); + } + if ((other_len == 0 && other_data != 0) || + (other_len > 0 && other_data == 0)) { + isc_throw(InvalidParameter, + "TKEY Other data length and data inconsistent"); + } + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key_len, key, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TKEY::TKEY(const TKEY& source) : Rdata(), impl_(new TKEYImpl(*source.impl_)) +{} + +TKEY& +TKEY::operator=(const TKEY& source) { + if (this == &source) { + return (*this); + } + + TKEYImpl* newimpl = new TKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TKEY::~TKEY() { + delete impl_; +} + +/// \brief Convert the \c TKEY to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TKEY(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TKEY object. +std::string +TKEY::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + timeToText32(impl_->inception_) + " " + + timeToText32(impl_->expire_) + " "; + if (impl_->mode_ == GSS_API_MODE) { + result += "GSS-API "; + } else { + result += lexical_cast<string>(impl_->mode_) + " "; + } + result += TSIGError(impl_->error_).toText() + " " + + lexical_cast<string>(impl_->key_.size()) + " "; + if (!impl_->key_.empty()) { + result += encodeBase64(impl_->key_) + " "; + } + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TKEYImpl::toWireCommon(Output& output) const { + output.writeUint32(inception_); + output.writeUint32(expire_); + output.writeUint16(mode_); + output.writeUint16(error_); + const uint16_t key_len = key_.size(); + output.writeUint16(key_len); + if (key_len > 0) { + output.writeData(&key_[0], key_len); + } + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TKEY in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TKEY::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TKEY in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TKEY::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TKEY RDATA. +/// +/// This method compares \c this and the \c other \c TKEY objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TKEY object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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. +int +TKEY::compare(const Rdata& other) const { + const TKEY& other_tkey = dynamic_cast<const TKEY&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tkey.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->inception_ != other_tkey.impl_->inception_) { + return (impl_->inception_ < other_tkey.impl_->inception_ ? -1 : 1); + } + if (impl_->expire_ != other_tkey.impl_->expire_) { + return (impl_->expire_ < other_tkey.impl_->expire_ ? -1 : 1); + } + if (impl_->mode_ != other_tkey.impl_->mode_) { + return (impl_->mode_ < other_tkey.impl_->mode_ ? -1 : 1); + } + if (impl_->error_ != other_tkey.impl_->error_) { + return (impl_->error_ < other_tkey.impl_->error_ ? -1 : 1); + } + + const int vcmp = vectorComp(impl_->key_, other_tkey.impl_->key_); + if (vcmp != 0) { + return (vcmp); + } + return (vectorComp(impl_->other_data_, other_tkey.impl_->other_data_)); +} + +const Name& +TKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint32_t +TKEY::getInception() const { + return (impl_->inception_); +} + +string +TKEY::getInceptionDate() const { + return (timeToText32(impl_->inception_)); +} + +uint32_t +TKEY::getExpire() const { + return (impl_->expire_); +} + +string +TKEY::getExpireDate() const { + return (timeToText32(impl_->expire_)); +} + +uint16_t +TKEY::getMode() const { + return (impl_->mode_); +} + +uint16_t +TKEY::getError() const { + return (impl_->error_); +} + +uint16_t +TKEY::getKeyLen() const { + return (impl_->key_.size()); +} + +const void* +TKEY::getKey() const { + if (!impl_->key_.empty()) { + return (&impl_->key_[0]); + } else { + return (0); + } +} + +uint16_t +TKEY::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TKEY::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (0); + } +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/tkey_249.h b/src/lib/dns/rdata/generic/tkey_249.h new file mode 100644 index 0000000..d630121 --- /dev/null +++ b/src/lib/dns/rdata/generic/tkey_249.h @@ -0,0 +1,142 @@ +// Copyright (C) 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct TKEYImpl; + +/// \brief \c rdata::TKEY class represents the TKEY RDATA as defined %in +/// RFC2930. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// TKEY RDATA. +class TKEY : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %TKEY RDATA + /// fields as defined %in RFC2930. + /// + /// This RR is pretty close to the TSIG RR with 32 bit timestamps, + /// or the RRSIG RR with a second "other" data field. + /// + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key_len The key length (0 means no key). + /// \param key The key (can be 0). + /// \param other_len The other data length (0 means no other data). + /// \param other_data The other data (can be and usually is 0). + TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, uint16_t key_len, + const void* key, uint16_t other_len, const void* other_data); + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + TKEY& operator=(const TKEY& source); + + /// \brief The destructor. + ~TKEY(); + + /// \brief Return the algorithm name. + /// + /// This method never throws an exception. + const Name& getAlgorithm() const; + + /// \brief Return the value of the Inception field as a number. + /// + /// This method never throws an exception. + uint32_t getInception() const; + + /// \brief Return the value of the Inception field as a string. + std::string getInceptionDate() const; + + /// \brief Return the value of the Expire field as a number. + /// + /// This method never throws an exception. + uint32_t getExpire() const; + + /// \brief Return the value of the Expire field as a string. + std::string getExpireDate() const; + + /// \brief Return the value of the Mode field. + /// + /// This method never throws an exception. + uint16_t getMode() const; + + /// \brief Return the value of the Error field. + /// + /// This method never throws an exception. + uint16_t getError() const; + + /// \brief Return the value of the Key Len field. + /// + /// This method never throws an exception. + uint16_t getKeyLen() const; + + /// \brief Return the value of the Key field. + /// + /// This method never throws an exception. + const void* getKey() const; + + /// \brief Return the value of the Other Len field. + /// + /// This method never throws an exception. + uint16_t getOtherLen() const; + + /// \brief Return the value of the Other Data field. + /// + /// The same note as \c getMAC() applies. + /// + /// This method never throws an exception. + const void* getOtherData() const; + + /// \brief The GSS_API constant for the Mode field. + static const uint16_t GSS_API_MODE; + +private: + TKEYImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + TKEYImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/tlsa_52.cc b/src/lib/dns/rdata/generic/tlsa_52.cc new file mode 100644 index 0000000..330b7a2 --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.cc @@ -0,0 +1,342 @@ +// Copyright (C) 2014-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 <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata_pimpl_holder.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct TLSAImpl { + // straightforward representation of TLSA RDATA fields + TLSAImpl(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const vector<uint8_t>& data) : + certificate_usage_(certificate_usage), + selector_(selector), + matching_type_(matching_type), + data_(data) + {} + + uint8_t certificate_usage_; + uint8_t selector_; + uint8_t matching_type_; + const vector<uint8_t> data_; +}; + +// helper function for string and lexer constructors +TLSAImpl* +TLSA::constructFromLexer(MasterLexer& lexer) { + const uint32_t certificate_usage = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (certificate_usage > 255) { + isc_throw(InvalidRdataText, + "TLSA certificate usage field out of range"); + } + + const uint32_t selector = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (selector > 255) { + isc_throw(InvalidRdataText, + "TLSA selector field out of range"); + } + + const uint32_t matching_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (matching_type > 255) { + isc_throw(InvalidRdataText, + "TLSA matching type field out of range"); + } + + std::string certificate_assoc_data; + std::string data_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + token.getString(data_substr); + certificate_assoc_data.append(data_substr); + } + lexer.ungetToken(); + + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + return (new TLSAImpl(certificate_usage, selector, matching_type, data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TLSA 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. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. +/// +/// The Certificate Association Data Field field may be absent, but if +/// present it must contain a valid hex encoding of the data. Whitespace +/// is allowed in the hex text. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, or are incorrect, or Certificate Association Data is +/// not a valid hex string. +/// +/// \param tlsa_str A string containing the RDATA to be created +TLSA::TLSA(const string& tlsa_str) : + impl_(NULL) +{ + // We use a smart pointer here because if there is an exception in + // this constructor, the destructor is not called and there could be + // a leak of the TLSAImpl that constructFromLexer() returns. + RdataPimplHolder<TLSAImpl> impl_ptr; + + try { + std::istringstream ss(tlsa_str); + 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 TLSA: " + << tlsa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct TLSA from '" << + tlsa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an TLSA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range, or are +/// incorrect, or Certificate Association Data is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TLSA::TLSA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid TLSA RDATA. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. It is okay for the certificate association data +/// to be missing (see the description of the constructor from string). +TLSA::TLSA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 3) { + isc_throw(InvalidRdataLength, "TLSA record too short"); + } + + const uint8_t certificate_usage = buffer.readUint8(); + const uint8_t selector = buffer.readUint8(); + const uint8_t matching_type = buffer.readUint8(); + + vector<uint8_t> data; + rdata_len -= 3; + + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, + "Empty TLSA certificate association data"); + } + + data.resize(rdata_len); + buffer.readData(&data[0], rdata_len); + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data) : + impl_(NULL) +{ + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(const TLSA& other) : + Rdata(), impl_(new TLSAImpl(*other.impl_)) +{} + +TLSA& +TLSA::operator=(const TLSA& source) { + if (this == &source) { + return (*this); + } + + TLSAImpl* newimpl = new TLSAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TLSA::~TLSA() { + delete impl_; +} + +void +TLSA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->certificate_usage_); + buffer.writeUint8(impl_->selector_); + buffer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + buffer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +void +TLSA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->certificate_usage_); + renderer.writeUint8(impl_->selector_); + renderer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + renderer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +string +TLSA::toText() const { + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + + return (lexical_cast<string>(static_cast<int>(impl_->certificate_usage_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->selector_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->matching_type_)) + " " + + encodeHex(impl_->data_)); +} + +int +TLSA::compare(const Rdata& other) const { + const TLSA& other_tlsa = dynamic_cast<const TLSA&>(other); + + if (impl_->certificate_usage_ < other_tlsa.impl_->certificate_usage_) { + return (-1); + } else if (impl_->certificate_usage_ > + other_tlsa.impl_->certificate_usage_) { + return (1); + } + + if (impl_->selector_ < other_tlsa.impl_->selector_) { + return (-1); + } else if (impl_->selector_ > other_tlsa.impl_->selector_) { + return (1); + } + + if (impl_->matching_type_ < other_tlsa.impl_->matching_type_) { + return (-1); + } else if (impl_->matching_type_ > + other_tlsa.impl_->matching_type_) { + return (1); + } + + const size_t this_len = impl_->data_.size(); + const size_t other_len = other_tlsa.impl_->data_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->data_[0], + &other_tlsa.impl_->data_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +TLSA::getCertificateUsage() const { + return (impl_->certificate_usage_); +} + +uint8_t +TLSA::getSelector() const { + return (impl_->selector_); +} + +uint8_t +TLSA::getMatchingType() const { + return (impl_->matching_type_); +} + +const std::vector<uint8_t>& +TLSA::getData() const { + return (impl_->data_); +} + +size_t +TLSA::getDataLength() const { + return (impl_->data_.size()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/tlsa_52.h b/src/lib/dns/rdata/generic/tlsa_52.h new file mode 100644 index 0000000..007aa43 --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.h @@ -0,0 +1,57 @@ +// Copyright (C) 2014-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct TLSAImpl; + +class TLSA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data); + TLSA& operator=(const TLSA& source); + ~TLSA(); + + /// + /// Specialized methods + /// + uint8_t getCertificateUsage() const; + uint8_t getSelector() const; + uint8_t getMatchingType() const; + const std::vector<uint8_t>& getData() const; + size_t getDataLength() const; + +private: + TLSAImpl* constructFromLexer(MasterLexer& lexer); + + TLSAImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc new file mode 100644 index 0000000..52d6b64 --- /dev/null +++ b/src/lib/dns/rdata/generic/txt_16.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 <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +TXT& +TXT::operator=(const TXT& source) { + if (this == &source) { + return (*this); + } + + TXTImpl* newimpl = new TXTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TXT::~TXT() { + delete impl_; +} + +TXT::TXT(InputBuffer& buffer, size_t rdata_len) : + impl_(new TXTImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \throw CharStringTooLong the parameter string length exceeds maximum. +/// \throw InvalidRdataText the method cannot process the parameter data +/// +/// \param lexer A \c MasterLexer object parsing a master file for this +/// RDATA. +TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new TXTImpl(lexer)) +{} + +TXT::TXT(const std::string& txtstr) : + impl_(new TXTImpl(txtstr)) +{} + +TXT::TXT(const TXT& other) : + Rdata(), impl_(new TXTImpl(*other.impl_)) +{} + +void +TXT::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +TXT::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +TXT::toText() const { + return (impl_->toText()); +} + +int +TXT::compare(const Rdata& other) const { + const TXT& other_txt = dynamic_cast<const TXT&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h new file mode 100644 index 0000000..83979c4 --- /dev/null +++ b/src/lib/dns/rdata/generic/txt_16.h @@ -0,0 +1,46 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +namespace detail { +template<class Type, uint16_t typeCode> class TXTLikeImpl; +} + +class TXT : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + TXT& operator=(const TXT& source); + ~TXT(); + +private: + typedef isc::dns::rdata::generic::detail::TXTLikeImpl<TXT, 16> TXTImpl; + TXTImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/hs_4/a_1.cc b/src/lib/dns/rdata/hs_4/a_1.cc new file mode 100644 index 0000000..cd4c824 --- /dev/null +++ b/src/lib/dns/rdata/hs_4/a_1.cc @@ -0,0 +1,64 @@ +// 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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/hs_4/a_1.h b/src/lib/dns/rdata/hs_4/a_1.h new file mode 100644 index 0000000..6f319b9 --- /dev/null +++ b/src/lib/dns/rdata/hs_4/a_1.h @@ -0,0 +1,32 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/in_1/a_1.cc b/src/lib/dns/rdata/in_1/a_1.cc new file mode 100644 index 0000000..c6585b9 --- /dev/null +++ b/src/lib/dns/rdata/in_1/a_1.cc @@ -0,0 +1,174 @@ +// 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 <config.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/master_loader_callbacks.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +namespace { +void +convertToIPv4Addr(const char* src, size_t src_len, uint32_t* dst) { + // This check specifically rejects invalid input that begins with valid + // address text followed by a nul character (and possibly followed by + // further garbage). It cannot be detected by inet_pton(). + // + // Note that this is private subroutine of the in::A constructors, which + // pass std::string.size() or StringRegion::len as src_len, so it should + // be equal to strlen() unless there's an intermediate nul character. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/A RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/A RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/A RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv4 +/// address as specified in RFC1035, that is, four decimal numbers separated +/// by dots without any embedded spaces. Note that it excludes abbreviated +/// forms such as "10.1" to mean "10.0.0.1". +/// +/// Internally, this implementation uses the standard inet_pton() library +/// function for the AF_INET family to parse and convert the textual +/// representation. While standard compliant implementations of this function +/// should accept exactly what this constructor expects, specific +/// implementation may behave differently, in which case this constructor +/// will simply accept the result of inet_pton(). In any case, the user of +/// the class shouldn't assume such specific implementation behavior of +/// inet_pton(). +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv4 address to be used as the +/// RDATA. +A::A(const std::string& addrstr) { + convertToIPv4Addr(addrstr.c_str(), addrstr.size(), &addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN A RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version accepts beginning +/// spaces and trailing spaces or other characters. Trailing non space +/// characters would be considered an invalid form in an RR representation, +/// but handling such errors is not the responsibility of this constructor. +/// It also accepts other unusual syntax that would be considered valid +/// in the context of DNS master file; for example, it accepts an IPv4 +/// address surrounded by parentheses, such as "(192.0.2.1)", although it's +/// very unlikely to be used for this type of RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +A::A(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv4Addr(token.getStringRegion().beg, token.getStringRegion().len, + &addr_); +} + +A::A(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: Invalid length: " + << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +/// \brief Copy constructor. +A::A(const A& other) : Rdata(), addr_(other.addr_) +{} + +void +A::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +A::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv4 address of the RDATA. +string +A::toText() const { + char addr_string[sizeof("255.255.255.255")]; + + if (inet_ntop(AF_INET, &addr_, addr_string, sizeof(addr_string)) == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/A RDATA to textual IPv4 address"); + } + + return (addr_string); +} + +/// \brief Compare two in::A RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 32-bit integer. +int +A::compare(const Rdata& other) const { + const A& other_a = dynamic_cast<const A&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/a_1.h b/src/lib/dns/rdata/in_1/a_1.h new file mode 100644 index 0000000..9aaeea8 --- /dev/null +++ b/src/lib/dns/rdata/in_1/a_1.h @@ -0,0 +1,38 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + //We can use the default destructor. + //virtual ~A() {} + // notyet: + //const struct in_addr& getAddress() const { return (addr_); } +private: + uint32_t addr_; // raw IPv4 address (network byte order) +}; +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/in_1/aaaa_28.cc b/src/lib/dns/rdata/in_1/aaaa_28.cc new file mode 100644 index 0000000..c967935 --- /dev/null +++ b/src/lib/dns/rdata/in_1/aaaa_28.cc @@ -0,0 +1,153 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +namespace { +void +convertToIPv6Addr(const char* src, size_t src_len, void* dst) { + // See a_1.cc for this check. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/AAAA RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET6, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/AAAA RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv6 +/// address as specified in RFC1886. +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv6 address to be used as the +/// RDATA. +AAAA::AAAA(const std::string& addrstr) { + convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN AAAA RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version is slightly more +/// flexible. See the similar constructor of \c in::A class; the same +/// notes apply here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +AAAA::AAAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len, + addr_); +} + +/// \brief Copy constructor. +AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "Invalid length: " << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +AAAA::AAAA(const AAAA& other) : Rdata() { + memcpy(addr_, other.addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv6 address of the RDATA. +void +AAAA::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +AAAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +string +AAAA::toText() const { + char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + + if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string)) + == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/AAAA RDATA to textual IPv6 address"); + } + + return (string(addr_string)); +} + +/// \brief Compare two in::AAAA RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 128-bit integer. +int +AAAA::compare(const Rdata& other) const { + const AAAA& other_a = dynamic_cast<const AAAA&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/aaaa_28.h b/src/lib/dns/rdata/in_1/aaaa_28.h new file mode 100644 index 0000000..a5cabf4 --- /dev/null +++ b/src/lib/dns/rdata/in_1/aaaa_28.h @@ -0,0 +1,38 @@ +// 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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <string> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class AAAA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + // notyet: + //const struct in6_addr& getAddress() const { return (addr_); } +private: + uint8_t addr_[16]; // raw IPv6 address (network byte order) +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc new file mode 100644 index 0000000..57c79c2 --- /dev/null +++ b/src/lib/dns/rdata/in_1/dhcid_49.cc @@ -0,0 +1,161 @@ +// Copyright (C) 2011-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 <config.h> + +#include <stdint.h> +#include <string.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +void +DHCID::constructFromLexer(MasterLexer& lexer) { + string digest_txt = lexer.getNextToken(MasterToken::STRING).getString(); + + // Whitespace is allowed within base64 text, so read to the end of input. + string digest_part; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + token.getString(digest_part); + digest_txt.append(digest_part); + } + lexer.ungetToken(); + + decodeBase64(digest_txt, digest_); +} + +/// \brief Constructor from string. +/// +/// \param dhcid_str A base-64 representation of the DHCID binary data. +/// +/// \throw InvalidRdataText if the string could not be parsed correctly. +DHCID::DHCID(const std::string& dhcid_str) { + try { + std::istringstream iss(dhcid_str); + MasterLexer lexer; + lexer.pushSource(iss); + + constructFromLexer(lexer); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DHCID: " + << dhcid_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DHCID from '" << + dhcid_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a DHCID RDATA. +/// +/// \throw BadValue if the text is not valid base-64. +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DHCID::DHCID(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) { + constructFromLexer(lexer); +} + +/// \brief Constructor from wire-format data. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes +DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, "Missing DHCID rdata"); + } + + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); +} + +/// \brief The copy constructor. +/// +/// This trivial copy constructor never throws an exception. +DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) +{} + +/// \brief Render the \c DHCID in the wire format. +/// +/// \param buffer An output buffer to store the wire data. +void +DHCID::toWire(OutputBuffer& buffer) const { + buffer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Render the \c DHCID in the wire format into a +/// \c MessageRenderer object. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer in which the \c DHCID is to be stored. +void +DHCID::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Convert the \c DHCID to a string. +/// +/// This method returns a \c std::string object representing the \c DHCID. +/// +/// \return A string representation of \c DHCID. +string +DHCID::toText() const { + return (encodeBase64(digest_)); +} + +/// \brief Compare two instances of \c DHCID RDATA. +/// +/// See documentation in \c Rdata. +int +DHCID::compare(const Rdata& other) const { + const DHCID& other_dhcid = dynamic_cast<const DHCID&>(other); + + size_t this_len = digest_.size(); + size_t other_len = other_dhcid.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +/// \brief Accessor method to get the DHCID digest +/// +/// \return A reference to the binary DHCID data +const std::vector<uint8_t>& +DHCID::getDigest() const { + return (digest_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/dhcid_49.h b/src/lib/dns/rdata/in_1/dhcid_49.h new file mode 100644 index 0000000..7f79602 --- /dev/null +++ b/src/lib/dns/rdata/in_1/dhcid_49.h @@ -0,0 +1,53 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +/// \brief \c rdata::DHCID class represents the DHCID RDATA as defined %in +/// RFC4701. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DHCID RDATA. +class DHCID : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Return the digest. + /// + /// This method never throws an exception. + const std::vector<uint8_t>& getDigest() const; + +private: + // helper for string and lexer constructors + void constructFromLexer(MasterLexer& lexer); + + /// \brief Private data representation + /// + /// Opaque data at least 3 octets long as per RFC4701. + /// + std::vector<uint8_t> digest_; +}; +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/in_1/srv_33.cc b/src/lib/dns/rdata/in_1/srv_33.cc new file mode 100644 index 0000000..a8a050c --- /dev/null +++ b/src/lib/dns/rdata/in_1/srv_33.cc @@ -0,0 +1,298 @@ +// Copyright (C) 2011-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 <config.h> + +#include <iostream> +#include <sstream> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::str; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct SRVImpl { + // straightforward representation of SRV RDATA fields + SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, + const Name& target) : + priority_(priority), weight_(weight), port_(port), + target_(target) + {} + + uint16_t priority_; + uint16_t weight_; + uint16_t port_; + Name target_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SRV 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. +/// +/// The TARGET name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SRV::SRV(const std::string& srv_str) : + impl_(NULL) +{ + try { + std::istringstream ss(srv_str); + MasterLexer lexer; + lexer.pushSource(ss); + + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority in: " << srv_str); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight in: " << srv_str); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port in: " << srv_str); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SRV: " + << srv_str); + } + + impl_ = new SRVImpl(priority, weight, port, targetname); + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SRV from '" << + srv_str << "': " << ex.what()); + } +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not end with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC2782, the Target field must be a non compressed form +/// of domain name. But this implementation accepts a %SRV RR even if that +/// field is compressed as suggested in RFC3597. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +SRV::SRV(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 6) { + isc_throw(InvalidRdataLength, "SRV too short"); + } + + const uint16_t priority = buffer.readUint16(); + const uint16_t weight = buffer.readUint16(); + const uint16_t port = buffer.readUint16(); + const Name targetname(buffer); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SRV RDATA. The TARGET field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PRIORITY, WEIGHT and PORT fields must each be a valid decimal +/// representation of an unsigned 16-bit integers respectively. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 TARGET when it +/// is non-absolute. +SRV::SRV(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority: " << num); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight: " << num); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port: " << num); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, origin); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +SRV::SRV(const SRV& source) : + Rdata(), impl_(new SRVImpl(*source.impl_)) +{} + +SRV& +SRV::operator=(const SRV& source) { + if (this == &source) { + return (*this); + } + + SRVImpl* newimpl = new SRVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SRV::~SRV() { + delete impl_; +} + +/// \brief Convert the \c SRV to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c SRV(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c SRV object. +string +SRV::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(impl_->priority_) + + " " + lexical_cast<string>(impl_->weight_) + + " " + lexical_cast<string>(impl_->port_) + + " " + impl_->target_.toText()); +} + +/// \brief Render the \c SRV in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +SRV::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->priority_); + buffer.writeUint16(impl_->weight_); + buffer.writeUint16(impl_->port_); + impl_->target_.toWire(buffer); +} + +/// \brief Render the \c SRV in the wire format with taking into account +/// compression. +/// +/// As specified in RFC2782, the Target field (a domain name) will not be +/// compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +SRV::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->priority_); + renderer.writeUint16(impl_->weight_); + renderer.writeUint16(impl_->port_); + renderer.writeName(impl_->target_, false); +} + +/// \brief Compare two instances of \c SRV RDATA. +/// +/// See documentation in \c Rdata. +int +SRV::compare(const Rdata& other) const { + const SRV& other_srv = dynamic_cast<const SRV&>(other); + + if (impl_->priority_ != other_srv.impl_->priority_) { + return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); + } + if (impl_->weight_ != other_srv.impl_->weight_) { + return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); + } + if (impl_->port_ != other_srv.impl_->port_) { + return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); + } + + return (compareNames(impl_->target_, other_srv.impl_->target_)); +} + +uint16_t +SRV::getPriority() const { + return (impl_->priority_); +} + +uint16_t +SRV::getWeight() const { + return (impl_->weight_); +} + +uint16_t +SRV::getPort() const { + return (impl_->port_); +} + +const Name& +SRV::getTarget() const { + return (impl_->target_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/srv_33.h b/src/lib/dns/rdata/in_1/srv_33.h new file mode 100644 index 0000000..aca210e --- /dev/null +++ b/src/lib/dns/rdata/in_1/srv_33.h @@ -0,0 +1,85 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct SRVImpl; + +/// \brief \c rdata::SRV class represents the SRV RDATA as defined %in +/// RFC2782. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// SRV RDATA. +class SRV : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + SRV& operator=(const SRV& source); + + /// \brief The destructor. + ~SRV(); + + /// + /// Specialized methods + /// + + /// \brief Return the value of the priority field. + /// + /// This method never throws an exception. + uint16_t getPriority() const; + + /// \brief Return the value of the weight field. + /// + /// This method never throws an exception. + uint16_t getWeight() const; + + /// \brief Return the value of the port field. + /// + /// This method never throws an exception. + uint16_t getPort() const; + + /// \brief Return the value of the target field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal target name. + /// + /// This method never throws an exception. + const Name& getTarget() const; + +private: + SRVImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc new file mode 100644 index 0000000..d205855 --- /dev/null +++ b/src/lib/dns/rdata/template.cc @@ -0,0 +1,67 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrtype.h> + +using namespace std; +using namespace isc::util; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +// To add RDATA implementation of a new RR type (say "MyType"), copy this +// template into the appropriate subdirectory with the appropriate name +// (see template.h). +// Then define (at least) the following common methods (that are inherited +// from the base abstract class). +// If you added member functions specific to this derived class, you'll need +// to implement them here, of course. + +MyType::MyType(MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks) +{ +} + +MyType::MyType(const string& type_str) { +} + +MyType::MyType(InputBuffer& buffer, size_t rdata_len) { +} + +MyType::MyType(const MyType& source) { +} + +std::string +MyType::toText() const { +} + +void +MyType::toWire(OutputBuffer& buffer) const { +} + +void +MyType::toWire(AbstractMessageRenderer& renderer) const { +} + +int +MyType::compare(const Rdata&) const { + // The compare method normally begins with this dynamic cast. + // cppcheck-suppress unreadVariable + // const MyType& other_mytype = dynamic_cast<const MyType&>(other); + // ... + return (0); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/template.h b/src/lib/dns/rdata/template.h new file mode 100644 index 0000000..d74790e --- /dev/null +++ b/src/lib/dns/rdata/template.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011-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/. + +// BEGIN_HEADER_GUARD + +#include <string> + +#include <dns/rdata.h> + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +// To add RDATA class definition of a new RR type (say "MyType"), copy this +// file to an appropriate subdirectory (if it's class-independent type, it +// should go to "generic/", if it's IN-class specific, it should be in +// "in_1/", and so on). The copied file should be named as type_nn.h where +// "type" is textual representation (all lower cased) of the RR type, and "nn" +// is the 16-bit type code of the RR type. +// Normally, you'll need to define some specific member variables in the +// "RR-type specific members" space (please make them private). In addition, +// you may want to define some specific member functions, either public or +// private (or, though unlikely for a leaf class, protected). +// +// Note: do not remove the comment lines beginning with "BEGIN_" and "END_". +// These are markers used by a script for auto-generating build-able source +// files. +// +// On completion of implementing a new type of Rdata, remove the corresponding +// entry from the meta_types dictionary of gen-rdatacode.py.in. Otherwise +// it will cause build failure. + +class MyType : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // Do not remove the BEGIN_xxx and END_xxx comment lines. + // END_COMMON_MEMBERS +private: + // RR-type specific members are here. +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdata_pimpl_holder.h b/src/lib/dns/rdata_pimpl_holder.h new file mode 100644 index 0000000..baa343a --- /dev/null +++ b/src/lib/dns/rdata_pimpl_holder.h @@ -0,0 +1,52 @@ +// Copyright (C) 2014-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_RDATA_PIMPL_HOLDER_H +#define DNS_RDATA_PIMPL_HOLDER_H 1 + +#include <boost/noncopyable.hpp> + +#include <cstddef> // for NULL + +namespace isc { +namespace dns { +namespace rdata { + +template <typename T> +class RdataPimplHolder : boost::noncopyable { +public: + RdataPimplHolder(T* obj = NULL) : + obj_(obj) + {} + + ~RdataPimplHolder() { + delete obj_; + } + + void reset(T* obj = NULL) { + delete obj_; + obj_ = obj; + } + + T* get() { + return (obj_); + } + + T* release() { + T* obj = obj_; + obj_ = NULL; + return (obj); + } + +private: + T* obj_; +}; + +} // namespace rdata +} // namespace dns +} // namespace isc + +#endif // DNS_RDATA_PIMPL_HOLDER_H diff --git a/src/lib/dns/rdataclass.cc b/src/lib/dns/rdataclass.cc new file mode 100644 index 0000000..dcd5e13 --- /dev/null +++ b/src/lib/dns/rdataclass.cc @@ -0,0 +1,7078 @@ +/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +// 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/. + +#include <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/tsigkey.h> +#include <dns/tsigerror.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +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; + +namespace isc { +namespace dns { +namespace rdata { +namespace any { + +// straightforward representation of TSIG RDATA fields +struct TSIGImpl { + TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + vector<uint8_t>& mac, uint16_t original_id, uint16_t error, + vector<uint8_t>& 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<const uint8_t*>(mac), + static_cast<const uint8_t*>(mac) + macsize), + original_id_(original_id), error_(error), + other_data_(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + other_len) + {} + template <typename Output> + void toWireCommon(Output& output) const; + + const Name algorithm_; + const uint64_t time_signed_; + const uint16_t fudge_; + const vector<uint8_t> mac_; + const uint16_t original_id_; + const uint16_t error_; + const vector<uint8_t> 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<uint64_t>(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<uint8_t> 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<uint32_t>(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<uint8_t> 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 <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>] +/// <Original ID> <Error> <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and +/// "BADTIME" are supported (case sensitive). In future versions other +/// representations that are compatible with the DNS RCODE may be supported. +/// +/// The MAC and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tsig_str. +/// The decoded data of the MAC field is MAC Size bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2845 does not define the standard presentation format +/// of %TSIG RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if MAC or Other Data is not validly encoded in base-64. +/// +/// \param tsig_str A string containing the RDATA to be created +TSIG::TSIG(const std::string& tsig_str) : 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 TSIGImpl that constructFromLexer() returns. + std::unique_ptr<TSIGImpl> impl_ptr; + + try { + std::istringstream ss(tsig_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TSIG: " << tsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TSIG from '" << tsig_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TSIG RDATA. +/// +/// See \c TSIG::TSIG(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TSIG::TSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TSIG RR even if that +/// field is compressed. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TSIG::TSIG(InputBuffer& buffer, size_t) : + impl_(NULL) +{ + Name algorithm(buffer); + + uint8_t time_signed_buf[6]; + buffer.readData(time_signed_buf, sizeof(time_signed_buf)); + const uint64_t time_signed = + (static_cast<uint64_t>(time_signed_buf[0]) << 40 | + static_cast<uint64_t>(time_signed_buf[1]) << 32 | + static_cast<uint64_t>(time_signed_buf[2]) << 24 | + static_cast<uint64_t>(time_signed_buf[3]) << 16 | + static_cast<uint64_t>(time_signed_buf[4]) << 8 | + static_cast<uint64_t>(time_signed_buf[5])); + + const uint16_t fudge = buffer.readUint16(); + + const uint16_t mac_size = buffer.readUint16(); + vector<uint8_t> mac(mac_size); + if (mac_size > 0) { + buffer.readData(&mac[0], mac_size); + } + + const uint16_t original_id = buffer.readUint16(); + const uint16_t error = buffer.readUint16(); + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, + original_id, error, other_data); +} + +TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + uint16_t mac_size, const void* mac, uint16_t original_id, + uint16_t error, uint16_t other_len, const void* other_data) : + impl_(NULL) +{ + // Time Signed is a 48-bit value. + if ((time_signed >> 48) != 0) { + isc_throw(OutOfRange, "TSIG Time Signed is too large: " << + time_signed); + } + if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) { + isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent"); + } + if ((other_len == 0 && other_data != NULL) || + (other_len > 0 && other_data == NULL)) { + isc_throw(InvalidParameter, + "TSIG Other data length and data inconsistent"); + } + const Name& canonical_algorithm_name = + (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? + TSIGKey::HMACMD5_NAME() : algorithm; + impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size, + mac, original_id, error, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_)) +{} + +TSIG& +TSIG::operator=(const TSIG& source) { + if (this == &source) { + return (*this); + } + + TSIGImpl* newimpl = new TSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TSIG::~TSIG() { + delete impl_; +} + +/// \brief Convert the \c TSIG to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TSIG(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TSIG object. +std::string +TSIG::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + lexical_cast<string>(impl_->time_signed_) + " " + + lexical_cast<string>(impl_->fudge_) + " " + + lexical_cast<string>(impl_->mac_.size()) + " "; + if (!impl_->mac_.empty()) { + result += encodeBase64(impl_->mac_) + " "; + } + result += lexical_cast<string>(impl_->original_id_) + " "; + result += TSIGError(impl_->error_).toText() + " "; + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TSIGImpl::toWireCommon(Output& output) const { + output.writeUint16(time_signed_ >> 32); + output.writeUint32(time_signed_ & 0xffffffff); + output.writeUint16(fudge_); + const uint16_t mac_size = mac_.size(); + output.writeUint16(mac_size); + if (mac_size > 0) { + output.writeData(&mac_[0], mac_size); + } + output.writeUint16(original_id_); + output.writeUint16(error_); + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TSIG in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TSIG::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TSIG in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TSIG::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TSIG::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TSIG RDATA. +/// +/// This method compares \c this and the \c other \c TSIG objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TSIG object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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. +int +TSIG::compare(const Rdata& other) const { + const TSIG& other_tsig = dynamic_cast<const TSIG&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tsig.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->time_signed_ != other_tsig.impl_->time_signed_) { + return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1); + } + if (impl_->fudge_ != other_tsig.impl_->fudge_) { + return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1); + } + const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_); + if (vcmp != 0) { + return (vcmp); + } + if (impl_->original_id_ != other_tsig.impl_->original_id_) { + return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1); + } + if (impl_->error_ != other_tsig.impl_->error_) { + return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1); + } + return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_)); +} + +const Name& +TSIG::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint64_t +TSIG::getTimeSigned() const { + return (impl_->time_signed_); +} + +uint16_t +TSIG::getFudge() const { + return (impl_->fudge_); +} + +uint16_t +TSIG::getMACSize() const { + return (impl_->mac_.size()); +} + +const void* +TSIG::getMAC() const { + if (!impl_->mac_.empty()) { + return (&impl_->mac_[0]); + } else { + return (NULL); + } +} + +uint16_t +TSIG::getOriginalID() const { + return (impl_->original_id_); +} + +uint16_t +TSIG::getError() const { + return (impl_->error_); +} + +uint16_t +TSIG::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TSIG::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (NULL); + } +} + +} // end of namespace "any" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace ch { + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +} // end of namespace "ch" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/lexical_cast.hpp> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c afsdb_str must be formatted as follows: +/// \code <subtype> <server name> +/// \endcode +/// where server name field must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "1 server.example.com." \endcode +/// +/// <b>Exceptions</b> +/// +/// \exception InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +AFSDB::AFSDB(const std::string& afsdb_str) : + subtype_(0), server_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(afsdb_str); + MasterLexer lexer; + lexer.pushSource(ss); + + createFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for AFSDB: " + << afsdb_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct AFSDB from '" << + afsdb_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an AFSDB RDATA. The SERVER field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The SUBTYPE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +AFSDB::AFSDB(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + subtype_(0), server_(".") +{ + createFromLexer(lexer, origin); +} + +void +AFSDB::createFromLexer(MasterLexer& lexer, const Name* origin) +{ + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid AFSDB subtype: " << num); + } + subtype_ = static_cast<uint16_t>(num); + + server_ = createNameFromLexer(lexer, origin); +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \exception std::bad_alloc Memory allocation fails. +/// \exception Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +AFSDB::AFSDB(InputBuffer& buffer, size_t) : + subtype_(buffer.readUint16()), server_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \exception std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +AFSDB::AFSDB(const AFSDB& other) : + Rdata(), subtype_(other.subtype_), server_(other.server_) +{} + +AFSDB& +AFSDB::operator=(const AFSDB& source) { + subtype_ = source.subtype_; + server_ = source.server_; + + return (*this); +} + +/// \brief Convert the \c AFSDB to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c AFSDB(const std::string&))). +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c AFSDB object. +string +AFSDB::toText() const { + return (lexical_cast<string>(subtype_) + " " + server_.toText()); +} + +/// \brief Render the \c AFSDB in the wire format without name compression. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +AFSDB::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(subtype_); + server_.toWire(buffer); +} + +/// \brief Render the \c AFSDB in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE AFSDB is not "well-known", the server +/// field (domain name) will not be compressed. +/// +/// \exception std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +AFSDB::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(subtype_); + renderer.writeName(server_, false); +} + +/// \brief Compare two instances of \c AFSDB RDATA. +/// +/// See documentation in \c Rdata. +int +AFSDB::compare(const Rdata& other) const { + const AFSDB& other_afsdb = dynamic_cast<const AFSDB&>(other); + if (subtype_ < other_afsdb.subtype_) { + return (-1); + } else if (subtype_ > other_afsdb.subtype_) { + return (1); + } + + return (compareNames(server_, other_afsdb.server_)); +} + +const Name& +AFSDB::getServer() const { + return (server_); +} + +uint16_t +AFSDB::getSubtype() const { + return (subtype_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2014-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 <config.h> + +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/char_string.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct CAAImpl { + // straightforward representation of CAA RDATA fields + CAAImpl(uint8_t flags, const std::string& tag, + const detail::CharStringData& value) : + flags_(flags), + tag_(tag), + value_(value) + { + if ((sizeof(flags) + 1 + tag_.size() + value_.size()) > 65535) { + isc_throw(InvalidRdataLength, + "CAA Value field is too large: " << value_.size()); + } + } + + uint8_t flags_; + const std::string tag_; + const detail::CharStringData value_; +}; + +// helper function for string and lexer constructors +CAAImpl* +CAA::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 255) { + isc_throw(InvalidRdataText, + "CAA flags field out of range"); + } + + // Tag field must not be empty. + const std::string tag = + lexer.getNextToken(MasterToken::STRING).getString(); + if (tag.empty()) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(InvalidRdataText, + "CAA tag field is too large: " << tag.size()); + } + + // Value field may be empty. + detail::CharStringData value; + MasterToken token = lexer.getNextToken(MasterToken::QSTRING, true); + if ((token.getType() != MasterToken::END_OF_FILE) && + (token.getType() != MasterToken::END_OF_LINE)) + { + detail::stringToCharStringData(token.getStringRegion(), value); + } + + return (new CAAImpl(flags, tag, value)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CAA 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. +/// +/// The Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, incorrect, or empty. +/// +/// \param caa_str A string containing the RDATA to be created +CAA::CAA(const string& caa_str) : + 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 CAAImpl that constructFromLexer() returns. + std::unique_ptr<CAAImpl> impl_ptr; + + try { + std::istringstream ss(caa_str); + 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 CAA: " + << caa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CAA from '" << + caa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an CAA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing +/// field. +/// \throw InvalidRdataText Fields are out of their valid ranges, +/// incorrect, or empty. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +CAA::CAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid CAA RDATA. +/// +/// The Flags, Tag and Value fields must be within their valid ranges, +/// but are not constrained to the values defined in RFC6844. The Tag +/// field must not be empty. +CAA::CAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "CAA record too short"); + } + + const uint8_t flags = buffer.readUint8(); + const uint8_t tag_length = buffer.readUint8(); + rdata_len -= 2; + if (tag_length == 0) { + isc_throw(InvalidRdataText, "CAA tag field is empty"); + } + + if (rdata_len < tag_length) { + isc_throw(InvalidRdataLength, + "RDATA is too short for CAA tag field"); + } + + std::vector<uint8_t> tag_vec(tag_length); + buffer.readData(&tag_vec[0], tag_length); + std::string tag(tag_vec.begin(), tag_vec.end()); + rdata_len -= tag_length; + + detail::CharStringData value; + value.resize(rdata_len); + if (rdata_len > 0) { + buffer.readData(&value[0], rdata_len); + } + + impl_ = new CAAImpl(flags, tag, value); +} + +CAA::CAA(uint8_t flags, const std::string& tag, const std::string& value) : + impl_(NULL) +{ + if (tag.empty()) { + isc_throw(isc::InvalidParameter, + "CAA tag field is empty"); + } else if (tag.size() > 255) { + isc_throw(isc::InvalidParameter, + "CAA tag field is too large: " << tag.size()); + } + + MasterToken::StringRegion region; + region.beg = &value[0]; // note std ensures this works even if str is empty + region.len = value.size(); + + detail::CharStringData value_vec; + detail::stringToCharStringData(region, value_vec); + + impl_ = new CAAImpl(flags, tag, value_vec); +} + +CAA::CAA(const CAA& other) : + Rdata(), impl_(new CAAImpl(*other.impl_)) +{} + +CAA& +CAA::operator=(const CAA& source) { + if (this == &source) { + return (*this); + } + + CAAImpl* newimpl = new CAAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +CAA::~CAA() { + delete impl_; +} + +void +CAA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + buffer.writeUint8(impl_->tag_.size()); + buffer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + buffer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +void +CAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->flags_); + + // The constructors must ensure that the tag field is not empty. + assert(!impl_->tag_.empty()); + renderer.writeUint8(impl_->tag_.size()); + renderer.writeData(&impl_->tag_[0], impl_->tag_.size()); + + if (!impl_->value_.empty()) { + renderer.writeData(&impl_->value_[0], + impl_->value_.size()); + } +} + +std::string +CAA::toText() const { + std::string result; + + result = lexical_cast<std::string>(static_cast<int>(impl_->flags_)); + result += " " + impl_->tag_; + result += " \"" + detail::charStringDataToString(impl_->value_) + "\""; + + return (result); +} + +int +CAA::compare(const Rdata& other) const { + const CAA& other_caa = dynamic_cast<const CAA&>(other); + + if (impl_->flags_ < other_caa.impl_->flags_) { + return (-1); + } else if (impl_->flags_ > other_caa.impl_->flags_) { + return (1); + } + + // Do a case-insensitive compare of the tag strings. + const int result = boost::ilexicographical_compare + <std::string, std::string>(impl_->tag_, other_caa.impl_->tag_); + if (result != 0) { + return (result); + } + + return (detail::compareCharStringDatas(impl_->value_, + other_caa.impl_->value_)); +} + +uint8_t +CAA::getFlags() const { + return (impl_->flags_); +} + +const std::string& +CAA::getTag() const { + return (impl_->tag_); +} + +const std::vector<uint8_t>& +CAA::getValue() const { + return (impl_->value_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid CNAME 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. +/// +/// The CNAME must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +CNAME::CNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + cname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + cname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for CNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct CNAME from '" << + namestr << "': " << ex.what()); + } +} + +CNAME::CNAME(InputBuffer& buffer, size_t) : + Rdata(), cname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a CNAME RDATA. The CNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 CNAME when it +/// is non-absolute. +CNAME::CNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + cname_(createNameFromLexer(lexer, origin)) +{} + +CNAME::CNAME(const CNAME& other) : + Rdata(), cname_(other.cname_) +{} + +CNAME::CNAME(const Name& cname) : + cname_(cname) +{} + +void +CNAME::toWire(OutputBuffer& buffer) const { + cname_.toWire(buffer); +} + +void +CNAME::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(cname_); +} + +string +CNAME::toText() const { + return (cname_.toText()); +} + +int +CNAME::compare(const Rdata& other) const { + const CNAME& other_cname = dynamic_cast<const CNAME&>(other); + + return (compareNames(cname_, other_cname.cname_)); +} + +const Name& +CNAME::getCname() const { + return (cname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const std::string& ds_str) : + impl_(new DLVImpl(ds_str)) +{} + +/// \brief Constructor from wire-format data. +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(InputBuffer& buffer, size_t rdata_len) : + impl_(new DLVImpl(buffer, rdata_len)) +{} + +DLV::DLV(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DLVImpl(lexer, origin, options, callbacks)) +{} + +/// \brief Copy constructor +/// +/// A copy of the implementation object is allocated and constructed. +DLV::DLV(const DLV& source) : + Rdata(), impl_(new DLVImpl(*source.impl_)) +{} + +/// \brief Assignment operator +/// +/// PIMPL-induced logic +DLV& +DLV::operator=(const DLV& source) { + if (this == &source) { + return (*this); + } + + DLVImpl* newimpl = new DLVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief Destructor +/// +/// Deallocates an internal resource. +DLV::~DLV() { + delete impl_; +} + +/// \brief Convert the \c DLV to a string. +/// +/// A pass-thru to the corresponding implementation method. +string +DLV::toText() const { + return (impl_->toText()); +} + +/// \brief Render the \c DLV in the wire format to a OutputBuffer object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c DLV in the wire format to a AbstractMessageRenderer +/// object +/// +/// A pass-thru to the corresponding implementation method. +void +DLV::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Compare two instances of \c DLV RDATA. +/// +/// The type check is performed here. Otherwise, a pass-thru to the +/// corresponding implementation method. +int +DLV::compare(const Rdata& other) const { + const DLV& other_ds = dynamic_cast<const DLV&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +/// \brief Tag accessor +uint16_t +DLV::getTag() const { + return (impl_->getTag()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNAME 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. +/// +/// The TARGET must be absolute since there's no parameter that specifies +/// the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +DNAME::DNAME(const std::string& namestr) : + // Fill in dummy name and replace it soon below. + dname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + dname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DNAME: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DNAME from '" << + namestr << "': " << ex.what()); + } +} + +DNAME::DNAME(InputBuffer& buffer, size_t) : + dname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a DNAME RDATA. The TARGET field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 TARGET when it +/// is non-absolute. +DNAME::DNAME(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + dname_(createNameFromLexer(lexer, origin)) +{} + +DNAME::DNAME(const DNAME& other) : + Rdata(), dname_(other.dname_) +{} + +DNAME::DNAME(const Name& dname) : + dname_(dname) +{} + +void +DNAME::toWire(OutputBuffer& buffer) const { + dname_.toWire(buffer); +} + +void +DNAME::toWire(AbstractMessageRenderer& renderer) const { + // Type DNAME is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(dname_, false); +} + +string +DNAME::toText() const { + return (dname_.toText()); +} + +int +DNAME::compare(const Rdata& other) const { + const DNAME& other_dname = dynamic_cast<const DNAME&>(other); + + return (compareNames(dname_, other_dname.dname_)); +} + +const Name& +DNAME::getDname() const { + return (dname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> +#include <boost/foreach.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct DNSKEYImpl { + // straightforward representation of DNSKEY RDATA fields + DNSKEYImpl(uint16_t flags, uint8_t protocol, uint8_t algorithm, + const vector<uint8_t>& keydata) : + flags_(flags), protocol_(protocol), algorithm_(algorithm), + keydata_(keydata) + {} + + uint16_t flags_; + uint8_t protocol_; + uint8_t algorithm_; + const vector<uint8_t> keydata_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid DNSKEY 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. +/// +/// The Protocol and Algorithm fields must be within their valid +/// ranges. The Public Key field must be present and must contain a +/// Base64 encoding of the public key. Whitespace is allowed within the +/// Base64 text. +/// +/// It is okay for the key data to be missing. Note: BIND 9 also accepts +/// DNSKEY missing key data. While the RFC is silent in this case, and it +/// may be debatable what an implementation should do, but since this field +/// is algorithm dependent and this implementations doesn't reject unknown +/// algorithms, it's lenient here. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param dnskey_str A string containing the RDATA to be created +DNSKEY::DNSKEY(const std::string& dnskey_str) : + 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 DNSKEYImpl that constructFromLexer() returns. + std::unique_ptr<DNSKEYImpl> impl_ptr; + + try { + std::istringstream ss(dnskey_str); + 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 DNSKEY: " << dnskey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct DNSKEY from '" << dnskey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid DNSKEY RDATA. +/// +/// The Protocol and Algorithm fields are not checked for unknown +/// values. It is okay for the key data to be missing (see the description +/// of the constructor from string). +DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, "DNSKEY too short: " << rdata_len); + } + + const uint16_t flags = buffer.readUint16(); + const uint16_t protocol = buffer.readUint8(); + const uint16_t algorithm = buffer.readUint8(); + + rdata_len -= 4; + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (rdata_len > 0) { + keydata.resize(rdata_len); + buffer.readData(&keydata[0], rdata_len); + } + + impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an DNSKEY RDATA. +/// +/// See \c DNSKEY::DNSKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DNSKEY::DNSKEY(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +DNSKEYImpl* +DNSKEY::constructFromLexer(MasterLexer& lexer) { + const uint32_t flags = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (flags > 0xffff) { + isc_throw(InvalidRdataText, + "DNSKEY flags out of range: " << flags); + } + + const uint32_t protocol = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (protocol > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY protocol out of range: " << protocol); + } + + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, + "DNSKEY algorithm out of range: " << algorithm); + } + + std::string keydata_str; + std::string keydata_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + // token is now assured to be of type STRING. + + token.getString(keydata_substr); + keydata_str.append(keydata_substr); + } + + lexer.ungetToken(); + + vector<uint8_t> keydata; + // If key data is missing, it's OK. See the API documentation of the + // constructor. + if (keydata_str.size() > 0) { + decodeBase64(keydata_str, keydata); + } + + return (new DNSKEYImpl(flags, protocol, algorithm, keydata)); +} + +DNSKEY::DNSKEY(const DNSKEY& source) : + Rdata(), impl_(new DNSKEYImpl(*source.impl_)) +{} + +DNSKEY& +DNSKEY::operator=(const DNSKEY& source) { + if (this == &source) { + return (*this); + } + + DNSKEYImpl* newimpl = new DNSKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DNSKEY::~DNSKEY() { + delete impl_; +} + +string +DNSKEY::toText() const { + return (boost::lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->protocol_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + encodeBase64(impl_->keydata_)); +} + +void +DNSKEY::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->flags_); + buffer.writeUint8(impl_->protocol_); + buffer.writeUint8(impl_->algorithm_); + buffer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +void +DNSKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->flags_); + renderer.writeUint8(impl_->protocol_); + renderer.writeUint8(impl_->algorithm_); + renderer.writeData(&impl_->keydata_[0], impl_->keydata_.size()); +} + +int +DNSKEY::compare(const Rdata& other) const { + const DNSKEY& other_dnskey = dynamic_cast<const DNSKEY&>(other); + + if (impl_->flags_ != other_dnskey.impl_->flags_) { + return (impl_->flags_ < other_dnskey.impl_->flags_ ? -1 : 1); + } + if (impl_->protocol_ != other_dnskey.impl_->protocol_) { + return (impl_->protocol_ < other_dnskey.impl_->protocol_ ? -1 : 1); + } + if (impl_->algorithm_ != other_dnskey.impl_->algorithm_) { + return (impl_->algorithm_ < other_dnskey.impl_->algorithm_ ? -1 : 1); + } + + const size_t this_len = impl_->keydata_.size(); + const size_t other_len = other_dnskey.impl_->keydata_.size(); + const size_t cmplen = min(this_len, other_len); + if (cmplen == 0) { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } + const int cmp = memcmp(&impl_->keydata_[0], + &other_dnskey.impl_->keydata_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +uint16_t +DNSKEY::getTag() const { + if (impl_->algorithm_ == 1) { + // See RFC 4034 appendix B.1 for why the key data must contain + // at least 4 bytes with RSA/MD5: 3 trailing bytes to extract + // the tag from, and 1 byte of exponent length subfield before + // modulus. + const int len = impl_->keydata_.size(); + if (len < 4) { + isc_throw(isc::OutOfRange, + "DNSKEY keydata too short for tag extraction"); + } + + return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]); + } + + uint32_t ac = impl_->flags_; + ac += (impl_->protocol_ << 8); + ac += impl_->algorithm_; + + const size_t size = impl_->keydata_.size(); + for (size_t i = 0; i < size; i ++) { + ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8); + } + ac += (ac >> 16) & 0xffff; + return (ac & 0xffff); +} + +uint16_t +DNSKEY::getFlags() const { + return (impl_->flags_); +} + +uint8_t +DNSKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/ds_like.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +DS::DS(const std::string& ds_str) : + impl_(new DSImpl(ds_str)) +{} + +DS::DS(InputBuffer& buffer, size_t rdata_len) : + impl_(new DSImpl(buffer, rdata_len)) +{} + +DS::DS(MasterLexer& lexer, const Name* origin, MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) : + impl_(new DSImpl(lexer, origin, options, callbacks)) +{} + +DS::DS(const DS& source) : + Rdata(), impl_(new DSImpl(*source.impl_)) +{} + +DS& +DS::operator=(const DS& source) { + if (this == &source) { + return (*this); + } + + DSImpl* newimpl = new DSImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +DS::~DS() { + delete impl_; +} + +string +DS::toText() const { + return (impl_->toText()); +} + +void +DS::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +DS::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +int +DS::compare(const Rdata& other) const { + const DS& other_ds = dynamic_cast<const DS&>(other); + + return (impl_->compare(*other_ds.impl_)); +} + +uint16_t +DS::getTag() const { + return (impl_->getTag()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +class HINFOImpl { +public: + HINFOImpl(const std::string& hinfo_str) { + std::istringstream ss(hinfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseHINFOData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid HINFO text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct HINFO RDATA from " + << hinfo_str << "': " << ex.what()); + } + } + + HINFOImpl(InputBuffer& buffer, size_t rdata_len) { + rdata_len -= detail::bufferToCharString(buffer, rdata_len, cpu); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, os); + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "HINFO RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + HINFOImpl(MasterLexer& lexer) + { + parseHINFOData(lexer); + } + +private: + void + parseHINFOData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), cpu); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), os); + } + +public: + detail::CharString cpu; + detail::CharString os; +}; + +HINFO::HINFO(const std::string& hinfo_str) : impl_(new HINFOImpl(hinfo_str)) +{} + + +HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) : + impl_(new HINFOImpl(buffer, rdata_len)) +{} + +HINFO::HINFO(const HINFO& source): + Rdata(), impl_(new HINFOImpl(*source.impl_)) +{ +} + +HINFO::HINFO(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new HINFOImpl(lexer)) +{} + +HINFO& +HINFO::operator=(const HINFO& source) +{ + impl_.reset(new HINFOImpl(*source.impl_)); + return (*this); +} + +HINFO::~HINFO() { +} + +std::string +HINFO::toText() const { + string result; + result += "\""; + result += detail::charStringToString(impl_->cpu); + result += "\" \""; + result += detail::charStringToString(impl_->os); + result += "\""; + return (result); +} + +void +HINFO::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); +} + +void +HINFO::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); +} + +int +HINFO::compare(const Rdata& other) const { + const HINFO& other_hinfo = dynamic_cast<const HINFO&>(other); + + const int cmp = compareCharStrings(impl_->cpu, other_hinfo.impl_->cpu); + if (cmp != 0) { + return (cmp); + } + return (compareCharStrings(impl_->os, other_hinfo.impl_->os)); +} + +const std::string +HINFO::getCPU() const { + return (detail::charStringToString(impl_->cpu)); +} + +const std::string +HINFO::getOS() const { + return (detail::charStringToString(impl_->os)); +} + +template <typename T> +void +HINFO::toWireHelper(T& outputer) const { + outputer.writeData(&impl_->cpu[0], impl_->cpu.size()); + outputer.writeData(&impl_->os[0], impl_->os.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c minfo_str must be formatted as follows: +/// \code <rmailbox name> <emailbox name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// An example of valid string is: +/// \code "rmail.example.com. email.example.com." \endcode +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the string is invalid. +MINFO::MINFO(const std::string& minfo_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(minfo_str); + MasterLexer lexer; + lexer.pushSource(ss); + + rmailbox_ = createNameFromLexer(lexer, NULL); + emailbox_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MINFO: " + << minfo_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MINFO from '" << + minfo_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MINFO RDATA. The RMAILBOX and EMAILBOX fields can be non-absolute +/// if \c origin is non-NULL, in which case \c origin is used to make them +/// absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +MINFO::MINFO(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + rmailbox_(createNameFromLexer(lexer, origin)), + emailbox_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +MINFO::MINFO(InputBuffer& buffer, size_t) : + rmailbox_(buffer), emailbox_(buffer) +{} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +MINFO::MINFO(const MINFO& other) : + Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_) +{} + +/// \brief Convert the \c MINFO to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c MINFO(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c MINFO object. +std::string +MINFO::toText() const { + return (rmailbox_.toText() + " " + emailbox_.toText()); +} + +/// \brief Render the \c MINFO in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +MINFO::toWire(OutputBuffer& buffer) const { + rmailbox_.toWire(buffer); + emailbox_.toWire(buffer); +} + +MINFO& +MINFO::operator=(const MINFO& source) { + rmailbox_ = source.rmailbox_; + emailbox_ = source.emailbox_; + + return (*this); +} + +/// \brief Render the \c MINFO in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and +/// emailbox fields (domain names) will be compressed. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +MINFO::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(rmailbox_); + renderer.writeName(emailbox_); +} + +/// \brief Compare two instances of \c MINFO RDATA. +/// +/// See documentation in \c Rdata. +int +MINFO::compare(const Rdata& other) const { + const MINFO& other_minfo = dynamic_cast<const MINFO&>(other); + + const int cmp = compareNames(rmailbox_, other_minfo.rmailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(emailbox_, other_minfo.emailbox_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +MX::MX(InputBuffer& buffer, size_t) : + preference_(buffer.readUint16()), mxname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid MX 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. +/// +/// The EXCHANGE name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +MX::MX(const std::string& mx_str) : + // Fill in dummy name and replace them soon below. + preference_(0), mxname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(mx_str); + MasterLexer lexer; + lexer.pushSource(ss); + + constructFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for MX: " + << mx_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct MX from '" << + mx_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an MX RDATA. The EXCHANGE field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PREFERENCE field must be a valid decimal representation of an +/// unsigned 16-bit integer. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 EXCHANGE when it +/// is non-absolute. +MX::MX(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + preference_(0), mxname_(Name::ROOT_NAME()) +{ + constructFromLexer(lexer, origin); +} + +void +MX::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid MX preference: " << num); + } + preference_ = static_cast<uint16_t>(num); + + mxname_ = createNameFromLexer(lexer, origin); +} + +MX::MX(uint16_t preference, const Name& mxname) : + preference_(preference), mxname_(mxname) +{} + +MX::MX(const MX& other) : + Rdata(), preference_(other.preference_), mxname_(other.mxname_) +{} + +void +MX::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(preference_); + mxname_.toWire(buffer); +} + +void +MX::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(preference_); + renderer.writeName(mxname_); +} + +string +MX::toText() const { + return (lexical_cast<string>(preference_) + " " + mxname_.toText()); +} + +int +MX::compare(const Rdata& other) const { + const MX& other_mx = dynamic_cast<const MX&>(other); + + if (preference_ < other_mx.preference_) { + return (-1); + } else if (preference_ > other_mx.preference_) { + return (1); + } + + return (compareNames(mxname_, other_mx.mxname_)); +} + +const Name& +MX::getMXName() const { + return (mxname_); +} + +uint16_t +MX::getMXPref() const { + return (preference_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <exceptions/exceptions.h> + +#include <string> +#include <boost/lexical_cast.hpp> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::dns; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +class NAPTRImpl { +public: + NAPTRImpl() : order(0), preference(0), replacement(".") {} + + NAPTRImpl(InputBuffer& buffer, size_t rdata_len) : replacement(".") { + if (rdata_len < 4 || buffer.getLength() - buffer.getPosition() < 4) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: insufficient length "); + } + order = buffer.readUint16(); + preference = buffer.readUint16(); + rdata_len -= 4; + + rdata_len -= detail::bufferToCharString(buffer, rdata_len, flags); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, services); + rdata_len -= detail::bufferToCharString(buffer, rdata_len, regexp); + replacement = Name(buffer); + if (rdata_len < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " + "NAPTR RDATA wire format: missing replacement name"); + } + rdata_len -= replacement.getLength(); + + if (rdata_len != 0) { + isc_throw(isc::dns::DNSMessageFORMERR, "Error in parsing " << + "NAPTR RDATA: bytes left at end: " << + static_cast<int>(rdata_len)); + } + } + + NAPTRImpl(const std::string& naptr_str) : replacement(".") { + std::istringstream ss(naptr_str); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + parseNAPTRData(lexer); + // Should be at end of data now + if (lexer.getNextToken(MasterToken::QSTRING, true).getType() != + MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: too many fields."); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NAPTR RDATA from " + << naptr_str << "': " << ex.what()); + } + } + + NAPTRImpl(MasterLexer& lexer) : replacement(".") + { + parseNAPTRData(lexer); + } + +private: + void + parseNAPTRData(MasterLexer& lexer) { + MasterToken token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: order out of range: " + << token.getNumber()); + } + order = token.getNumber(); + token = lexer.getNextToken(MasterToken::NUMBER); + if (token.getNumber() > 65535) { + isc_throw(InvalidRdataText, + "Invalid NAPTR text format: preference out of range: " + << token.getNumber()); + } + preference = token.getNumber(); + + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), flags); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), services); + token = lexer.getNextToken(MasterToken::QSTRING); + stringToCharString(token.getStringRegion(), regexp); + + token = lexer.getNextToken(MasterToken::STRING); + replacement = Name(token.getString()); + } + + +public: + uint16_t order; + uint16_t preference; + detail::CharString flags; + detail::CharString services; + detail::CharString regexp; + Name replacement; +}; + +NAPTR::NAPTR(InputBuffer& buffer, size_t rdata_len) : + impl_(new NAPTRImpl(buffer, rdata_len)) +{} + +NAPTR::NAPTR(const std::string& naptr_str) : impl_(new NAPTRImpl(naptr_str)) +{} + +NAPTR::NAPTR(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(new NAPTRImpl(lexer)) +{} + +NAPTR::NAPTR(const NAPTR& naptr) : Rdata(), + impl_(new NAPTRImpl(*naptr.impl_)) +{} + +NAPTR& +NAPTR::operator=(const NAPTR& source) +{ + impl_.reset(new NAPTRImpl(*source.impl_)); + return (*this); +} + +NAPTR::~NAPTR() { +} + +void +NAPTR::toWire(OutputBuffer& buffer) const { + toWireHelper(buffer); + impl_->replacement.toWire(buffer); +} + +void +NAPTR::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(renderer); + // Type NAPTR is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->replacement, false); +} + +string +NAPTR::toText() const { + string result; + result += lexical_cast<string>(impl_->order); + result += " "; + result += lexical_cast<string>(impl_->preference); + result += " \""; + result += detail::charStringToString(impl_->flags); + result += "\" \""; + result += detail::charStringToString(impl_->services); + result += "\" \""; + result += detail::charStringToString(impl_->regexp); + result += "\" "; + result += impl_->replacement.toText(); + return (result); +} + +int +NAPTR::compare(const Rdata& other) const { + const NAPTR other_naptr = dynamic_cast<const NAPTR&>(other); + + if (impl_->order < other_naptr.impl_->order) { + return (-1); + } else if (impl_->order > other_naptr.impl_->order) { + return (1); + } + + if (impl_->preference < other_naptr.impl_->preference) { + return (-1); + } else if (impl_->preference > other_naptr.impl_->preference) { + return (1); + } + + const int fcmp = detail::compareCharStrings(impl_->flags, + other_naptr.impl_->flags); + if (fcmp != 0) { + return (fcmp); + } + + const int scmp = detail::compareCharStrings(impl_->services, + other_naptr.impl_->services); + if (scmp != 0) { + return (scmp); + } + + const int rcmp = detail::compareCharStrings(impl_->regexp, + other_naptr.impl_->regexp); + if (rcmp != 0) { + return (rcmp); + } + + return (compareNames(impl_->replacement, other_naptr.impl_->replacement)); +} + +uint16_t +NAPTR::getOrder() const { + return (impl_->order); +} + +uint16_t +NAPTR::getPreference() const { + return (impl_->preference); +} + +const std::string +NAPTR::getFlags() const { + return (detail::charStringToString(impl_->flags)); +} + +const std::string +NAPTR::getServices() const { + return (detail::charStringToString(impl_->services)); +} + +const std::string +NAPTR::getRegexp() const { + return (detail::charStringToString(impl_->regexp)); +} + +const Name& +NAPTR::getReplacement() const { + return (impl_->replacement); +} + +template <typename T> +void +NAPTR::toWireHelper(T& outputer) const { + outputer.writeUint16(impl_->order); + outputer.writeUint16(impl_->preference); + + outputer.writeData(&impl_->flags[0], impl_->flags.size()); + outputer.writeData(&impl_->services[0], impl_->services.size()); + outputer.writeData(&impl_->regexp[0], impl_->regexp.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NS 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. +/// +/// The NSDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +NS::NS(const std::string& namestr) : + // Fill in dummy name and replace them soon below. + nsname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); + + nsname_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for NS: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NS from '" << + namestr << "': " << ex.what()); + } +} + +NS::NS(InputBuffer& buffer, size_t) : + nsname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NS RDATA. The NSDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 NSDNAME when it +/// is non-absolute. +NS::NS(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + nsname_(createNameFromLexer(lexer, origin)) +{} + +NS::NS(const NS& other) : + Rdata(), nsname_(other.nsname_) +{} + +void +NS::toWire(OutputBuffer& buffer) const { + nsname_.toWire(buffer); +} + +void +NS::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(nsname_); +} + +string +NS::toText() const { + return (nsname_.toText()); +} + +int +NS::compare(const Rdata& other) const { + const NS& other_ns = dynamic_cast<const NS&>(other); + + return (compareNames(nsname_, other_ns.nsname_)); +} + +const Name& +NS::getNSName() const { + return (nsname_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <iostream> +#include <iomanip> +#include <string> +#include <sstream> +#include <vector> +#include <cassert> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base32hex.h> +#include <util/encode/hex.h> +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <memory> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::dns::rdata::generic::detail::nsec; +using namespace isc::dns::rdata::generic::detail::nsec3; +using namespace isc::util::encode; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSEC3Impl { + // straightforward representation of NSEC3 RDATA fields + NSEC3Impl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + vector<uint8_t>salt, vector<uint8_t>next, + vector<uint8_t> typebits) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), + salt_(salt), next_(next), typebits_(typebits) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; + const vector<uint8_t> next_; + const vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3 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. +/// +/// The Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3_str A string containing the RDATA to be created +NSEC3::NSEC3(const std::string& nsec3_str) : + 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 NSEC3Impl that constructFromLexer() returns. + std::unique_ptr<NSEC3Impl> impl_ptr; + + try { + std::istringstream ss(nsec3_str); + 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 NSEC3: " << nsec3_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3 from '" << nsec3_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3 RDATA. +/// +/// See \c NSEC3::NSEC3(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3::NSEC3(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3Impl* +NSEC3::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3", lexer, salt); + + const string& nexthash = + lexer.getNextToken(MasterToken::STRING).getString(); + if (*nexthash.rbegin() == '=') { + isc_throw(InvalidRdataText, "NSEC3 hash has padding: " << nexthash); + } + + vector<uint8_t> next; + decodeBase32Hex(nexthash, next); + if (next.size() > 255) { + isc_throw(InvalidRdataText, "NSEC3 hash is too long: " + << next.size() << " bytes"); + } + + vector<uint8_t> typebits; + // For NSEC3 empty bitmap is possible and allowed. + buildBitmapsFromLexer("NSEC3", lexer, typebits, true); + return (new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits)); +} + +NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3", buffer, rdata_len, salt); + + if (rdata_len < 1) { + isc_throw(DNSMessageFORMERR, "NSEC3 too short to contain hash length, " + "length: " << rdata_len + salt.size() + 5); + } + const uint8_t nextlen = buffer.readUint8(); + --rdata_len; + if (nextlen == 0 || rdata_len < nextlen) { + isc_throw(DNSMessageFORMERR, "NSEC3 invalid hash length: " << + static_cast<unsigned int>(nextlen)); + } + + vector<uint8_t> next(nextlen); + buffer.readData(&next[0], nextlen); + rdata_len -= nextlen; + + vector<uint8_t> typebits(rdata_len); + if (rdata_len > 0) { + // Read and parse the bitmaps only when they exist; empty bitmap + // is possible for NSEC3. + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC3", typebits); + } + + impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations, + salt, next, typebits); +} + +NSEC3::NSEC3(const NSEC3& source) : + Rdata(), impl_(new NSEC3Impl(*source.impl_)) +{} + +NSEC3& +NSEC3::operator=(const NSEC3& source) { + if (this == &source) { + return (*this); + } + + NSEC3Impl* newimpl = new NSEC3Impl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3::~NSEC3() { + delete impl_; +} + +string +NSEC3::toText() const { + ostringstream s; + bitmapsToText(impl_->typebits_, s); + + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)) + + " " + encodeBase32Hex(impl_->next_) + s.str()); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3Impl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } + assert(!impl.next_.empty()); + output.writeUint8(impl.next_.size()); + output.writeData(&impl.next_[0], impl.next_.size()); + if (!impl.typebits_.empty()) { + output.writeData(&impl.typebits_[0], impl.typebits_.size()); + } +} + +void +NSEC3::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +namespace { +// This is a helper subroutine for compare(). It compares two binary +// data stored in vector<uint8_t> objects based on the "Canonical RR Ordering" +// as defined in Section 6.3 of RFC4034, that is, the data are treated +// "as a left-justified unsigned octet sequence in which the absence of an +// octet sorts before a zero octet." +// +// If check_length_first is true, it treats the compared data as if they +// began with a single-octet "length" field whose value is the size of the +// corresponding vector. In this case, if the sizes of the two vectors are +// different the shorter one is always considered the "smaller"; the contents +// of the vector don't matter. +// +// This function returns: +// -1 if v1 is considered smaller than v2 +// 1 if v1 is considered larger than v2 +// 0 otherwise +int +compareVectors(const vector<uint8_t>& v1, const vector<uint8_t>& v2, + bool check_length_first = true) +{ + const size_t len1 = v1.size(); + const size_t len2 = v2.size(); + if (check_length_first && len1 != len2) { + return (len1 - len2); + } + const size_t cmplen = min(len1, len2); + const int cmp = cmplen == 0 ? 0 : memcmp(&v1.at(0), &v2.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (len1 - len2); + } +} +} + +int +NSEC3::compare(const Rdata& other) const { + const NSEC3& other_nsec3 = dynamic_cast<const NSEC3&>(other); + + if (impl_->hashalg_ != other_nsec3.impl_->hashalg_) { + return (impl_->hashalg_ < other_nsec3.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_nsec3.impl_->flags_) { + return (impl_->flags_ < other_nsec3.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_nsec3.impl_->iterations_) { + return (impl_->iterations_ < other_nsec3.impl_->iterations_ ? -1 : 1); + } + + int cmp = compareVectors(impl_->salt_, other_nsec3.impl_->salt_); + if (cmp != 0) { + return (cmp); + } + cmp = compareVectors(impl_->next_, other_nsec3.impl_->next_); + if (cmp != 0) { + return (cmp); + } + // Note that bitmap doesn't have a dedicated length field, so we shouldn't + // terminate the comparison just because the lengths are different. + return (compareVectors(impl_->typebits_, other_nsec3.impl_->typebits_, + false)); +} + +uint8_t +NSEC3::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3::getSalt() const { + return (impl_->salt_); +} + +const vector<uint8_t>& +NSEC3::getNext() const { + return (impl_->next_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> + +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec3param_common.h> + +#include <boost/lexical_cast.hpp> + +#include <memory> +#include <string> +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSEC3PARAMImpl { + // straightforward representation of NSEC3PARAM RDATA fields + NSEC3PARAMImpl(uint8_t hashalg, uint8_t flags, uint16_t iterations, + const vector<uint8_t>& salt) : + hashalg_(hashalg), flags_(flags), iterations_(iterations), salt_(salt) + {} + + const uint8_t hashalg_; + const uint8_t flags_; + const uint16_t iterations_; + const vector<uint8_t> salt_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC3PARAM 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. +/// +/// The Hash Algorithm, Flags and Iterations fields must be within their +/// valid ranges. The Salt field may contain "-" to indicate that the +/// salt is of length 0. The Salt field must not contain any whitespace. +/// +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param nsec3param_str A string containing the RDATA to be created +NSEC3PARAM::NSEC3PARAM(const std::string& nsec3param_str) : + 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 NSEC3PARAMImpl that constructFromLexer() returns. + std::unique_ptr<NSEC3PARAMImpl> impl_ptr; + + try { + std::istringstream ss(nsec3param_str); + 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 NSEC3PARAM: " << nsec3param_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC3PARAM from '" << nsec3param_str + << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC3PARAM RDATA. +/// +/// See \c NSEC3PARAM::NSEC3PARAM(const std::string&) for description of +/// the expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +NSEC3PARAM::NSEC3PARAM(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(NULL) +{ + impl_ = constructFromLexer(lexer); +} + +NSEC3PARAMImpl* +NSEC3PARAM::constructFromLexer(MasterLexer& lexer) { + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamFromLexer("NSEC3PARAM", lexer, salt); + + return (new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt)); +} + +NSEC3PARAM::NSEC3PARAM(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + vector<uint8_t> salt; + const ParseNSEC3ParamResult params = + parseNSEC3ParamWire("NSEC3PARAM", buffer, rdata_len, salt); + + impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags, + params.iterations, salt); +} + +NSEC3PARAM::NSEC3PARAM(const NSEC3PARAM& source) : + Rdata(), impl_(new NSEC3PARAMImpl(*source.impl_)) +{} + +NSEC3PARAM& +NSEC3PARAM::operator=(const NSEC3PARAM& source) { + if (this == &source) { + return (*this); + } + + NSEC3PARAMImpl* newimpl = new NSEC3PARAMImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC3PARAM::~NSEC3PARAM() { + delete impl_; +} + +string +NSEC3PARAM::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) + + " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) + + " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_))); +} + +template <typename OUTPUT_TYPE> +void +toWireHelper(const NSEC3PARAMImpl& impl, OUTPUT_TYPE& output) { + output.writeUint8(impl.hashalg_); + output.writeUint8(impl.flags_); + output.writeUint16(impl.iterations_); + output.writeUint8(impl.salt_.size()); + if (!impl.salt_.empty()) { + output.writeData(&impl.salt_[0], impl.salt_.size()); + } +} + +void +NSEC3PARAM::toWire(OutputBuffer& buffer) const { + toWireHelper(*impl_, buffer); +} + +void +NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const { + toWireHelper(*impl_, renderer); +} + +int +NSEC3PARAM::compare(const Rdata& other) const { + const NSEC3PARAM& other_param = dynamic_cast<const NSEC3PARAM&>(other); + + if (impl_->hashalg_ != other_param.impl_->hashalg_) { + return (impl_->hashalg_ < other_param.impl_->hashalg_ ? -1 : 1); + } + if (impl_->flags_ != other_param.impl_->flags_) { + return (impl_->flags_ < other_param.impl_->flags_ ? -1 : 1); + } + if (impl_->iterations_ != other_param.impl_->iterations_) { + return (impl_->iterations_ < other_param.impl_->iterations_ ? -1 : 1); + } + + const size_t this_len = impl_->salt_.size(); + const size_t other_len = other_param.impl_->salt_.size(); + if (this_len != other_len) { + return (this_len - other_len); + } + const size_t cmplen = min(this_len, other_len); + const int cmp = (cmplen == 0) ? 0 : + memcmp(&impl_->salt_.at(0), &other_param.impl_->salt_.at(0), cmplen); + if (cmp != 0) { + return (cmp); + } else { + return (this_len - other_len); + } +} + +uint8_t +NSEC3PARAM::getHashalg() const { + return (impl_->hashalg_); +} + +uint8_t +NSEC3PARAM::getFlags() const { + return (impl_->flags_); +} + +uint16_t +NSEC3PARAM::getIterations() const { + return (impl_->iterations_); +} + +const vector<uint8_t>& +NSEC3PARAM::getSalt() const { + return (impl_->salt_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/nsec_bitmap.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata::generic::detail::nsec; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct NSECImpl { + // straightforward representation of NSEC RDATA fields + NSECImpl(const Name& next, vector<uint8_t> typebits) : + nextname_(next), typebits_(typebits) + {} + + Name nextname_; + vector<uint8_t> typebits_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid NSEC 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. +/// +/// The Next Domain Name field must be absolute since there's no +/// parameter that specifies the origin name; if it is not absolute, +/// \c MissingNameOrigin exception will be thrown. This must not be +/// represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not absolute. +/// \throw InvalidRdataText if any fields are out of their valid range. +/// +/// \param nsec_str A string containing the RDATA to be created +NSEC::NSEC(const std::string& nsec_str) : + impl_(NULL) +{ + try { + std::istringstream ss(nsec_str); + MasterLexer lexer; + lexer.pushSource(ss); + + const Name origin_name(createNameFromLexer(lexer, NULL)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(origin_name, typebits); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for NSEC: " << nsec_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct NSEC from '" << nsec_str << "': " + << ex.what()); + } +} + +NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) { + const size_t pos = buffer.getPosition(); + const Name nextname(buffer); + + // rdata_len must be sufficiently large to hold non empty bitmap. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(DNSMessageFORMERR, + "NSEC RDATA from wire too short: " << rdata_len << "bytes"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> typebits(rdata_len); + buffer.readData(&typebits[0], rdata_len); + checkRRTypeBitmaps("NSEC", typebits); + + impl_ = new NSECImpl(nextname, typebits); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NSEC RDATA. +/// +/// The Next Domain Name field can be non-absolute if \c origin is +/// non-NULL, in which case \c origin is used to make it absolute. It +/// must not be represented as a quoted string. +/// +/// The type mnemonics must be valid, and separated by whitespace. If +/// any invalid mnemonics are found, InvalidRdataText exception is +/// thrown. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw MissingNameOrigin Thrown when the Next Domain Name is not +/// absolute and \c origin is NULL. +/// \throw InvalidRdataText if any fields are out of their valid range. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin The origin to use with a relative Next Domain Name +/// field +NSEC::NSEC(MasterLexer& lexer, const Name* origin, MasterLoader::Options, + MasterLoaderCallbacks&) +{ + const Name next_name(createNameFromLexer(lexer, origin)); + + vector<uint8_t> typebits; + buildBitmapsFromLexer("NSEC", lexer, typebits); + + impl_ = new NSECImpl(next_name, typebits); +} + +NSEC::NSEC(const NSEC& source) : + Rdata(), impl_(new NSECImpl(*source.impl_)) +{} + +NSEC& +NSEC::operator=(const NSEC& source) { + if (this == &source) { + return (*this); + } + + NSECImpl* newimpl = new NSECImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +NSEC::~NSEC() { + delete impl_; +} + +string +NSEC::toText() const { + ostringstream s; + s << impl_->nextname_; + bitmapsToText(impl_->typebits_, s); + return (s.str()); +} + +void +NSEC::toWire(OutputBuffer& buffer) const { + impl_->nextname_.toWire(buffer); + buffer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +void +NSEC::toWire(AbstractMessageRenderer& renderer) const { + // Type NSEC is not "well-known", and name compression must be disabled + // per RFC3597. + renderer.writeName(impl_->nextname_, false); + renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size()); +} + +const Name& +NSEC::getNextName() const { + return (impl_->nextname_); +} + +int +NSEC::compare(const Rdata& other) const { + const NSEC& other_nsec = dynamic_cast<const NSEC&>(other); + + int cmp = compareNames(impl_->nextname_, other_nsec.impl_->nextname_); + if (cmp != 0) { + return (cmp); + } + + const size_t this_len = impl_->typebits_.size(); + const size_t other_len = other_nsec.impl_->typebits_.size(); + const size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->typebits_[0], &other_nsec.impl_->typebits_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <boost/foreach.hpp> + +#include <string> +#include <string.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor. +OPT::PseudoRR::PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data) : + code_(code), + data_(data) +{ +} + +uint16_t +OPT::PseudoRR::getCode() const { + return (code_); +} + +const uint8_t* +OPT::PseudoRR::getData() const { + return (&(*data_)[0]); +} + +uint16_t +OPT::PseudoRR::getLength() const { + return (data_->size()); +} + +struct OPTImpl { + OPTImpl() : + rdlength_(0) + {} + + uint16_t rdlength_; + std::vector<OPT::PseudoRR> pseudo_rrs_; +}; + +/// \brief Default constructor. +OPT::OPT() : + impl_(new OPTImpl) +{ +} + +/// \brief Constructor from string. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(const std::string&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// This constructor cannot be used, and always throws an exception. +/// +/// \throw InvalidRdataText OPT RR cannot be constructed from text. +OPT::OPT(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(NULL) +{ + isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); +} + +OPT::OPT(InputBuffer& buffer, size_t rdata_len) : + impl_(NULL) +{ + std::unique_ptr<OPTImpl> impl_ptr(new OPTImpl); + + while (true) { + if (rdata_len == 0) { + break; + } + + if (rdata_len < 4) { + isc_throw(InvalidRdataLength, + "Pseudo OPT RR record too short: " + << rdata_len << " bytes"); + } + + const uint16_t option_code = buffer.readUint16(); + const uint16_t option_length = buffer.readUint16(); + rdata_len -= 4; + + if (static_cast<uint16_t>(impl_ptr->rdlength_ + option_length) < + impl_ptr->rdlength_) + { + isc_throw(InvalidRdataText, + "Option length " << option_length + << " would overflow OPT RR RDLEN (currently " + << impl_ptr->rdlength_ << ")."); + } + + if (rdata_len < option_length) { + isc_throw(InvalidRdataLength, "Corrupt pseudo OPT RR record"); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(option_length)); + buffer.readData(&(*option_data)[0], option_length); + impl_ptr->pseudo_rrs_.push_back(PseudoRR(option_code, option_data)); + impl_ptr->rdlength_ += option_length; + rdata_len -= option_length; + } + + impl_ = impl_ptr.release(); +} + +OPT::OPT(const OPT& other) : + Rdata(), impl_(new OPTImpl(*other.impl_)) +{ +} + +OPT& +OPT::operator=(const OPT& source) { + if (this == &source) { + return (*this); + } + + OPTImpl* newimpl = new OPTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +OPT::~OPT() { + delete impl_; +} + +std::string +OPT::toText() const { + isc_throw(isc::InvalidOperation, + "OPT RRs do not have a presentation format"); +} + +void +OPT::toWire(OutputBuffer& buffer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + buffer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + buffer.writeUint16(length); + if (length > 0) { + buffer.writeData(pseudo_rr.getData(), length); + } + } +} + +void +OPT::toWire(AbstractMessageRenderer& renderer) const { + BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) { + renderer.writeUint16(pseudo_rr.getCode()); + const uint16_t length = pseudo_rr.getLength(); + renderer.writeUint16(length); + if (length > 0) { + renderer.writeData(pseudo_rr.getData(), length); + } + } +} + +int +OPT::compare(const Rdata&) const { + isc_throw(isc::InvalidOperation, + "It is meaningless to compare a set of OPT pseudo RRs; " + "they have unspecified order"); + return (0); +} + +void +OPT::appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length) { + // See if it overflows 16-bit length field. We only worry about the + // pseudo-RR length here, not the whole message length (which should + // be checked and enforced elsewhere). + if (static_cast<uint16_t>(impl_->rdlength_ + length) < + impl_->rdlength_) + { + isc_throw(isc::InvalidParameter, + "Option length " << length + << " would overflow OPT RR RDLEN (currently " + << impl_->rdlength_ << ")."); + } + + boost::shared_ptr<std::vector<uint8_t> > + option_data(new std::vector<uint8_t>(length)); + if (length != 0) { + std::memcpy(&(*option_data)[0], data, length); + } + impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data)); + impl_->rdlength_ += length; +} + +const std::vector<OPT::PseudoRR>& +OPT::getPseudoRRs() const { + return (impl_->pseudo_rrs_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// The given string must represent a valid PTR 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. +/// +/// The PTRDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +PTR::PTR(const std::string& type_str) : + // Fill in dummy name and replace them soon below. + ptr_name_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(type_str); + MasterLexer lexer; + lexer.pushSource(ss); + + ptr_name_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for PTR: " + << type_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct PTR from '" << + type_str << "': " << ex.what()); + } +} + +PTR::PTR(InputBuffer& buffer, size_t) : + ptr_name_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of a PTR RDATA. The PTRDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 PTRDNAME when it +/// is non-absolute. +PTR::PTR(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + ptr_name_(createNameFromLexer(lexer, origin)) +{} + +PTR::PTR(const PTR& source) : + Rdata(), ptr_name_(source.ptr_name_) +{} + +std::string +PTR::toText() const { + return (ptr_name_.toText()); +} + +void +PTR::toWire(OutputBuffer& buffer) const { + ptr_name_.toWire(buffer); +} + +void +PTR::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(ptr_name_); +} + +int +PTR::compare(const Rdata& other) const { + // The compare method normally begins with this dynamic cast. + const PTR& other_ptr = dynamic_cast<const PTR&>(other); + + return (compareNames(ptr_name_, other_ptr.ptr_name_)); + +} + +const Name& +PTR::getPTRName() const { + return (ptr_name_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> +#include <sstream> + +#include <util/buffer.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief Constructor from string. +/// +/// \c rp_str must be formatted as follows: +/// \code <mailbox name> <text name> +/// \endcode +/// where both fields must represent a valid domain name. +/// +/// \throw InvalidRdataText The number of RDATA fields (must be 2) is +/// incorrect. +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// given name is invalid. +RP::RP(const std::string& rp_str) : + // We cannot construct both names in the initialization list due to the + // necessary text processing, so we have to initialize them with a dummy + // name and replace them later. + mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(rp_str); + MasterLexer lexer; + lexer.pushSource(ss); + + mailbox_ = createNameFromLexer(lexer, NULL); + text_ = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RP: " + << rp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RP from '" << + rp_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RP RDATA. The MAILBOX and TEXT fields can be non-absolute if \c +/// origin is non-NULL, in which case \c origin is used to make them absolute. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and constructors if construction of +/// textual fields as these objects fail. +/// +/// \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 SERVER when it +/// is non-absolute. +RP::RP(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mailbox_(createNameFromLexer(lexer, origin)), + text_(createNameFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// This constructor doesn't check the validity of the second parameter (rdata +/// length) for parsing. +/// If necessary, the caller will check consistency. +/// +/// \throw std::bad_alloc Memory allocation for names fails. +/// \throw Other The constructor of the \c Name class will throw if the +/// names in the wire is invalid. +RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) { +} + +/// \brief Copy constructor. +/// +/// \throw std::bad_alloc Memory allocation fails in copying internal +/// member variables (this should be very rare). +RP::RP(const RP& other) : + Rdata(), mailbox_(other.mailbox_), text_(other.text_) +{} + +/// \brief Convert the \c RP to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c RP(const std::string&))). +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \return A \c string object that represents the \c RP object. +std::string +RP::toText() const { + return (mailbox_.toText() + " " + text_.toText()); +} + +/// \brief Render the \c RP in the wire format without name compression. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param buffer An output buffer to store the wire data. +void +RP::toWire(OutputBuffer& buffer) const { + mailbox_.toWire(buffer); + text_.toWire(buffer); +} + +/// \brief Render the \c RP in the wire format with taking into account +/// compression. +/// +// Type RP is not "well-known", and name compression must be disabled +// per RFC3597. +/// +/// \throw std::bad_alloc Internal resource allocation fails. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +RP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mailbox_, false); + renderer.writeName(text_, false); +} + +/// \brief Compare two instances of \c RP RDATA. +/// +/// See documentation in \c Rdata. +int +RP::compare(const Rdata& other) const { + const RP& other_rp = dynamic_cast<const RP&>(other); + + const int cmp = compareNames(mailbox_, other_rp.mailbox_); + if (cmp != 0) { + return (cmp); + } + return (compareNames(text_, other_rp.text_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/encode/base64.h> +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <stdio.h> +#include <time.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +namespace { +// This is the minimum necessary length of all wire-format RRSIG RDATA: +// - two 8-bit fields (algorithm and labels) +// - two 16-bit fields (covered and tag) +// - three 32-bit fields (original TTL, expire and inception) +const size_t RRSIG_MINIMUM_LEN = 2 * sizeof(uint8_t) + 2 * sizeof(uint16_t) + + 3 * sizeof(uint32_t); +} + +struct RRSIGImpl { + // straightforward representation of RRSIG RDATA fields + RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels, + uint32_t originalttl, uint32_t timeexpire, + uint32_t timeinception, uint16_t tag, const Name& signer, + const vector<uint8_t>& signature) : + covered_(covered), algorithm_(algorithm), labels_(labels), + originalttl_(originalttl), timeexpire_(timeexpire), + timeinception_(timeinception), tag_(tag), signer_(signer), + signature_(signature) + {} + + const RRType covered_; + uint8_t algorithm_; + uint8_t labels_; + uint32_t originalttl_; + uint32_t timeexpire_; + uint32_t timeinception_; + uint16_t tag_; + const Name signer_; + const vector<uint8_t> signature_; +}; + +// helper function for string and lexer constructors +RRSIGImpl* +RRSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const RRType covered(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 0xff) { + isc_throw(InvalidRdataText, "RRSIG algorithm out of range"); + } + const uint32_t labels = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (labels > 0xff) { + isc_throw(InvalidRdataText, "RRSIG labels out of range"); + } + const uint32_t originalttl = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + const uint32_t timeexpire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t timeinception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + const uint32_t tag = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (tag > 0xffff) { + isc_throw(InvalidRdataText, "RRSIG key tag out of range"); + } + const Name& signer = createNameFromLexer(lexer, origin); + + string signature_txt; + string signature_part; + // Whitespace is allowed within base64 text, 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)) { + break; + } + token.getString(signature_part); + signature_txt.append(signature_part); + } + lexer.ungetToken(); + + vector<uint8_t> signature; + // missing signature is okay + if (signature_txt.size() > 0) { + decodeBase64(signature_txt, signature); + } + + return (new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, + static_cast<uint16_t>(tag), signer, signature)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid RRSIG 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. +/// +/// The Signer's Name must be absolute since there's no parameter that +/// specifies the origin name; if this is not absolute, \c MissingNameOrigin +/// exception will be thrown. This must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name constructor. +/// \throw InvalidRdataText Other general syntax errors. +RRSIG::RRSIG(const std::string& rrsig_str) : + 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 RRSIGImpl that constructFromLexer() returns. + std::unique_ptr<RRSIGImpl> impl_ptr; + + try { + std::istringstream iss(rrsig_str); + MasterLexer lexer; + lexer.pushSource(iss); + + impl_ptr.reset(constructFromLexer(lexer, NULL)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for RRSIG: " + << rrsig_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct RRSIG from '" << + rrsig_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an RRSIG RDATA. The Signer's Name fields can be non absolute if \c +/// origin is non NULL, in which case \c origin is used to make it absolute. +/// This must not be represented as a quoted string. +/// +/// The Original TTL field is a valid decimal representation of an unsigned +/// 32-bit integer. Note that alternate textual representations of \c RRTTL, +/// such as "1H" for 3600 seconds, are not allowed here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name constructor if +/// construction of textual fields as these objects fail. +/// +/// \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 Signer's Name when +/// it is non absolute. +RRSIG::RRSIG(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +RRSIG::RRSIG(InputBuffer& buffer, size_t rdata_len) { + size_t pos = buffer.getPosition(); + + if (rdata_len < RRSIG_MINIMUM_LEN) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + + RRType covered(buffer); + uint8_t algorithm = buffer.readUint8(); + uint8_t labels = buffer.readUint8(); + uint32_t originalttl = buffer.readUint32(); + uint32_t timeexpire = buffer.readUint32(); + uint32_t timeinception = buffer.readUint32(); + uint16_t tag = buffer.readUint16(); + Name signer(buffer); + + // rdata_len must be sufficiently large to hold non empty signature data. + if (rdata_len <= buffer.getPosition() - pos) { + isc_throw(InvalidRdataLength, "RRSIG too short"); + } + rdata_len -= (buffer.getPosition() - pos); + + vector<uint8_t> signature(rdata_len); + buffer.readData(&signature[0], rdata_len); + + impl_ = new RRSIGImpl(covered, algorithm, labels, + originalttl, timeexpire, timeinception, tag, + signer, signature); +} + +RRSIG::RRSIG(const RRSIG& source) : + Rdata(), impl_(new RRSIGImpl(*source.impl_)) +{} + +RRSIG& +RRSIG::operator=(const RRSIG& source) { + if (this == &source) { + return (*this); + } + + RRSIGImpl* newimpl = new RRSIGImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +RRSIG::~RRSIG() { + delete impl_; +} + +string +RRSIG::toText() const { + return (impl_->covered_.toText() + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_)) + + " " + boost::lexical_cast<string>(impl_->originalttl_) + + " " + timeToText32(impl_->timeexpire_) + + " " + timeToText32(impl_->timeinception_) + + " " + boost::lexical_cast<string>(impl_->tag_) + + " " + impl_->signer_.toText() + + " " + encodeBase64(impl_->signature_)); +} + +void +RRSIG::toWire(OutputBuffer& buffer) const { + impl_->covered_.toWire(buffer); + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->labels_); + buffer.writeUint32(impl_->originalttl_); + buffer.writeUint32(impl_->timeexpire_); + buffer.writeUint32(impl_->timeinception_); + buffer.writeUint16(impl_->tag_); + impl_->signer_.toWire(buffer); + buffer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +void +RRSIG::toWire(AbstractMessageRenderer& renderer) const { + impl_->covered_.toWire(renderer); + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->labels_); + renderer.writeUint32(impl_->originalttl_); + renderer.writeUint32(impl_->timeexpire_); + renderer.writeUint32(impl_->timeinception_); + renderer.writeUint16(impl_->tag_); + renderer.writeName(impl_->signer_, false); + renderer.writeData(&impl_->signature_[0], impl_->signature_.size()); +} + +int +RRSIG::compare(const Rdata& other) const { + const RRSIG& other_rrsig = dynamic_cast<const RRSIG&>(other); + + if (impl_->covered_.getCode() != other_rrsig.impl_->covered_.getCode()) { + return (impl_->covered_.getCode() < + other_rrsig.impl_->covered_.getCode() ? -1 : 1); + } + if (impl_->algorithm_ != other_rrsig.impl_->algorithm_) { + return (impl_->algorithm_ < other_rrsig.impl_->algorithm_ ? -1 : 1); + } + if (impl_->labels_ != other_rrsig.impl_->labels_) { + return (impl_->labels_ < other_rrsig.impl_->labels_ ? -1 : 1); + } + if (impl_->originalttl_ != other_rrsig.impl_->originalttl_) { + return (impl_->originalttl_ < other_rrsig.impl_->originalttl_ ? + -1 : 1); + } + if (impl_->timeexpire_ != other_rrsig.impl_->timeexpire_) { + return (impl_->timeexpire_ < other_rrsig.impl_->timeexpire_ ? + -1 : 1); + } + if (impl_->timeinception_ != other_rrsig.impl_->timeinception_) { + return (impl_->timeinception_ < other_rrsig.impl_->timeinception_ ? + -1 : 1); + } + if (impl_->tag_ != other_rrsig.impl_->tag_) { + return (impl_->tag_ < other_rrsig.impl_->tag_ ? -1 : 1); + } + + int cmp = compareNames(impl_->signer_, other_rrsig.impl_->signer_); + if (cmp != 0) { + return (cmp); + } + + size_t this_len = impl_->signature_.size(); + size_t other_len = other_rrsig.impl_->signature_.size(); + size_t cmplen = min(this_len, other_len); + cmp = memcmp(&impl_->signature_[0], &other_rrsig.impl_->signature_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +const RRType& +RRSIG::typeCovered() const { + return (impl_->covered_); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +#include <boost/static_assert.hpp> +#include <boost/lexical_cast.hpp> + +#include <string> +#include <sstream> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +SOA::SOA(InputBuffer& buffer, size_t) : + mname_(buffer), rname_(buffer) +{ + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. + buffer.readData(numdata_, sizeof(numdata_)); +} + +namespace { +void +fillParameters(MasterLexer& lexer, uint8_t numdata[20]) { + // Copy serial, refresh, retry, expire, minimum. We accept the extended + // TTL-compatible style for the latter four. + OutputBuffer buffer(20); + buffer.writeUint32(lexer.getNextToken(MasterToken::NUMBER).getNumber()); + for (int i = 0; i < 4; ++i) { + buffer.writeUint32(RRTTL(lexer.getNextToken(MasterToken::STRING). + getString()).getValue()); + } + memcpy(numdata, buffer.getData(), buffer.getLength()); +} +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SOA 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. +/// +/// The MNAME and RNAME must be absolute since there's no parameter that +/// specifies the origin name; if these are not absolute, \c MissingNameOrigin +/// exception will be thrown. These must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SOA::SOA(const std::string& soastr) : + // Fill in dummy name and replace them soon below. + mname_(Name::ROOT_NAME()), rname_(Name::ROOT_NAME()) +{ + try { + std::istringstream ss(soastr); + MasterLexer lexer; + lexer.pushSource(ss); + + mname_ = createNameFromLexer(lexer, NULL); + rname_ = createNameFromLexer(lexer, NULL); + fillParameters(lexer, numdata_); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SOA: " + << soastr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SOA from '" << + soastr << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SOA RDATA. The MNAME and RNAME fields can be non absolute if +/// \c origin is non NULL, in which case \c origin is used to make them +/// absolute. These must not be represented as a quoted string. +/// +/// The REFRESH, RETRY, EXPIRE, and MINIMUM fields can be either a valid +/// decimal representation of an unsigned 32-bit integer or other +/// valid textual representation of \c RRTTL such as "1H" (which means 3600). +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 MNAME and RNAME when +/// they are non absolute. +SOA::SOA(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + mname_(createNameFromLexer(lexer, origin)), + rname_(createNameFromLexer(lexer, origin)) +{ + fillParameters(lexer, numdata_); +} + +SOA::SOA(const Name& mname, const Name& rname, uint32_t serial, + uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) : + mname_(mname), rname_(rname) +{ + OutputBuffer b(20); + b.writeUint32(serial); + b.writeUint32(refresh); + b.writeUint32(retry); + b.writeUint32(expire); + b.writeUint32(minimum); + assert(b.getLength() == sizeof(numdata_)); + memcpy(numdata_, b.getData(), sizeof(numdata_)); +} + +SOA::SOA(const SOA& other) : + Rdata(), mname_(other.mname_), rname_(other.rname_) +{ + memcpy(numdata_, other.numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(OutputBuffer& buffer) const { + mname_.toWire(buffer); + rname_.toWire(buffer); + buffer.writeData(numdata_, sizeof(numdata_)); +} + +void +SOA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(mname_); + renderer.writeName(rname_); + renderer.writeData(numdata_, sizeof(numdata_)); +} + +Serial +SOA::getSerial() const { + InputBuffer b(numdata_, sizeof(numdata_)); + return (Serial(b.readUint32())); +} + +uint32_t +SOA::getMinimum() const { + // Make sure the buffer access is safe. + BOOST_STATIC_ASSERT(sizeof(numdata_) == + sizeof(uint32_t) * 4 + sizeof(uint32_t)); + + InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t)); + return (b.readUint32()); +} + +string +SOA::toText() const { + InputBuffer b(numdata_, sizeof(numdata_)); + uint32_t serial = b.readUint32(); + uint32_t refresh = b.readUint32(); + uint32_t retry = b.readUint32(); + uint32_t expire = b.readUint32(); + uint32_t minimum = b.readUint32(); + + return (mname_.toText() + " " + rname_.toText() + " " + + lexical_cast<string>(serial) + " " + + lexical_cast<string>(refresh) + " " + + lexical_cast<string>(retry) + " " + + lexical_cast<string>(expire) + " " + + lexical_cast<string>(minimum)); +} + +int +SOA::compare(const Rdata& other) const { + const SOA& other_soa = dynamic_cast<const SOA&>(other); + + int order = compareNames(mname_, other_soa.mname_); + if (order != 0) { + return (order); + } + + order = compareNames(rname_, other_soa.rname_); + if (order != 0) { + return (order); + } + + return (memcmp(numdata_, other_soa.numdata_, sizeof(numdata_))); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class. The semantics of the class is provided by +/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF. +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +/// \brief The assignment operator +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +SPF& +SPF::operator=(const SPF& source) { + if (this == &source) { + return (*this); + } + + SPFImpl* newimpl = new SPFImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +/// \brief The destructor +SPF::~SPF() { + delete impl_; +} + +/// \brief Constructor from wire-format data. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(InputBuffer& buffer, size_t rdata_len) : + impl_(new SPFImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \throw CharStringTooLong the parameter string length exceeds maximum. +/// \throw InvalidRdataText the method cannot process the parameter data +/// +/// \param lexer A \c MasterLexer object parsing a master file for this +/// RDATA. +SPF::SPF(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new SPFImpl(lexer)) +{} + +/// \brief Constructor from string. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const std::string& txtstr) : + impl_(new SPFImpl(txtstr)) +{} + +/// \brief Copy constructor +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +SPF::SPF(const SPF& other) : + Rdata(), impl_(new SPFImpl(*other.impl_)) +{} + +/// \brief Render the \c SPF in the wire format to a OutputBuffer object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +/// \brief Render the \c SPF in the wire format to an AbstractMessageRenderer +/// object +/// +/// \return is the return of the corresponding implementation method. +void +SPF::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +/// \brief Convert the \c SPF to a string. +/// +/// \return is the return of the corresponding implementation method. +string +SPF::toText() const { + return (impl_->toText()); +} + +/// \brief Compare two instances of \c SPF RDATA. +/// +/// This method compares \c this and the \c other \c SPF objects. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c SPF object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// +/// \param other the right-hand operand to compare against. +/// \return is the return of the corresponding implementation method. +int +SPF::compare(const Rdata& other) const { + const SPF& other_txt = dynamic_cast<const SPF&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2012-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 <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct SSHFPImpl { + // straightforward representation of SSHFP RDATA fields + SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type, + const vector<uint8_t>& fingerprint) : + algorithm_(algorithm), + fingerprint_type_(fingerprint_type), + fingerprint_(fingerprint) + {} + + uint8_t algorithm_; + uint8_t fingerprint_type_; + const vector<uint8_t> fingerprint_; +}; + +// helper function for string and lexer constructors +SSHFPImpl* +SSHFP::constructFromLexer(MasterLexer& lexer) { + const uint32_t algorithm = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (algorithm > 255) { + isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); + } + + const uint32_t fingerprint_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (fingerprint_type > 255) { + isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); + } + + std::string fingerprint_str; + std::string fingerprint_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + token.getString(fingerprint_substr); + fingerprint_str.append(fingerprint_substr); + } + lexer.ungetToken(); + + vector<uint8_t> fingerprint; + // If fingerprint is missing, it's OK. See the API documentation of the + // constructor. + if (fingerprint_str.size() > 0) { + try { + decodeHex(fingerprint_str, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + } + + return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SSHFP 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. +/// +/// The Algorithm and Fingerprint Type fields must be within their valid +/// ranges, but are not constrained to the values defined in RFC4255. +/// +/// The Fingerprint field may be absent, but if present it must contain a +/// valid hex encoding of the fingerprint. For compatibility with BIND 9, +/// whitespace is allowed in the hex text (RFC4255 is silent on the matter). +/// +/// \throw InvalidRdataText if any fields are missing, are out of their +/// valid ranges or are incorrect, or if the fingerprint is not a valid +/// hex string. +/// +/// \param sshfp_str A string containing the RDATA to be created +SSHFP::SSHFP(const string& sshfp_str) : + 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 SSHFPImpl that constructFromLexer() returns. + std::unique_ptr<SSHFPImpl> impl_ptr; + + try { + std::istringstream ss(sshfp_str); + 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 SSHFP: " + << sshfp_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" << + sshfp_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SSHFP RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range or are +/// incorrect, or if the fingerprint is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +SSHFP::SSHFP(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid SSHFP RDATA. +/// +/// The Algorithm and Fingerprint Type fields are not checked for unknown +/// values. It is okay for the fingerprint data to be missing (see the +/// description of the constructor from string). +SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "SSHFP record too short"); + } + + const uint8_t algorithm = buffer.readUint8(); + const uint8_t fingerprint_type = buffer.readUint8(); + + vector<uint8_t> fingerprint; + rdata_len -= 2; + if (rdata_len > 0) { + fingerprint.resize(rdata_len); + buffer.readData(&fingerprint[0], rdata_len); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, + const string& fingerprint_txt) : + impl_(NULL) +{ + vector<uint8_t> fingerprint; + try { + decodeHex(fingerprint_txt, fingerprint); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what()); + } + + impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint); +} + +SSHFP::SSHFP(const SSHFP& other) : + Rdata(), impl_(new SSHFPImpl(*other.impl_)) +{} + +SSHFP& +SSHFP::operator=(const SSHFP& source) { + if (this == &source) { + return (*this); + } + + SSHFPImpl* newimpl = new SSHFPImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SSHFP::~SSHFP() { + delete impl_; +} + +void +SSHFP::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->algorithm_); + buffer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + buffer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +void +SSHFP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->algorithm_); + renderer.writeUint8(impl_->fingerprint_type_); + + if (!impl_->fingerprint_.empty()) { + renderer.writeData(&impl_->fingerprint_[0], + impl_->fingerprint_.size()); + } +} + +string +SSHFP::toText() const { + return (lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->fingerprint_type_)) + + (impl_->fingerprint_.empty() ? "" : + " " + encodeHex(impl_->fingerprint_))); +} + +int +SSHFP::compare(const Rdata& other) const { + const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other); + + if (impl_->algorithm_ < other_sshfp.impl_->algorithm_) { + return (-1); + } else if (impl_->algorithm_ > other_sshfp.impl_->algorithm_) { + return (1); + } + + if (impl_->fingerprint_type_ < other_sshfp.impl_->fingerprint_type_) { + return (-1); + } else if (impl_->fingerprint_type_ > + other_sshfp.impl_->fingerprint_type_) { + return (1); + } + + const size_t this_len = impl_->fingerprint_.size(); + const size_t other_len = other_sshfp.impl_->fingerprint_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->fingerprint_[0], + &other_sshfp.impl_->fingerprint_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +SSHFP::getAlgorithmNumber() const { + return (impl_->algorithm_); +} + +uint8_t +SSHFP::getFingerprintType() const { + return (impl_->fingerprint_type_); +} + +const std::vector<uint8_t>& +SSHFP::getFingerprint() const { + return (impl_->fingerprint_); +} + +size_t +SSHFP::getFingerprintLength() const { + return (impl_->fingerprint_.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 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 <config.h> + +#include <string> +#include <sstream> +#include <vector> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <util/time_utilities.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rcode.h> +#include <dns/rdata/generic/detail/lexer_util.h> + +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; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +const uint16_t TKEY::GSS_API_MODE = 3; + +// straightforward representation of TKEY RDATA fields +struct TKEYImpl { + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key The key (can be empty). + /// \param other_data The other data (can be and usually is empty). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, vector<uint8_t>& key, + vector<uint8_t>& other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), key_(key), other_data_(other_data) + {} + + /// \brief Constructor from RDATA field parameters. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key_len The key length (0 means no key). + /// \param key The key (can be 0). + /// \param other_len The other data length (0 means no other data). + /// \param other_data The other data (can be and usually is 0). + TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, size_t key_len, + const void* key, size_t other_len, const void* other_data) : + algorithm_(algorithm), inception_(inception), expire_(expire), + mode_(mode), error_(error), + key_(key_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(key), + static_cast<const uint8_t*>(key) + key_len) : + vector<uint8_t>(key_len)), + other_data_(other_len > 0 ? + vector<uint8_t>(static_cast<const uint8_t*>(other_data), + static_cast<const uint8_t*>(other_data) + + other_len) : + vector<uint8_t>(other_len)) + {} + + /// \brief Common part of toWire methods. + /// \tparam Output \c OutputBuffer or \c AbstractMessageRenderer. + template <typename Output> + void toWireCommon(Output& output) const; + + /// \brief The DNS name of the algorithm e.g. gss-tsig. + const Name algorithm_; + + /// \brief The inception time (in seconds since 1970). + const uint32_t inception_; + + /// \brief The expire time (in seconds since 1970). + const uint32_t expire_; + + /// \brief The mode e.g. Diffie-Hellman (2) or GSS-API (3). + const uint16_t mode_; + + /// \brief The error code (extended error space shared with TSIG). + const uint16_t error_; + + /// \brief The key (can be empty). + const vector<uint8_t> key_; + + /// \brief The other data (can be and usually is empty). + const vector<uint8_t> other_data_; +}; + +// helper function for string and lexer constructors +TKEYImpl* +TKEY::constructFromLexer(MasterLexer& lexer, const Name* origin) { + const Name& algorithm = + createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); + + const uint32_t inception = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + const uint32_t expire = + timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); + + /// The mode is either a mnemonic (only one is defined: GSS-API) or + /// a number. + const string& mode_txt = + lexer.getNextToken(MasterToken::STRING).getString(); + uint32_t mode = 0; + if (mode_txt == "GSS-API") { + mode = GSS_API_MODE; + } else { + /// we cast to uint32_t and range-check, because casting directly to + /// uint16_t will convert negative numbers to large positive numbers + try { + mode = boost::lexical_cast<uint32_t>(mode_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Mode"); + } + if (mode > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Mode 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<uint32_t>(error_txt); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, "Invalid TKEY Error"); + } + if (error > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Error out of range"); + } + } + + const uint32_t keylen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (keylen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Key Len out of range"); + } + const string keydata_txt = (keylen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> key_data; + decodeBase64(keydata_txt, key_data); + if (key_data.size() != keylen) { + isc_throw(InvalidRdataText, + "TKEY Key Data length does not match Other Len"); + } + + const uint32_t otherlen = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (otherlen > 0xffff) { + isc_throw(InvalidRdataText, "TKEY Other Len out of range"); + } + const string otherdata_txt = (otherlen > 0) ? + lexer.getNextToken(MasterToken::STRING).getString() : ""; + vector<uint8_t> other_data; + decodeBase64(otherdata_txt, other_data); + if (other_data.size() != otherlen) { + isc_throw(InvalidRdataText, + "TKEY 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 TKEYImpl(algorithm, inception, expire, mode, error, + key_data, other_data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TKEY 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 tkey_str must be formatted as follows: +/// \code <Algorithm Name> <Inception> <Expire> <Mode> <Error> +/// <Key Len> [<Key Data>] <Other Len> [<Other Data>] +/// \endcode +/// +/// Note that, since the Algorithm Name field is defined to be "in domain name +/// syntax", but it is not actually a domain name, it does not have to be +/// fully qualified. +/// +/// The Mode field is an unsigned 16-bit decimal integer as specified +/// in RFC2930 or a common mnemonic. Currently only "GSS-API" (case sensitive) +/// is supported ("Diffie-Hellman" is not). +/// +/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic +/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", +/// "BADTIME", "BADMODE", "BADNAME", and "BADALG" are supported +/// (case sensitive). In future versions other representations that +/// are compatible with the DNS RCODE may be supported. +/// +/// The Key Data and Other Data fields are base-64 encoded strings that do not +/// contain space characters. +/// If the Key Len field is 0, the Key Data field must not appear in +/// \c tkey_str. +/// If the Other Len field is 0, the Other Data field must not appear in +/// \c tkey_str. +/// The decoded data of the Key Data field is Key Len bytes of binary stream. +/// The decoded data of the Other Data field is Other Len bytes of binary +/// stream. +/// +/// An example of valid string is: +/// \code "gss-tsig. 20210501120000 20210501130000 0 3 aabbcc 0" \endcode +/// In this example Other Data is missing because Other Len is 0. +/// +/// Note that RFC2930 does not define the standard presentation format +/// of %TKEY RR, so the above syntax is implementation specific. +/// This is, however, compatible with the format acceptable to BIND 9's +/// RDATA parser. +/// +/// \throw Others Exception from the Name constructors. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// \throw BadValue if Key Data or Other Data is not validly encoded +/// in base-64. +/// +/// \param tkey_str A string containing the RDATA to be created +TKEY::TKEY(const std::string& tkey_str) : impl_(0) { + // 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 TKEYImpl that constructFromLexer() returns. + std::unique_ptr<TKEYImpl> impl_ptr; + + try { + std::istringstream ss(tkey_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer, 0)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, + "Extra input text for TKEY: " << tkey_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, + "Failed to construct TKEY from '" << tkey_str << "': " + << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an TKEY RDATA. +/// +/// See \c TKEY::TKEY(const std::string&) for description of the +/// expected RDATA fields. +/// +/// \throw MasterLexer::LexerError General parsing error such as +/// missing field. +/// \throw InvalidRdataText if any fields are out of their valid range, +/// or are incorrect. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TKEY::TKEY(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer, origin)) +{ +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not begin with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC3597, the Algorithm field must be a non compressed form +/// of domain name. But this implementation accepts a %TKEY RR even if that +/// field is compressed. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +/// But this constructor does not use this parameter; if necessary, the caller +/// must check consistency between the length parameter and the actual +/// RDATA length. +TKEY::TKEY(InputBuffer& buffer, size_t) : + impl_(0) +{ + Name algorithm(buffer); + + const uint32_t inception = buffer.readUint32(); + + const uint32_t expire = buffer.readUint32(); + + const uint16_t mode = buffer.readUint16(); + + const uint16_t error = buffer.readUint16(); + + const uint16_t key_len = buffer.readUint16(); + vector<uint8_t> key(key_len); + if (key_len > 0) { + buffer.readData(&key[0], key_len); + } + + const uint16_t other_len = buffer.readUint16(); + vector<uint8_t> other_data(other_len); + if (other_len > 0) { + buffer.readData(&other_data[0], other_len); + } + + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key, other_data); +} + +TKEY::TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, uint16_t key_len, + const void* key, uint16_t other_len, const void* other_data) : + impl_(0) +{ + if ((key_len == 0 && key != 0) || (key_len > 0 && key == 0)) { + isc_throw(InvalidParameter, "TKEY Key length and data inconsistent"); + } + if ((other_len == 0 && other_data != 0) || + (other_len > 0 && other_data == 0)) { + isc_throw(InvalidParameter, + "TKEY Other data length and data inconsistent"); + } + impl_ = new TKEYImpl(algorithm, inception, expire, mode, error, + key_len, key, other_len, other_data); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +TKEY::TKEY(const TKEY& source) : Rdata(), impl_(new TKEYImpl(*source.impl_)) +{} + +TKEY& +TKEY::operator=(const TKEY& source) { + if (this == &source) { + return (*this); + } + + TKEYImpl* newimpl = new TKEYImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TKEY::~TKEY() { + delete impl_; +} + +/// \brief Convert the \c TKEY to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c TKEY(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c TKEY object. +std::string +TKEY::toText() const { + string result; + + result += impl_->algorithm_.toText() + " " + + timeToText32(impl_->inception_) + " " + + timeToText32(impl_->expire_) + " "; + if (impl_->mode_ == GSS_API_MODE) { + result += "GSS-API "; + } else { + result += lexical_cast<string>(impl_->mode_) + " "; + } + result += TSIGError(impl_->error_).toText() + " " + + lexical_cast<string>(impl_->key_.size()) + " "; + if (!impl_->key_.empty()) { + result += encodeBase64(impl_->key_) + " "; + } + result += lexical_cast<string>(impl_->other_data_.size()); + if (!impl_->other_data_.empty()) { + result += " " + encodeBase64(impl_->other_data_); + } + + return (result); +} + +// Common sequence of toWire() operations used for the two versions of +// toWire(). +template <typename Output> +void +TKEYImpl::toWireCommon(Output& output) const { + output.writeUint32(inception_); + output.writeUint32(expire_); + output.writeUint16(mode_); + output.writeUint16(error_); + const uint16_t key_len = key_.size(); + output.writeUint16(key_len); + if (key_len > 0) { + output.writeData(&key_[0], key_len); + } + const uint16_t other_len = other_data_.size(); + output.writeUint16(other_len); + if (other_len > 0) { + output.writeData(&other_data_[0], other_len); + } +} + +/// \brief Render the \c TKEY in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +TKEY::toWire(OutputBuffer& buffer) const { + impl_->algorithm_.toWire(buffer); + impl_->toWireCommon<OutputBuffer>(buffer); +} + +/// \brief Render the \c TKEY in the wire format with taking into account +/// compression. +/// +/// As specified in RFC3597, the Algorithm field (a domain name) will not +/// be compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +TKEY::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(impl_->algorithm_, false); + impl_->toWireCommon<AbstractMessageRenderer>(renderer); +} + +// A helper function commonly used for TKEY::compare(). +int +vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { + const size_t this_size = v1.size(); + const size_t other_size = v2.size(); + if (this_size != other_size) { + return (this_size < other_size ? -1 : 1); + } + if (this_size > 0) { + return (memcmp(&v1[0], &v2[0], this_size)); + } + return (0); +} + +/// \brief Compare two instances of \c TKEY RDATA. +/// +/// This method compares \c this and the \c other \c TKEY objects +/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns +/// the result as an integer. +/// +/// This method is expected to be used in a polymorphic way, and the +/// parameter to compare against is therefore of the abstract \c Rdata class. +/// However, comparing two \c Rdata objects of different RR types +/// is meaningless, and \c other must point to a \c TKEY object; +/// otherwise, the standard \c bad_cast exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \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. +int +TKEY::compare(const Rdata& other) const { + const TKEY& other_tkey = dynamic_cast<const TKEY&>(other); + + const int ncmp = compareNames(impl_->algorithm_, + other_tkey.impl_->algorithm_); + if (ncmp != 0) { + return (ncmp); + } + + if (impl_->inception_ != other_tkey.impl_->inception_) { + return (impl_->inception_ < other_tkey.impl_->inception_ ? -1 : 1); + } + if (impl_->expire_ != other_tkey.impl_->expire_) { + return (impl_->expire_ < other_tkey.impl_->expire_ ? -1 : 1); + } + if (impl_->mode_ != other_tkey.impl_->mode_) { + return (impl_->mode_ < other_tkey.impl_->mode_ ? -1 : 1); + } + if (impl_->error_ != other_tkey.impl_->error_) { + return (impl_->error_ < other_tkey.impl_->error_ ? -1 : 1); + } + + const int vcmp = vectorComp(impl_->key_, other_tkey.impl_->key_); + if (vcmp != 0) { + return (vcmp); + } + return (vectorComp(impl_->other_data_, other_tkey.impl_->other_data_)); +} + +const Name& +TKEY::getAlgorithm() const { + return (impl_->algorithm_); +} + +uint32_t +TKEY::getInception() const { + return (impl_->inception_); +} + +string +TKEY::getInceptionDate() const { + return (timeToText32(impl_->inception_)); +} + +uint32_t +TKEY::getExpire() const { + return (impl_->expire_); +} + +string +TKEY::getExpireDate() const { + return (timeToText32(impl_->expire_)); +} + +uint16_t +TKEY::getMode() const { + return (impl_->mode_); +} + +uint16_t +TKEY::getError() const { + return (impl_->error_); +} + +uint16_t +TKEY::getKeyLen() const { + return (impl_->key_.size()); +} + +const void* +TKEY::getKey() const { + if (!impl_->key_.empty()) { + return (&impl_->key_[0]); + } else { + return (0); + } +} + +uint16_t +TKEY::getOtherLen() const { + return (impl_->other_data_.size()); +} + +const void* +TKEY::getOtherData() const { + if (!impl_->other_data_.empty()) { + return (&impl_->other_data_[0]); + } else { + return (0); + } +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// Copyright (C) 2014-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 <config.h> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/hex.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata_pimpl_holder.h> + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +struct TLSAImpl { + // straightforward representation of TLSA RDATA fields + TLSAImpl(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const vector<uint8_t>& data) : + certificate_usage_(certificate_usage), + selector_(selector), + matching_type_(matching_type), + data_(data) + {} + + uint8_t certificate_usage_; + uint8_t selector_; + uint8_t matching_type_; + const vector<uint8_t> data_; +}; + +// helper function for string and lexer constructors +TLSAImpl* +TLSA::constructFromLexer(MasterLexer& lexer) { + const uint32_t certificate_usage = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (certificate_usage > 255) { + isc_throw(InvalidRdataText, + "TLSA certificate usage field out of range"); + } + + const uint32_t selector = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (selector > 255) { + isc_throw(InvalidRdataText, + "TLSA selector field out of range"); + } + + const uint32_t matching_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (matching_type > 255) { + isc_throw(InvalidRdataText, + "TLSA matching type field out of range"); + } + + std::string certificate_assoc_data; + std::string data_substr; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + + token.getString(data_substr); + certificate_assoc_data.append(data_substr); + } + lexer.ungetToken(); + + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + return (new TLSAImpl(certificate_usage, selector, matching_type, data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TLSA 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. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. +/// +/// The Certificate Association Data Field field may be absent, but if +/// present it must contain a valid hex encoding of the data. Whitespace +/// is allowed in the hex text. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, or are incorrect, or Certificate Association Data is +/// not a valid hex string. +/// +/// \param tlsa_str A string containing the RDATA to be created +TLSA::TLSA(const string& tlsa_str) : + impl_(NULL) +{ + // We use a smart pointer here because if there is an exception in + // this constructor, the destructor is not called and there could be + // a leak of the TLSAImpl that constructFromLexer() returns. + RdataPimplHolder<TLSAImpl> impl_ptr; + + try { + std::istringstream ss(tlsa_str); + 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 TLSA: " + << tlsa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct TLSA from '" << + tlsa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an TLSA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range, or are +/// incorrect, or Certificate Association Data is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TLSA::TLSA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid TLSA RDATA. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. It is okay for the certificate association data +/// to be missing (see the description of the constructor from string). +TLSA::TLSA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 3) { + isc_throw(InvalidRdataLength, "TLSA record too short"); + } + + const uint8_t certificate_usage = buffer.readUint8(); + const uint8_t selector = buffer.readUint8(); + const uint8_t matching_type = buffer.readUint8(); + + vector<uint8_t> data; + rdata_len -= 3; + + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, + "Empty TLSA certificate association data"); + } + + data.resize(rdata_len); + buffer.readData(&data[0], rdata_len); + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data) : + impl_(NULL) +{ + if (certificate_assoc_data.empty()) { + isc_throw(InvalidRdataText, "Empty TLSA certificate association data"); + } + + vector<uint8_t> data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(const TLSA& other) : + Rdata(), impl_(new TLSAImpl(*other.impl_)) +{} + +TLSA& +TLSA::operator=(const TLSA& source) { + if (this == &source) { + return (*this); + } + + TLSAImpl* newimpl = new TLSAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TLSA::~TLSA() { + delete impl_; +} + +void +TLSA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->certificate_usage_); + buffer.writeUint8(impl_->selector_); + buffer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + buffer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +void +TLSA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->certificate_usage_); + renderer.writeUint8(impl_->selector_); + renderer.writeUint8(impl_->matching_type_); + + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + renderer.writeData(&impl_->data_[0], impl_->data_.size()); +} + +string +TLSA::toText() const { + // The constructors must ensure that the certificate association + // data field is not empty. + assert(!impl_->data_.empty()); + + return (lexical_cast<string>(static_cast<int>(impl_->certificate_usage_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->selector_)) + " " + + lexical_cast<string>(static_cast<int>(impl_->matching_type_)) + " " + + encodeHex(impl_->data_)); +} + +int +TLSA::compare(const Rdata& other) const { + const TLSA& other_tlsa = dynamic_cast<const TLSA&>(other); + + if (impl_->certificate_usage_ < other_tlsa.impl_->certificate_usage_) { + return (-1); + } else if (impl_->certificate_usage_ > + other_tlsa.impl_->certificate_usage_) { + return (1); + } + + if (impl_->selector_ < other_tlsa.impl_->selector_) { + return (-1); + } else if (impl_->selector_ > other_tlsa.impl_->selector_) { + return (1); + } + + if (impl_->matching_type_ < other_tlsa.impl_->matching_type_) { + return (-1); + } else if (impl_->matching_type_ > + other_tlsa.impl_->matching_type_) { + return (1); + } + + const size_t this_len = impl_->data_.size(); + const size_t other_len = other_tlsa.impl_->data_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->data_[0], + &other_tlsa.impl_->data_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +TLSA::getCertificateUsage() const { + return (impl_->certificate_usage_); +} + +uint8_t +TLSA::getSelector() const { + return (impl_->selector_); +} + +uint8_t +TLSA::getMatchingType() const { + return (impl_->matching_type_); +} + +const std::vector<uint8_t>& +TLSA::getData() const { + return (impl_->data_); +} + +size_t +TLSA::getDataLength() const { + return (impl_->data_.size()); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <string> +#include <vector> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdata/generic/detail/txt_like.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { + +TXT& +TXT::operator=(const TXT& source) { + if (this == &source) { + return (*this); + } + + TXTImpl* newimpl = new TXTImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TXT::~TXT() { + delete impl_; +} + +TXT::TXT(InputBuffer& buffer, size_t rdata_len) : + impl_(new TXTImpl(buffer, rdata_len)) +{} + +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \throw CharStringTooLong the parameter string length exceeds maximum. +/// \throw InvalidRdataText the method cannot process the parameter data +/// +/// \param lexer A \c MasterLexer object parsing a master file for this +/// RDATA. +TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new TXTImpl(lexer)) +{} + +TXT::TXT(const std::string& txtstr) : + impl_(new TXTImpl(txtstr)) +{} + +TXT::TXT(const TXT& other) : + Rdata(), impl_(new TXTImpl(*other.impl_)) +{} + +void +TXT::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +TXT::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +TXT::toText() const { + return (impl_->toText()); +} + +int +TXT::compare(const Rdata& other) const { + const TXT& other_txt = dynamic_cast<const TXT&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace hs { + +A::A(const std::string&) { + // TBD +} + +A::A(MasterLexer&, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + // TBD +} + +A::A(InputBuffer&, size_t) { + // TBD +} + +A::A(const A&) : Rdata() { + // TBD +} + +void +A::toWire(OutputBuffer&) const { + // TBD +} + +void +A::toWire(AbstractMessageRenderer&) const { + // TBD +} + +string +A::toText() const { + // TBD + isc_throw(InvalidRdataText, "Not implemented yet"); +} + +int +A::compare(const Rdata&) const { + return (0); // dummy. TBD +} + +} // end of namespace "hs" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/master_loader_callbacks.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +namespace { +void +convertToIPv4Addr(const char* src, size_t src_len, uint32_t* dst) { + // This check specifically rejects invalid input that begins with valid + // address text followed by a nul character (and possibly followed by + // further garbage). It cannot be detected by inet_pton(). + // + // Note that this is private subroutine of the in::A constructors, which + // pass std::string.size() or StringRegion::len as src_len, so it should + // be equal to strlen() unless there's an intermediate nul character. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/A RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/A RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/A RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv4 +/// address as specified in RFC1035, that is, four decimal numbers separated +/// by dots without any embedded spaces. Note that it excludes abbreviated +/// forms such as "10.1" to mean "10.0.0.1". +/// +/// Internally, this implementation uses the standard inet_pton() library +/// function for the AF_INET family to parse and convert the textual +/// representation. While standard compliant implementations of this function +/// should accept exactly what this constructor expects, specific +/// implementation may behave differently, in which case this constructor +/// will simply accept the result of inet_pton(). In any case, the user of +/// the class shouldn't assume such specific implementation behavior of +/// inet_pton(). +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv4 address to be used as the +/// RDATA. +A::A(const std::string& addrstr) { + convertToIPv4Addr(addrstr.c_str(), addrstr.size(), &addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN A RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version accepts beginning +/// spaces and trailing spaces or other characters. Trailing non space +/// characters would be considered an invalid form in an RR representation, +/// but handling such errors is not the responsibility of this constructor. +/// It also accepts other unusual syntax that would be considered valid +/// in the context of DNS master file; for example, it accepts an IPv4 +/// address surrounded by parentheses, such as "(192.0.2.1)", although it's +/// very unlikely to be used for this type of RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv4 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +A::A(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv4Addr(token.getStringRegion().beg, token.getStringRegion().len, + &addr_); +} + +A::A(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: Invalid length: " + << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/A RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +/// \brief Copy constructor. +A::A(const A& other) : Rdata(), addr_(other.addr_) +{} + +void +A::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +A::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv4 address of the RDATA. +string +A::toText() const { + char addr_string[sizeof("255.255.255.255")]; + + if (inet_ntop(AF_INET, &addr_, addr_string, sizeof(addr_string)) == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/A RDATA to textual IPv4 address"); + } + + return (addr_string); +} + +/// \brief Compare two in::A RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 32-bit integer. +int +A::compare(const Rdata& other) const { + const A& other_a = dynamic_cast<const A&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> + +#include <stdint.h> +#include <string.h> + +#include <cerrno> +#include <cstring> +#include <string> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <sys/socket.h> // for AF_INET/AF_INET6 + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +namespace { +void +convertToIPv6Addr(const char* src, size_t src_len, void* dst) { + // See a_1.cc for this check. + if (src_len != strlen(src)) { + isc_throw(InvalidRdataText, + "Bad IN/AAAA RDATA text: unexpected nul in string: '" + << src << "'"); + } + const int result = inet_pton(AF_INET6, src, dst); + if (result == 0) { + isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'"); + } else if (result < 0) { + isc_throw(isc::Unexpected, + "Unexpected failure in parsing IN/AAAA RDATA text: '" + << src << "': " << std::strerror(errno)); + } +} +} + +/// \brief Constructor from string. +/// +/// The given string must be a valid textual representation of an IPv6 +/// address as specified in RFC1886. +/// +/// No extra character should be contained in \c addrstr other than the +/// textual address. These include spaces and the nul character. +/// +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param addrstr Textual representation of IPv6 address to be used as the +/// RDATA. +AAAA::AAAA(const std::string& addrstr) { + convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a class IN AAAA RDATA. +/// +/// The acceptable form of the textual address is generally the same as the +/// string version of the constructor, but this version is slightly more +/// flexible. See the similar constructor of \c in::A class; the same +/// notes apply here. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdata The text extracted by the lexer isn't recognized as +/// a valid IPv6 address. +/// \throw Unexpected Unexpected system error in conversion (this should be +/// very rare). +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +AAAA::AAAA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + const MasterToken& token = lexer.getNextToken(MasterToken::STRING); + convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len, + addr_); +} + +/// \brief Copy constructor. +AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len != sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "Invalid length: " << rdata_len); + } + if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { + isc_throw(DNSMessageFORMERR, + "IN/AAAA RDATA construction from wire failed: " + "insufficient buffer length: " + << buffer.getLength() - buffer.getPosition()); + } + buffer.readData(&addr_, sizeof(addr_)); +} + +AAAA::AAAA(const AAAA& other) : Rdata() { + memcpy(addr_, other.addr_, sizeof(addr_)); +} + +/// \brief Return a textual form of the underlying IPv6 address of the RDATA. +void +AAAA::toWire(OutputBuffer& buffer) const { + buffer.writeData(&addr_, sizeof(addr_)); +} + +void +AAAA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&addr_, sizeof(addr_)); +} + +string +AAAA::toText() const { + char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + + if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string)) + == NULL) { + isc_throw(Unexpected, + "Failed to convert IN/AAAA RDATA to textual IPv6 address"); + } + + return (string(addr_string)); +} + +/// \brief Compare two in::AAAA RDATAs. +/// +/// In effect, it compares the two RDATA as an unsigned 128-bit integer. +int +AAAA::compare(const Rdata& other) const { + const AAAA& other_a = dynamic_cast<const AAAA&>(other); + return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <stdint.h> +#include <string.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::encode; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +void +DHCID::constructFromLexer(MasterLexer& lexer) { + string digest_txt = lexer.getNextToken(MasterToken::STRING).getString(); + + // Whitespace is allowed within base64 text, so read to the end of input. + string digest_part; + while (true) { + const MasterToken& token = + lexer.getNextToken(MasterToken::STRING, true); + if ((token.getType() == MasterToken::END_OF_FILE) || + (token.getType() == MasterToken::END_OF_LINE)) { + break; + } + token.getString(digest_part); + digest_txt.append(digest_part); + } + lexer.ungetToken(); + + decodeBase64(digest_txt, digest_); +} + +/// \brief Constructor from string. +/// +/// \param dhcid_str A base-64 representation of the DHCID binary data. +/// +/// \throw InvalidRdataText if the string could not be parsed correctly. +DHCID::DHCID(const std::string& dhcid_str) { + try { + std::istringstream iss(dhcid_str); + MasterLexer lexer; + lexer.pushSource(iss); + + constructFromLexer(lexer); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for DHCID: " + << dhcid_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct DHCID from '" << + dhcid_str << "': " << ex.what()); + } +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of a DHCID RDATA. +/// +/// \throw BadValue if the text is not valid base-64. +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +DHCID::DHCID(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) { + constructFromLexer(lexer); +} + +/// \brief Constructor from wire-format data. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes +DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len == 0) { + isc_throw(InvalidRdataLength, "Missing DHCID rdata"); + } + + digest_.resize(rdata_len); + buffer.readData(&digest_[0], rdata_len); +} + +/// \brief The copy constructor. +/// +/// This trivial copy constructor never throws an exception. +DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) +{} + +/// \brief Render the \c DHCID in the wire format. +/// +/// \param buffer An output buffer to store the wire data. +void +DHCID::toWire(OutputBuffer& buffer) const { + buffer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Render the \c DHCID in the wire format into a +/// \c MessageRenderer object. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer in which the \c DHCID is to be stored. +void +DHCID::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeData(&digest_[0], digest_.size()); +} + +/// \brief Convert the \c DHCID to a string. +/// +/// This method returns a \c std::string object representing the \c DHCID. +/// +/// \return A string representation of \c DHCID. +string +DHCID::toText() const { + return (encodeBase64(digest_)); +} + +/// \brief Compare two instances of \c DHCID RDATA. +/// +/// See documentation in \c Rdata. +int +DHCID::compare(const Rdata& other) const { + const DHCID& other_dhcid = dynamic_cast<const DHCID&>(other); + + size_t this_len = digest_.size(); + size_t other_len = other_dhcid.digest_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +/// \brief Accessor method to get the DHCID digest +/// +/// \return A reference to the binary DHCID data +const std::vector<uint8_t>& +DHCID::getDigest() const { + return (digest_); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +// 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/. + +#include <config.h> + +#include <iostream> +#include <sstream> + +#include <boost/lexical_cast.hpp> + +#include <util/buffer.h> +#include <util/strutil.h> + +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +#include <dns/rdata/generic/detail/lexer_util.h> + +using namespace std; +using namespace isc::util; +using namespace isc::util::str; +using isc::dns::rdata::generic::detail::createNameFromLexer; + +namespace isc { +namespace dns { +namespace rdata { +namespace in { + +struct SRVImpl { + // straightforward representation of SRV RDATA fields + SRVImpl(uint16_t priority, uint16_t weight, uint16_t port, + const Name& target) : + priority_(priority), weight_(weight), port_(port), + target_(target) + {} + + uint16_t priority_; + uint16_t weight_; + uint16_t port_; + Name target_; +}; + +/// \brief Constructor from string. +/// +/// The given string must represent a valid SRV 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. +/// +/// The TARGET name must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c MissingNameOrigin +/// exception will be thrown. It must not be represented as a quoted +/// string. +/// +/// See the construction that takes \c MasterLexer for other fields. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +SRV::SRV(const std::string& srv_str) : + impl_(NULL) +{ + try { + std::istringstream ss(srv_str); + MasterLexer lexer; + lexer.pushSource(ss); + + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority in: " << srv_str); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight in: " << srv_str); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port in: " << srv_str); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, NULL); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for SRV: " + << srv_str); + } + + impl_ = new SRVImpl(priority, weight, port, targetname); + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct SRV from '" << + srv_str << "': " << ex.what()); + } +} + +/// \brief Constructor from wire-format data. +/// +/// When a read operation on \c buffer fails (e.g., due to a corrupted +/// message) a corresponding exception from the \c InputBuffer class will +/// be thrown. +/// If the wire-format data does not end with a valid domain name, +/// a corresponding exception from the \c Name class will be thrown. +/// In addition, this constructor internally involves resource allocation, +/// and if it fails a corresponding standard exception will be thrown. +/// +/// According to RFC2782, the Target field must be a non compressed form +/// of domain name. But this implementation accepts a %SRV RR even if that +/// field is compressed as suggested in RFC3597. +/// +/// \param buffer A buffer storing the wire format data. +/// \param rdata_len The length of the RDATA in bytes, normally expected +/// to be the value of the RDLENGTH field of the corresponding RR. +SRV::SRV(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 6) { + isc_throw(InvalidRdataLength, "SRV too short"); + } + + const uint16_t priority = buffer.readUint16(); + const uint16_t weight = buffer.readUint16(); + const uint16_t port = buffer.readUint16(); + const Name targetname(buffer); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an SRV RDATA. The TARGET field can be non-absolute if \c origin +/// is non-NULL, in which case \c origin is used to make it absolute. +/// It must not be represented as a quoted string. +/// +/// The PRIORITY, WEIGHT and PORT fields must each be a valid decimal +/// representation of an unsigned 16-bit integers respectively. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \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 TARGET when it +/// is non-absolute. +SRV::SRV(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) +{ + uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV priority: " << num); + } + const uint16_t priority = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV weight: " << num); + } + const uint16_t weight = static_cast<uint16_t>(num); + + num = lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (num > 65535) { + isc_throw(InvalidRdataText, "Invalid SRV port: " << num); + } + const uint16_t port = static_cast<uint16_t>(num); + + const Name targetname = createNameFromLexer(lexer, origin); + + impl_ = new SRVImpl(priority, weight, port, targetname); +} + +/// \brief The copy constructor. +/// +/// It internally allocates a resource, and if it fails a corresponding +/// standard exception will be thrown. +/// This constructor never throws an exception otherwise. +SRV::SRV(const SRV& source) : + Rdata(), impl_(new SRVImpl(*source.impl_)) +{} + +SRV& +SRV::operator=(const SRV& source) { + if (this == &source) { + return (*this); + } + + SRVImpl* newimpl = new SRVImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +SRV::~SRV() { + delete impl_; +} + +/// \brief Convert the \c SRV to a string. +/// +/// The output of this method is formatted as described in the "from string" +/// constructor (\c SRV(const std::string&))). +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// +/// \return A \c string object that represents the \c SRV object. +string +SRV::toText() const { + using boost::lexical_cast; + return (lexical_cast<string>(impl_->priority_) + + " " + lexical_cast<string>(impl_->weight_) + + " " + lexical_cast<string>(impl_->port_) + + " " + impl_->target_.toText()); +} + +/// \brief Render the \c SRV in the wire format without name compression. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param buffer An output buffer to store the wire data. +void +SRV::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(impl_->priority_); + buffer.writeUint16(impl_->weight_); + buffer.writeUint16(impl_->port_); + impl_->target_.toWire(buffer); +} + +/// \brief Render the \c SRV in the wire format with taking into account +/// compression. +/// +/// As specified in RFC2782, the Target field (a domain name) will not be +/// compressed. However, the domain name could be a target of compression +/// of other compressible names (though pretty unlikely), the offset +/// information of the algorithm name may be recorded in \c renderer. +/// +/// If internal resource allocation fails, a corresponding +/// standard exception will be thrown. +/// This method never throws an exception otherwise. +/// +/// \param renderer DNS message rendering context that encapsulates the +/// output buffer and name compression information. +void +SRV::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(impl_->priority_); + renderer.writeUint16(impl_->weight_); + renderer.writeUint16(impl_->port_); + renderer.writeName(impl_->target_, false); +} + +/// \brief Compare two instances of \c SRV RDATA. +/// +/// See documentation in \c Rdata. +int +SRV::compare(const Rdata& other) const { + const SRV& other_srv = dynamic_cast<const SRV&>(other); + + if (impl_->priority_ != other_srv.impl_->priority_) { + return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1); + } + if (impl_->weight_ != other_srv.impl_->weight_) { + return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1); + } + if (impl_->port_ != other_srv.impl_->port_) { + return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1); + } + + return (compareNames(impl_->target_, other_srv.impl_->target_)); +} + +uint16_t +SRV::getPriority() const { + return (impl_->priority_); +} + +uint16_t +SRV::getWeight() const { + return (impl_->weight_); +} + +uint16_t +SRV::getPort() const { + return (impl_->port_); +} + +const Name& +SRV::getTarget() const { + return (impl_->target_); +} + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" diff --git a/src/lib/dns/rdataclass.h b/src/lib/dns/rdataclass.h new file mode 100644 index 0000000..85e6551 --- /dev/null +++ b/src/lib/dns/rdataclass.h @@ -0,0 +1,2746 @@ +/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + + +#ifndef DNS_RDATACLASS_H +#define DNS_RDATACLASS_H 1 + +#include <dns/master_loader.h> + +namespace isc { +namespace dns { +class Name; +class MasterLexer; +class MasterLoaderCallbacks; +} +} +// 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/. + +#ifndef ANY_TSIG_250_H +#define ANY_TSIG_250_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace any { + +struct TSIGImpl; + +/// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in +/// RFC2845. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// TSIG RDATA. +class TSIG : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit TSIG(const std::string& type_str); + TSIG(isc::util::InputBuffer& buffer, size_t rdata_len); + TSIG(const TSIG& other); + TSIG( + 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; + + // END_COMMON_MEMBERS + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %TSIG RDATA + /// fields as defined %in RFC2845, but there are some implementation + /// specific notes as follows. + /// + /// \c algorithm is a \c Name object that specifies the algorithm. + /// For example, if the algorithm is HMAC-SHA256, \c algorithm would be + /// \c Name("hmac-sha256"). + /// + /// \c time_signed corresponds to the Time Signed field, which is of + /// 48-bit unsigned integer type, and therefore cannot exceed 2^48-1; + /// otherwise, an exception of type \c OutOfRange will be thrown. + /// + /// \c mac_size and \c mac correspond to the MAC Size and MAC fields, + /// respectively. When the MAC field is empty, \c mac must be NULL. + /// \c mac_size and \c mac must be consistent %in that \c mac_size is 0 if + /// and only if \c mac is NULL; otherwise an exception of type + /// InvalidParameter will be thrown. + /// + /// The same restriction applies to \c other_len and \c other_data, + /// which correspond to the Other Len and Other Data fields, respectively. + /// + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, + uint16_t mac_size, const void* mac, uint16_t original_id, + uint16_t error, uint16_t other_len, const void* other_data); + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + TSIG& operator=(const TSIG& source); + + /// \brief The destructor. + ~TSIG(); + + /// \brief Return the algorithm name. + /// + /// This method never throws an exception. + const Name& getAlgorithm() const; + + /// \brief Return the value of the Time Signed field. + /// + /// The returned value does not exceed 2^48-1. + /// + /// This method never throws an exception. + uint64_t getTimeSigned() const; + + /// \brief Return the value of the Fudge field. + /// + /// This method never throws an exception. + uint16_t getFudge() const; + + /// \brief Return the value of the MAC Size field. + /// + /// This method never throws an exception. + uint16_t getMACSize() const; + + /// \brief Return the value of the MAC field. + /// + /// If the MAC field is empty, it returns NULL. + /// Otherwise, the memory region beginning at the address returned by + /// this method is valid up to the bytes specified by the return value + /// of \c getMACSize(). + /// The memory region is only valid while the corresponding \c TSIG + /// object is valid. The caller must hold the \c TSIG object while + /// it needs to refer to the region or it must make a local copy of the + /// region. + /// + /// This method never throws an exception. + const void* getMAC() const; + + /// \brief Return the value of the Original ID field. + /// + /// This method never throws an exception. + uint16_t getOriginalID() const; + + /// \brief Return the value of the Error field. + /// + /// This method never throws an exception. + uint16_t getError() const; + + /// \brief Return the value of the Other Len field. + /// + /// This method never throws an exception. + uint16_t getOtherLen() const; + + /// \brief Return the value of the Other Data field. + /// + /// The same note as \c getMAC() applies. + /// + /// This method never throws an exception. + const void* getOtherData() const; +private: + TSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + TSIGImpl* impl_; +}; + +} // end of namespace "any" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // ANY_TSIG_250_H + +// 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/. + +#ifndef CH_A_1_H +#define CH_A_1_H 1 + +#include <string> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace ch { + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit A(const std::string& type_str); + A(isc::util::InputBuffer& buffer, size_t rdata_len); + A(const A& other); + A( + 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; + + // END_COMMON_MEMBERS +}; + +} // end of namespace "ch" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // CH_A_1_H + +// 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/. + +#ifndef GENERIC_AFSDB_18_H +#define GENERIC_AFSDB_18_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +/// \brief \c rdata::AFSDB class represents the AFSDB RDATA as defined %in +/// RFC1183. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// AFSDB RDATA. +class AFSDB : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit AFSDB(const std::string& type_str); + AFSDB(isc::util::InputBuffer& buffer, size_t rdata_len); + AFSDB(const AFSDB& other); + AFSDB( + 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; + + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// This method never throws an exception. + AFSDB& operator=(const AFSDB& source); + /// + /// Specialized methods + /// + + /// \brief Return the value of the server field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal server name. + /// + /// This method never throws an exception. + const Name& getServer() const; + + /// \brief Return the value of the subtype field. + /// + /// This method never throws an exception. + uint16_t getSubtype() const; + +private: + void createFromLexer(MasterLexer& lexer, const Name* origin); + + uint16_t subtype_; + Name server_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_AFSDB_18_H + +// Copyright (C) 2014-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/. + +#ifndef GENERIC_CAA_257_H +#define GENERIC_CAA_257_H 1 + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct CAAImpl; + +class CAA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit CAA(const std::string& type_str); + CAA(isc::util::InputBuffer& buffer, size_t rdata_len); + CAA(const CAA& other); + CAA( + 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; + + // END_COMMON_MEMBERS + + CAA(uint8_t flags, const std::string& tag, const std::string& value); + CAA& operator=(const CAA& source); + ~CAA(); + + /// + /// Specialized methods + /// + + /// \brief Return the Flags field of the CAA RDATA. + uint8_t getFlags() const; + + /// \brief Return the Tag field of the CAA RDATA. + const std::string& getTag() const; + + /// \brief Return the Value field of the CAA RDATA. + /// + /// Note: The const reference which is returned is valid only during + /// the lifetime of this \c generic::CAA object. It should not be + /// used afterwards. + const std::vector<uint8_t>& getValue() const; + +private: + CAAImpl* constructFromLexer(MasterLexer& lexer); + + CAAImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_CAA_257_H + +// 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/. + +#ifndef GENERIC_CNAME_5_H +#define GENERIC_CNAME_5_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class CNAME : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit CNAME(const std::string& type_str); + CNAME(isc::util::InputBuffer& buffer, size_t rdata_len); + CNAME(const CNAME& other); + CNAME( + 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; + + // END_COMMON_MEMBERS + + // CNAME specific methods + CNAME(const Name& cname); + const Name& getCname() const; +private: + Name cname_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_CNAME_5_H + +// 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/. + +#ifndef GENERIC_DLV_32769_H +#define GENERIC_DLV_32769_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +namespace detail { +template <class Type, uint16_t typeCode> class DSLikeImpl; +} + +/// \brief \c rdata::generic::DLV class represents the DLV RDATA as defined in +/// RFC4431. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DLV RDATA. +class DLV : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit DLV(const std::string& type_str); + DLV(isc::util::InputBuffer& buffer, size_t rdata_len); + DLV(const DLV& other); + DLV( + 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; + + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + DLV& operator=(const DLV& source); + + /// \brief The destructor. + ~DLV(); + + /// \brief Return the value of the Tag field. + /// + /// This method never throws an exception. + uint16_t getTag() const; +private: + typedef detail::DSLikeImpl<DLV, 32769> DLVImpl; + DLVImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_DLV_32769_H + +// 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/. + +#ifndef GENERIC_DNAME_39_H +#define GENERIC_DNAME_39_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class DNAME : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit DNAME(const std::string& type_str); + DNAME(isc::util::InputBuffer& buffer, size_t rdata_len); + DNAME(const DNAME& other); + DNAME( + 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; + + // END_COMMON_MEMBERS + + // DNAME specific methods + DNAME(const Name& dname); + const Name& getDname() const; +private: + Name dname_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_DNAME_39_H + +// 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/. + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +#ifndef GENERIC_DNSKEY_48_H +#define GENERIC_DNSKEY_48_H 1 + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct DNSKEYImpl; + +class DNSKEY : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit DNSKEY(const std::string& type_str); + DNSKEY(isc::util::InputBuffer& buffer, size_t rdata_len); + DNSKEY(const DNSKEY& other); + DNSKEY( + 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; + + // END_COMMON_MEMBERS + DNSKEY& operator=(const DNSKEY& source); + ~DNSKEY(); + + /// + /// Specialized methods + /// + + /// \brief Returns the key tag + /// + /// \throw isc::OutOfRange if the key data for RSA/MD5 is too short + /// to support tag extraction. + uint16_t getTag() const; + + uint16_t getFlags() const; + uint8_t getAlgorithm() const; + +private: + DNSKEYImpl* constructFromLexer(isc::dns::MasterLexer& lexer); + + DNSKEYImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_DNSKEY_48_H + +// 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/. + +#ifndef GENERIC_DS_43_H +#define GENERIC_DS_43_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +namespace detail { +template <class Type, uint16_t typeCode> class DSLikeImpl; +} + +/// \brief \c rdata::generic::DS class represents the DS RDATA as defined in +/// RFC3658. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DS RDATA. +class DS : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit DS(const std::string& type_str); + DS(isc::util::InputBuffer& buffer, size_t rdata_len); + DS(const DS& other); + DS( + 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; + + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + DS& operator=(const DS& source); + + /// \brief The destructor. + ~DS(); + + /// \brief Return the value of the Tag field. + /// + /// This method never throws an exception. + uint16_t getTag() const; +private: + typedef detail::DSLikeImpl<DS, 43> DSImpl; + DSImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_DS_43_H + +// 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/. + +#ifndef GENERIC_HINFO_13_H +#define GENERIC_HINFO_13_H 1 +#include <stdint.h> + +#include <string> + +#include <boost/scoped_ptr.hpp> +#include <boost/noncopyable.hpp> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <util/buffer.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class HINFOImpl; + +/// \brief \c HINFO class represents the HINFO rdata defined in +/// RFC1034, RFC1035 +/// +/// This class implements the basic interfaces inherited from the +/// \c rdata::Rdata class, and provides accessors specific to the +/// HINFO rdata. +class HINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit HINFO(const std::string& type_str); + HINFO(isc::util::InputBuffer& buffer, size_t rdata_len); + HINFO(const HINFO& other); + HINFO( + 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; + + // END_COMMON_MEMBERS + + // HINFO specific methods + ~HINFO(); + + HINFO& operator=(const HINFO&); + + const std::string getCPU() const; + const std::string getOS() const; + +private: + /// Helper template function for toWire() + /// + /// \param outputer Where to write data in + template <typename T> + void toWireHelper(T& outputer) const; + + boost::scoped_ptr<HINFOImpl> impl_; +}; + + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_HINFO_13_H + +// 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/. + +#ifndef GENERIC_MINFO_14_H +#define GENERIC_MINFO_14_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +/// \brief \c rdata::generic::MINFO class represents the MINFO RDATA as +/// defined in RFC1035. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// MINFO RDATA. +class MINFO : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit MINFO(const std::string& type_str); + MINFO(isc::util::InputBuffer& buffer, size_t rdata_len); + MINFO(const MINFO& other); + MINFO( + 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; + + // END_COMMON_MEMBERS + + /// \brief Define the assignment operator. + /// + /// \exception std::bad_alloc Memory allocation fails in copying + /// internal member variables (this should be very rare). + MINFO& operator=(const MINFO& source); + + /// \brief Return the value of the rmailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + /// + /// \note + /// Unlike the case of some other RDATA classes (such as + /// \c NS::getNSName()), this method constructs a new \c Name object + /// and returns it, instead of returning a reference to a \c Name object + /// internally maintained in the class (which is a private member). + /// This is based on the observation that this method will be rarely + /// used and even when it's used it will not be in a performance context + /// (for example, a recursive resolver won't need this field in its + /// resolution process). By returning a new object we have flexibility + /// of changing the internal representation without the risk of changing + /// the interface or method property. + /// The same note applies to the \c getEmailbox() method. + Name getRmailbox() const { return (rmailbox_); } + + /// \brief Return the value of the emailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + Name getEmailbox() const { return (emailbox_); } + +private: + Name rmailbox_; + Name emailbox_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_MINFO_14_H + +// 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/. + +#ifndef GENERIC_MX_15_H +#define GENERIC_MX_15_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class MX : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit MX(const std::string& type_str); + MX(isc::util::InputBuffer& buffer, size_t rdata_len); + MX(const MX& other); + MX( + 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; + + // END_COMMON_MEMBERS + + MX(uint16_t preference, const Name& mxname); + + /// + /// Specialized methods + /// + const Name& getMXName() const; + uint16_t getMXPref() const; + +private: + void constructFromLexer(isc::dns::MasterLexer& lexer, + const isc::dns::Name* origin); + + /// Note: this is a prototype version; we may reconsider + /// this representation later. + uint16_t preference_; + Name mxname_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_MX_15_H + +// 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/. + +#ifndef GENERIC_NAPTR_35_H +#define GENERIC_NAPTR_35_H 1 + +#include <string> + +#include <boost/scoped_ptr.hpp> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <util/buffer.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class NAPTRImpl; + +/// \brief \c NAPTR class represents the NAPTR rdata defined in +/// RFC2915, RFC2168 and RFC3403 +/// +/// This class implements the basic interfaces inherited from the +/// \c rdata::Rdata class, and provides accessors specific to the +/// NAPTR rdata. +class NAPTR : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit NAPTR(const std::string& type_str); + NAPTR(isc::util::InputBuffer& buffer, size_t rdata_len); + NAPTR(const NAPTR& other); + NAPTR( + 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; + + // END_COMMON_MEMBERS + + // NAPTR specific methods + ~NAPTR(); + + NAPTR& operator=(const NAPTR& source); + + uint16_t getOrder() const; + uint16_t getPreference() const; + const std::string getFlags() const; + const std::string getServices() const; + const std::string getRegexp() const; + const Name& getReplacement() const; +private: + /// Helper template function for toWire() + /// + /// \param outputer Where to write data in + template <typename T> + void toWireHelper(T& outputer) const; + + boost::scoped_ptr<NAPTRImpl> impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_NAPTR_35_H + +// 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/. + +#ifndef GENERIC_NS_2_H +#define GENERIC_NS_2_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class NS : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit NS(const std::string& type_str); + NS(isc::util::InputBuffer& buffer, size_t rdata_len); + NS(const NS& other); + NS( + 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; + + // END_COMMON_MEMBERS + /// + /// Specialized constructor + /// + explicit NS(const Name& nsname) : nsname_(nsname) {} + /// + /// Specialized methods + /// + const Name& getNSName() const; +private: + Name nsname_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_NS_2_H + +// 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/. + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +#ifndef GENERIC_NSEC3_50_H +#define GENERIC_NSEC3_50_H 1 + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct NSEC3Impl; + +class NSEC3 : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit NSEC3(const std::string& type_str); + NSEC3(isc::util::InputBuffer& buffer, size_t rdata_len); + NSEC3(const NSEC3& other); + NSEC3( + 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; + + // END_COMMON_MEMBERS + NSEC3& operator=(const NSEC3& source); + ~NSEC3(); + + uint8_t getHashalg() const; + uint8_t getFlags() const; + uint16_t getIterations() const; + const std::vector<uint8_t>& getSalt() const; + const std::vector<uint8_t>& getNext() const; + +private: + NSEC3Impl* constructFromLexer(isc::dns::MasterLexer& lexer); + + NSEC3Impl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_NSEC3_50_H + +// 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/. + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +#ifndef GENERIC_NSEC3PARAM_51_H +#define GENERIC_NSEC3PARAM_51_H 1 + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct NSEC3PARAMImpl; + +class NSEC3PARAM : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit NSEC3PARAM(const std::string& type_str); + NSEC3PARAM(isc::util::InputBuffer& buffer, size_t rdata_len); + NSEC3PARAM(const NSEC3PARAM& other); + NSEC3PARAM( + 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; + + // END_COMMON_MEMBERS + NSEC3PARAM& operator=(const NSEC3PARAM& source); + ~NSEC3PARAM(); + + /// + /// Specialized methods + /// + uint8_t getHashalg() const; + uint8_t getFlags() const; + uint16_t getIterations() const; + const std::vector<uint8_t>& getSalt() const; + +private: + NSEC3PARAMImpl* constructFromLexer(isc::dns::MasterLexer& lexer); + + NSEC3PARAMImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_NSEC3PARAM_51_H + +// 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/. + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> + +#ifndef GENERIC_NSEC_47_H +#define GENERIC_NSEC_47_H 1 + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct NSECImpl; + +class NSEC : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit NSEC(const std::string& type_str); + NSEC(isc::util::InputBuffer& buffer, size_t rdata_len); + NSEC(const NSEC& other); + NSEC( + 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; + + // END_COMMON_MEMBERS + NSEC& operator=(const NSEC& source); + ~NSEC(); + + // specialized methods + + /// Return the next domain name. + /// + /// \exception std::bad_alloc Resource allocation failure in name copy. + /// + /// \return The next domain name field in the form of \c Name object. + const Name& getNextName() const; + +private: + NSECImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_NSEC_47_H + +// 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/. + +#ifndef GENERIC_OPT_41_H +#define GENERIC_OPT_41_H 1 + +#include <string> + +#include <dns/rdata.h> + +#include <boost/shared_ptr.hpp> + +#include <vector> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct OPTImpl; + +class OPT : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit OPT(const std::string& type_str); + OPT(isc::util::InputBuffer& buffer, size_t rdata_len); + OPT(const OPT& other); + OPT( + 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; + + // END_COMMON_MEMBERS + + // The default constructor makes sense for OPT as it can be empty. + OPT(); + OPT& operator=(const OPT& source); + ~OPT(); + + /// \brief A class representing a pseudo RR (or option) within an + /// OPT RR (see RFC 6891). + class PseudoRR { + public: + /// \brief Constructor. + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo + /// RR. OPTION-LENGTH is set to the length of this vector. + PseudoRR(uint16_t code, + boost::shared_ptr<std::vector<uint8_t> >& data); + + /// \brief Return the option code of this pseudo RR. + uint16_t getCode() const; + + /// \brief Return the option data of this pseudo RR. + const uint8_t* getData() const; + + /// \brief Return the length of the option data of this + /// pseudo RR. + uint16_t getLength() const; + + private: + uint16_t code_; + boost::shared_ptr<std::vector<uint8_t> > data_; + }; + + /// \brief Append a pseudo RR (option) in this OPT RR. + /// + /// \param code The OPTION-CODE field of the pseudo RR. + /// \param data The OPTION-DATA field of the pseudo RR. + /// \param length The size of the \c data argument. OPTION-LENGTH is + /// set to this size. + /// \throw isc::InvalidParameter if this pseudo RR would cause + /// the OPT RDATA to overflow its RDLENGTH. + void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length); + + /// \brief Return a vector of the pseudo RRs (options) in this + /// OPT RR. + /// + /// Note: The returned reference is only valid during the lifetime + /// of this \c generic::OPT object. It should not be used + /// afterwards. + const std::vector<PseudoRR>& getPseudoRRs() const; + +private: + OPTImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_OPT_41_H + +// 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/. + +#ifndef GENERIC_PTR_12_H +#define GENERIC_PTR_12_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class PTR : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit PTR(const std::string& type_str); + PTR(isc::util::InputBuffer& buffer, size_t rdata_len); + PTR(const PTR& other); + PTR( + 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; + + // END_COMMON_MEMBERS + + /// + /// Specialized constructor + /// + explicit PTR(const Name& ptr_name) : ptr_name_(ptr_name) {} + /// + /// Specialized methods + /// + const Name& getPTRName() const; +private: + Name ptr_name_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_PTR_12_H + +// 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/. + +#ifndef GENERIC_RP_17_H +#define GENERIC_RP_17_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +/// \brief \c rdata::generic::RP class represents the RP RDATA as defined in +/// RFC1183. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// RP RDATA. +class RP : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit RP(const std::string& type_str); + RP(isc::util::InputBuffer& buffer, size_t rdata_len); + RP(const RP& other); + RP( + 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; + + // END_COMMON_MEMBERS + + /// We use the default copy constructor and assignment operator. + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %RP RDATA + /// fields as defined in RFC1183. + RP(const Name& mailbox, const Name& text) : + mailbox_(mailbox), text_(text) + {} + + /// \brief Return the value of the mailbox field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + /// + /// \note + /// Unlike the case of some other RDATA classes (such as + /// \c NS::getNSName()), this method constructs a new \c Name object + /// and returns it, instead of returning a reference to a \c Name object + /// internally maintained in the class (which is a private member). + /// This is based on the observation that this method will be rarely used + /// and even when it's used it will not be in a performance context + /// (for example, a recursive resolver won't need this field in its + /// resolution process). By returning a new object we have flexibility of + /// changing the internal representation without the risk of changing + /// the interface or method property. + /// The same note applies to the \c getText() method. + Name getMailbox() const { return (mailbox_); } + + /// \brief Return the value of the text field. + /// + /// \throw std::bad_alloc If resource allocation for the returned + /// \c Name fails. + Name getText() const { return (text_); } + +private: + Name mailbox_; + Name text_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_RP_17_H + +// 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/. + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> + +#ifndef GENERIC_RRSIG_46_H +#define GENERIC_RRSIG_46_H 1 + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct RRSIGImpl; + +/// \brief \c rdata::RRSIG class represents the RRSIG RDATA as defined %in +/// RFC4034. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// RRSIG RDATA. +class RRSIG : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit RRSIG(const std::string& type_str); + RRSIG(isc::util::InputBuffer& buffer, size_t rdata_len); + RRSIG(const RRSIG& other); + RRSIG( + 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; + + // END_COMMON_MEMBERS + RRSIG& operator=(const RRSIG& source); + ~RRSIG(); + + // specialized methods + const RRType& typeCovered() const; +private: + // helper function for string and lexer constructors + RRSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + RRSIGImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_RRSIG_46_H + +// 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/. + +#ifndef GENERIC_SOA_6_H +#define GENERIC_SOA_6_H 1 + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/serial.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +class SOA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit SOA(const std::string& type_str); + SOA(isc::util::InputBuffer& buffer, size_t rdata_len); + SOA(const SOA& other); + SOA( + 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; + + // END_COMMON_MEMBERS + + SOA(const Name& mname, const Name& rname, uint32_t serial, + uint32_t refresh, uint32_t retry, uint32_t expire, + uint32_t minimum); + + /// \brief Returns the serial stored in the SOA. + Serial getSerial() const; + + /// brief Returns the minimum TTL field value of the SOA. + uint32_t getMinimum() const; +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + Name mname_; + Name rname_; + /// serial, refresh, retry, expire, minimum, stored in network byte order + uint8_t numdata_[20]; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_SOA_6_H + +// 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/. + +#ifndef GENERIC_SPF_99_H +#define GENERIC_SPF_99_H 1 + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +namespace detail { +template<class Type, uint16_t typeCode> class TXTLikeImpl; +} + +/// \brief \c rdata::SPF class represents the SPF RDATA as defined %in +/// RFC4408. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class. The semantics of the class is provided by +/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF. +class SPF : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit SPF(const std::string& type_str); + SPF(isc::util::InputBuffer& buffer, size_t rdata_len); + SPF(const SPF& other); + SPF( + 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; + + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + SPF& operator=(const SPF& source); + + /// \brief The destructor. + ~SPF(); + + /// + /// Specialized methods + /// + + /// \brief Return a reference to the data strings + /// + /// This method never throws an exception. + const std::vector<std::vector<uint8_t> >& getString() const; + +private: + typedef isc::dns::rdata::generic::detail::TXTLikeImpl<SPF, 99> SPFImpl; + SPFImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_SPF_99_H + +// Copyright (C) 2012-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/. + +#ifndef GENERIC_SSHFP_44_H +#define GENERIC_SSHFP_44_H 1 + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct SSHFPImpl; + +class SSHFP : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit SSHFP(const std::string& type_str); + SSHFP(isc::util::InputBuffer& buffer, size_t rdata_len); + SSHFP(const SSHFP& other); + SSHFP( + 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; + + // END_COMMON_MEMBERS + + SSHFP(uint8_t algorithm, uint8_t fingerprint_type, + const std::string& fingerprint); + SSHFP& operator=(const SSHFP& source); + ~SSHFP(); + + /// + /// Specialized methods + /// + uint8_t getAlgorithmNumber() const; + uint8_t getFingerprintType() const; + const std::vector<uint8_t>& getFingerprint() const; + size_t getFingerprintLength() const; + +private: + SSHFPImpl* constructFromLexer(MasterLexer& lexer); + + SSHFPImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_SSHFP_44_H + +// Copyright (C) 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/. + +#ifndef GENERIC_TKEY_249_H +#define GENERIC_TKEY_249_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct TKEYImpl; + +/// \brief \c rdata::TKEY class represents the TKEY RDATA as defined %in +/// RFC2930. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// TKEY RDATA. +class TKEY : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit TKEY(const std::string& type_str); + TKEY(isc::util::InputBuffer& buffer, size_t rdata_len); + TKEY(const TKEY& other); + TKEY( + 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; + + // END_COMMON_MEMBERS + + /// \brief Constructor from RDATA field parameters. + /// + /// The parameters are a straightforward mapping of %TKEY RDATA + /// fields as defined %in RFC2930. + /// + /// This RR is pretty close to the TSIG RR with 32 bit timestamps, + /// or the RRSIG RR with a second "other" data field. + /// + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + /// + /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. + /// \param inception The inception time (in seconds since 1970). + /// \param expire The expire time (in seconds since 1970). + /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). + /// \param error The error code (extended error space shared with TSIG). + /// \param key_len The key length (0 means no key). + /// \param key The key (can be 0). + /// \param other_len The other data length (0 means no other data). + /// \param other_data The other data (can be and usually is 0). + TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, + uint16_t mode, uint16_t error, uint16_t key_len, + const void* key, uint16_t other_len, const void* other_data); + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + TKEY& operator=(const TKEY& source); + + /// \brief The destructor. + ~TKEY(); + + /// \brief Return the algorithm name. + /// + /// This method never throws an exception. + const Name& getAlgorithm() const; + + /// \brief Return the value of the Inception field as a number. + /// + /// This method never throws an exception. + uint32_t getInception() const; + + /// \brief Return the value of the Inception field as a string. + std::string getInceptionDate() const; + + /// \brief Return the value of the Expire field as a number. + /// + /// This method never throws an exception. + uint32_t getExpire() const; + + /// \brief Return the value of the Expire field as a string. + std::string getExpireDate() const; + + /// \brief Return the value of the Mode field. + /// + /// This method never throws an exception. + uint16_t getMode() const; + + /// \brief Return the value of the Error field. + /// + /// This method never throws an exception. + uint16_t getError() const; + + /// \brief Return the value of the Key Len field. + /// + /// This method never throws an exception. + uint16_t getKeyLen() const; + + /// \brief Return the value of the Key field. + /// + /// This method never throws an exception. + const void* getKey() const; + + /// \brief Return the value of the Other Len field. + /// + /// This method never throws an exception. + uint16_t getOtherLen() const; + + /// \brief Return the value of the Other Data field. + /// + /// The same note as \c getMAC() applies. + /// + /// This method never throws an exception. + const void* getOtherData() const; + + /// \brief The GSS_API constant for the Mode field. + static const uint16_t GSS_API_MODE; + +private: + TKEYImpl* constructFromLexer(MasterLexer& lexer, const Name* origin); + + TKEYImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_TKEY_249_H + +// Copyright (C) 2014-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/. + +#ifndef GENERIC_TLSA_52_H +#define GENERIC_TLSA_52_H 1 + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +#include <string> +#include <vector> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +struct TLSAImpl; + +class TLSA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit TLSA(const std::string& type_str); + TLSA(isc::util::InputBuffer& buffer, size_t rdata_len); + TLSA(const TLSA& other); + TLSA( + 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; + + // END_COMMON_MEMBERS + + TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data); + TLSA& operator=(const TLSA& source); + ~TLSA(); + + /// + /// Specialized methods + /// + uint8_t getCertificateUsage() const; + uint8_t getSelector() const; + uint8_t getMatchingType() const; + const std::vector<uint8_t>& getData() const; + size_t getDataLength() const; + +private: + TLSAImpl* constructFromLexer(MasterLexer& lexer); + + TLSAImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_TLSA_52_H + +// 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/. + +#ifndef GENERIC_TXT_16_H +#define GENERIC_TXT_16_H 1 + +#include <stdint.h> + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace generic { + +namespace detail { +template<class Type, uint16_t typeCode> class TXTLikeImpl; +} + +class TXT : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit TXT(const std::string& type_str); + TXT(isc::util::InputBuffer& buffer, size_t rdata_len); + TXT(const TXT& other); + TXT( + 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; + + // END_COMMON_MEMBERS + + TXT& operator=(const TXT& source); + ~TXT(); + +private: + typedef isc::dns::rdata::generic::detail::TXTLikeImpl<TXT, 16> TXTImpl; + TXTImpl* impl_; +}; + +} // end of namespace "generic" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // GENERIC_TXT_16_H + +// 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/. + +#ifndef HS_A_1_H +#define HS_A_1_H 1 + +#include <string> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace hs { + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit A(const std::string& type_str); + A(isc::util::InputBuffer& buffer, size_t rdata_len); + A(const A& other); + A( + 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; + + // END_COMMON_MEMBERS +}; + +} // end of namespace "hs" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // HS_A_1_H + +// 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/. + +#ifndef IN_A_1_H +#define IN_A_1_H 1 + +#include <string> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace in { + +class A : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit A(const std::string& type_str); + A(isc::util::InputBuffer& buffer, size_t rdata_len); + A(const A& other); + A( + 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; + + // END_COMMON_MEMBERS + + //We can use the default destructor. + //virtual ~A() {} + // notyet: + //const struct in_addr& getAddress() const { return (addr_); } +private: + uint32_t addr_; // raw IPv4 address (network byte order) +}; +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // IN_A_1_H + +// 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/. + +#ifndef IN_AAAA_28_H +#define IN_AAAA_28_H 1 + +#include <stdint.h> + +#include <string> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace in { + +class AAAA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit AAAA(const std::string& type_str); + AAAA(isc::util::InputBuffer& buffer, size_t rdata_len); + AAAA(const AAAA& other); + AAAA( + 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; + + // END_COMMON_MEMBERS + // notyet: + //const struct in6_addr& getAddress() const { return (addr_); } +private: + uint8_t addr_[16]; // raw IPv6 address (network byte order) +}; + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // IN_AAAA_28_H + +// 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/. + +#ifndef IN_DHCID_49_H +#define IN_DHCID_49_H 1 + +#include <string> +#include <vector> + +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace in { + +/// \brief \c rdata::DHCID class represents the DHCID RDATA as defined %in +/// RFC4701. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// DHCID RDATA. +class DHCID : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit DHCID(const std::string& type_str); + DHCID(isc::util::InputBuffer& buffer, size_t rdata_len); + DHCID(const DHCID& other); + DHCID( + 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; + + // END_COMMON_MEMBERS + + /// \brief Return the digest. + /// + /// This method never throws an exception. + const std::vector<uint8_t>& getDigest() const; + +private: + // helper for string and lexer constructors + void constructFromLexer(MasterLexer& lexer); + + /// \brief Private data representation + /// + /// Opaque data at least 3 octets long as per RFC4701. + /// + std::vector<uint8_t> digest_; +}; +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // IN_DHCID_49_H + +// 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/. + +#ifndef IN_SRV_33_H +#define IN_SRV_33_H 1 + +#include <stdint.h> + +#include <dns/name.h> +#include <dns/rdata.h> + +namespace isc { +namespace util { + +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// BEGIN_COMMON_DECLARATIONS + +class AbstractMessageRenderer; + +// END_COMMON_DECLARATIONS + +namespace rdata { +namespace in { + +struct SRVImpl; + +/// \brief \c rdata::SRV class represents the SRV RDATA as defined %in +/// RFC2782. +/// +/// This class implements the basic interfaces inherited from the abstract +/// \c rdata::Rdata class, and provides trivial accessors specific to the +/// SRV RDATA. +class SRV : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + + explicit SRV(const std::string& type_str); + SRV(isc::util::InputBuffer& buffer, size_t rdata_len); + SRV(const SRV& other); + SRV( + 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; + + // END_COMMON_MEMBERS + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + SRV& operator=(const SRV& source); + + /// \brief The destructor. + ~SRV(); + + /// + /// Specialized methods + /// + + /// \brief Return the value of the priority field. + /// + /// This method never throws an exception. + uint16_t getPriority() const; + + /// \brief Return the value of the weight field. + /// + /// This method never throws an exception. + uint16_t getWeight() const; + + /// \brief Return the value of the port field. + /// + /// This method never throws an exception. + uint16_t getPort() const; + + /// \brief Return the value of the target field. + /// + /// \return A reference to a \c Name class object corresponding to the + /// internal target name. + /// + /// This method never throws an exception. + const Name& getTarget() const; + +private: + SRVImpl* impl_; +}; + +} // end of namespace "in" +} // end of namespace "rdata" +} // end of namespace "dns" +} // end of namespace "isc" +#endif // IN_SRV_33_H + + +#endif // DNS_RDATACLASS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc new file mode 100644 index 0000000..e02ec8f --- /dev/null +++ b/src/lib/dns/rdatafields.cc @@ -0,0 +1,216 @@ +// Copyright (C) 2010-2015,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/. + +#include <config.h> + +#include <stdint.h> + +#include <cassert> +#include <vector> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdatafields.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::util::OutputBuffer; +using isc::util::InputBuffer; + +namespace isc { +namespace dns { +namespace rdata { + +/// This is a helper class for \c RdataFields. +/// +/// It manages a local storage for the data when \c RdataFields is constructed +/// from an \c Rdata. +/// To minimize construction overhead in the other case, an instance of +/// this class is instantiated only when necessary - we don't need the vectors +/// when only rendering. +struct RdataFields::RdataFieldsDetail { + RdataFieldsDetail(const vector<FieldSpec>& fields, + const uint8_t* data, size_t data_length) : + allocated_fields_(fields), + allocated_data_(data, data + data_length) + {} + const vector<FieldSpec> allocated_fields_; + const vector<uint8_t> allocated_data_; +}; + +namespace { +// This class is used to divide the content of RDATA into \c RdataField +// fields via message rendering logic. +// The idea is to identify domain name fields in the writeName() method, +// and determine whether they are compressible using the "compress" +// parameter. +// Other types of data are simply copied into the internal buffer, and +// consecutive such fields are combined into a single \c RdataField field. +// +// Technically, this use of inheritance may be considered a violation of +// Liskov Substitution Principle in that it doesn't actually compress domain +// names, and some of the methods are not expected to be used. +// In fact, skip() or trim() may not be make much sense in this context. +// Nevertheless we keep this idea at the moment. Since the usage is limited +// (it's only used within this file, and only used with \c Rdata variants), +// it's hopefully an acceptable practice. +class RdataFieldComposer : public AbstractMessageRenderer { +public: + RdataFieldComposer() : + truncated_(false), length_limit_(65535), + mode_(CASE_INSENSITIVE), last_data_pos_(0) + {} + virtual ~RdataFieldComposer() {} + virtual bool isTruncated() const { return (truncated_); } + virtual size_t getLengthLimit() const { return (length_limit_); } + virtual CompressMode getCompressMode() const { return (mode_); } + virtual void setTruncated() { truncated_ = true; } + virtual void setLengthLimit(size_t len) { length_limit_ = len; } + virtual void setCompressMode(CompressMode mode) { mode_ = mode; } + virtual void writeName(const LabelSequence&, bool) {} + virtual void writeName(const Name& name, bool compress) { + extendData(); + const RdataFields::Type field_type = + compress ? RdataFields::COMPRESSIBLE_NAME : + RdataFields::INCOMPRESSIBLE_NAME; + // TODO: When we get rid of need for getBuffer, we can output the name + // to a buffer and then write the buffer inside + name.toWire(getBuffer()); + fields_.push_back(RdataFields::FieldSpec(field_type, + name.getLength())); + last_data_pos_ = getLength(); + } + + virtual void clear() { + isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer"); + } + bool truncated_; + size_t length_limit_; + CompressMode mode_; + vector<RdataFields::FieldSpec> fields_; + vector<RdataFields::FieldSpec>& getFields() { + extendData(); + return (fields_); + } + // We use generic write* methods, with the exception of writeName. + // So new data can arrive without us knowing it, this considers all new + // data to be just data and extends the fields to take it into account. + size_t last_data_pos_; + void extendData() { + // No news, return to work + if (getLength() == last_data_pos_) { + return; + } + // The new bytes are just ordinary uninteresting data + if (fields_.empty() || fields_.back().type != RdataFields::DATA) { + fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0)); + } + // We added this much data from last time + fields_.back().len += getLength() - last_data_pos_; + last_data_pos_ = getLength(); + } +}; + +} + +RdataFields::RdataFields(const Rdata& rdata) { + RdataFieldComposer field_composer; + rdata.toWire(field_composer); + nfields_ = field_composer.getFields().size(); + data_length_ = field_composer.getLength(); + if (nfields_ > 0) { + assert(data_length_ > 0); + detail_ = new RdataFieldsDetail(field_composer.getFields(), + static_cast<const uint8_t*> + (field_composer.getData()), + field_composer.getLength()); + data_ = &detail_->allocated_data_[0]; + fields_ = &detail_->allocated_fields_[0]; + } else { + assert(data_length_ == 0); + detail_ = NULL; + data_ = NULL; + fields_ = NULL; + } +} + +RdataFields::RdataFields(const void* fields, const unsigned int fields_length, + const void* data, const size_t data_length) : + fields_(static_cast<const FieldSpec*>(fields)), + nfields_(fields_length / sizeof(*fields_)), + data_(static_cast<const uint8_t*>(data)), + data_length_(data_length), + detail_(NULL) +{ + if ((fields_ == NULL && nfields_ > 0) || + (fields_ != NULL && nfields_ == 0)) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: fields_length (" + << fields_length << ") and fields conflict each other"); + } + if ((data_ == NULL && data_length_ > 0) || + (data_ != NULL && data_length_ == 0)) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: data length (" + << data_length_ << ") and data conflict each other"); + } + + size_t total_length = 0; + for (unsigned int i = 0; i < nfields_; ++i) { + total_length += fields_[i].len; + } + if (total_length != data_length_) { + isc_throw(InvalidParameter, + "Inconsistent parameters for RdataFields: " + "fields len: " << total_length << + " data len: " << data_length_); + } +} + +RdataFields::~RdataFields() { + delete detail_; +} + +RdataFields::FieldSpec +RdataFields::getFieldSpec(const unsigned int field_id) const { + if (field_id >= nfields_) { + isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id); + } + return (fields_[field_id]); +} + +void +RdataFields::toWire(AbstractMessageRenderer& renderer) const { + size_t offset = 0; + + for (unsigned int i = 0; i < nfields_; ++i) { + if (fields_[i].type == DATA) { + renderer.writeData(data_ + offset, fields_[i].len); + } else { + // XXX: this is inefficient. Even if it's quite likely the + // data is a valid wire representation of a name we parse + // it to construct the Name object in the generic mode. + // This should be improved in a future version. + InputBuffer buffer(data_ + offset, fields_[i].len); + renderer.writeName(Name(buffer), + fields_[i].type == COMPRESSIBLE_NAME); + } + offset += fields_[i].len; + } +} + +void +RdataFields::toWire(OutputBuffer& buffer) const { + buffer.writeData(data_, data_length_); +} +} // end of namespace rdata +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/rdatafields.h b/src/lib/dns/rdatafields.h new file mode 100644 index 0000000..c4a64ad --- /dev/null +++ b/src/lib/dns/rdatafields.h @@ -0,0 +1,419 @@ +// 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 RDATAFIELDS_H +#define RDATAFIELDS_H 1 + +#include <stdint.h> + +#include <cstddef> + +namespace isc { +namespace util { +class OutputBuffer; +} +namespace dns { +class AbstractMessageRenderer; + +namespace rdata { +class Rdata; + +/// A low-level, RR type-independent representation of DNS RDATA. +/// +/// <b>Purpose of the Class</b> +/// +/// This class intends to help "serialization" of the content of RDATA +/// in a space-efficient manner. Specific derived classes of \c Rdata +/// focus on the convenience of accessing RDATA fields for RR type-specific +/// protocol operations, and can be inefficient in terms of space. +/// For example, a DNS character string may be internally represented as a +/// \c std::string object with all of the overhead of the richer class. +/// If an application needs to maintain a very large number of RRs and it +/// does not have to perform RR specific operation so often, it may make more +/// sense to store the data in memory in a lower-level but space efficient +/// form. +/// +/// Another purpose of this class is to improve rendering performance for +/// RDATA. If the only requirement were space efficiency, it would be just +/// sufficient to convert the \c RDATA into a binary sequence in the wire +/// format. However, to render the data in a DNS message, we'd have to +/// re-construct a corresponding \c Rdata object in the case where name +/// compression is necessary. This is not desirable, and this class is +/// provided to avoid such unnecessary overhead. +/// +/// <b>Data Format</b> +/// +/// To meet these goals, this class helps convert an \c Rdata object into +/// two pieces of information: Wire-format representation of the \c Rdata +/// and associated meta information for efficient rendering. +/// +/// Specifically, it maintains the wire-format data as a sequence of typed +/// fields. The types are: +/// - Compressible name: a domain name as an RDATA field that can be compressed +/// - Incompressible name: a domain name as an RDATA field that cannot be +/// compressed +/// - Other data: any other fields of RDATA, which should be treated as opaque +/// +/// (See also the description of \c RdataFields::Type) +/// Whether a name can or cannot be compressed is determined according to +/// RFC3597. +/// +/// A "other data" field may not always correspond to a single RDATA field. +/// A \c RdataFields field (of other data) is just a contiguous region of the +/// wire-format data that does not involve name compression. +/// For example, the SOA RDATA begins with two "compressible" names followed +/// by 5 32-bit fields. +/// In \c RdataFields the last 5 fields would be considered a single 20-byte +/// field. +/// +/// Each \c RdataFields field is identified by the \c FieldSpec structure, +/// which provides the type and length of the field. +/// An \c RdataFields object internally maintains a sequence of \c FieldSpec +/// objects in a form of plain C-style array, which can be referenced via +/// a pointer returned by the \c getFieldSpecData() method. +/// The \c \c FieldSpec for a specific field can also be retrieved by the +/// \c getFieldSpec() method. +/// +/// The following diagram shows the internal memory representation of +/// an SOA RDATA in the form of \c RdataFields object and how an application +/// can get access to the memory region. +/** \verbatim +accessible via |0 getDataLength() bytes +getData()----------> <MNAME><RNAME><Rest of the data> + <---------- 3 * sizeof(FieldSpec) bytes -------------> +getFieldSpecData()-> { compressible name { compressible name { other data + len: MNAME-len } len: RNAME-len } len: 20 } +\endverbatim + */ +/// where MNAME and RNAME are wire format representations of the MNAME and +/// RNAME fields of the SOA RDATA, respectively, and "Rest of the data" +/// encodes the remaining 20 bytes of the RDATA in network byte order. +/// +/// <b>Usage of the Class</b> +/// +/// One major and common use case of the \c RdataFields class is to convert +/// a \c Rdata object (possibly given from a DNS message or some configuration +/// source such as a zone file) in the serialized format and store a copy of +/// the data somewhere in memory. The following code sample implements this +/// scenario: +/// \code // assume "rdata" is a reference type to Rdata +/// const RdataFields fields(rdata); +/// const unsigned int fields_size = fields.getFieldDataSize(); +/// memcpy(some_place, fields.getFieldSpecData(), fields_size); +/// const size_t data_length = fields.getDataLength(); +/// memcpy(other_place, fields.getData(), data_length); +/// // (fields_size and data_length should be stored somewhere, too) +/// \endcode +/// +/// Another typical usage is to render the stored data in the wire format +/// as efficiently as possible. The following code is an example of such +/// usage: +/// \code // assume "renderer" is of type MessageRenderer +/// // retrieve data_length and fields_size from the storage +/// RdataFields(some_place, fields_size, other_place, +/// data_length).toWire(renderer); +/// \endcode +/// +/// <b>Notes to Users</b> +/// +/// The main purposes of this class is to help efficient operation +/// for some (limited classes of) performance sensitive application. +/// For this reason the interface and implementation rely on relatively +/// lower-level, riskier primitives such as passing around bare pointers. +/// +/// It is therefore discouraged to use this class for general purpose +/// applications that do not need to maximize performance in terms of either +/// memory footprint or rendering speed. +/// All functionality provided by this class can be achieved via higher level +/// interfaces such as the \c Rdata class variants. +/// Normal applications should use those interfaces. +/// +/// The data format is public information so that an application can examine +/// and use selected parts of data. For example, an application may want to +/// encode domain names in RDATA in a different way while storing the other +/// data in a separate place. +/// However, at this moment the format is still in flux, and it may not +/// be compatible with future versions (see below). +/// +/// <b>Development Notes</b> +/// +/// We should conduct benchmark tests to measure rendering performance. +/// +/// The current implementation needs to re-construct name objects from +/// compressible and incompressible name fields as wire-format data. +/// This is not efficient, and we'll probably want to improve this in a +/// future version. One possibility is to store offset information as well +/// as the name data (at the cost of increasing memory footprint), and +/// to use the pair of data for faster rendering. +class RdataFields { +public: + /// Types of \c RdataFields fields. + /// + /// \c COMPRESSIBLE_NAME and \c INCOMPRESSIBLE_NAME represent a domain + /// name used as a field of an RDATA that can and cannot be compressed + /// per RFC3597. + /// \c DATA means all other types of fields. + enum Type { + DATA, ///< Plain data. + COMPRESSIBLE_NAME, ///< A domain name subject to name compression. + INCOMPRESSIBLE_NAME ///< A domain name that shouldn't be compressed. + }; + + /// Structure that specifies a single \c RdataFields field. + /// + /// This is a straightforward pair of the type and length of a single + /// \c RdataFields field. + /// + /// In some cases an application may want to do deeper inspection of + /// some \c RdataFields field(s). For example, an application may want + /// to construct a \c Name object for each domain name field of an RDATA + /// and use it for some special purpose. + /// The \c FieldSpec structure provides necessary parameters to get access + /// to a specific \c RdataFields field. + /// + /// The following code snippet implements the above example scenario: + /// \code // assume "fields" is of type RdataFields + /// size_t offset = 0; + /// for (int i = 0; i < fields.getFieldCount(); ++i) { + /// const FieldSpec spec = fields.getFieldSpec(i); + /// if (spec.type == RdataFields::COMPRESSIBLE_NAME || + /// spec.type == RdataFields::INCOMPRESSIBLE_NAME) { + /// InputBuffer ibuffer(fields.getData() + offset, spec.len); + /// Name name(ibuffer); + /// // do something with name + /// } + /// offset += spec.len; + /// } \endcode + /// + /// Note that the offset is not included in \c FieldSpec. + /// This is because such deeper inspection would be a relatively rare + /// operation while it is desirable to keep this structure as small as + /// possible for the purpose of space efficiency. + /// Also, if and when an application wants to look into a specific field, + /// it would be quite likely that the application iterates over all fields + /// and does something special for selected fields like the above example. + /// In that case the application can easily and efficiently identify the + /// necessary offset, again, as shown in the above code example. + /// + /// \todo We might find that 16bits per field is generally too much and + /// squeeze the two bit type into it as well, having 14bit length + /// (in the rare case of having too long field, it could be split into + /// multiple ones). That would save 2 bytes per item (one for the type, + /// one for padding). + struct FieldSpec { + FieldSpec(Type type_param, uint16_t len_param) : + type(type_param), len(len_param) + {} + Type type; ///< The type of the field. + uint16_t len; ///< The length of the field in bytes. + }; + + /// + /// \name Constructors and Destructor. + /// + /// \b Note: + /// The copy constructor and the assignment operator are intentionally + /// defined as private, making this class non copyable. + //@{ +private: + RdataFields(const RdataFields& source); + RdataFields& operator=(const RdataFields& source); + +public: + /// Constructor from Rdata. + /// + /// This constructor converts the data of a given \c Rdata object into + /// an \c RdataFields object so that the resulting data can be stored + /// in memory in a space-efficient way. + /// + /// It makes a local copy of the original data and dynamically allocates + /// necessary memory, so is not very efficient. + /// The basic idea is to perform the expensive conversion once and keep + /// using the result as long as possible to improve overall performance + /// in a longer term. + /// + /// If the internal resource allocation fails, a corresponding standard + /// exception will be thrown. + /// The current implementation of this constructor internally calls + /// the <code>Rdata::toWire(AbstractMessageRenderer&) const</code> method + /// for the conversion. + /// If that method throws an exception it will be propagated to the caller + /// of this constructor. + /// + /// \param rdata The RDATA for which the \c RdataFields to be constructed. + RdataFields(const Rdata& rdata); + + /// Constructor from field parameters. + /// + /// The intended usage of this version of constructor is to form a + /// structured representation of \c RDATA encoded by the other + /// constructor so that the resulting object can be used for subsequent + /// operations such as rendering in the wire format. + /// This version is intended to be efficient by not making any copy + /// of variable length data or expensive data inspection. + /// + /// This constructor is basically exception free, except against bogus + /// input parameters. + /// Specifically, the parameters must meet the following conditions; + /// otherwise an exception of class \c InvalidParameter will be thrown. + /// - \c fields can be \c NULL if and only if \c nfields is 0 + /// - \c data can be \c NULL if and only if \c data_length is 0 + /// - the sum of the lengths of \c fields entries must be equal to + /// \c data_length + /// + /// This constructor assumes that the memory region pointed by \c data (if + /// non \c NULL) is encoded as a sequence of valid \c RdataFields fields, + /// and does not perform deep inspection on each field. + /// In particular, for fields of type \c COMPRESSIBLE_NAME or + /// \c INCOMPRESSIBLE_NAME, this constructor assumes the corresponding + /// memory region is a valid representation of domain name. + /// Otherwise, a subsequent method call such as + /// <code>toWire(AbstractMessageRenderer&) const</code> + /// may trigger an unexpected exception. It also expects the fields reside + /// on address that is valid for them (eg. it has valid alignment), see + /// getFieldSpecData() for details. + /// + /// It is the caller's responsibility to ensure this assumption. + /// In general, this constructor is expected to be used for serialized data + /// generated by the other constructor from a valid \c Rdata. + /// The result is not guaranteed if the data is generated in any other + /// ways. + /// + /// The resulting \c RdataFields object does not maintain a copy of + /// \c fields or \c data. It is the caller's responsibility to ensure + /// the memory regions pointed to by these parameters are valid and intact + /// as long as the \c RdataFields object is used. + /// + /// \param fields An array of \c FieldSpec entries. This can be \c NULL. + /// \param fields_length The total length of the \c fields. + /// \param data A pointer to memory region for the entire RDATA. This can + /// be NULL. + /// \param data_length The length of \c data in bytes. + RdataFields(const void* fields, const unsigned int fields_length, + const void* data, const size_t data_length); + + /// The destructor. + ~RdataFields(); + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Return the length of the entire RDATA encoded in the + /// \c RdataFields in bytes. + /// + /// This method never throws an exception. + unsigned int getDataLength() const { return (data_length_); } + + /// \brief Return a pointer to the RDATA encoded in the \c RdataFields. + /// + /// The RdataFields holds ownership of the data. + /// + /// This method never throws an exception. + const void* getData() const { return (data_); } + + /// \brief Return the number of bytes the buffer returned by + /// getFieldSpecData() will occupy. + /// + /// This method never throws an exception. + unsigned int getFieldSpecDataSize() const { return (nfields_ * + sizeof (*fields_)); } + + /// \brief Return the number of specs fields. + /// + /// It specifies the range of parameter for getFieldSpec(). + /// + /// This method never throws. + unsigned int getFieldCount() const { return (nfields_); } + + /// \brief Return a pointer to a sequence of \c FieldSpec for the + /// \c RdataFields. + /// + /// This should be treated as an opaque internal representation you can + /// just store off somewhere and use it to construct a new RdataFields. + /// from it. If you are really interested, you can typecast it to + /// FieldSpec * (which is what it really is internally). + /// + /// The RdataFields holds ownership of the data. + /// + /// \note You should, however, be aware of alignment issues. The pointer + /// you pass to the constructor must be an address where the FieldSpec + /// can live. If you store it at a wrong address (eg. even one with + /// current implementation on most architectures), it might lead bad + /// things from slow access to SIGBUS. The easiest way is not to + /// interleave the fields with data from getData(). It is OK to place + /// all the fields first (even from multiple RdataFields) and then + /// place all the data after them. + /// + /// This method never throws an exception. + const void* getFieldSpecData() const { + return (fields_); + } + + /// \brief Return the specification of the field identified by the given + /// index. + /// + /// \c field_id is the field index, which must be in the range of + /// <code>[0, getFieldCount())</code>. 0 means the first field, and + /// <code>getFieldCount()-1</code> means the last. + /// + /// If the given index is not in the valid range, an exception of class + /// \c OutOfRange will be thrown. + /// This method never throws an exception otherwise. + /// + /// \param field_id The index of an \c RdataFields field to be returned. + /// \return A \c FieldSpec structure that contains the information of + /// the \c field_id-th field. + FieldSpec getFieldSpec(const unsigned int field_id) const; + //@} + + /// + /// \name Converter Methods + /// + //@{ + /// \brief Render the RdataFields in the wire format with name compression. + /// + /// This method may require resource allocation in \c renderer. + /// If it fails, a corresponding standard exception will be thrown. + /// It should not throw any other exception as long as the \c RdataFields + /// object was constructed from valid parameters (see the description of + /// constructors). The result is not guaranteed if it's constructed in + /// any other ways. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + void toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the RdataFields in the wire format without name + /// compression. + /// + /// This method may require resource allocation in \c buffer. + /// If it fails, a corresponding standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + +private: + const FieldSpec* fields_; + unsigned int nfields_; + const uint8_t* data_; + size_t data_length_; + + // hide further details within the implementation and don't create vectors + // every time we don't need them. + struct RdataFieldsDetail; + RdataFieldsDetail* detail_; +}; +} +} +} +#endif // RDATAFIELDS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h new file mode 100644 index 0000000..9804c57 --- /dev/null +++ b/src/lib/dns/rrclass-placeholder.h @@ -0,0 +1,312 @@ +// Copyright (C) 2010-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 RRCLASS_H +#define RRCLASS_H 1 + +#include <stdint.h> + +#include <string> +#include <ostream> + +#include <dns/exceptions.h> + +#include <boost/optional.hpp> + +// Undefine the macro IN which is defined in some operating systems +// but conflicts the IN RR class. + +#ifdef IN +#undef IN +#endif + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// forward declarations +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if an RRClass object +/// is being constructed from an unrecognized string. +/// +class InvalidRRClass : public DNSTextError { +public: + InvalidRRClass(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if an RRClass object +/// is being constructed from a incomplete (too short) wire-format data. +/// +class IncompleteRRClass : public isc::dns::Exception { +public: + IncompleteRRClass(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// The \c RRClass class encapsulates DNS resource record classes. +/// +/// This class manages the 16-bit integer class codes in quite a straightforward +/// way. The only non trivial task is to handle textual representations of +/// RR classes, such as "IN", "CH", or "CLASS65534". +/// +/// This class consults a helper \c RRParamRegistry class, which is a registry +/// of RR related parameters and has the singleton object. This registry +/// provides a mapping between RR class codes and their "well-known" textual +/// representations. +/// Parameters of RR classes defined by DNS protocol standards are automatically +/// registered at initialization time and are ensured to be always available for +/// applications unless the application explicitly modifies the registry. +/// +/// For convenience, this class defines constant class objects corresponding to +/// standard RR classes. These are generally referred to as the form of +/// <code>RRClass::{class-text}()</code>. +/// For example, \c RRClass::IN() is an \c RRClass object corresponding to the +/// IN class (class code 1). +/// Note that these constants are used through a "proxy" function. +/// This is because they may be used to initialize another non-local (e.g. +/// global or namespace-scope) static object as follows: +/// +/// \code +/// namespace foo { +/// const RRClass default_class = RRClass::IN(); +/// } \endcode +/// +/// In order to ensure that the constant RRClass object has been initialized +/// before the initialization for \c default_class, we need help from +/// the proxy function. +/// +/// Note to developers: same note as \c RRType applies. +class RRClass { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// Constructor from an integer class code. + /// + /// This constructor never throws an exception. + /// + /// \param classcode An 16-bit integer code corresponding to the RRClass. + explicit RRClass(uint16_t classcode) : classcode_(classcode) {} + /// + /// A valid string is one of "well-known" textual class representations + /// such as "IN" or "CH", or in the standard format for "unknown" + /// classes as defined in RFC3597, i.e., "CLASSnnnn". + /// + /// More precisely, the "well-known" representations are the ones stored + /// in the \c RRParamRegistry registry (see the class description). + /// + /// As for the format of "CLASSnnnn", "nnnn" must represent a valid 16-bit + /// unsigned integer, which may contain leading 0's as long as it consists + /// of at most 5 characters (inclusive). + /// For example, "CLASS1" and "CLASS0001" are valid and represent the same + /// class, but "CLASS65536" and "CLASS000001" are invalid. + /// A "CLASSnnnn" representation is valid even if the corresponding class + /// code is registered in the \c RRParamRegistry object. For example, both + /// "IN" and "CLASS1" are valid and represent the same class. + /// + /// All of these representations are case insensitive; "IN" and "in", and + /// "CLASS1" and "class1" are all valid and represent the same classes, + /// respectively. + /// + /// If the given string is not recognized as a valid representation of + /// an RR class, an exception of class \c InvalidRRClass will be thrown. + /// + /// \param class_str A string representation of the \c RRClass + explicit RRClass(const std::string& class_str); + /// Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the RRClass to be constructed. The current read position of + /// the buffer points to the head of the class. + /// + /// If the given data does not large enough to contain a 16-bit integer, + /// an exception of class \c IncompleteRRClass will be thrown. + /// + /// \param buffer A buffer storing the wire format data. + explicit RRClass(isc::util::InputBuffer& buffer); + + /// A separate factory of RRClass from text. + /// + /// This static method is similar to the constructor that takes a + /// string object, but works as a factory and reports parsing + /// failure in the form of the return value. Normally the + /// constructor version should suffice, but in some cases the caller + /// may have to expect mixture of valid and invalid input, and may + /// want to minimize the overhead of possible exception handling. + /// This version is provided for such purpose. + /// + /// For the format of the \c class_str argument, see the + /// <code>RRClass(const std::string&)</code> constructor. + /// + /// If the given text represents a valid RRClass, it returns a + /// pointer to a new \c RRClass object. If the given text does not + /// represent a valid RRClass, it returns \c NULL. + /// + /// One main purpose of this function is to minimize the overhead + /// when the given text does not represent a valid RR class. For + /// this reason this function intentionally omits the capability of + /// delivering a detailed reason for the parse failure, such as in the + /// \c want() string when exception is thrown from the constructor + /// (it will internally require a creation of string object, which + /// is relatively expensive). If such detailed information is + /// necessary, the constructor version should be used to catch the + /// resulting exception. + /// + /// This function never throws the \c InvalidRRClass exception. + /// + /// \param class_str A string representation of the \c RRClass. + /// \return A new RRClass object for the given text or a \c NULL + /// value. + static RRClass* createFromText(const std::string& class_str); + + /// + /// We use the default copy constructor intentionally. + //@} + /// We use the default copy assignment operator intentionally. + /// + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the \c RRClass to a string. + /// + /// If a "well known" textual representation for the class code is + /// registered in the RR parameter registry (see the class description), + /// that will be used as the return value of this method. Otherwise, this + /// method creates a new string for an "unknown" class in the format defined + /// in RFC3597, i.e., "CLASSnnnn", and returns it. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c RRClass. + const std::string toText() const; + /// \brief Render the \c RRClass in the wire format. + /// + /// This method renders the class code in network byte order via + /// \c renderer, which encapsulates output buffer and other rendering + /// contexts. + /// + /// 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 in which the RRClass is to be stored. + void toWire(AbstractMessageRenderer& renderer) const; + /// \brief Render the \c RRClass in the wire format. + /// + /// This method renders the class code in network byte order into the + /// \c buffer. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the RR class code as a 16-bit unsigned integer. + /// + /// This method never throws an exception. + /// + /// \return An 16-bit integer code corresponding to the RRClass. + uint16_t getCode() const { return (classcode_); } + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Return true iff two RRClasses are equal. + /// + /// Two RRClasses are equal iff their class codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if the two RRClasses are equal; otherwise false. + bool equals(const RRClass& other) const + { return (classcode_ == other.classcode_); } + /// \brief Same as \c equals(). + bool operator==(const RRClass& other) const { return (equals(other)); } + + /// \brief Return true iff two RRClasses are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if the two RRClasses are not equal; otherwise false. + bool nequals(const RRClass& other) const + { return (classcode_ != other.classcode_); } + /// \brief Same as \c nequals(). + bool operator!=(const RRClass& other) const { return (nequals(other)); } + + /// \brief Less-than comparison for RRClass against \c other + /// + /// We define the less-than relationship based on their class codes; + /// one RRClass is less than the other iff the code of the former is less + /// than that of the other as unsigned integers. + /// The relationship is meaningless in terms of DNS protocol; the only + /// reason we define this method is that RRClass objects can be stored in + /// STL containers without requiring user-defined less-than relationship. + /// We therefore don't define other comparison operators. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if \c this RRClass is less than the \c other; otherwise + /// false. + bool operator<(const RRClass& other) const + { return (classcode_ < other.classcode_); } + + // BEGIN_WELL_KNOWN_CLASS_DECLARATIONS + // END_WELL_KNOWN_CLASS_DECLARATIONS + +private: + uint16_t classcode_; +}; + +// BEGIN_WELL_KNOWN_CLASS_DEFINITIONS +// END_WELL_KNOWN_CLASS_DEFINITIONS + +/// +/// \brief Insert the \c RRClass as a string into stream. +/// +/// This method convert the \c rrclass 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 RRClass objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrclass The \c RRClass 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 RRClass& rrclass); +} +} +#endif // RRCLASS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc new file mode 100644 index 0000000..4617a3e --- /dev/null +++ b/src/lib/dns/rrclass.cc @@ -0,0 +1,74 @@ +// 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 <config.h> + +#include <stdint.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrparamregistry.h> +#include <dns/rrclass.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; + +namespace isc { +namespace dns { + +RRClass::RRClass(const std::string& class_str) { + uint16_t classcode; + if (!RRParamRegistry::getRegistry().textToClassCode(class_str, classcode)) { + isc_throw(InvalidRRClass, + "Unrecognized RR class string: " + class_str); + } + classcode_ = classcode; +} + +RRClass::RRClass(InputBuffer& buffer) { + if (buffer.getLength() - buffer.getPosition() < sizeof(uint16_t)) { + isc_throw(IncompleteRRClass, "incomplete wire-format RR class"); + } + classcode_ = buffer.readUint16(); +} + +const string +RRClass::toText() const { + return (RRParamRegistry::getRegistry().codeToClassText(classcode_)); +} + +void +RRClass::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(classcode_); +} + +void +RRClass::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(classcode_); +} + +RRClass* +RRClass::createFromText(const string& class_str) { + uint16_t class_code; + if (RRParamRegistry::getRegistry().textToClassCode(class_str, + class_code)) { + return (new RRClass(class_code)); + } + return (NULL); +} + +ostream& +operator<<(ostream& os, const RRClass& rrclass) { + os << rrclass.toText(); + return (os); +} +} +} diff --git a/src/lib/dns/rrclass.h b/src/lib/dns/rrclass.h new file mode 100644 index 0000000..745efa6 --- /dev/null +++ b/src/lib/dns/rrclass.h @@ -0,0 +1,354 @@ +/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +// 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/. + +#ifndef RRCLASS_H +#define RRCLASS_H 1 + +#include <stdint.h> + +#include <string> +#include <ostream> + +#include <dns/exceptions.h> + +#include <boost/optional.hpp> + +// Undefine the macro IN which is defined in some operating systems +// but conflicts the IN RR class. + +#ifdef IN +#undef IN +#endif + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// forward declarations +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if an RRClass object +/// is being constructed from an unrecognized string. +/// +class InvalidRRClass : public DNSTextError { +public: + InvalidRRClass(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if an RRClass object +/// is being constructed from a incomplete (too short) wire-format data. +/// +class IncompleteRRClass : public isc::dns::Exception { +public: + IncompleteRRClass(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// The \c RRClass class encapsulates DNS resource record classes. +/// +/// This class manages the 16-bit integer class codes in quite a straightforward +/// way. The only non trivial task is to handle textual representations of +/// RR classes, such as "IN", "CH", or "CLASS65534". +/// +/// This class consults a helper \c RRParamRegistry class, which is a registry +/// of RR related parameters and has the singleton object. This registry +/// provides a mapping between RR class codes and their "well-known" textual +/// representations. +/// Parameters of RR classes defined by DNS protocol standards are automatically +/// registered at initialization time and are ensured to be always available for +/// applications unless the application explicitly modifies the registry. +/// +/// For convenience, this class defines constant class objects corresponding to +/// standard RR classes. These are generally referred to as the form of +/// <code>RRClass::{class-text}()</code>. +/// For example, \c RRClass::IN() is an \c RRClass object corresponding to the +/// IN class (class code 1). +/// Note that these constants are used through a "proxy" function. +/// This is because they may be used to initialize another non-local (e.g. +/// global or namespace-scope) static object as follows: +/// +/// \code +/// namespace foo { +/// const RRClass default_class = RRClass::IN(); +/// } \endcode +/// +/// In order to ensure that the constant RRClass object has been initialized +/// before the initialization for \c default_class, we need help from +/// the proxy function. +/// +/// Note to developers: same note as \c RRType applies. +class RRClass { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// Constructor from an integer class code. + /// + /// This constructor never throws an exception. + /// + /// \param classcode An 16-bit integer code corresponding to the RRClass. + explicit RRClass(uint16_t classcode) : classcode_(classcode) {} + /// + /// A valid string is one of "well-known" textual class representations + /// such as "IN" or "CH", or in the standard format for "unknown" + /// classes as defined in RFC3597, i.e., "CLASSnnnn". + /// + /// More precisely, the "well-known" representations are the ones stored + /// in the \c RRParamRegistry registry (see the class description). + /// + /// As for the format of "CLASSnnnn", "nnnn" must represent a valid 16-bit + /// unsigned integer, which may contain leading 0's as long as it consists + /// of at most 5 characters (inclusive). + /// For example, "CLASS1" and "CLASS0001" are valid and represent the same + /// class, but "CLASS65536" and "CLASS000001" are invalid. + /// A "CLASSnnnn" representation is valid even if the corresponding class + /// code is registered in the \c RRParamRegistry object. For example, both + /// "IN" and "CLASS1" are valid and represent the same class. + /// + /// All of these representations are case insensitive; "IN" and "in", and + /// "CLASS1" and "class1" are all valid and represent the same classes, + /// respectively. + /// + /// If the given string is not recognized as a valid representation of + /// an RR class, an exception of class \c InvalidRRClass will be thrown. + /// + /// \param class_str A string representation of the \c RRClass + explicit RRClass(const std::string& class_str); + /// Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the RRClass to be constructed. The current read position of + /// the buffer points to the head of the class. + /// + /// If the given data does not large enough to contain a 16-bit integer, + /// an exception of class \c IncompleteRRClass will be thrown. + /// + /// \param buffer A buffer storing the wire format data. + explicit RRClass(isc::util::InputBuffer& buffer); + + /// A separate factory of RRClass from text. + /// + /// This static method is similar to the constructor that takes a + /// string object, but works as a factory and reports parsing + /// failure in the form of the return value. Normally the + /// constructor version should suffice, but in some cases the caller + /// may have to expect mixture of valid and invalid input, and may + /// want to minimize the overhead of possible exception handling. + /// This version is provided for such purpose. + /// + /// For the format of the \c class_str argument, see the + /// <code>RRClass(const std::string&)</code> constructor. + /// + /// If the given text represents a valid RRClass, it returns a + /// pointer to a new \c RRClass object. If the given text does not + /// represent a valid RRClass, it returns \c NULL. + /// + /// One main purpose of this function is to minimize the overhead + /// when the given text does not represent a valid RR class. For + /// this reason this function intentionally omits the capability of + /// delivering a detailed reason for the parse failure, such as in the + /// \c want() string when exception is thrown from the constructor + /// (it will internally require a creation of string object, which + /// is relatively expensive). If such detailed information is + /// necessary, the constructor version should be used to catch the + /// resulting exception. + /// + /// This function never throws the \c InvalidRRClass exception. + /// + /// \param class_str A string representation of the \c RRClass. + /// \return A new RRClass object for the given text or a \c NULL + /// value. + static RRClass* createFromText(const std::string& class_str); + + /// + /// We use the default copy constructor intentionally. + //@} + /// We use the default copy assignment operator intentionally. + /// + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the \c RRClass to a string. + /// + /// If a "well known" textual representation for the class code is + /// registered in the RR parameter registry (see the class description), + /// that will be used as the return value of this method. Otherwise, this + /// method creates a new string for an "unknown" class in the format defined + /// in RFC3597, i.e., "CLASSnnnn", and returns it. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c RRClass. + const std::string toText() const; + /// \brief Render the \c RRClass in the wire format. + /// + /// This method renders the class code in network byte order via + /// \c renderer, which encapsulates output buffer and other rendering + /// contexts. + /// + /// 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 in which the RRClass is to be stored. + void toWire(AbstractMessageRenderer& renderer) const; + /// \brief Render the \c RRClass in the wire format. + /// + /// This method renders the class code in network byte order into the + /// \c buffer. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the RR class code as a 16-bit unsigned integer. + /// + /// This method never throws an exception. + /// + /// \return An 16-bit integer code corresponding to the RRClass. + uint16_t getCode() const { return (classcode_); } + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Return true iff two RRClasses are equal. + /// + /// Two RRClasses are equal iff their class codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if the two RRClasses are equal; otherwise false. + bool equals(const RRClass& other) const + { return (classcode_ == other.classcode_); } + /// \brief Same as \c equals(). + bool operator==(const RRClass& other) const { return (equals(other)); } + + /// \brief Return true iff two RRClasses are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if the two RRClasses are not equal; otherwise false. + bool nequals(const RRClass& other) const + { return (classcode_ != other.classcode_); } + /// \brief Same as \c nequals(). + bool operator!=(const RRClass& other) const { return (nequals(other)); } + + /// \brief Less-than comparison for RRClass against \c other + /// + /// We define the less-than relationship based on their class codes; + /// one RRClass is less than the other iff the code of the former is less + /// than that of the other as unsigned integers. + /// The relationship is meaningless in terms of DNS protocol; the only + /// reason we define this method is that RRClass objects can be stored in + /// STL containers without requiring user-defined less-than relationship. + /// We therefore don't define other comparison operators. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRClass object to compare against. + /// \return true if \c this RRClass is less than the \c other; otherwise + /// false. + bool operator<(const RRClass& other) const + { return (classcode_ < other.classcode_); } + + // BEGIN_WELL_KNOWN_CLASS_DECLARATIONS + static const RRClass& ANY(); + static const RRClass& CH(); + static const RRClass& HS(); + static const RRClass& IN(); + static const RRClass& NONE(); + // END_WELL_KNOWN_CLASS_DECLARATIONS + +private: + uint16_t classcode_; +}; + +// BEGIN_WELL_KNOWN_CLASS_DEFINITIONS +inline const RRClass& +RRClass::ANY() { + static RRClass rrclass(255); + return (rrclass); +} + +inline const RRClass& +RRClass::CH() { + static RRClass rrclass(3); + return (rrclass); +} + +inline const RRClass& +RRClass::HS() { + static RRClass rrclass(4); + return (rrclass); +} + +inline const RRClass& +RRClass::IN() { + static RRClass rrclass(1); + return (rrclass); +} + +inline const RRClass& +RRClass::NONE() { + static RRClass rrclass(254); + return (rrclass); +} + +// END_WELL_KNOWN_CLASS_DEFINITIONS + +/// +/// \brief Insert the \c RRClass as a string into stream. +/// +/// This method convert the \c rrclass 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 RRClass objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrclass The \c RRClass 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 RRClass& rrclass); +} +} +#endif // RRCLASS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrcollator.cc b/src/lib/dns/rrcollator.cc new file mode 100644 index 0000000..8378021 --- /dev/null +++ b/src/lib/dns/rrcollator.cc @@ -0,0 +1,105 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +// include this first to check the header is self-contained. +#include <dns/rrcollator.h> + +#include <dns/name.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdata.h> +#include <dns/rrset.h> + +#include <algorithm> +#include <functional> + +using namespace isc::dns::rdata; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { + +class RRCollator::Impl { +public: + Impl(const AddRRsetCallback& callback) : callback_(callback) {} + + void addRR(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& rrttl, + const RdataPtr& rdata); + + RRsetPtr current_rrset_; + const AddRRsetCallback callback_; +}; + +namespace { +inline bool +isSameType(RRType type1, const ConstRdataPtr& rdata1, + const ConstRRsetPtr& rrset) +{ + if (type1 != rrset->getType()) { + return (false); + } + if (type1 == RRType::RRSIG()) { + RdataIteratorPtr rit = rrset->getRdataIterator(); + return (dynamic_cast<const generic::RRSIG&>(*rdata1).typeCovered() + == dynamic_cast<const generic::RRSIG&>( + rit->getCurrent()).typeCovered()); + } + return (true); +} +} + +void +RRCollator::Impl::addRR(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& rrttl, + const RdataPtr& rdata) +{ + if (current_rrset_ && (!isSameType(rrtype, rdata, current_rrset_) || + current_rrset_->getClass() != rrclass || + current_rrset_->getName() != name)) { + callback_(current_rrset_); + current_rrset_.reset(); + } + + if (!current_rrset_) { + current_rrset_ = RRsetPtr(new RRset(name, rrclass, rrtype, rrttl)); + } else if (current_rrset_->getTTL() != rrttl) { + // RRs with different TTLs are given. Smaller TTL should win. + current_rrset_->setTTL(std::min(current_rrset_->getTTL(), rrttl)); + } + current_rrset_->addRdata(rdata); +} + +RRCollator::RRCollator(const AddRRsetCallback& callback) : + impl_(new Impl(callback)) +{} + +RRCollator::~RRCollator() { + delete impl_; +} + +AddRRCallback +RRCollator::getCallback() { + return (std::bind(&RRCollator::Impl::addRR, this->impl_, + ph::_1, ph::_2, ph::_3, ph::_4, ph::_5)); +} + +void +RRCollator::flush() { + if (impl_->current_rrset_) { + impl_->callback_(impl_->current_rrset_); + impl_->current_rrset_.reset(); + } +} + +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/rrcollator.h b/src/lib/dns/rrcollator.h new file mode 100644 index 0000000..9442fd3 --- /dev/null +++ b/src/lib/dns/rrcollator.h @@ -0,0 +1,125 @@ +// 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 RRCOLLATOR_H +#define RRCOLLATOR_H 1 + +#include <dns/master_loader_callbacks.h> +#include <dns/rrset.h> + +#include <boost/noncopyable.hpp> +#include <functional> + +namespace isc { +namespace dns { + +/// \brief A converter from a stream of RRs to a stream of collated RRsets +/// +/// This class is mainly intended to be a helper used as an adapter for +/// user applications of the \c MasterLoader class; it works as a callback +/// for \c MasterLoader, buffers given RRs from the loader, collating +/// consecutive RRs that belong to the same RRset (ones having the same +/// owner name, RR type and class), and produces a stream of RRsets through +/// its own callback. RRSIGs are also separated if their type covered fields +/// have different values even if the owner name and RR class are the same. +/// +/// It also "normalizes" TTLs of the RR; if collated RRs have different TTLs, +/// this class guarantees that the TTL of the resulting RRsets has the +/// smallest TTL among them. +/// +/// The conversion will be useful for applications of \c MasterLoader because +/// many of this library have interfaces that take an RRset object (or +/// a pointer to it). Note, however, that this class doesn't guarantee that +/// all RRs that would belong to the same RRset are collated into the same +/// single RRset. In fact, it can only collate RRs that are consecutive +/// in the original stream; once it encounters an RR of a different RRset, +/// any subsequent RRs of the previous RRset will form a separate RRset object. +/// +/// This class is non-copyable; it's partially for the convenience of internal +/// implementation details, but it actually doesn't make sense to copy +/// an object of this class, if not harmful, for the intended usage of +/// the class. +class RRCollator : boost::noncopyable { +public: + /// \brief Callback functor type for \c RRCollator. + /// + /// This type of callback is given to an \c RRCollator object on its + /// construction, and will be called for each collated RRset built in + /// the \c RRCollator. + /// + /// \param rrset The collated RRset. + typedef std::function<void(const RRsetPtr& rrset)> AddRRsetCallback; + + /// \brief Constructor. + /// + /// \throw std::bad_alloc Internal memory allocation fails. This should + /// be very rare. + /// + /// \param callback The callback functor to be called for each collated + /// RRset. + RRCollator(const AddRRsetCallback& callback); + + /// \brief Destructor. + /// + /// It only performs trivial internal cleanup. In particular, even if + /// it still has a buffered RRset it will be simply discarded. This is + /// because the given callback could throw an exception, and it's + /// impossible to predict how this class is used (to see if it's a very + /// rare case where propagating an exception from a destructor is + /// justified). Instead, the application needs to make sure that + /// \c flush() is called before the object of this class is destroyed. + /// + /// \throw None + ~RRCollator(); + + /// \brief Call the callback on the remaining RRset, if any. + /// + /// This method is expected to be called that it's supposed all RRs have + /// been passed to this class object. Since there is no explicit + /// indicator of the end of the stream, the user of this class needs to + /// explicitly call this method to call the callback for the last buffered + /// RRset (see also the destructor's description). + /// + /// If there is no buffered RRset, this method does nothing. It can happen + /// if it's called without receiving any RRs, or called more than once. + /// + /// It propagates any exception thrown from the callback; otherwise it + /// doesn't throw anything. + void flush(); + + /// \brief Return \c MasterLoader compatible callback. + /// + /// This method returns a functor in the form of \c AddRRCallback + /// that works as an adapter between \c MasterLoader and an application + /// that needs to get a stream of RRsets. When the returned callback + /// is called, this \c RRCollator object accepts the corresponding RR, + /// and collates it with other RRs of the same RRset if necessary. + /// Every time the \c RRCollator object encounters an RR of a different + /// RRset, it calls the callback passed to the constructor with the RRset + /// built so far. + /// + /// Like \c flush(), this \c AddRRCallback functor propagates any exception + /// thrown from the callback. + /// + /// This method is expected to be called only once for a given + /// \c RRCollator object. It doesn't prohibit duplicate calls, but + /// returned functor objects internally refer to the same \c RRCollator + /// object, and calling the both callbacks randomly will just cause + /// confusion. + AddRRCallback getCallback(); + +private: + class Impl; + Impl* impl_; +}; + +} // namespace dns +} // namespace isc +#endif // RRCOLLATOR_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc new file mode 100644 index 0000000..4021460 --- /dev/null +++ b/src/lib/dns/rrparamregistry-placeholder.cc @@ -0,0 +1,556 @@ +// Copyright (C) 2010-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 <config.h> + +#include <cassert> +#include <algorithm> +#include <cctype> +#include <functional> +#include <map> +#include <string> +#include <sstream> +#include <utility> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/rrparamregistry.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; + +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { + +namespace { +/// +/// The following function and class are a helper to define case-insensitive +/// equivalence relationship on strings. They are used in the mapping +/// containers below. +/// +bool +CICharLess(char c1, char c2) { + return (tolower(static_cast<unsigned char>(c1)) < + tolower(static_cast<unsigned char>(c2))); +} + +struct CIStringLess { + bool operator()(const string& s1, const string& s2) const + { + return (lexicographical_compare(s1.begin(), s1.end(), + s2.begin(), s2.end(), CICharLess)); + } +}; + +struct RRTypeParam { + RRTypeParam(const string& code_string, uint16_t code) : + code_string_(code_string), code_(code) {} + string code_string_; + uint16_t code_; + + /// magic constants + static const unsigned int MAX_CODE = 0xffff; + static const string& UNKNOWN_PREFIX(); + static size_t UNKNOWN_PREFIXLEN(); + static const string& UNKNOWN_MAX(); + static size_t UNKNOWN_MAXLEN(); +}; + +typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr; +typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap; +typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap; + +inline const string& +RRTypeParam::UNKNOWN_PREFIX() { + static const string p("TYPE"); + return (p); +} + +inline size_t +RRTypeParam::UNKNOWN_PREFIXLEN() { + static size_t plen = UNKNOWN_PREFIX().size(); + return (plen); +} + +inline const string& +RRTypeParam::UNKNOWN_MAX() { + static const string p("TYPE65535"); + return (p); +} + +inline size_t +RRTypeParam::UNKNOWN_MAXLEN() { + static size_t plen = UNKNOWN_MAX().size(); + return (plen); +} + +struct RRClassParam { + RRClassParam(const string& code_string, uint16_t code) : + code_string_(code_string), code_(code) {} + string code_string_; + uint16_t code_; + + /// magic constants + static const unsigned int MAX_CODE = 0xffff; + static const string& UNKNOWN_PREFIX(); + static size_t UNKNOWN_PREFIXLEN(); + static const string& UNKNOWN_MAX(); + static size_t UNKNOWN_MAXLEN(); +}; + +typedef boost::shared_ptr<RRClassParam> RRClassParamPtr; +typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap; +typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap; + +inline const string& +RRClassParam::UNKNOWN_PREFIX() { + static const string p("CLASS"); + return (p); +} + +inline size_t +RRClassParam::UNKNOWN_PREFIXLEN() { + static size_t plen = UNKNOWN_PREFIX().size(); + return (plen); +} + +inline const string& +RRClassParam::UNKNOWN_MAX() { + static const string p("CLASS65535"); + return (p); +} + +inline size_t +RRClassParam::UNKNOWN_MAXLEN() { + static size_t plen = UNKNOWN_MAX().size(); + return (plen); +} +} // end of anonymous namespace + +/// Note: the element ordering in the type/class pair is intentional. +/// The standard library will perform inequality comparison (i.e, '<') +/// in the way that the second elements (RRClass) are compared only when +/// the first elements are equivalent. +/// In practice, when we compare two pairs of RRType and RRClass, RRClass +/// would be the same (and, in particular, be class IN) in the majority of +/// cases. So this comparison ordering should be more efficient in common +/// cases. +typedef pair<RRType, RRClass> RRTypeClass; +typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap; +typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap; + +template <typename T> +class RdataFactory : public AbstractRdataFactory { +public: + virtual RdataPtr create(const string& rdata_str) const + { + return (RdataPtr(new T(rdata_str))); + } + + virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const + { + return (RdataPtr(new T(buffer, rdata_len))); + } + + virtual RdataPtr create(const Rdata& source) const + { + return (RdataPtr(new T(dynamic_cast<const T&>(source)))); + } + + virtual RdataPtr create(MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) const + { + return (RdataPtr(new T(lexer, origin, options, callbacks))); + } +}; + +/// +/// \brief The \c RRParamRegistryImpl class is the actual implementation of +/// \c RRParamRegistry. +/// +/// The implementation is hidden from applications. We can refer to specific +/// members of this class only within the implementation source file. +/// +struct RRParamRegistryImpl { + /// Mappings from RR type codes to textual representations. + StrRRTypeMap str2typemap; + /// Mappings from textual representations of RR types to integer codes. + CodeRRTypeMap code2typemap; + /// Mappings from RR class codes to textual representations. + StrRRClassMap str2classmap; + /// Mappings from textual representations of RR classes to integer codes. + CodeRRClassMap code2classmap; + RdataFactoryMap rdata_factories; + GenericRdataFactoryMap genericrdata_factories; +}; + +RRParamRegistry::RRParamRegistry() { + impl_ = new RRParamRegistryImpl; + + // set up parameters for well-known RRs + try { + // BEGIN_WELL_KNOWN_PARAMS + // END_WELL_KNOWN_PARAMS + } catch (...) { + delete impl_; + throw; + } +} + +RRParamRegistry::~RRParamRegistry() { + delete impl_; +} + +RRParamRegistry& +RRParamRegistry::getRegistry() { + static RRParamRegistry registry; + + return (registry); +} + +void +RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, + RdataFactoryPtr rdata_factory) +{ + bool type_added = false; + try { + type_added = addType(typecode_string, typecode); + impl_->genericrdata_factories.insert(pair<RRType, RdataFactoryPtr>( + RRType(typecode), + rdata_factory)); + } catch (...) { + if (type_added) { + removeType(typecode); + } + throw; + } +} + +void +RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, + const std::string& classcode_string, uint16_t classcode, + RdataFactoryPtr rdata_factory) +{ + // Rollback logic on failure is complicated. If adding the new type or + // class fails, we should revert to the original state, cleaning up + // intermediate state. But we need to make sure that we don't remove + // existing data. addType()/addClass() will simply ignore an attempt to + // add the same data, so the cleanup should be performed only when we add + // something new but we fail in other part of the process. + bool type_added = false; + bool class_added = false; + + try { + type_added = addType(typecode_string, typecode); + class_added = addClass(classcode_string, classcode); + impl_->rdata_factories.insert(pair<RRTypeClass, RdataFactoryPtr>( + RRTypeClass(RRType(typecode), + RRClass(classcode)), + rdata_factory)); + } catch (...) { + if (type_added) { + removeType(typecode); + } + if (class_added) { + removeClass(classcode); + } + throw; + } +} + +bool +RRParamRegistry::removeRdataFactory(const RRType& rrtype, + const RRClass& rrclass) +{ + RdataFactoryMap::iterator found = + impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass)); + if (found != impl_->rdata_factories.end()) { + impl_->rdata_factories.erase(found); + return (true); + } + + return (false); +} + +bool +RRParamRegistry::removeRdataFactory(const RRType& rrtype) { + GenericRdataFactoryMap::iterator found = + impl_->genericrdata_factories.find(rrtype); + if (found != impl_->genericrdata_factories.end()) { + impl_->genericrdata_factories.erase(found); + return (true); + } + + return (false); +} + +namespace { +/// +/// These are helper functions to implement case-insensitive string comparison. +/// This could be simplified using strncasecmp(), but unfortunately it's not +/// included in <cstring>. To be as much as portable within the C++ standard +/// we take the "in house" approach here. +/// +bool CICharEqual(char c1, char c2) { + return (tolower(static_cast<unsigned char>(c1)) == + tolower(static_cast<unsigned char>(c2))); +} + +bool +caseStringEqual(const string& s1, const string& s2, size_t n) { + assert(s1.size() >= n && s2.size() >= n); + + return (mismatch(s1.begin(), s1.begin() + n, s2.begin(), CICharEqual).first + == s1.begin() + n); +} + +/// Code logic for RRTypes and RRClasses is mostly common except (C++) type and +/// member names. So we define type-independent templates to describe the +/// common logic and let concrete classes use it to avoid code duplicates. +/// The following summarize template parameters used in the set of template +/// functions: +/// PT: parameter type, either RRTypeParam or RRClassParam +/// MC: type of mapping class from code: either CodeRRTypeMap or CodeRRClassMap +/// MS: type of mapping class from string: either StrRRTypeMap or StrRRClassMap +/// ET: exception type for error handling: either InvalidRRType or +/// InvalidRRClass +template <typename PT, typename MC, typename MS, typename ET> +inline bool +addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap) +{ + // Duplicate type check + typename MC::const_iterator found = codemap.find(code); + if (found != codemap.end()) { + if (found->second->code_string_ != code_string) { + isc_throw(ET, "Duplicate RR parameter registration"); + } + return (false); + } + + typedef boost::shared_ptr<PT> ParamPtr; + typedef pair<string, ParamPtr> StrParamPair; + typedef pair<uint16_t, ParamPtr> CodeParamPair; + ParamPtr param = ParamPtr(new PT(code_string, code)); + try { + stringmap.insert(StrParamPair(code_string, param)); + codemap.insert(CodeParamPair(code, param)); + } catch (...) { + // Rollback to the previous state: not all of the erase operations will + // find the entry, but we don't care. + stringmap.erase(code_string); + codemap.erase(code); + throw; + } + + return (true); +} + +template <typename MC, typename MS> +inline bool +removeParam(uint16_t code, MC& codemap, MS& stringmap) { + typename MC::iterator found = codemap.find(code); + + if (found != codemap.end()) { + size_t erased = stringmap.erase(found->second->code_string_); + // We must have a corresponding entry of the str2 map exists + assert(erased == 1); + + codemap.erase(found); + + return (true); + } + + return (false); +} + +template <typename PT, typename MS> +inline bool +textToCode(const string& code_str, MS& stringmap, uint16_t& ret_code) { + typename MS::const_iterator found; + + found = stringmap.find(code_str); + if (found != stringmap.end()) { + ret_code = found->second->code_; + return (true); + } + + size_t l = code_str.size(); + if (l > PT::UNKNOWN_PREFIXLEN() && + l <= PT::UNKNOWN_MAXLEN() && + caseStringEqual(code_str, PT::UNKNOWN_PREFIX(), + PT::UNKNOWN_PREFIXLEN())) { + unsigned int code; + istringstream iss(code_str.substr(PT::UNKNOWN_PREFIXLEN(), + l - PT::UNKNOWN_PREFIXLEN())); + iss >> dec >> code; + if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) { + ret_code = code; + return (true); + } + } + + return (false); +} + +template <typename PT, typename MC> +inline string +codeToText(uint16_t code, MC& codemap) { + typename MC::const_iterator found; + + found = codemap.find(code); + if (found != codemap.end()) { + return (found->second->code_string_); + } + + ostringstream ss; + ss << code; + return (PT::UNKNOWN_PREFIX() + ss.str()); +} +} + +bool +RRParamRegistry::addType(const string& type_string, uint16_t code) { + return (addParam<RRTypeParam, CodeRRTypeMap, StrRRTypeMap, RRTypeExists> + (type_string, code, impl_->code2typemap, impl_->str2typemap)); +} + +bool +RRParamRegistry::removeType(uint16_t code) { + return (removeParam<CodeRRTypeMap, StrRRTypeMap>(code, impl_->code2typemap, + impl_->str2typemap)); +} + +bool +RRParamRegistry::textToTypeCode(const string& type_string, + uint16_t& type_code) const +{ + return (textToCode<RRTypeParam, StrRRTypeMap> + (type_string, impl_->str2typemap, type_code)); +} + +string +RRParamRegistry::codeToTypeText(uint16_t code) const { + return (codeToText<RRTypeParam, CodeRRTypeMap>(code, impl_->code2typemap)); +} + +bool +RRParamRegistry::addClass(const string& class_string, uint16_t code) { + return (addParam<RRClassParam, CodeRRClassMap, StrRRClassMap, RRClassExists> + (class_string, code, impl_->code2classmap, impl_->str2classmap)); +} + +bool +RRParamRegistry::removeClass(uint16_t code) { + return (removeParam<CodeRRClassMap, StrRRClassMap>(code, + impl_->code2classmap, + impl_->str2classmap)); +} + +bool +RRParamRegistry::textToClassCode(const string& class_string, + uint16_t& class_code) const +{ + return (textToCode<RRClassParam, StrRRClassMap> + (class_string, impl_->str2classmap, class_code)); +} + +string +RRParamRegistry::codeToClassText(uint16_t code) const { + return (codeToText<RRClassParam, CodeRRClassMap>(code, + impl_->code2classmap)); +} + +namespace { +inline const AbstractRdataFactory* +findRdataFactory(RRParamRegistryImpl* reg_impl, + const RRType& rrtype, const RRClass& rrclass) +{ + RdataFactoryMap::const_iterator found; + found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass)); + if (found != reg_impl->rdata_factories.end()) { + return (found->second.get()); + } + + GenericRdataFactoryMap::const_iterator genfound = + reg_impl->genericrdata_factories.find(rrtype); + if (genfound != reg_impl->genericrdata_factories.end()) { + return (genfound->second.get()); + } + + return (NULL); +} +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + const std::string& rdata_string) +{ + // If the text indicates that it's rdata of an "unknown" type (beginning + // with '\# n'), parse it that way. (TBD) + + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(rdata_string)); + } + + return (RdataPtr(new generic::Generic(rdata_string))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + InputBuffer& buffer, size_t rdata_len) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(buffer, rdata_len)); + } + + return (RdataPtr(new generic::Generic(buffer, rdata_len))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + const Rdata& source) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(source)); + } + + return (RdataPtr(new rdata::generic::Generic( + dynamic_cast<const generic::Generic&>(source)))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(lexer, name, options, callbacks)); + } + + return (RdataPtr(new generic::Generic(lexer, name, options, callbacks))); +} +} +} diff --git a/src/lib/dns/rrparamregistry.cc b/src/lib/dns/rrparamregistry.cc new file mode 100644 index 0000000..23c6382 --- /dev/null +++ b/src/lib/dns/rrparamregistry.cc @@ -0,0 +1,660 @@ +// Copyright (C) 2010-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/. + +/// @file rrparamregistry.cc +/// +/// THIS FILE USED TO BE AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/// Once this library was taken over by Kea project, we do not develop +/// the DNS capabilities anymore, as Kea focuses on DHCP, not DNS. +/// As such, we stopped adding new RR types as those we have are +/// sufficient. Therefore it's unlikely this file will ever be +/// regenerated. + +#include <config.h> + +#include <cassert> +#include <algorithm> +#include <cctype> +#include <functional> +#include <map> +#include <string> +#include <sstream> +#include <utility> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/rrparamregistry.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + +using namespace std; + +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { + +namespace { +/// +/// The following function and class are a helper to define case-insensitive +/// equivalence relationship on strings. They are used in the mapping +/// containers below. +/// +bool +CICharLess(char c1, char c2) { + return (tolower(static_cast<unsigned char>(c1)) < + tolower(static_cast<unsigned char>(c2))); +} + +struct CIStringLess { + bool operator()(const string& s1, const string& s2) const + { + return (lexicographical_compare(s1.begin(), s1.end(), + s2.begin(), s2.end(), CICharLess)); + } +}; + +struct RRTypeParam { + RRTypeParam(const string& code_string, uint16_t code) : + code_string_(code_string), code_(code) {} + string code_string_; + uint16_t code_; + + /// magic constants + static const unsigned int MAX_CODE = 0xffff; + static const string& UNKNOWN_PREFIX(); + static size_t UNKNOWN_PREFIXLEN(); + static const string& UNKNOWN_MAX(); + static size_t UNKNOWN_MAXLEN(); +}; + +typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr; +typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap; +typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap; + +inline const string& +RRTypeParam::UNKNOWN_PREFIX() { + static const string p("TYPE"); + return (p); +} + +inline size_t +RRTypeParam::UNKNOWN_PREFIXLEN() { + static size_t plen = UNKNOWN_PREFIX().size(); + return (plen); +} + +inline const string& +RRTypeParam::UNKNOWN_MAX() { + static const string p("TYPE65535"); + return (p); +} + +inline size_t +RRTypeParam::UNKNOWN_MAXLEN() { + static size_t plen = UNKNOWN_MAX().size(); + return (plen); +} + +struct RRClassParam { + RRClassParam(const string& code_string, uint16_t code) : + code_string_(code_string), code_(code) {} + string code_string_; + uint16_t code_; + + /// magic constants + static const unsigned int MAX_CODE = 0xffff; + static const string& UNKNOWN_PREFIX(); + static size_t UNKNOWN_PREFIXLEN(); + static const string& UNKNOWN_MAX(); + static size_t UNKNOWN_MAXLEN(); +}; + +typedef boost::shared_ptr<RRClassParam> RRClassParamPtr; +typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap; +typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap; + +inline const string& +RRClassParam::UNKNOWN_PREFIX() { + static const string p("CLASS"); + return (p); +} + +inline size_t +RRClassParam::UNKNOWN_PREFIXLEN() { + static size_t plen = UNKNOWN_PREFIX().size(); + return (plen); +} + +inline const string& +RRClassParam::UNKNOWN_MAX() { + static const string p("CLASS65535"); + return (p); +} + +inline size_t +RRClassParam::UNKNOWN_MAXLEN() { + static size_t plen = UNKNOWN_MAX().size(); + return (plen); +} +} // end of anonymous namespace + +/// Note: the element ordering in the type/class pair is intentional. +/// The standard library will perform inequality comparison (i.e, '<') +/// in the way that the second elements (RRClass) are compared only when +/// the first elements are equivalent. +/// In practice, when we compare two pairs of RRType and RRClass, RRClass +/// would be the same (and, in particular, be class IN) in the majority of +/// cases. So this comparison ordering should be more efficient in common +/// cases. +typedef pair<RRType, RRClass> RRTypeClass; +typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap; +typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap; + +template <typename T> +class RdataFactory : public AbstractRdataFactory { +public: + virtual RdataPtr create(const string& rdata_str) const + { + return (RdataPtr(new T(rdata_str))); + } + + virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const + { + return (RdataPtr(new T(buffer, rdata_len))); + } + + virtual RdataPtr create(const Rdata& source) const + { + return (RdataPtr(new T(dynamic_cast<const T&>(source)))); + } + + virtual RdataPtr create(MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) const + { + return (RdataPtr(new T(lexer, origin, options, callbacks))); + } +}; + +/// +/// \brief The \c RRParamRegistryImpl class is the actual implementation of +/// \c RRParamRegistry. +/// +/// The implementation is hidden from applications. We can refer to specific +/// members of this class only within the implementation source file. +/// +struct RRParamRegistryImpl { + /// Mappings from RR type codes to textual representations. + StrRRTypeMap str2typemap; + /// Mappings from textual representations of RR types to integer codes. + CodeRRTypeMap code2typemap; + /// Mappings from RR class codes to textual representations. + StrRRClassMap str2classmap; + /// Mappings from textual representations of RR classes to integer codes. + CodeRRClassMap code2classmap; + RdataFactoryMap rdata_factories; + GenericRdataFactoryMap genericrdata_factories; +}; + +RRParamRegistry::RRParamRegistry() { + impl_ = new RRParamRegistryImpl; + + // set up parameters for well-known RRs + try { + // BEGIN_WELL_KNOWN_PARAMS + add("A", 1, "IN", 1, RdataFactoryPtr(new RdataFactory<in::A>())); + add("NS", 2, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NS>())); + add("CNAME", 5, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::CNAME>())); + add("SOA", 6, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SOA>())); + add("PTR", 12, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::PTR>())); + add("HINFO", 13, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::HINFO>())); + add("MINFO", 14, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::MINFO>())); + add("MX", 15, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::MX>())); + add("TXT", 16, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TXT>())); + add("RP", 17, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::RP>())); + add("AFSDB", 18, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::AFSDB>())); + add("AAAA", 28, "IN", 1, RdataFactoryPtr(new RdataFactory<in::AAAA>())); + add("SRV", 33, "IN", 1, RdataFactoryPtr(new RdataFactory<in::SRV>())); + add("NAPTR", 35, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NAPTR>())); + add("DNAME", 39, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DNAME>())); + add("OPT", 41, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::OPT>())); + add("DS", 43, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DS>())); + add("SSHFP", 44, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SSHFP>())); + add("RRSIG", 46, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::RRSIG>())); + add("NSEC", 47, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC>())); + add("DNSKEY", 48, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DNSKEY>())); + add("DHCID", 49, "IN", 1, RdataFactoryPtr(new RdataFactory<in::DHCID>())); + add("NSEC3", 50, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC3>())); + add("NSEC3PARAM", 51, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC3PARAM>())); + add("TLSA", 52, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TLSA>())); + add("SPF", 99, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SPF>())); + add("TKEY", 249, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TKEY>())); + add("CAA", 257, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::CAA>())); + add("DLV", 32769, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DLV>())); + add("A", 1, "CH", 3, RdataFactoryPtr(new RdataFactory<ch::A>())); + add("A", 1, "HS", 4, RdataFactoryPtr(new RdataFactory<hs::A>())); + add("TSIG", 250, "ANY", 255, RdataFactoryPtr(new RdataFactory<any::TSIG>())); + add("NS", 2, RdataFactoryPtr(new RdataFactory<generic::NS>())); + add("CNAME", 5, RdataFactoryPtr(new RdataFactory<generic::CNAME>())); + add("SOA", 6, RdataFactoryPtr(new RdataFactory<generic::SOA>())); + add("PTR", 12, RdataFactoryPtr(new RdataFactory<generic::PTR>())); + add("HINFO", 13, RdataFactoryPtr(new RdataFactory<generic::HINFO>())); + add("MINFO", 14, RdataFactoryPtr(new RdataFactory<generic::MINFO>())); + add("MX", 15, RdataFactoryPtr(new RdataFactory<generic::MX>())); + add("TXT", 16, RdataFactoryPtr(new RdataFactory<generic::TXT>())); + add("RP", 17, RdataFactoryPtr(new RdataFactory<generic::RP>())); + add("AFSDB", 18, RdataFactoryPtr(new RdataFactory<generic::AFSDB>())); + add("NAPTR", 35, RdataFactoryPtr(new RdataFactory<generic::NAPTR>())); + add("DNAME", 39, RdataFactoryPtr(new RdataFactory<generic::DNAME>())); + add("OPT", 41, RdataFactoryPtr(new RdataFactory<generic::OPT>())); + add("DS", 43, RdataFactoryPtr(new RdataFactory<generic::DS>())); + add("SSHFP", 44, RdataFactoryPtr(new RdataFactory<generic::SSHFP>())); + add("RRSIG", 46, RdataFactoryPtr(new RdataFactory<generic::RRSIG>())); + add("NSEC", 47, RdataFactoryPtr(new RdataFactory<generic::NSEC>())); + add("DNSKEY", 48, RdataFactoryPtr(new RdataFactory<generic::DNSKEY>())); + add("NSEC3", 50, RdataFactoryPtr(new RdataFactory<generic::NSEC3>())); + add("NSEC3PARAM", 51, RdataFactoryPtr(new RdataFactory<generic::NSEC3PARAM>())); + add("TLSA", 52, RdataFactoryPtr(new RdataFactory<generic::TLSA>())); + add("SPF", 99, RdataFactoryPtr(new RdataFactory<generic::SPF>())); + add("TKEY", 249, RdataFactoryPtr(new RdataFactory<generic::TKEY>())); + add("CAA", 257, RdataFactoryPtr(new RdataFactory<generic::CAA>())); + add("DLV", 32769, RdataFactoryPtr(new RdataFactory<generic::DLV>())); + // Meta and non-implemented RR types + addType("IXFR", 251); + addType("AXFR", 252); + addType("ANY", 255); + addType("MD", 3); + addType("MF", 4); + addType("MB", 7); + addType("MG", 8); + addType("MR", 9); + addType("NXT", 30); + addType("A6", 38); + addType("MAILA", 254); + addType("NULL", 10); + addType("WKS", 11); + addType("X25", 19); + addType("RT", 21); + addType("NSAP", 22); + addType("NSAP-PTR", 23); + addType("SIG", 24); + addType("ISDN", 20); + addType("KEY", 25); + addType("PX", 26); + addType("GPOS", 27); + addType("LOC", 29); + addType("KX", 36); + addType("CERT", 37); + addType("APL", 42); + addType("IPSECKEY", 45); + addType("HIP", 55); + addType("UNSPEC", 103); + addType("NID", 104); + addType("L32", 105); + addType("L64", 106); + addType("LP", 107); + addType("MAILB", 253); + addType("URI", 256); + // Meta classes + addClass("NONE", 254); + // END_WELL_KNOWN_PARAMS + } catch (...) { + delete impl_; + throw; + } +} + +RRParamRegistry::~RRParamRegistry() { + delete impl_; +} + +RRParamRegistry& +RRParamRegistry::getRegistry() { + static RRParamRegistry registry; + + return (registry); +} + +void +RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, + RdataFactoryPtr rdata_factory) +{ + bool type_added = false; + try { + type_added = addType(typecode_string, typecode); + impl_->genericrdata_factories.insert(pair<RRType, RdataFactoryPtr>( + RRType(typecode), + rdata_factory)); + } catch (...) { + if (type_added) { + removeType(typecode); + } + throw; + } +} + +void +RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, + const std::string& classcode_string, uint16_t classcode, + RdataFactoryPtr rdata_factory) +{ + // Rollback logic on failure is complicated. If adding the new type or + // class fails, we should revert to the original state, cleaning up + // intermediate state. But we need to make sure that we don't remove + // existing data. addType()/addClass() will simply ignore an attempt to + // add the same data, so the cleanup should be performed only when we add + // something new but we fail in other part of the process. + bool type_added = false; + bool class_added = false; + + try { + type_added = addType(typecode_string, typecode); + class_added = addClass(classcode_string, classcode); + impl_->rdata_factories.insert(pair<RRTypeClass, RdataFactoryPtr>( + RRTypeClass(RRType(typecode), + RRClass(classcode)), + rdata_factory)); + } catch (...) { + if (type_added) { + removeType(typecode); + } + if (class_added) { + removeClass(classcode); + } + throw; + } +} + +bool +RRParamRegistry::removeRdataFactory(const RRType& rrtype, + const RRClass& rrclass) +{ + RdataFactoryMap::iterator found = + impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass)); + if (found != impl_->rdata_factories.end()) { + impl_->rdata_factories.erase(found); + return (true); + } + + return (false); +} + +bool +RRParamRegistry::removeRdataFactory(const RRType& rrtype) { + GenericRdataFactoryMap::iterator found = + impl_->genericrdata_factories.find(rrtype); + if (found != impl_->genericrdata_factories.end()) { + impl_->genericrdata_factories.erase(found); + return (true); + } + + return (false); +} + +namespace { +/// +/// These are helper functions to implement case-insensitive string comparison. +/// This could be simplified using strncasecmp(), but unfortunately it's not +/// included in <cstring>. To be as much as portable within the C++ standard +/// we take the "in house" approach here. +/// +bool CICharEqual(char c1, char c2) { + return (tolower(static_cast<unsigned char>(c1)) == + tolower(static_cast<unsigned char>(c2))); +} + +bool +caseStringEqual(const string& s1, const string& s2, size_t n) { + assert(s1.size() >= n && s2.size() >= n); + + return (mismatch(s1.begin(), s1.begin() + n, s2.begin(), CICharEqual).first + == s1.begin() + n); +} + +/// Code logic for RRTypes and RRClasses is mostly common except (C++) type and +/// member names. So we define type-independent templates to describe the +/// common logic and let concrete classes use it to avoid code duplicates. +/// The following summarize template parameters used in the set of template +/// functions: +/// PT: parameter type, either RRTypeParam or RRClassParam +/// MC: type of mapping class from code: either CodeRRTypeMap or CodeRRClassMap +/// MS: type of mapping class from string: either StrRRTypeMap or StrRRClassMap +/// ET: exception type for error handling: either InvalidRRType or +/// InvalidRRClass +template <typename PT, typename MC, typename MS, typename ET> +inline bool +addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap) +{ + // Duplicate type check + typename MC::const_iterator found = codemap.find(code); + if (found != codemap.end()) { + if (found->second->code_string_ != code_string) { + isc_throw(ET, "Duplicate RR parameter registration"); + } + return (false); + } + + typedef boost::shared_ptr<PT> ParamPtr; + typedef pair<string, ParamPtr> StrParamPair; + typedef pair<uint16_t, ParamPtr> CodeParamPair; + ParamPtr param = ParamPtr(new PT(code_string, code)); + try { + stringmap.insert(StrParamPair(code_string, param)); + codemap.insert(CodeParamPair(code, param)); + } catch (...) { + // Rollback to the previous state: not all of the erase operations will + // find the entry, but we don't care. + stringmap.erase(code_string); + codemap.erase(code); + throw; + } + + return (true); +} + +template <typename MC, typename MS> +inline bool +removeParam(uint16_t code, MC& codemap, MS& stringmap) { + typename MC::iterator found = codemap.find(code); + + if (found != codemap.end()) { + size_t erased = stringmap.erase(found->second->code_string_); + // We must have a corresponding entry of the str2 map exists + assert(erased == 1); + + codemap.erase(found); + + return (true); + } + + return (false); +} + +template <typename PT, typename MS> +inline bool +textToCode(const string& code_str, MS& stringmap, uint16_t& ret_code) { + typename MS::const_iterator found; + + found = stringmap.find(code_str); + if (found != stringmap.end()) { + ret_code = found->second->code_; + return (true); + } + + size_t l = code_str.size(); + if (l > PT::UNKNOWN_PREFIXLEN() && + l <= PT::UNKNOWN_MAXLEN() && + caseStringEqual(code_str, PT::UNKNOWN_PREFIX(), + PT::UNKNOWN_PREFIXLEN())) { + unsigned int code; + istringstream iss(code_str.substr(PT::UNKNOWN_PREFIXLEN(), + l - PT::UNKNOWN_PREFIXLEN())); + iss >> dec >> code; + if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) { + ret_code = code; + return (true); + } + } + + return (false); +} + +template <typename PT, typename MC> +inline string +codeToText(uint16_t code, MC& codemap) { + typename MC::const_iterator found; + + found = codemap.find(code); + if (found != codemap.end()) { + return (found->second->code_string_); + } + + ostringstream ss; + ss << code; + return (PT::UNKNOWN_PREFIX() + ss.str()); +} +} + +bool +RRParamRegistry::addType(const string& type_string, uint16_t code) { + return (addParam<RRTypeParam, CodeRRTypeMap, StrRRTypeMap, RRTypeExists> + (type_string, code, impl_->code2typemap, impl_->str2typemap)); +} + +bool +RRParamRegistry::removeType(uint16_t code) { + return (removeParam<CodeRRTypeMap, StrRRTypeMap>(code, impl_->code2typemap, + impl_->str2typemap)); +} + +bool +RRParamRegistry::textToTypeCode(const string& type_string, + uint16_t& type_code) const +{ + return (textToCode<RRTypeParam, StrRRTypeMap> + (type_string, impl_->str2typemap, type_code)); +} + +string +RRParamRegistry::codeToTypeText(uint16_t code) const { + return (codeToText<RRTypeParam, CodeRRTypeMap>(code, impl_->code2typemap)); +} + +bool +RRParamRegistry::addClass(const string& class_string, uint16_t code) { + return (addParam<RRClassParam, CodeRRClassMap, StrRRClassMap, RRClassExists> + (class_string, code, impl_->code2classmap, impl_->str2classmap)); +} + +bool +RRParamRegistry::removeClass(uint16_t code) { + return (removeParam<CodeRRClassMap, StrRRClassMap>(code, + impl_->code2classmap, + impl_->str2classmap)); +} + +bool +RRParamRegistry::textToClassCode(const string& class_string, + uint16_t& class_code) const +{ + return (textToCode<RRClassParam, StrRRClassMap> + (class_string, impl_->str2classmap, class_code)); +} + +string +RRParamRegistry::codeToClassText(uint16_t code) const { + return (codeToText<RRClassParam, CodeRRClassMap>(code, + impl_->code2classmap)); +} + +namespace { +inline const AbstractRdataFactory* +findRdataFactory(RRParamRegistryImpl* reg_impl, + const RRType& rrtype, const RRClass& rrclass) +{ + RdataFactoryMap::const_iterator found; + found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass)); + if (found != reg_impl->rdata_factories.end()) { + return (found->second.get()); + } + + GenericRdataFactoryMap::const_iterator genfound = + reg_impl->genericrdata_factories.find(rrtype); + if (genfound != reg_impl->genericrdata_factories.end()) { + return (genfound->second.get()); + } + + return (NULL); +} +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + const std::string& rdata_string) +{ + // If the text indicates that it's rdata of an "unknown" type (beginning + // with '\# n'), parse it that way. (TBD) + + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(rdata_string)); + } + + return (RdataPtr(new generic::Generic(rdata_string))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + InputBuffer& buffer, size_t rdata_len) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(buffer, rdata_len)); + } + + return (RdataPtr(new generic::Generic(buffer, rdata_len))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + const Rdata& source) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(source)); + } + + return (RdataPtr(new rdata::generic::Generic( + dynamic_cast<const generic::Generic&>(source)))); +} + +RdataPtr +RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) +{ + const AbstractRdataFactory* factory = + findRdataFactory(impl_, rrtype, rrclass); + if (factory != NULL) { + return (factory->create(lexer, name, options, callbacks)); + } + + return (RdataPtr(new generic::Generic(lexer, name, options, callbacks))); +} +} +} diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h new file mode 100644 index 0000000..27e8a4f --- /dev/null +++ b/src/lib/dns/rrparamregistry.h @@ -0,0 +1,545 @@ +// 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 RRPARAMREGISTRY_H +#define RRPARAMREGISTRY_H 1 + +#include <string> + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +#include <dns/exceptions.h> + +#include <dns/rdata.h> + +namespace isc { +namespace dns { + +// forward declarations +struct RRParamRegistryImpl; + +/// +/// \brief A standard DNS module exception that is thrown if a new RR type is +/// being registered with a different type string. +/// +class RRTypeExists : public isc::dns::Exception { +public: + RRTypeExists(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 new RR class is +/// being registered with a different type string. +/// +class RRClassExists : public isc::dns::Exception { +public: + RRClassExists(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +namespace rdata { +/// \brief The \c AbstractRdataFactory class is an abstract base class to +/// encapsulate a set of Rdata factory methods in a polymorphic way. +/// +/// An external developer who wants to introduce a new or experimental RR type +/// is expected to define a corresponding derived class of \c +/// AbstractRdataFactory and register it via \c RRParamRegistry. +/// +/// Other users of this API normally do not have to care about this class +/// or its derived classes; this class is generally intended to be used +/// as an internal utility of the API implementation. +class AbstractRdataFactory { + /// + /// \name Constructors and Destructor + /// + //@{ +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). + AbstractRdataFactory() {} +public: + /// The destructor. + virtual ~AbstractRdataFactory() {}; + //@} + + /// + /// \name Factory methods for polymorphic creation. + /// + //@{ + + /// \brief Create RDATA from a string. + /// + /// This method creates from a string an \c Rdata object of specific class + /// corresponding to the specific derived class of \c AbstractRdataFactory. + /// + /// \param rdata_str A string of textual representation of the \c Rdata. + /// \return An \c RdataPtr object pointing to the created \c Rdata object. + virtual RdataPtr create(const std::string& rdata_str) const = 0; + + /// \brief Create RDATA from wire-format data. + /// + /// This method creates from wire-format binary data an \c Rdata object + /// of specific class corresponding to the specific derived class of + /// \c AbstractRdataFactory. + /// + /// \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. + /// \return An \c RdataPtr object pointing to the created \c Rdata object. + virtual RdataPtr create(isc::util::InputBuffer& buffer, size_t rdata_len) const = 0; + + /// \brief Create RDATA from another \c Rdata object of the same type. + /// + /// This method creates an \c Rdata object of specific class corresponding + /// to the specific derived class of \c AbstractRdataFactory, copying the + /// content of the given \c Rdata, \c source. + /// + /// \c source must be an object of the concrete derived class corresponding + /// to the specific derived class of \c AbstractRdataFactory; + /// otherwise, an exception of class \c std::bad_cast will be thrown. + /// + /// \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. + virtual RdataPtr create(const rdata::Rdata& source) const = 0; + + /// \brief Create RDATA using MasterLexer. + /// + /// This version of the method defines the entry point of factory + /// of a specific RR type and class for \c RRParamRegistry::createRdata() + /// that uses \c MasterLexer. See its description for the expected + /// behavior and meaning of the parameters. + virtual RdataPtr create(MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) const = 0; + //@} +}; + +/// +/// The \c RdataFactoryPtr type is a pointer-like type, pointing to an +/// object of some concrete derived class of \c AbstractRdataFactory. +/// +typedef boost::shared_ptr<AbstractRdataFactory> RdataFactoryPtr; +} // end of namespace rdata + +/// +/// The \c RRParamRegistry class represents a registry of parameters to +/// manipulate DNS resource records (RRs). +/// +/// A \c RRParamRegistry class object stores a mapping between RR types or +/// classes and their textual representations. It will also have knowledge of +/// how to create an RDATA object for a specific pair of RR type and class +/// (not implemented in this version). +/// +/// Normal applications that only handle standard DNS protocols won't have to +/// care about this class. This is mostly an internal class to the DNS library +/// to manage standard parameters. Some advanced applications may still need +/// to use this class explicitly. For example, if an application wants to +/// define and use an experimental non-standard RR type, it may want to register +/// related protocol parameters for its convenience. This class is designed to +/// allow such usage without modifying the library source code or rebuilding +/// the library. +/// +/// It is assumed that at most one instance of this class can exist so that +/// the application uses the consistent set of registered parameters. To ensure +/// this, this class is designed and implemented as a "singleton class": the +/// constructor is intentionally private, and applications must get access to +/// the single instance via the \c getRegistry() static member function. +/// +/// In the current implementation, access to the singleton \c RRParamRegistry +/// object is not thread safe. +/// The application should ensure that multiple threads don't race in the +/// first invocation of \c getRegistry(), and, if the registry needs to +/// be changed dynamically, read and write operations are performed +/// exclusively. +/// Since this class should be static in common usage this restriction would +/// be acceptable in practice. +/// In the future, we may extend the implementation so that multiple threads can +/// get access to the registry fully concurrently without any restriction. +/// +/// Note: the implementation of this class is incomplete: we should at least +/// add RDATA related parameters. This will be done in a near future version, +/// at which point some of method signatures will be changed. +class RRParamRegistry { + /// + /// \name Constructors and Destructor + /// + /// These are intentionally hidden (see the class description). + //@{ +private: + RRParamRegistry(); + RRParamRegistry(const RRParamRegistry& orig); + ~RRParamRegistry(); + //@} +public: + /// + /// \brief Return the singleton instance of \c RRParamRegistry. + /// + /// This method is a unified access point to the singleton instance of + /// the RR parameter registry (\c RRParamRegistry). + /// On first invocation it internally constructs an instance of the + /// \c RRParamRegistry class and returns a reference to it. + /// This is a static object inside this method and will remain valid + /// throughout the rest of the application lifetime. + /// On subsequent calls this method simply returns a reference to the + /// singleton object. + /// + /// If resource allocation fails in the first invocation, + /// a corresponding standard exception will be thrown. + /// This method never fails otherwise. In particular, this method + /// doesn't throw an exception once the singleton instance is constructed. + /// + /// \return A reference to the singleton instance of \c RRParamRegistry. + static RRParamRegistry& getRegistry(); + + /// + /// \name Registry Update Methods + /// + //@{ + /// + /// \brief Add a set of parameters for a pair of RR type and class. + /// + /// This method adds to the registry a specified set of RR parameters, + /// including mappings between type/class codes and their textual + /// representations. + /// + /// Regarding the mappings between textual representations and integer + /// codes, this method behaves in the same way as \c addType() and + /// \c addClass(). That is, it ignores any overriding attempt as + /// long as the mapping is the same; otherwise the corresponding exception + /// will be thrown. + /// + /// This method provides the strong exception guarantee: unless an + /// exception is thrown the entire specified set of parameters must be + /// stored in the registry; if this method throws an exception the + /// registry will remain in the state before this method is called. + /// + /// \param type_string The textual representation of the RR type. + /// \param type_code The integer code of the RR type. + /// \param class_string The textual representation of the RR class. + /// \param class_code The integer code of the RR class. + /// \param rdata_factory An \c RdataFactoryPtr object pointing to a + /// concrete RDATA factory. + void add(const std::string& type_string, uint16_t type_code, + const std::string& class_string, uint16_t class_code, + rdata::RdataFactoryPtr rdata_factory); + + /// \brief Add a set of parameters for a class-independent RR type. + /// + /// This method behaves as exactly same as the other \c add method except + /// that it handles class-independent types (such as NS, CNAME, or SOA). + /// + /// \param type_string The textual representation of the RR type. + /// \param type_code The integer code of the RR type. + /// \param rdata_factory An \c RdataFactoryPtr object pointing to a + /// concrete RDATA factory. + void add(const std::string& type_string, uint16_t type_code, + rdata::RdataFactoryPtr rdata_factory); + + /// \brief Add mappings between RR type code and textual representation. + /// + /// This method adds a mapping from the type code of an RR to its textual + /// representation and the reverse mapping in the registry. + /// + /// If the given RR type is already registered with the same textual + /// representation, this method simply ignores the duplicate mapping; + /// if the given type is registered and a new pair with a different + /// textual representation is being added,an exception of class + /// \c RRTypeExist will be thrown. + /// To replace an existing mapping with a different textual representation, + /// the existing one must be removed by the \c removeType() method + /// beforehand. + /// + /// In addition, if resource allocation for the new mapping entries fails, + /// a corresponding standard exception will be thrown. + /// + /// This method provides the strong exception guarantee: unless an exception + /// is thrown the specified mappings must be stored in the registry + /// (although it may be an already existing one) on completion of the + /// method; if this method throws an exception the registry will remain + /// in the state before this method is called. + /// + /// \param type_string The textual representation of the RR type. + /// \param type_code The integer code of the RR type. + /// \return \c true if a new mapping is added to the repository; \c false + /// if the same mapping is already registered. + bool addType(const std::string& type_string, uint16_t type_code); + + /// \brief Remove mappings between RR type code and textual representation + /// for a given type. + /// + /// This method can safely be called whether or not the specified mappings + /// exist in the registry. If not, this method simply ignores the attempt + /// and returns \c false. + /// + /// This method never throws an exception. + /// + /// \param type_code The integer code of the RR type. + /// \return \c true if mappings for the specified RR type exists and is + /// removed; \c false if no such mapping is in the registry. + bool removeType(uint16_t type_code); + + /// \brief Add mappings between RR class code and textual representation. + /// + /// This method adds a mapping from the class code of an RR to its textual + /// representation and the reverse mapping in the registry. + /// + /// If the given RR class is already registered with the same textual + /// representation, this method simply ignores the duplicate mapping; + /// if the given class is registered and a new pair with a different + /// textual representation is being added,an exception of class + /// \c RRClassExist will be thrown. + /// To replace an existing mapping with a different textual representation, + /// the existing one must be removed by the \c removeClass() method + /// beforehand. + /// + /// In addition, if resource allocation for the new mapping entries fails, + /// a corresponding standard exception will be thrown. + /// + /// This method provides the strong exception guarantee: unless an exception + /// is thrown the specified mappings must be stored in the registry + /// (although it may be an already existing one) on completion of the + /// method; if this method throws an exception the registry will remain + /// in the state before this method is called. + /// + /// \param class_string The textual representation of the RR class. + /// \param class_code The integer code of the RR class. + /// \return \c true if a new mapping is added to the repository; \c false + /// if the same mapping is already registered. + bool addClass(const std::string& class_string, uint16_t class_code); + + /// \brief Remove mappings between RR class code and textual representation + /// for a given class. + /// + /// This method can safely be called whether or not the specified mappings + /// exist in the registry. If not, this method simply ignores the attempt + /// and returns \c false. + /// + /// This method never throws an exception. + /// + /// \param class_code The integer code of the RR class. + /// \return \c true if mappings for the specified RR type exists and is + /// removed; \c false if no such mapping is in the registry. + bool removeClass(uint16_t class_code); + + /// \brief Remove registered RDATA factory for the given pair of \c RRType + /// and \c RRClass. + /// + /// This method can safely be called whether or not the specified factory + /// object exist in the registry. If not, this method simply ignores the + /// attempt and returns \c false. + /// + /// This method never throws an exception. + /// + /// \param rrtype An \c RRType object specifying the type/class pair. + /// \param rrclass An \c RRClass object specifying the type/class pair. + /// \return \c true if a factory object for the specified RR type/class + /// pair exists and is removed; \c false if no such object is in the + /// registry. + bool removeRdataFactory(const RRType& rrtype, const RRClass& rrclass); + + /// \brief Remove registered RDATA factory for the given pair of \c RRType + /// and \c RRClass. + /// + /// This method can safely be called whether or not the specified factory + /// object exist in the registry. If not, this method simply ignores the + /// attempt and returns \c false. + /// + /// This method never throws an exception. + /// + /// \param rrtype An \c RRType object specifying the type/class pair. + /// \return \c true if a factory object for the specified RR type/class + /// pair exists and is removed; \c false if no such object is in the + /// registry. + bool removeRdataFactory(const RRType& rrtype); + //@} + + /// + /// \name Parameter Conversion Methods + /// + //@{ + /// \brief Convert a textual representation of an RR type to the + /// corresponding 16-bit integer code. + /// + /// This method searches the \c RRParamRegistry for the mapping from + /// the given textual representation of RR type to the corresponding + /// integer code. If a mapping is found, it returns true with the + /// associated type code in \c type_code; otherwise, if the given + /// string is in the form of "TYPEnnnn", it returns true with the + /// corresponding number as the type code in \c type_code; + /// otherwise, it returns false and \c type_code is untouched. + /// + /// It returns \c false and avoids throwing an exception in the case + /// of an error to avoid the exception overhead in some situations. + /// + /// \param type_string The textual representation of the RR type. + /// \param type_code Returns the RR type code in this argument. + /// \return true if conversion is successful, false otherwise. + bool textToTypeCode(const std::string& type_string, + uint16_t& type_code) const; + + /// \brief Convert type code into its textual representation. + /// + /// This method searches the \c RRParamRegistry for the mapping from the + /// given RR type code to its textual representation. + /// If a mapping is found, it returns (a copy of) the associated string; + /// otherwise, this method creates a new string in the form of "TYPEnnnn" + /// where "nnnn" is the decimal representation of the type code, and + /// returns the new string. + /// + /// If resource allocation for the returned string fails, + /// a corresponding standard exception will be thrown. + /// This method never fails otherwise. + /// + /// \param type_code The integer code of the RR type. + /// \return A textual representation of the RR type for code \c type_code. + std::string codeToTypeText(uint16_t type_code) const; + + /// \brief Convert a textual representation of an RR class to the + /// corresponding 16-bit integer code. + /// + /// This method searches the \c RRParamRegistry for the mapping from + /// the given textual representation of RR class to the + /// corresponding integer code. If a mapping is found, it returns + /// true with the associated class code in \c class_code; otherwise, + /// if the given string is in the form of "CLASSnnnn", it returns + /// true with the corresponding number as the class code in + /// \c class_code; otherwise, it returns false and \c class_code is + /// untouched. + /// + /// It returns \c false and avoids throwing an exception in the case + /// of an error to avoid the exception overhead in some situations. + /// + /// \param class_string The textual representation of the RR class. + /// \param class_code Returns the RR class code in this argument. + /// \return true if conversion is successful, false otherwise. + bool textToClassCode(const std::string& class_string, + uint16_t& class_code) const; + + /// \brief Convert class code into its textual representation. + /// + /// This method searches the \c RRParamRegistry for the mapping from the + /// given RR class code to its textual representation. + /// If a mapping is found, it returns (a copy of) the associated string; + /// otherwise, this method creates a new string in the form of "CLASSnnnn" + /// where "nnnn" is the decimal representation of the class code, and + /// returns the new string. + /// + /// If resource allocation for the returned string fails, + /// a corresponding standard exception will be thrown. + /// This method never fails otherwise. + /// + /// \param class_code The integer code of the RR class. + /// \return A textual representation of the RR class for code \c class_code. + std::string codeToClassText(uint16_t class_code) const; + //@} + + /// + /// \name RDATA Factories + /// + /// This set of methods provide a unified interface to create an + /// \c rdata::Rdata object in a parameterized polymorphic way, + /// that is, these methods 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::Rdata. + /// + /// These methods first search the \c RRParamRegistry for a factory + /// method (a member of a concrete derived class of + /// \c AbstractRdataFactory) for the given RR type and class pair. + /// If the search fails, they then search for a factory method for + /// the given type ignoring the class, in case a RRClass independent + /// factory method is registered. + /// If it still fails, these methods assume the RDATA is of an "unknown" + /// type, and creates a new object by calling a constructor of the + /// \c rdata::generic::Generic class. + /// + //@{ + /// \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 rdata::RdataPtr object pointing to the created \c Rdata + /// object. + rdata::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. + /// + /// \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 rdata::RdataPtr object pointing to the created \c Rdata + /// object. + rdata::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. + /// + /// \c source must be an object of the concrete derived class of + /// \c rdata::Rdata for the given pair of RR type and class; + /// otherwise, an exception of class \c std::bad_cast will be thrown. + /// In case the \c RRParamRegistry doesn't have a factory method for + /// the given pair and it is assumed to be of an "unknown" type, + /// \c source must reference an object of class + /// \c rdata::generic::Generic; otherwise, an exception of class + /// \c std::bad_cast 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 source A reference to an \c rdata::Rdata object whose content + /// is to be copied to the created \c rdata::Rdata object. + /// \return An \c rdata::RdataPtr object pointing to the created + /// \c rdata::Rdata object. + rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + const rdata::Rdata& source); + + /// \brief Create RDATA using MasterLexer + /// + /// This method is expected to be used as the underlying implementation + /// of the same signature of \c rdata::createRdata(). One main + /// difference is that this method is only responsible for constructing + /// the Rdata; it doesn't update the lexer to reach the end of line or + /// file or doesn't care about whether there's an extra (garbage) token + /// after the textual RDATA representation. Another difference is that + /// this method can throw on error and never returns a NULL pointer. + /// + /// For other details and parameters, see the description of + /// \c rdata::createRdata(). + rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, + MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks); + //@} + +private: + RRParamRegistryImpl* impl_; +}; + +} +} +#endif // RRPARAMREGISTRY_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc new file mode 100644 index 0000000..b217002 --- /dev/null +++ b/src/lib/dns/rrset.cc @@ -0,0 +1,466 @@ +// 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/. + +#include <config.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include <boost/shared_ptr.hpp> +#include <boost/foreach.hpp> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rrset.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { +void +AbstractRRset::addRdata(const Rdata& rdata) { + addRdata(createRdata(getType(), getClass(), rdata)); +} + +string +AbstractRRset::toText() const { + string s; + RdataIteratorPtr it = getRdataIterator(); + + // In the case of an empty rrset, just print name, ttl, class, and + // type + if (it->isLast()) { + // But only for class ANY or NONE + if (getClass() != RRClass::ANY() && + getClass() != RRClass::NONE()) { + isc_throw(EmptyRRset, "toText() is attempted for an empty RRset"); + } + + s += getName().toText() + " " + getTTL().toText() + " " + + getClass().toText() + " " + getType().toText() + "\n"; + return (s); + } + + do { + s += getName().toText() + " " + getTTL().toText() + " " + + getClass().toText() + " " + getType().toText() + " " + + it->getCurrent().toText() + "\n"; + it->next(); + } while (!it->isLast()); + + if (getRRsig()) { + s += getRRsig()->toText(); + } + + return (s); +} + +namespace { // unnamed namespace + +// FIXME: This method's code should somehow be unified with +// BasicRRsetImpl::toWire() below to avoid duplication. +template <typename T> +inline unsigned int +rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) { + unsigned int n = 0; + RdataIteratorPtr it = rrset.getRdataIterator(); + + if (it->isLast()) { + // empty rrsets are only allowed for classes ANY and NONE + if (rrset.getClass() != RRClass::ANY() && + rrset.getClass() != RRClass::NONE()) { + isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset"); + } + + // For an empty RRset, write the name, type, class and TTL once, + // followed by empty rdata. + rrset.getName().toWire(output); + rrset.getType().toWire(output); + rrset.getClass().toWire(output); + rrset.getTTL().toWire(output); + output.writeUint16(0); + // Still counts as 1 'rr'; it does show up in the message + return (1); + } + + // sort the set of Rdata based on rrset-order and sortlist, and possible + // other options. Details to be considered. + do { + const size_t pos0 = output.getLength(); + assert(pos0 < 65536); + + rrset.getName().toWire(output); + rrset.getType().toWire(output); + rrset.getClass().toWire(output); + rrset.getTTL().toWire(output); + + const size_t pos = output.getLength(); + output.skip(sizeof(uint16_t)); // leave the space for RDLENGTH + it->getCurrent().toWire(output); + output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos); + + if (limit > 0 && output.getLength() > limit) { + // truncation is needed + output.trim(output.getLength() - pos0); + return (n); + } + + it->next(); + ++n; + } while (!it->isLast()); + + return (n); +} + +} // end of unnamed namespace + +unsigned int +AbstractRRset::toWire(OutputBuffer& buffer) const { + return (rrsetToWire<OutputBuffer>(*this, buffer, 0)); +} + +unsigned int +AbstractRRset::toWire(AbstractMessageRenderer& renderer) const { + const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>( + *this, renderer, renderer.getLengthLimit()); + if (getRdataCount() > rrs_written) { + renderer.setTruncated(); + } + return (rrs_written); +} + +bool +AbstractRRset::isSameKind(const AbstractRRset& other) const { + // Compare classes last as they're likely to be identical. Compare + // names late in the list too, as these are expensive. So we compare + // types first, names second and classes last. + return (getType() == other.getType() && + getName() == other.getName() && + getClass() == other.getClass()); +} + +ostream& +operator<<(ostream& os, const AbstractRRset& rrset) { + os << rrset.toText(); + return (os); +} + +/// \brief This encapsulates the actual implementation of the \c BasicRRset +/// class. It's hidden from applications. +class BasicRRsetImpl { +public: + BasicRRsetImpl(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl) : + name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {} + + unsigned int toWire(AbstractMessageRenderer& renderer, size_t limit) const; + + Name name_; + RRClass rrclass_; + RRType rrtype_; + RRTTL ttl_; + // XXX: "list" is not a good name: It in fact isn't a list; more conceptual + // name than a data structure name is generally better. But since this + // is only used in the internal implementation we'll live with it. + vector<ConstRdataPtr> rdatalist_; +}; + +// FIXME: This method's code should somehow be unified with +// rrsetToWire() above to avoid duplication. +unsigned int +BasicRRsetImpl::toWire(AbstractMessageRenderer& renderer, size_t limit) const { + if (rdatalist_.empty()) { + // empty rrsets are only allowed for classes ANY and NONE + if (rrclass_ != RRClass::ANY() && + rrclass_ != RRClass::NONE()) { + isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset"); + } + + // For an empty RRset, write the name, type, class and TTL once, + // followed by empty rdata. + name_.toWire(renderer); + rrtype_.toWire(renderer); + rrclass_.toWire(renderer); + ttl_.toWire(renderer); + renderer.writeUint16(0); + // Still counts as 1 'rr'; it does show up in the message + return (1); + } + + unsigned int n = 0; + + // sort the set of Rdata based on rrset-order and sortlist, and possible + // other options. Details to be considered. + BOOST_FOREACH(const ConstRdataPtr& rdata, rdatalist_) { + const size_t pos0 = renderer.getLength(); + assert(pos0 < 65536); + + name_.toWire(renderer); + rrtype_.toWire(renderer); + rrclass_.toWire(renderer); + ttl_.toWire(renderer); + + const size_t pos = renderer.getLength(); + renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH + rdata->toWire(renderer); + renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t), + pos); + + if (limit > 0 && renderer.getLength() > limit) { + // truncation is needed + renderer.trim(renderer.getLength() - pos0); + return (n); + } + ++n; + } + + return (n); +} + +BasicRRset::BasicRRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl) +{ + impl_ = new BasicRRsetImpl(name, rrclass, rrtype, ttl); +} + +BasicRRset::~BasicRRset() { + delete impl_; +} + +void +BasicRRset::addRdata(ConstRdataPtr rdata) { + impl_->rdatalist_.push_back(rdata); +} + +void +BasicRRset::addRdata(const Rdata& rdata) { + AbstractRRset::addRdata(rdata); +} + +void +BasicRRset::addRdata(const std::string& rdata_str) { + addRdata(createRdata(getType(), getClass(), rdata_str)); +} + +unsigned int +BasicRRset::getRdataCount() const { + return (impl_->rdatalist_.size()); +} + +const Name& +BasicRRset::getName() const { + return (impl_->name_); +} + +const RRClass& +BasicRRset::getClass() const { + return (impl_->rrclass_); +} + +const RRType& +BasicRRset::getType() const { + return (impl_->rrtype_); +} + +const RRTTL& +BasicRRset::getTTL() const { + return (impl_->ttl_); +} + +void +BasicRRset::setTTL(const RRTTL& ttl) { + impl_->ttl_ = ttl; +} + +string +BasicRRset::toText() const { + return (AbstractRRset::toText()); +} + +uint16_t +BasicRRset::getLength() const { + uint16_t length = 0; + RdataIteratorPtr it = getRdataIterator(); + + if (it->isLast()) { + // empty rrsets are only allowed for classes ANY and NONE + if (getClass() != RRClass::ANY() && + getClass() != RRClass::NONE()) { + isc_throw(EmptyRRset, "getLength() is attempted for an empty RRset"); + } + + // For an empty RRset, write the name, type, class and TTL once, + // followed by empty rdata. + length += getName().getLength(); + length += 2; // TYPE field + length += 2; // CLASS field + length += 4; // TTL field + length += 2; // RDLENGTH field (=0 in wire format) + + return (length); + } + + do { + // This is a size_t as some of the following additions may + // overflow due to a programming mistake somewhere. + size_t rrlen = 0; + + rrlen += getName().getLength(); + rrlen += 2; // TYPE field + rrlen += 2; // CLASS field + rrlen += 4; // TTL field + rrlen += 2; // RDLENGTH field + rrlen += it->getCurrent().getLength(); + + assert(length + rrlen < 65536); + length += rrlen; + + it->next(); + } while (!it->isLast()); + + return (length); +} + +unsigned int +BasicRRset::toWire(OutputBuffer& buffer) const { + return (AbstractRRset::toWire(buffer)); +} + +unsigned int +BasicRRset::toWire(AbstractMessageRenderer& renderer) const { + const unsigned int rrs_written = impl_->toWire(renderer, + renderer.getLengthLimit()); + if (impl_->rdatalist_.size() > rrs_written) { + renderer.setTruncated(); + } + return (rrs_written); +} + +RRset::RRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl) : + BasicRRset(name, rrclass, rrtype, ttl) +{ + rrsig_ = RRsetPtr(); +} + +RRset::~RRset() {} + +unsigned int +RRset::getRRsigDataCount() const { + if (rrsig_) { + return (rrsig_->getRdataCount()); + } else { + return (0); + } +} + +uint16_t +RRset::getLength() const { + uint16_t length = BasicRRset::getLength(); + + if (rrsig_) { + const uint16_t rrsigs_length = rrsig_->getLength(); + // the uint16_ts are promoted to ints during addition below, so + // it won't overflow a 16-bit register. + assert(length + rrsigs_length < 65536); + length += rrsigs_length; + } + + return (length); +} + +unsigned int +RRset::toWire(OutputBuffer& buffer) const { + unsigned int rrs_written = BasicRRset::toWire(buffer); + if (getRdataCount() > rrs_written) { + return (rrs_written); + } + + if (rrsig_) { + rrs_written += rrsig_->toWire(buffer); + } + + return (rrs_written); +} + +unsigned int +RRset::toWire(AbstractMessageRenderer& renderer) const { + unsigned int rrs_written = BasicRRset::toWire(renderer); + if (getRdataCount() > rrs_written) { + return (rrs_written); + } + + if (rrsig_) { + rrs_written += rrsig_->toWire(renderer); + + if (getRdataCount() + getRRsigDataCount() > rrs_written) { + renderer.setTruncated(); + } + } + + return (rrs_written); +} + +namespace { + +class BasicRdataIterator : public RdataIterator { +public: + /// @brief Constructor. + BasicRdataIterator(const std::vector<rdata::ConstRdataPtr>& datavector) : + datavector_(&datavector), it_(datavector_->begin()) {} + + /// @brief Destructor. + ~BasicRdataIterator() {} + + /// @brief Set iterator at first position. + virtual void first() { + it_ = datavector_->begin(); + } + + /// @brief Advance iterator. + virtual void next() { + ++it_; + } + + /// @brief Get value at current iterator position. + /// + /// @return The value at current iterator position. + virtual const rdata::Rdata& getCurrent() const { + return (**it_); + } + + /// @brief Check if iterator has reached the end. + /// + /// @return true if iterator has reached the end, false otherwise. + virtual bool isLast() const { + return (it_ == datavector_->end()); + } + +private: + /// @brief Vector containing data. + const std::vector<rdata::ConstRdataPtr>* datavector_; + + /// @brief Iterator used to retrieve data. + std::vector<rdata::ConstRdataPtr>::const_iterator it_; +}; + +} + +RdataIteratorPtr +BasicRRset::getRdataIterator() const { + return (RdataIteratorPtr(new BasicRdataIterator(impl_->rdatalist_))); +} + +} +} diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h new file mode 100644 index 0000000..d17846a --- /dev/null +++ b/src/lib/dns/rrset.h @@ -0,0 +1,958 @@ +// 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/. + +#ifndef RRSET_H +#define RRSET_H 1 + +#include <iostream> +#include <string> + +#include <boost/shared_ptr.hpp> + +#include <dns/exceptions.h> + +#include <dns/rdata.h> +#include <dns/rrtype.h> + +namespace isc { +namespace util { +class OututBuffer; +} + +namespace dns { + +/// +/// \brief A standard DNS module exception that is thrown if an RRset object +/// does not contain any RDATA where required. +/// +class EmptyRRset : public isc::dns::Exception { +public: + EmptyRRset(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +// forward declarations +class Name; +class RRType; +class RRClass; +class RRTTL; +class AbstractMessageRenderer; +class AbstractRRset; +class BasicRRset; +class RdataIterator; +class BasicRRsetImpl; +class RRset; + +/// \brief A pointer-like type pointing to an \c RRset object. +/// +/// This type is commonly used as an argument of various functions defined +/// in this library in order to handle RRsets in a polymorphic manner. +typedef boost::shared_ptr<AbstractRRset> RRsetPtr; + +/// \brief A pointer-like type pointing to an (immutable) \c RRset +/// object. +/// +/// This type is commonly used as an argument of various functions defined +/// in this library in order to handle RRsets in a polymorphic manner. +typedef boost::shared_ptr<const AbstractRRset> ConstRRsetPtr; + +/// \brief A pointer-like type point to an \c RdataIterator object. +typedef boost::shared_ptr<RdataIterator> RdataIteratorPtr; + +/// \brief The \c AbstractRRset class is an abstract base class that +/// models a DNS RRset. +/// +/// An object of (a specific derived class of) \c AbstractRRset +/// models an RRset as described in the DNS standard: +/// A set of DNS resource records (RRs) of the same type and class. +/// The standard requires the TTL of all RRs in an RRset be the same; +/// this class follows that requirement. + +/// Note about duplicate RDATA: RFC2181 states that it's meaningless that an +/// RRset contains two identical RRs and that name servers should suppress +/// such duplicates. +/// This class is not responsible for ensuring this requirement: For example, +/// \c addRdata() method doesn't check if there's already RDATA identical +/// to the one being added. +/// This is because such checks can be expensive, and it's often easy to +/// ensure the uniqueness requirement at the %data preparation phase +/// (e.g. when loading a zone). +/// When parsing an incoming DNS message, the uniqueness may not be guaranteed, +/// so the application needs to detect and ignore any duplicate RDATA +/// (the \c Message class of this library should provide this responsibility). +/// +/// Another point to note is that \c AbstractRRset and its derived classes +/// allow an object to have an empty set of RDATA. +/// Even though there's no corresponding notion in the protocol specification, +/// it would be more intuitive for a container-like %data structure +/// to allow an empty set. +/// +/// Since \c AbstractRRset is an abstract class, it is generally used +/// via a pointer (or pointer like object) or a reference. +/// In particular, \c RRsetPtr, a pointer like type for \c AbstractRRset, +/// is used for polymorphic RRset operations throughout this library. +/// +/// The \c AbstractRRset class is also intended to be a major customization +/// point. For example, a high performance server implementation may want +/// to define an optimized "pre-compiled" RRset and provide an optimized +/// implementation of the \c toWire() method. +/// +/// Note about design choice: In BIND9, a set of RDATA with a common tuple +/// of RR class, RR type, and TTL was represented in a structure named +/// \c rdataset. Unlike the RRset classes, an \c rdataset did not contain +/// the information of the owner name. +/// This might be advantageous if we want to handle "RRsets", that is, +/// a set of different types of RRset for the same owner name, because +/// a single "name" structure can be used for multiple RRsets, minimizing +/// %data copy and memory footprint. +/// On the other hand, it's inconvenient for API users since in many cases +/// a pair of name and an \c rdataset must be maintained. It's also counter +/// intuitive in implementing protocol operations as an RRset is often used +/// as an atomic entity in DNS protocols while an \c rdataset is a component +/// of an RRset. +/// +/// We have therefore defined the notion of RRset explicitly in our initial +/// API design. We believe memory footprint is not a big concern because +/// RRsets are generally expected to be used as temporary objects, e.g. +/// while parsing or constructing a DNS message, or searching a DNS %data +/// source; for longer term purposes such as in-memory %data source entries, +/// the corresponding %data would be represented in a different, memory +/// optimized format. As for the concern about %data copy, we believe +/// it can be mitigated by using copy-efficient implementation for the +/// \c Name class implementation, such as reference counted objects. +/// Later, We plan to perform benchmark tests later to see if this assumption +/// is valid and to revisit the design if necessary. +/// +/// Note about terminology: there has been a discussion at the IETF +/// namedroppers ML about RRset vs RRSet (case of "s") +/// [http://ops.ietf.org/lists/namedroppers/namedroppers.2009/msg02737.html]. +/// While RFC2181 uses the latter, many other RFCs use the former, +/// and most of the list members who showed their opinion seem to prefer +/// "RRset". We follow that preference in this implementation. +/// +/// The current design of \c AbstractRRset is still in flux. +/// There are many open questions in design details: +/// - support more set-like operations, e.g, merge two RRsets of the same +/// type? +/// - more convenient methods or non member utility functions, e.g. +/// "sort" and "search(find)" method? +/// - what about comparing two RRsets of the same type? If we need this, +/// should it compare rdata's as a set or as a list (i.e. compare +/// each rdata one by one or as a whole)? c.f. NLnet Labs' ldns +/// (http://www.nlnetlabs.nl/projects/ldns/doc/index.html) +/// has \c ldns_rr_list_compare(), which takes the latter approach +/// (seemingly assuming the caller sorts the lists beforehand). +/// - BIND9 libdns has some special DNSSEC-related methods +/// such as \c addnoqname() or \c addclosest(). Do we need these? +/// (Probably not. We wouldn't want to make the class design too +/// monolithic.) +/// - Do we need to allow the user to remove specific Rdata? +/// Probably not, according to the current usage of the BIND9 code. +class AbstractRRset { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are intentionally + /// defined as private to make it explicit that this is a pure base class. + //@{ +private: + AbstractRRset(const AbstractRRset& source); + AbstractRRset& operator=(const AbstractRRset& source); +protected: + /// \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). + AbstractRRset() {} +public: + /// The destructor. + virtual ~AbstractRRset() {} + //@} + + /// + /// \name Getter and Setter Methods + /// + /// These methods are generally expected to be exception free, but it's + /// not guaranteed at the interface level; + /// for example, some performance optimized derived class may manage + /// the information corresponding to the class "attributes" to get or set, + /// and may require dynamic memory allocation to execute the method. + /// Consult the derived class description to see if a specific derived + /// \c RRset class may throw an exception from these methods. + /// + /// Note that setter methods are not provided for \c RRClass and + /// \c RRType. This is intentional. Since the format and semantics of + /// \c Rdata are dependent on the RR type (and RR class for some RR types), + /// allowing dynamically modify these attributes can easily lead to a + /// bug where the RDATA and type and/or class become inconsistent. + /// We want to avoid that situation by restricting the access. + //@{ + /// \brief Returns the number of \c Rdata objects contained in the \c RRset. + /// + /// Note that an \c RRset with an empty set of \c Rdata can exist, so + /// this method may return 0. + /// + /// \return The number of \c Rdata objects contained. + virtual unsigned int getRdataCount() const = 0; + + /// \brief Get the wire format length of the \c AbstractRRset. + /// + /// This method returns the wire format length of the + /// \c AbstractRRset, which is calculated by summing the individual + /// lengths of the various fields that make up each RR. + /// + /// NOTE: When including name lengths, the allocation for + /// uncompressed name wire format representation is used. + /// + /// \return The length of the wire format representation of the + /// \c AbstractRRset. + /// \throw EmptyRRset if the \c AbstractRRset is empty. + virtual uint16_t getLength() const = 0; + + /// \brief Returns the owner name of the \c RRset. + /// + /// \return A reference to a \c Name class object corresponding to the + /// \c RRset owner name. + virtual const Name& getName() const = 0; + + /// \brief Returns the RR Class of the \c RRset. + /// + /// \return A reference to a \c RRClass class object corresponding to the + /// RR class of the \c RRset. + virtual const RRClass& getClass() const = 0; + + /// \brief Returns the RR Type of the \c RRset. + /// + /// \return A reference to a \c RRType class object corresponding to the + /// RR type of the \c RRset. + virtual const RRType& getType() const = 0; + + /// \brief Returns the TTL of the RRset. + /// + /// \return A reference to a \c RRTTL class object corresponding to the + /// TTL of the \c RRset. + virtual const RRTTL& getTTL() const = 0; + + /// \brief Updates the TTL of the \c RRset. + /// + /// \param ttl A reference to a \c RRTTL class object to be copied as the + /// new TTL. + virtual void setTTL(const RRTTL& ttl) = 0; + //@} + + /// + /// \name Converter Methods + /// + /// These methods have the default implementation that can be reused by + /// derived classes. + /// Since they are defined as pure virtual, derived classes + /// that want to reuse the default implementation must explicitly + /// invoke their base class version (see the description for + /// <code>addRdata(const rdata::Rdata&)</code>). + /// + /// Design Note: the default implementations are defined only using + /// other public methods of the \c AbstractRRset class, and could be + /// implemented as non member functions (as some C++ textbooks suggest). + /// However, since derived classes may want to provide customized versions + /// (especially of the \c toWire() method for performance reasons) + /// we chose to define them as virtual functions, and, as a result, + /// member functions. + //@{ + /// \brief Convert the RRset to a string. + /// + /// Unlike other similar methods of this library, this method terminates + /// the resulting string with a trailing newline character. + /// (following the BIND9 convention) + /// + /// If any RRSIGs are associated with the RRset, they are also + /// appended to the returned string. + /// + /// If the class is not ANY or NONE, the RRset must contain some RDATA; + /// otherwise, an exception of class \c EmptyRRset will be thrown. + /// If resource allocation fails, a corresponding standard exception + /// will be thrown. + /// The default implementation may throw other exceptions if the + /// \c toText() method of the RDATA objects throws. + /// If a derived class of \c AbstractRRset overrides the default + /// implementation, the derived version may throw its own exceptions. + /// + /// Open issue: We may want to support multiple output formats as + /// BIND9 does. For example, we might want to allow omitting the owner + /// name when possible in the context of zone dump. This is a future + /// TODO item. + /// + /// \return A string representation of the RRset. + virtual std::string toText() const = 0; + + /// \brief Render the RRset in the wire format with name compression and + /// truncation handling. + /// + /// This method compresses the owner name of the RRset and domain names + /// used in RDATA that should be compressed. + /// In addition, this method detects the case where rendering the entire + /// RRset would cause truncation, and handles the case appropriately + /// (this is a TODO item, and not implemented in this version). + /// + /// If any RRSIGs are associated with the RRset, they are also rendered. + /// + /// Note: perhaps we may want to add more arguments to convey optional + /// information such as an "rrset-order" policy or how to handle truncation + /// case. This is a TODO item. + /// + /// If resource allocation fails, a corresponding standard exception + /// will be thrown. + /// If the class is not ANY or NONE, the RRset must contain some RDATA; + /// otherwise, an exception of class \c EmptyRRset will be thrown. + /// The default implementation may throw other exceptions if the + /// \c toWire() method of the RDATA objects throws. + /// If a derived class of \c AbstractRRset overrides the default + /// implementation, the derived version may throw its own exceptions. + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// \return The number of RRs rendered. If the truncation is necessary + /// this value may be different from the number of RDATA objects contained + /// in the RRset. + virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0; + + /// \brief Render the RRset in the wire format without any compression. + /// + /// See the other toWire() description about possible exceptions. + /// + /// \param buffer An output buffer to store the wire data. + /// \return The number of RRs rendered. + virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const = 0; + //@} + + /// + /// \name RDATA Manipulation Methods + /// + //@{ + /// \brief Add an RDATA to the RRset (pointer version). + /// + /// This method adds the given RDATA (as a pointer-like type to a + /// derived class object of \c rdata::Rdata) to the \c RRset. + /// + /// \param rdata A pointer (like) type of \c rdata::RdataPtr to be added + /// to the \c RRset. + virtual void addRdata(rdata::ConstRdataPtr rdata) = 0; + + /// \brief Add an RDATA to the RRset (reference version). + /// + /// This method adds the given RDATA (as a reference to a + /// derived class object of \c rdata::Rdata) to the \c RRset. + /// + /// This method has the default implementation that can be reused by + /// derived classes. + /// Since this method is defined as pure virtual, derived classes + /// that want to reuse the default implementation must explicitly + /// invoke this base class version. + /// For example, if the class \c CustomizedRRset, a derived class of + /// \c AbstractRRset, wants to reuse the default implementation of + /// \c %addRdata() (reference version), it would be defined as follows: + /// \code void + /// CustomizedRRset::addRdata(const rdata::Rdata& rdata) + /// { + /// AbstractRRset::addRdata(rdata); + /// } + /// \endcode + /// + /// This method is more strictly typed than the pointer version: + /// If \c rdata does not refer to the appropriate derived + /// \c Rdata class + /// for the \c RRType for this \c RRset, it throws an exception of class + /// \c std::bad_cast. + /// If resource allocation fails, a corresponding standard exception + /// will be thrown. + /// The RRset must contain some RDATA; otherwise, an exception of class + /// \c EmptyRRset will be thrown. + /// The default implementation may throw other exceptions if the + /// \c toWire() method of the RDATA objects throws. + /// If a derived class of \c AbstractRRset overrides the default + /// implementation, the derived version may throw its own exceptions. + /// + /// The default implementation simply constructs an \c rdata::RdataPtr + /// object from a newly allocated RDATA object copying from parameter + /// \c rdata, and calls the other version of + /// \c addRdata(const rdata::RdataPtr). + /// So it is inherently less efficient than the other version. + /// Still, this version would offer a more intuitive interface and is + /// provided as such. + /// + /// NOTE: Because a new Rdata object is constructed, this method can + /// throw a std::bad_cast exception if this RRset's class is NONE, + /// or if some other error occurs. If you want to be able to add + /// RDATA to an RRset whose class is NONE, please use the other + /// variant of \c addRdata() which accepts a \c ConstRdataPtr + /// argument. + /// + /// \param rdata A reference to a \c rdata::RdataPtr (derived) class + /// object, a copy of which is to be added to the \c RRset. + virtual void addRdata(const rdata::Rdata& rdata) = 0; + + /// \brief Add an RDATA to the RRset (string version). + /// + /// This method constructs an Rdata object from the given + /// \c rdata_str in presentation format and adds it to the \c RRset. + /// + /// \param rdata_str RDATA string in presentation format. + /// \throw InvalidRdataText if the \c rdata_str is invalid for this + /// \c RRset. + virtual void addRdata(const std::string& rdata_str) = 0; + + /// \brief Return an iterator to go through all RDATA stored in the + /// \c RRset. + /// + /// The rdata cursor of the returned iterator will point to the first + /// RDATA, that is, it effectively calls \c RdataIterator::first() + /// internally. + /// + /// Using the design pattern terminology, \c getRdataIterator() + /// is an example of a <em>factory method</em>. + /// + /// Whether this method throws an exception depends on the actual + /// implementation of the derived \c AbstractRRset class, but in general + /// it will involve resource allocation and can throw a standard exception + /// if it fails. + /// + /// \return A pointer-like object pointing to the derived \c RdataIterator + /// object. + virtual RdataIteratorPtr getRdataIterator() const = 0; + //@} + + /// + /// \name Associated RRSIG methods + /// + /// These methods access an "associated" RRset, that containing the DNSSEC + /// signatures for this RRset. It can be argued that this is not a + /// fundamental part of the RRset abstraction, since RFC 2181 defined an + /// RRset as a group of records with the same label, class and type but + /// different data. However, BIND 10 had to deal with DNSSEC and in + /// practice, including the information at the AbstractRRset level makes + /// implementation easier. (If a class is ever needed that must be + /// ignorant of the idea of an associated RRSIG RRset - e.g. a specialised + /// RRSIG RRset class - these methods can just throw a "NotImplemented" + /// exception.) DNSSEC is unlikely to be ever needed in Kea, but it does + /// not make sense to redesign the abstract RRSet class now. + //@{ + /// \brief Return pointer to this RRset's RRSIG RRset + /// + /// \return Pointer to the associated RRSIG RRset or null if there is none. + virtual RRsetPtr getRRsig() const = 0; + + /// \brief Returns the number of \c RRSIG records associated with + /// the \c RRset. + /// + /// Note that an \c RRset with no RRSIG records may exist, so this + /// method may return 0. + /// + /// \return The number of \c RRSIG records associated. + virtual unsigned int getRRsigDataCount() const = 0; + + /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset + /// + /// Adds the (assumed) RRSIG rdata the RRSIG RRset associated with this + /// RRset. If one does not exist, it is created using the data given. + /// + /// \param rdata Pointer to RRSIG rdata to be added. + virtual void addRRsig(const rdata::ConstRdataPtr& rdata) = 0; + + /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset + /// + /// Adds the (assumed) RRSIG rdata the RRSIG RRset associated with this + /// RRset. If one does not exist, it is created using the data given. + /// + /// (This overload is for an older version of boost that doesn't support + /// conversion from shared_ptr<X> to shared_ptr<const X>.) + /// + /// \param rdata Pointer to RRSIG rdata to be added. + virtual void addRRsig(const rdata::RdataPtr& rdata) = 0; + + /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset + /// + /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG + /// RRset associated with this RRset. If one does not exist, it is created + /// using the data given. + /// + /// \param sigs RRSIG RRset containing signatures to be added to the + /// RRSIG RRset associated with this class. + virtual void addRRsig(const AbstractRRset& sigs) = 0; + + /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset + /// + /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG + /// RRset associated with this RRset. If one does not exist, it is created + /// using the data given. + /// + /// \param sigs Pointer to a RRSIG RRset containing signatures to be added + /// to the RRSIG RRset associated with this class. + virtual void addRRsig(const ConstRRsetPtr& sigs) = 0; + + /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset + /// + /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG + /// RRset associated with this RRset. If one does not exist, it is created + /// using the data given. + /// + /// (This overload is for an older version of boost that doesn't support + /// conversion from shared_ptr<X> to shared_ptr<const X>.) + /// + /// \param sigs Pointer to a RRSIG RRset containing signatures to be added + /// to the RRSIG RRset associated with this class. + virtual void addRRsig(const RRsetPtr& sigs) = 0; + + /// \brief Clear the RRSIGs for this RRset + virtual void removeRRsig() = 0; + + /// \brief Check whether two RRsets are of the same kind + /// + /// Checks if two RRsets have the same name, RR type, and RR class. + /// + /// \param other Pointer to another AbstractRRset to compare + /// against. + virtual bool isSameKind(const AbstractRRset& other) const; + //@} + +}; + +/// \brief The \c RdataIterator class is an abstract base class that +/// provides an interface for accessing RDATA objects stored in an RRset. +/// +/// While different derived classes of \c AbstractRRset may maintain the RDATA +/// objects in different ways, the \c RdataIterator class provides a +/// unified interface to iterate over the RDATA objects in a polymorphic +/// manner. +/// +/// Each derived class of \c AbstractRRset is expected to provide a concrete +/// derived class of \c RdataIterator, and each derived \c RdataIterator +/// class implements the unified interface in a way specific to the +/// implementation of the corresponding derived \c AbstractRRset class. +/// Using the design pattern terminology, this is a typical example of +/// the \e Iterator pattern. +/// +/// The RDATA objects stored in the \c RRset are considered to form +/// a unidirectional list from the \c RdataIterator point of view (while +/// the actual implementation in the derived \c RRset may not use a list). +/// We call this unidirectional list the <em>rdata list</em>. +/// +/// An \c RdataIterator object internally (and conceptually) holds a +/// <em>rdata cursor</em>, which points to a specific item of the rdata list. +/// +/// Note about design choice: as is clear from the interface, \c RdataIterator +/// is not compatible with the standard iterator classes. +/// Although it would be useful (for example, we could then use STL algorithms) +/// and is not necessarily impossible, it would make the iterator implementation +/// much more complicated. +/// For instance, any standard iterator must be assignable and +/// copy-constructible. +/// So we'd need to implement \c RdataIterator::operator=() in a polymorphic +/// way. This will require non-trivial implementation tricks. +/// We believe the simplified iterator interface as provided by the +/// \c RdataIterator class is sufficient in practice: +/// Most applications will simply go through the RDATA objects contained in +/// an RRset, examining (and possibly using) each object, as one path +/// operation. +class RdataIterator { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are intentionally + /// defined as private to make it explicit that this is a pure base class. + //@{ +protected: + /// \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). + RdataIterator() {} +public: + /// \brief Destructor + virtual ~RdataIterator() {} +private: + RdataIterator(const RdataIterator& source); + RdataIterator& operator=(const RdataIterator& source); + //@} + +public: + /// \brief Move the rdata cursor to the first RDATA in the rdata list + /// (if any). + /// + /// This method can safely be called multiple times, even after moving + /// the rdata cursor forward by the \c next() method. + /// + /// This method should never throw an exception. + virtual void first() = 0; + + /// \brief Move the rdata cursor to the next RDATA in the rdata list + /// (if any). + /// + /// This method should never throw an exception. + virtual void next() = 0; + + /// \brief Return the current \c Rdata corresponding to the rdata cursor. + /// + /// \return A reference to an \c rdata::Rdata object corresponding + /// to the rdata cursor. + virtual const rdata::Rdata& getCurrent() const = 0; + + /// \brief Return true iff the rdata cursor has reached the end of the + /// rdata list. + /// + /// Once this method returns \c true, the behavior of any subsequent + /// call to \c next() or \c getCurrent() is undefined. + /// Likewise, the result of \c isLast() call followed by such undefined + /// operations is also undefined. + /// + /// This method should never throw an exception. + /// + /// \return \c true if the rdata cursor has reached the end of the + /// rdata list; otherwise \c false. + virtual bool isLast() const = 0; +}; + +/// \brief The \c BasicRRset class is a concrete derived class of +/// \c AbstractRRset that defines a straightforward RRset implementation. +/// +/// This class is designed to be as portable as possible, and so it adopts +/// the Pimpl idiom to hide as many details as possible. +/// Performance is a secondary concern for this class. +/// +/// This class is intended to be used by applications that only need +/// moderate level of performance with full functionality provided by +/// the \c AbstractRRset interfaces. +/// Highly performance-sensitive applications, such as a large scale +/// authoritative or caching name servers will implement and use a customized +/// version of derived \c AbstractRRset class. +class BasicRRset : public AbstractRRset { + /// + /// \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 BasicRRset object; once created, it would normally be used + /// as a \c const object (via references). + //@{ +private: + BasicRRset(const BasicRRset& source); + BasicRRset& operator=(const BasicRRset& source); +public: + /// \brief Constructor from (mostly) fixed parameters of the RRset. + /// + /// This constructor is normally 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 RRset. + /// \param rrclass The RR class of the RRset. + /// \param rrtype The RR type of the RRset. + /// \param ttl The TTL of the RRset. + BasicRRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl); + /// \brief The destructor. + virtual ~BasicRRset(); + //@} + + /// + /// \name Getter and Setter Methods + /// + //@{ + /// \brief Returns the number of \c Rdata objects contained in the \c RRset. + /// + /// This method never throws an exception. + /// + /// \return The number of \c Rdata objects contained. + virtual unsigned int getRdataCount() const; + + /// \brief Get the wire format length of the \c BasicRRset. + /// + /// \return The length of the wire format representation of the + /// \c BasicRRset. + /// \throw EmptyRRset if the \c BasicRRset is empty. + virtual uint16_t getLength() const; + + /// \brief Returns the owner name of the \c RRset. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c Name class object corresponding to the + /// \c RRset owner name. + virtual const Name& getName() const; + + /// \brief Returns the RR Class of the \c RRset. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c RRClass class object corresponding to the + /// RR class of the \c RRset. + virtual const RRClass& getClass() const; + + /// \brief Returns the RR Type of the \c RRset. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c RRType class object corresponding to the + /// RR type of the \c RRset. + virtual const RRType& getType() const; + + /// \brief Returns the TTL of the \c RRset. + /// + /// This method never throws an exception. + /// + /// \return A reference to a \c RRTTL class object corresponding to the + /// TTL of the \c RRset. + virtual const RRTTL& getTTL() const; + + /// \brief Updates the TTL of the \c RRset. + /// + /// This method never throws an exception. + /// + /// \param ttl A reference to a \c RRTTL class object to be copied as the + /// new TTL. + virtual void setTTL(const RRTTL& ttl); + //@} + + /// + /// \name Converter Methods + /// + //@{ + /// \brief Convert the RRset to a string. + /// + /// This method simply uses the default implementation. + /// See \c AbstractRRset::toText(). + virtual std::string toText() const; + + /// \brief Render the RRset in the wire format with name compression and + /// truncation handling. + /// + /// This method simply uses the default implementation. + /// See \c AbstractRRset::toWire(MessageRenderer&)const. + virtual unsigned int toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the RRset in the wire format without any compression. + /// + /// This method simply uses the default implementation. + /// See \c AbstractRRset::toWire(OutputBuffer&)const. + virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name RDATA manipulation methods + /// + //@{ + /// \brief Add an RDATA to the RRset (pointer version). + /// + /// This method is normally expected to be exception free, but it may + /// involve resource allocation, and if it fails the corresponding + /// standard exception will be thrown. + /// + /// \param rdata A pointer (like) type of \c rdata::RdataPtr to be added + /// to the \c BasicRRset. + virtual void addRdata(rdata::ConstRdataPtr rdata); + + /// \brief Add an RDATA to the RRset (reference version). + /// + /// This method simply uses the default implementation. + /// See \c AbstractRRset::addRdata(const rdata::Rdata&). + virtual void addRdata(const rdata::Rdata& rdata); + + /// \brief Add an RDATA to the RRset (string version). + /// + /// \param rdata_str RDATA string in presentation format. + /// \throw InvalidRdataText if the \c rdata_str is invalid for this + /// \c RRset. + virtual void addRdata(const std::string& rdata_str); + + /// \brief Return an iterator to go through all RDATA stored in the + /// \c BasicRRset. + /// + /// This is a concrete derived implementation of + /// \c AbstractRRset::getRdataIterator(). + /// + /// This method dynamically allocates resources. If it fails it will + /// throw the corresponding standard exception. + /// The iterator methods for the \c BasicRRset class are exception free. + /// + /// \return A pointer-like object pointing to the derived \c RdataIterator + /// object for the \c BasicRRset class. + virtual RdataIteratorPtr getRdataIterator() const; + //@} + + /// + /// \name Associated RRSIG methods + /// + /// The associated RRSIG RRset is not supported in BasicRRset. For + /// ease of use, getRRsig() returns a null pointer (indicating no RRset). + /// The addRRsig()/removeRRsig() methods throw a "NotImplemented" + /// exception - if you are using a BasicRRset, you should not be trying + /// to modify signatures on it. + //@{ + /// \brief Return pointer to this RRset's RRSIG RRset + /// + /// \return Null pointer, as this class does not support RRSIG records. + virtual RRsetPtr getRRsig() const { + return (RRsetPtr()); + } + + /// \brief Returns the number of \c RRSIG records associated with + /// the \c RRset. + /// + /// \return Always returns 0. Associated RRSIG RRsets are not + /// supported in this class. + virtual unsigned int getRRsigDataCount() const { + return (0); + } + + virtual void addRRsig(const rdata::ConstRdataPtr&) { + isc_throw(NotImplemented, + "BasicRRset does not implement the addRRsig() method"); + } + + virtual void addRRsig(const rdata::RdataPtr&) { + isc_throw(NotImplemented, + "BasicRRset does not implement the addRRsig() method"); + } + + virtual void addRRsig(const AbstractRRset&) { + isc_throw(NotImplemented, + "BasicRRset does not implement the addRRsig() method"); + } + + virtual void addRRsig(const ConstRRsetPtr&) { + isc_throw(NotImplemented, + "BasicRRset does not implement the addRRsig() method"); + } + + virtual void addRRsig(const RRsetPtr&) { + isc_throw(NotImplemented, + "BasicRRset does not implement the addRRsig() method"); + } + + virtual void removeRRsig() { + isc_throw(NotImplemented, + "BasicRRset does not implement the removeRRsig() method"); + } + //@} +private: + BasicRRsetImpl* impl_; +}; + +/// \brief The \c RRset class is a concrete derived class of +/// \c BasicRRset which contains a pointer to an additional RRset +/// containing associated RRSIG records. This allows DNSSEC aware +/// applications to treat data associated with a particular +/// QNAME/QTYPE/QCLASS as a single object. +class RRset : public BasicRRset { +public: + RRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& ttl); + + virtual ~RRset(); + + /// \brief Get the wire format length of the \c RRset. + /// + /// \return The length of the wire format representation of the + /// \c RRset. + /// \throw EmptyRRset if the \c RRset is empty. + virtual uint16_t getLength() const; + + /// \brief Render the RRset in the wire format with name compression and + /// truncation handling. + /// + /// See \c AbstractRRset::toWire(MessageRenderer&)const. + virtual unsigned int toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the RRset in the wire format without any compression. + /// + /// See \c AbstractRRset::toWire(OutputBuffer&)const. + virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const; + + /// \brief Updates the owner name of the \c RRset, including RRSIGs if any + virtual void setTTL(const RRTTL& ttl) { + BasicRRset::setTTL(ttl); + if (rrsig_) { + rrsig_->setTTL(ttl); + } + } + + /// \brief Adds an RRSIG RR to this RRset's signatures + virtual void addRRsig(const rdata::ConstRdataPtr& rdata) { + if (!rrsig_) { + rrsig_ = RRsetPtr(new RRset(getName(), getClass(), + RRType::RRSIG(), getTTL())); + } + rrsig_->addRdata(rdata); + } + + // Workaround for older versions of boost: some don't support implicit + // conversion from shared_ptr<X> to shared_ptr<const X>. Note: we should + // revisit the interface of managing RRset signatures, at which point this + // problem may go away. + virtual void addRRsig(const rdata::RdataPtr& rdata) { + // Don't try to convert as a reference here. SunStudio will reject it. + addRRsig(static_cast<const rdata::ConstRdataPtr>(rdata)); + } + + /// \brief Adds an RRSIG RRset to this RRset + virtual void addRRsig(const AbstractRRset& sigs) { + RdataIteratorPtr it = sigs.getRdataIterator(); + + if (!rrsig_) { + rrsig_ = RRsetPtr(new RRset(getName(), getClass(), + RRType::RRSIG(), getTTL())); + } + + for (it->first(); !it->isLast(); it->next()) { + rrsig_->addRdata(it->getCurrent()); + } + } + + virtual void addRRsig(const ConstRRsetPtr& sigs) { addRRsig(*sigs); } + + // Another workaround for older boost (see above) + virtual void addRRsig(const RRsetPtr& sigs) { addRRsig(*sigs); } + + /// \brief Clear the RRSIGs for this RRset + virtual void removeRRsig() { rrsig_ = RRsetPtr(); } + + /// \brief Return a pointer to this RRset's RRSIG RRset + virtual RRsetPtr getRRsig() const { return (rrsig_); } + + /// \brief Returns the number of \c RRSIG records associated with + /// the \c RRset. + /// + /// Note that an \c RRset with no RRSIG records may exist, so this + /// method may return 0. + /// + /// \return The number of \c RRSIG records associated. + virtual unsigned int getRRsigDataCount() const; + +private: + RRsetPtr rrsig_; +}; + + +/// \brief Insert the \c RRset as a string into stream. +/// +/// This method convert the \c rrset 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 RRset objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrset A reference to a (derived class of) \c AbstractRRset 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 AbstractRRset& rrset); +} // end of namespace dns +} // end of namespace isc +#endif // RRSET_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrset_collection.cc b/src/lib/dns/rrset_collection.cc new file mode 100644 index 0000000..a98ed16 --- /dev/null +++ b/src/lib/dns/rrset_collection.cc @@ -0,0 +1,123 @@ +// 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 <config.h> + +#include <dns/rrset_collection.h> +#include <dns/master_loader_callbacks.h> +#include <dns/master_loader.h> +#include <dns/rrcollator.h> + +#include <exceptions/exceptions.h> + +#include <functional> + +using namespace isc; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { + +void +RRsetCollection::loaderCallback(const std::string&, size_t, const std::string&) +{ + // We just ignore callbacks for errors and warnings. +} + +void +RRsetCollection::addRRset(RRsetPtr rrset) { + const CollectionKey key(rrset->getClass(), rrset->getType(), + rrset->getName()); + CollectionMap::const_iterator it = rrsets_.find(key); + if (it != rrsets_.end()) { + isc_throw(InvalidParameter, + "RRset for " << rrset->getName() << "/" << rrset->getClass() + << " with type " << rrset->getType() << " already exists"); + } + + rrsets_.insert(std::pair<CollectionKey, RRsetPtr>(key, rrset)); +} + +template<typename T> +void +RRsetCollection::constructHelper(T source, const isc::dns::Name& origin, + const isc::dns::RRClass& rrclass) +{ + RRCollator collator(std::bind(&RRsetCollection::addRRset, this, ph::_1)); + MasterLoaderCallbacks callbacks + (std::bind(&RRsetCollection::loaderCallback, this, ph::_1, ph::_2, ph::_3), + std::bind(&RRsetCollection::loaderCallback, this, ph::_1, ph::_2, ph::_3)); + MasterLoader loader(source, origin, rrclass, callbacks, + collator.getCallback(), + MasterLoader::DEFAULT); + loader.load(); + collator.flush(); +} + +RRsetCollection::RRsetCollection(const char* filename, const Name& origin, + const RRClass& rrclass) +{ + constructHelper(filename, origin, rrclass); +} + +RRsetCollection::RRsetCollection(std::istream& input_stream, const Name& origin, + const RRClass& rrclass) +{ + constructHelper<std::istream&>(input_stream, origin, rrclass); +} + +RRsetPtr +RRsetCollection::find(const Name& name, const RRClass& rrclass, + const RRType& rrtype) { + const CollectionKey key(rrclass, rrtype, name); + CollectionMap::iterator it = rrsets_.find(key); + if (it != rrsets_.end()) { + return (it->second); + } + return (RRsetPtr()); +} + +ConstRRsetPtr +RRsetCollection::find(const Name& name, const RRClass& rrclass, + const RRType& rrtype) const +{ + const CollectionKey key(rrclass, rrtype, name); + CollectionMap::const_iterator it = rrsets_.find(key); + if (it != rrsets_.end()) { + return (it->second); + } + return (ConstRRsetPtr()); +} + +bool +RRsetCollection::removeRRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype) +{ + const CollectionKey key(rrclass, rrtype, name); + + CollectionMap::iterator it = rrsets_.find(key); + if (it == rrsets_.end()) { + return (false); + } + + rrsets_.erase(it); + return (true); +} + +RRsetCollectionBase::IterPtr +RRsetCollection::getBeginning() { + CollectionMap::iterator it = rrsets_.begin(); + return (RRsetCollectionBase::IterPtr(new DnsIter(it))); +} + +RRsetCollectionBase::IterPtr +RRsetCollection::getEnd() { + CollectionMap::iterator it = rrsets_.end(); + return (RRsetCollectionBase::IterPtr(new DnsIter(it))); +} + +} // end of namespace dns +} // end of namespace isc diff --git a/src/lib/dns/rrset_collection.h b/src/lib/dns/rrset_collection.h new file mode 100644 index 0000000..7fb1e9c --- /dev/null +++ b/src/lib/dns/rrset_collection.h @@ -0,0 +1,164 @@ +// 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 RRSET_COLLECTION_H +#define RRSET_COLLECTION_H 1 + +#include <dns/rrset_collection_base.h> +#include <dns/rrclass.h> + +#include <boost/tuple/tuple.hpp> +#include <boost/tuple/tuple_comparison.hpp> + +#include <map> + +namespace isc { +namespace dns { + +/// \brief libdns++ implementation of RRsetCollectionBase using an STL +/// container. +class RRsetCollection : public RRsetCollectionBase { +public: + /// \brief Constructor. + /// + /// This constructor creates an empty collection without any data in + /// it. RRsets can be added to the collection with the \c addRRset() + /// method. + RRsetCollection() {} + + /// \brief Constructor. + /// + /// The \c origin and \c rrclass arguments are required for the zone + /// loading, but \c RRsetCollection itself does not do any + /// validation, and the collection of RRsets does not have to form a + /// valid zone. + /// + /// \throws MasterLoaderError if there is an error during loading. + /// \param filename Name of a file containing a collection of RRs in + /// the master file format (which may or may not form a valid zone). + /// \param origin The zone origin. + /// \param rrclass The zone class. + RRsetCollection(const char* filename, const isc::dns::Name& origin, + const isc::dns::RRClass& rrclass); + + /// \brief Constructor. + /// + /// This constructor is similar to the previous one, but instead of + /// taking a filename to load a zone from, it takes an input + /// stream. + /// + /// \throws MasterLoaderError if there is an error during loading. + /// \param input_stream The input stream to load from. + /// \param origin The zone origin. + /// \param rrclass The zone class. + RRsetCollection(std::istream& input_stream, const isc::dns::Name& origin, + const isc::dns::RRClass& rrclass); + + /// \brief Destructor + virtual ~RRsetCollection() {} + + /// \brief Add an RRset to the collection. + /// + /// Does not do any validation whether \c rrset belongs to a + /// particular zone or not. A reference to \c rrset is taken in an + /// internally managed \c shared_ptr, so even if the caller's + /// \c RRsetPtr is destroyed, the RRset it wrapped is still alive + /// and managed by the \c RRsetCollection. It throws an + /// \c isc::InvalidParameter exception if an rrset with the same + /// class, type and name already exists. + /// + /// Callers must not modify the RRset after adding it to the + /// collection, as the rrset is indexed internally by the + /// collection. + void addRRset(isc::dns::RRsetPtr rrset); + + /// \brief Remove an RRset from the collection. + /// + /// RRset(s) matching the \c name, \c rrclass and \c rrtype are + /// removed from the collection. + /// + /// \return \c true if a matching RRset was deleted, \c false if no + /// such RRset exists. + bool removeRRset(const isc::dns::Name& name, + const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype); + + /// \brief Find a matching RRset in the collection. + /// + /// Returns the RRset in the collection that exactly matches the + /// given \c name, \c rrclass and \c rrtype. If no matching RRset + /// is found, \c NULL is returned. + /// + /// \param name The name of the RRset to search for. + /// \param rrclass The class of the RRset to search for. + /// \param rrtype The type of the RRset to search for. + /// \return The RRset if found, \c NULL otherwise. + virtual isc::dns::ConstRRsetPtr find(const isc::dns::Name& name, + const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype) const; + + /// \brief Find a matching RRset in the collection (non-const + /// variant). + /// + /// See above for a description of the method and arguments. + isc::dns::RRsetPtr find(const isc::dns::Name& name, + const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype); + +private: + template<typename T> + void constructHelper(T source, const isc::dns::Name& origin, + const isc::dns::RRClass& rrclass); + void loaderCallback(const std::string&, size_t, const std::string&); + + typedef boost::tuple<isc::dns::RRClass, isc::dns::RRType, isc::dns::Name> + CollectionKey; + typedef std::map<CollectionKey, isc::dns::RRsetPtr> CollectionMap; + + CollectionMap rrsets_; + +protected: + class DnsIter : public RRsetCollectionBase::Iter { + public: + DnsIter(CollectionMap::iterator& iter) : + iter_(iter) + {} + + virtual const isc::dns::AbstractRRset& getValue() { + isc::dns::RRsetPtr& rrset = iter_->second; + return (*rrset); + } + + virtual IterPtr getNext() { + CollectionMap::iterator it = iter_; + ++it; + return (RRsetCollectionBase::IterPtr(new DnsIter(it))); + } + + virtual bool equals(Iter& other) { + const DnsIter* other_real = dynamic_cast<DnsIter*>(&other); + if (other_real == NULL) { + return (false); + } + return (iter_ == other_real->iter_); + } + + private: + CollectionMap::iterator iter_; + }; + + virtual RRsetCollectionBase::IterPtr getBeginning(); + virtual RRsetCollectionBase::IterPtr getEnd(); +}; + +} // end of namespace dns +} // end of namespace isc + +#endif // RRSET_COLLECTION_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrset_collection_base.h b/src/lib/dns/rrset_collection_base.h new file mode 100644 index 0000000..ac77756 --- /dev/null +++ b/src/lib/dns/rrset_collection_base.h @@ -0,0 +1,213 @@ +// Copyright (C) 2012-2015,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 RRSET_COLLECTION_BASE_H +#define RRSET_COLLECTION_BASE_H 1 + +#include <dns/rrset.h> +#include <dns/name.h> + +#include <boost/shared_ptr.hpp> + +#include <iterator> + +namespace isc { +namespace dns { + +/// \brief Error during RRsetCollectionBase find() operation +/// +/// This exception is thrown when calling an implementation of +/// \c RRsetCollectionBase::find() results in an error which is not due +/// to unmatched data, but because of some other underlying error +/// condition. +class RRsetCollectionError : public Exception { +public: + RRsetCollectionError(const char* file, size_t line, const char* what) : + Exception(file, line, what) + {} +}; + +/// \brief Generic class to represent a set of RRsets. +/// +/// This is a generic container and the stored set of RRsets does not +/// necessarily form a valid zone (e.g. there doesn't necessarily have +/// to be an SOA at the "origin"). Instead, it will be used to represent +/// a single zone for the purpose of zone loading/checking. It provides +/// a simple find() method to find an RRset for the given name and type +/// (and maybe class) and a way to iterate over all RRsets. +/// +/// See \c RRsetCollection for a simple libdns++ implementation using an +/// STL container. libdatasrc will have another implementation. +class RRsetCollectionBase { +public: + /// \brief Find a matching RRset in the collection. + /// + /// Returns the RRset in the collection that exactly matches the + /// given \c name, \c rrclass and \c rrtype. If no matching RRset + /// is found, \c NULL is returned. + /// + /// This method's implementations currently are not specified to + /// handle \c RRTypes such as RRSIG and NSEC3. This interface may be + /// refined to clarify this point in the future, and perhaps, provide + /// additional API for these RRType. + /// + /// As for RRSIG, there are some fundamental open questions. For + /// example, it's not clear whether we want to return all RRSIGs of + /// the given name covering any RR types (in which case, we need to + /// figure out how), or we need to extend the interface so we can + /// specify the covered type. A specific derived implementation may + /// return something if type RRSIG is specified, but this is not + /// specified here at the base class level. So, for RRSIGs the + /// behavior should be assumed as undefined. + /// + /// As for NSEC3, it's not clear whether owner names (which included + /// hashed labels) are the best choice of search key, because in many + /// cases, what the application wants to find is an NSEC3 that has the + /// hash of some particular "normal" domain names. Also, if the underlying + /// implementation encapsulates a single zone, NSEC3 records conceptually + /// belong to a separate name space, which may cause implementation + /// difficulty. + /// + /// Behavior with meta types such as ANY and AXFR are also + /// undefined. A specific implementation may return something for + /// these. But, unlike the case of RRSIGs, these types of RRsets + /// are not expected to be added to any implementation of + /// collection in the first place (by the definition of "meta + /// types"), so querying for such types is basically an invalid + /// operation. The API doesn't require implementations to check + /// this condition and reject it, so the behavior is + /// undefined. This interface will not be refined in future + /// versions for these meta types. + /// + /// \throw RRsetCollectionError if find() results in some + /// implementation-specific error. + /// \param name The name of the RRset to search for. + /// \param rrtype The type of the RRset to search for. + /// \param rrclass The class of the RRset to search for. + /// \return The RRset if found, \c NULL otherwise. + virtual isc::dns::ConstRRsetPtr find + (const isc::dns::Name& name, const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype) + const = 0; + + /// \brief Destructor + virtual ~RRsetCollectionBase() {} + +protected: + class Iter; // forward declaration + + /// \brief Wraps Iter with a reference count. + typedef boost::shared_ptr<Iter> IterPtr; + + /// \brief A helper iterator interface for \c RRsetCollectionBase. + /// + /// This is a protected iterator class that is a helper interface + /// used by the public iterator. Derived classes of + /// \c RRsetCollectionBase are supposed to implement this class and + /// the \c getBeginning() and \c getEnd() methods, so that the + /// public iterator interface can be provided. This is a forward + /// iterator only. + class Iter { + public: + virtual ~Iter() {}; + + /// \brief Returns the \c AbstractRRset currently pointed to by + /// the iterator. + virtual const isc::dns::AbstractRRset& getValue() = 0; + + /// \brief Returns an \c IterPtr wrapping an Iter pointing to + /// the next \c AbstractRRset in sequence in the collection. + virtual IterPtr getNext() = 0; + + /// \brief Check if another iterator is equal to this one. + /// + /// Returns \c true if this iterator is equal to \c other, + /// \c false otherwise. Note that if \c other is not the same + /// type as \c this, or cannot be compared meaningfully, the + /// method must return \c false. + /// + /// \param other The other iterator to compare against. + /// \return \c true if equal, \c false otherwise. + virtual bool equals(Iter& other) = 0; + }; + + /// \brief Returns an \c IterPtr wrapping an Iter pointing to the + /// beginning of the collection. + /// + /// \throw isc::dns::RRsetCollectionError if using the iterator + /// results in some underlying datasrc error. + virtual IterPtr getBeginning() = 0; + + /// \brief Returns an \c IterPtr wrapping an Iter pointing past the + /// end of the collection. + /// + /// \throw isc::dns::RRsetCollectionError if using the iterator + /// results in some underlying datasrc error. + virtual IterPtr getEnd() = 0; + +public: + /// \brief A forward \c std::iterator for \c RRsetCollectionBase. + /// + /// It behaves like a \c std::iterator forward iterator, so please + /// see its documentation for usage. + class Iterator : std::iterator<std::forward_iterator_tag, + const isc::dns::AbstractRRset> + { + public: + explicit Iterator(IterPtr iter) : + iter_(iter) + {} + + reference operator*() { + return (iter_->getValue()); + } + + Iterator& operator++() { + iter_ = iter_->getNext(); + return (*this); + } + + Iterator operator++(int) { + Iterator tmp(iter_); + ++*this; + return (tmp); + } + + bool operator==(const Iterator& other) const { + return (iter_->equals(*other.iter_)); + } + + bool operator!=(const Iterator& other) const { + return (!iter_->equals(*other.iter_)); + } + + private: + IterPtr iter_; + }; + + /// \brief Returns an iterator pointing to the beginning of the + /// collection. + Iterator begin() { + return Iterator(getBeginning()); + } + + /// \brief Returns an iterator pointing past the end of the + /// collection. + Iterator end() { + return Iterator(getEnd()); + } +}; + +typedef boost::shared_ptr<RRsetCollectionBase> RRsetCollectionPtr; + +} // end of namespace dns +} // end of namespace isc + +#endif // RRSET_COLLECTION_BASE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc new file mode 100644 index 0000000..83877c7 --- /dev/null +++ b/src/lib/dns/rrttl.cc @@ -0,0 +1,214 @@ +// 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 <config.h> + +#include <stdint.h> + +#include <sstream> +#include <ostream> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrttl.h> + +#include <boost/lexical_cast.hpp> +#include <algorithm> +#include <cctype> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; + +namespace { + +// We wrap the C isalpha, because it seems to be overloaded with something. +// Then the find_if doesn't work. +bool +myIsalpha(char c) { + return (isalpha(c) != 0); +} + +// The conversion of units to their size +struct Unit { + char unit; + uint32_t multiply; + uint32_t max_allowed; +}; + +Unit units[] = { + { 'S', 1, 0xffffffff / 1 }, + { 'M', 60, 0xffffffff / 60 }, + { 'H', 60 * 60, 0xffffffff / (60 * 60) }, + { 'D', 24 * 60 * 60, 0xffffffff / (24 * 60 * 60) }, + { 'W', 7 * 24 * 60 * 60, 0xffffffff / (7 * 24 * 60 * 60) } +}; + +} + +namespace isc { +namespace dns { + +namespace { +bool +parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) { + if (ttlstr.empty()) { + if (error_txt != NULL) { + *error_txt = "Empty TTL string"; + } + return (false); + } + + // We use a larger data type to handle negative number cases. + uint64_t val = 0; + const string::const_iterator end = ttlstr.end(); + string::const_iterator pos = ttlstr.begin(); + + try { + // When we detect we have some units + bool units_mode = false; + + while (pos != end) { + // Find the first unit, if there's any. + const string::const_iterator unit = find_if(pos, end, myIsalpha); + // No unit + if (unit == end) { + if (units_mode) { + // We had some units before. The last one is missing unit. + if (error_txt != NULL) { + *error_txt = "Missing the last unit: " + ttlstr; + } + return (false); + } else { + // Case without any units at all. Just convert and store + // it. + val = boost::lexical_cast<uint64_t>(ttlstr); + break; + } + } + // There's a unit now. + units_mode = true; + // Find the unit and get the size. + uint32_t multiply = 1; // initialize to silence compiler warnings + uint32_t max_allowed = 0xffffffff; + bool found = false; + for (size_t i = 0; i < sizeof(units) / sizeof(*units); ++i) { + if (toupper(*unit) == units[i].unit) { + found = true; + multiply = units[i].multiply; + max_allowed = units[i].max_allowed; + break; + } + } + if (!found) { + if (error_txt != NULL) { + *error_txt = "Unknown unit used: " + + boost::lexical_cast<string>(*unit) + " in: " + ttlstr; + } + return (false); + } + // Now extract the number. + if (unit == pos) { + if (error_txt != NULL) { + *error_txt = "Missing number in TTL: " + ttlstr; + } + return (false); + } + const uint64_t value = + boost::lexical_cast<uint64_t>(string(pos, unit)); + if (value > max_allowed) { + if (error_txt != NULL) { + *error_txt = "Part of TTL out of range: " + ttlstr; + } + return (false); + } + + // seconds cannot be out of range at this point. + const uint64_t seconds = value * multiply; + assert(seconds <= 0xffffffff); + + // Add what we found + val += seconds; + // Check the partial value is still in range (the value can only + // grow, so if we get out of range now, it won't get better, so + // there's no need to continue). + if (val < seconds || val > 0xffffffff) { + if (error_txt != NULL) { + *error_txt = "Part of TTL out of range: " + ttlstr; + } + return (false); + } + // Move to after the unit. + pos = unit + 1; + } + } catch (const boost::bad_lexical_cast&) { + if (error_txt != NULL) { + *error_txt = "invalid TTL: " + ttlstr; + } + return (false); + } + + if (val <= 0xffffffff) { + ttlval = val; + } else { + // This could be due to negative numbers in input, etc. + if (error_txt != NULL) { + *error_txt = "TTL out of range: " + ttlstr; + } + return (false); + } + + return (true); +} +} + +RRTTL::RRTTL(const std::string& ttlstr) { + string error_txt; + if (!parseTTLString(ttlstr, ttlval_, &error_txt)) { + isc_throw(InvalidRRTTL, error_txt); + } +} + +RRTTL* +RRTTL::createFromText(const string& ttlstr) { + uint32_t ttlval; + if (parseTTLString(ttlstr, ttlval, NULL)) { + return (new RRTTL(ttlval)); + } + return (NULL); +} + +RRTTL::RRTTL(InputBuffer& buffer) { + if (buffer.getLength() - buffer.getPosition() < sizeof(uint32_t)) { + isc_throw(IncompleteRRTTL, "incomplete wire-format TTL value"); + } + ttlval_ = buffer.readUint32(); +} + +const string +RRTTL::toText() const { + ostringstream oss; + oss << ttlval_; + return (oss.str()); +} + +void +RRTTL::toWire(OutputBuffer& buffer) const { + buffer.writeUint32(ttlval_); +} + +void +RRTTL::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint32(ttlval_); +} + +ostream& +operator<<(ostream& os, const RRTTL& rrttl) { + os << rrttl.toText(); + return (os); +} +} +} diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h new file mode 100644 index 0000000..abda3e5 --- /dev/null +++ b/src/lib/dns/rrttl.h @@ -0,0 +1,306 @@ +// 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 RRTTL_H +#define RRTTL_H 1 + +#include <dns/exceptions.h> + +#include <boost/optional.hpp> + +#include <stdint.h> + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// forward declarations +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if an RRTTL object +/// is being constructed from an unrecognized string. +/// +class InvalidRRTTL : public DNSTextError { +public: + InvalidRRTTL(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if an RRTTL object +/// is being constructed from a incomplete (too short) wire-format data. +/// +class IncompleteRRTTL : public isc::dns::Exception { +public: + IncompleteRRTTL(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// The \c RRTTL class encapsulates TTLs used in DNS resource records. +/// +/// This is a straightforward class; an \c RRTTL object simply maintains a +/// 32-bit unsigned integer corresponding to the TTL value. The main purpose +/// of this class is to provide convenient interfaces to convert a textual +/// representation into the integer TTL value and vice versa, and to handle +/// wire-format representations. +class RRTTL { +public: + /// + /// \name Constructors, Factory and Destructor + /// + /// Note: We use the default copy constructor and the default copy + /// assignment operator intentionally. + //@{ + /// Constructor from an integer TTL value. + /// + /// This constructor never throws an exception. + /// + /// \param ttlval An 32-bit integer of the RRTTL. + explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {} + + /// Constructor from a string. + /// + /// It accepts either a decimal number, specifying number of seconds. Or, + /// it can be given a sequence of numbers and units, like "2H" (meaning + /// two hours), "1W3D" (one week and 3 days). The allowed units are W + /// (week), D (day), H (hour), M (minute) and S (second). They can be also + /// specified in lower-case. No further restrictions are checked (so they + /// can be specified in arbitrary order and even things like "1D1D" can + /// be used to specify two days). + /// + /// \param ttlstr A string representation of the \c RRTTL. + /// + /// \throw InvalidRRTTL in case the string is not recognized as valid + /// TTL representation. + explicit RRTTL(const std::string& ttlstr); + + /// Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the RRTTL to be constructed. The current read position of + /// the buffer points to the head of the type. + /// + /// If the given data does not large enough to contain a 16-bit integer, + /// an exception of class \c IncompleteRRTTL will be thrown. + /// + /// \param buffer A buffer storing the wire format data. + explicit RRTTL(isc::util::InputBuffer& buffer); + + /// A separate factory of RRTTL from text. + /// + /// This static method is similar to the constructor that takes a string + /// object, but works as a factory and reports parsing failure in the + /// form of the return value. Normally the constructor version should + /// suffice, but in some cases the caller may have to expect mixture of + /// valid and invalid input, and may want to minimize the overhead of + /// possible exception handling. This version is provided for such + /// purpose. + /// + /// If the given text represents a valid RRTTL, it returns a pointer + /// to a new RRTTL object. If the given text does not represent a + /// valid RRTTL, it returns \c NULL.. + /// + /// One main purpose of this function is to minimize the overhead + /// when the given text does not represent a valid RR TTL. For this + /// reason this function intentionally omits the capability of delivering + /// a detailed reason for the parse failure, such as in the \c want() + /// string when exception is thrown from the constructor (it will + /// internally require a creation of string object, which is relatively + /// expensive). If such detailed information is necessary, the constructor + /// version should be used to catch the resulting exception. + /// + /// This function never throws the \c InvalidRRTTL exception. + /// + /// \param ttlstr A string representation of the \c RRTTL. + /// \return A new RRTTL object for the given text or a \c NULL value. + static RRTTL* createFromText(const std::string& ttlstr); + /// + //@} + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the \c RRTTL to a string. + /// + /// This version of implementation simply converts the TTL value into the + /// numeric textual representation. We may introduce more human-readable + /// format depending on the context in future versions. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \return A string representation of the \c RRTTL. + const std::string toText() const; + /// \brief Render the \c RRTTL in the wire format. + /// + /// This method renders the TTL value in network byte order via \c renderer, + /// which encapsulates output buffer and other rendering contexts. + /// + /// 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 in which the RRTTL is to be stored. + void toWire(AbstractMessageRenderer& renderer) const; + /// \brief Render the \c RRTTL in the wire format. + /// + /// This method renders the TTL value in network byte order into the + /// \c buffer. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the TTL value as a 32-bit unsigned integer. + /// + /// This method never throws an exception. + /// + /// \return An 32-bit integer corresponding to the RRTTL. + uint32_t getValue() const { return (ttlval_); } + //@} + + /// + /// \name Comparison methods + /// + /// Comparison between two \c RRTTL objects is performed in a + /// straightforward way, that is, comparing the corresponding TTL values + /// (which is the result of the \c getValue() method) as 32-bit unsigned + /// integers. + //@{ + /// \brief Return true iff two RRTTLs are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + bool equals(const RRTTL& other) const + { return (ttlval_ == other.ttlval_); } + /// \brief Same as \c equals(). + bool operator==(const RRTTL& other) const + { return (ttlval_ == other.ttlval_); } + /// \brief Return true iff two RRTTLs are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + bool nequals(const RRTTL& other) const + { return (ttlval_ != other.ttlval_); } + /// \brief Same as \c nequals(). + bool operator!=(const RRTTL& other) const + { return (ttlval_ != other.ttlval_); } + /// \brief Less-than or equal comparison for RRTTL against \c other. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + /// \return true if \c this RRTTL is less than or equal to the \c other; + /// otherwise false. + bool leq(const RRTTL& other) const + { return (ttlval_ <= other.ttlval_); } + + /// Same as \c leq() + bool operator<=(const RRTTL& other) const + { return (ttlval_ <= other.ttlval_); } + + /// \brief Greater-than or equal comparison for RRTTL against \c other. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + /// \return true if \c this RRTTL is greater than or equal to the \c other; + /// otherwise false. + bool geq(const RRTTL& other) const + { return (ttlval_ >= other.ttlval_); } + + /// Same as \c geq() + bool operator>=(const RRTTL& other) const + { return (ttlval_ >= other.ttlval_); } + + /// \brief Less-than comparison for RRTTL against \c other. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + /// \return true if \c this RRTTL is less than the \c other; + /// otherwise false. + bool lthan(const RRTTL& other) const + { return (ttlval_ < other.ttlval_); } + + /// Same as \c lthan() + bool operator<(const RRTTL& other) const + { return (ttlval_ < other.ttlval_); } + + /// \brief Greater-than comparison for RRTTL against \c other. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRTTL object to compare against. + /// \return true if \c this RRTTL is greater than the \c other; + /// otherwise false. + bool gthan(const RRTTL& other) const + { return (ttlval_ > other.ttlval_); } + + /// Same as \c gthan() + bool operator>(const RRTTL& other) const + { return (ttlval_ > other.ttlval_); } + //@} + + /// + /// \name Protocol constants + /// + //@{ + /// \brief The TTL of the max allowable value, per RFC2181 Section 8. + /// + /// The max value is the largest unsigned 31 bit integer, 2^31-1. + /// + /// \note At the moment an RRTTL object can have a value larger than + /// this limit. We may revisit it in a future version. + static const RRTTL& MAX_TTL() { + static const RRTTL max_ttl(0x7fffffff); + return (max_ttl); + } + //@} + +private: + uint32_t ttlval_; +}; + +/// +/// \brief Insert the \c RRTTL as a string into stream. +/// +/// This method convert the \c rrttl 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 RRTTL objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrttl The \c RRTTL 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 RRTTL& rrttl); +} +} +#endif // RRTTL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h new file mode 100644 index 0000000..e86e2f6 --- /dev/null +++ b/src/lib/dns/rrtype-placeholder.h @@ -0,0 +1,286 @@ +// 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 RRTYPE_H +#define RRTYPE_H 1 + +#include <stdint.h> + +#include <string> +#include <ostream> + +#include <dns/exceptions.h> + +// Solaris x86 defines DS in <sys/regset.h>, which gets pulled in by Boost +#if defined(__sun) && defined(DS) +# undef DS +#endif + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// forward declarations +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if an RRType object +/// is being constructed from an unrecognized string. +/// +class InvalidRRType : public DNSTextError { +public: + InvalidRRType(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if an RRType object +/// is being constructed from a incomplete (too short) wire-format data. +/// +class IncompleteRRType : public isc::dns::Exception { +public: + IncompleteRRType(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// The \c RRType class encapsulates DNS resource record types. +/// +/// This class manages the 16-bit integer type codes in quite a straightforward +/// way. The only non trivial task is to handle textual representations of +/// RR types, such as "A", "AAAA", or "TYPE65534". +/// +/// This class consults a helper \c RRParamRegistry class, which is a registry +/// of RR related parameters and has the singleton object. This registry +/// provides a mapping between RR type codes and their "well-known" textual +/// representations. +/// Parameters of RR types defined by DNS protocol standards are automatically +/// registered at initialization time and are ensured to be always available for +/// applications unless the application explicitly modifies the registry. +/// +/// For convenience, this class defines constant class objects corresponding to +/// standard RR types. These are generally referred to as the form of +/// <code>RRType::{type-text}()</code>. +/// For example, \c RRType::NS() is an \c RRType object corresponding to the NS +/// resource record (type code 2). +/// Note that these constants are used through a "proxy" function. +/// This is because they may be used to initialize another non-local (e.g. +/// global or namespace-scope) static object as follows: +/// +/// \code +/// namespace foo { +/// const RRType default_type = RRType::A(); +/// } \endcode +/// +/// In order to ensure that the constant RRType object has been initialized +/// before the initialization for \c default_type, we need help from +/// the proxy function. +/// +/// In the current implementation, the initialization of the well-known +/// static objects is not thread safe. The same consideration as the +/// \c RRParamRegistry class applies. We may extend the implementation so +/// that the initialization is ensured to be thread safe in a future version. +/// +/// Note to developers: since it's expected that some of these constant +/// \c RRType objects are frequently used in a performance sensitive path, +/// we define these proxy functions as inline. This makes sense only when +/// the corresponding static objects are defined only once even if they used +/// in different source files. Sufficiently modern compilers should meet +/// this assumption, but if we encounter memory bloat due to this problem with +/// particular compilers we need to revisit the design or think about +/// workaround. +class RRType { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// Constructor from an integer type code. + /// + /// This constructor never throws an exception. + /// + /// \param typecode An 16-bit integer code corresponding to the RRType. + explicit RRType(uint16_t typecode) : typecode_(typecode) {} + /// Constructor from a string. + /// + /// A valid string is one of "well-known" textual type representations + /// such as "A", "AAAA", or "NS", or in the standard format for "unknown" + /// RR types as defined in RFC3597, i.e., "TYPEnnnn". + /// + /// More precisely, the "well-known" representations are the ones stored + /// in the \c RRParamRegistry registry (see the class description). + /// + /// As for the format of "TYPEnnnn", "nnnn" must represent a valid 16-bit + /// unsigned integer, which may contain leading 0's as long as it consists + /// of at most 5 characters (inclusive). + /// For example, "TYPE1" and "TYPE001" are valid and represent the same + /// RR type, but "TYPE65536" and "TYPE000001" are invalid. + /// A "TYPEnnnn" representation is valid even if the corresponding type code + /// is registered in the \c RRParamRegistry object. For example, both + /// "A" and "TYPE1" are valid and represent the same RR type. + /// + /// All of these representations are case insensitive; "NS" and "ns", and + /// "TYPE1" and "type1" are all valid and represent the same RR types, + /// respectively. + /// + /// If the given string is not recognized as a valid representation of + /// an RR type, an exception of class \c InvalidRRType will be thrown. + /// + /// \param typestr A string representation of the \c RRType + explicit RRType(const std::string& typestr); + /// Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the RRType to be constructed. The current read position of + /// the buffer points to the head of the type. + /// + /// If the given data does not large enough to contain a 16-bit integer, + /// an exception of class \c IncompleteRRType will be thrown. + /// + /// \param buffer A buffer storing the wire format data. + explicit RRType(isc::util::InputBuffer& buffer); + /// + /// We use the default copy constructor intentionally. + //@} + /// We use the default copy assignment operator intentionally. + /// + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the \c RRType to a string. + /// + /// If a "well known" textual representation for the type code is registered + /// in the RR parameter registry (see the class description), that will be + /// used as the return value of this method. Otherwise, this method creates + /// a new string for an "unknown" RR type in the format defined in RFC3597, + /// i.e., "TYPEnnnn", and returns it. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c RRType. + const std::string toText() const; + /// \brief Render the \c RRType in the wire format. + /// + /// This method renders the type code in network byte order via \c renderer, + /// which encapsulates output buffer and other rendering contexts. + /// + /// 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 in which the RRType is to be stored. + void toWire(AbstractMessageRenderer& renderer) const; + /// \brief Render the \c RRType in the wire format. + /// + /// This method renders the type code in network byte order into the + /// \c buffer. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the RR type code as a 16-bit unsigned integer. + /// + /// This method never throws an exception. + /// + /// \return An 16-bit integer code corresponding to the RRType. + uint16_t getCode() const { return (typecode_); } + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Return true iff two RRTypes are equal. + /// + /// Two RRTypes are equal iff their type codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if the two RRTypes are equal; otherwise false. + bool equals(const RRType& other) const + { return (typecode_ == other.typecode_); } + /// \brief Same as \c equals(). + bool operator==(const RRType& other) const { return (equals(other)); } + + /// \brief Return true iff two RRTypes are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if the two RRTypes are not equal; otherwise false. + bool nequals(const RRType& other) const + { return (typecode_ != other.typecode_); } + /// \brief Same as \c nequals(). + bool operator!=(const RRType& other) const { return (nequals(other)); } + + /// \brief Less-than comparison for RRType against \c other + /// + /// We define the less-than relationship based on their type codes; + /// one RRType is less than the other iff the code of the former is less + /// than that of the other as unsigned integers. + /// The relationship is meaningless in terms of DNS protocol; the only + /// reason we define this method is that RRType objects can be stored in + /// STL containers without requiring user-defined less-than relationship. + /// We therefore don't define other comparison operators. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if \c this RRType is less than the \c other; otherwise + /// false. + bool operator<(const RRType& other) const + { return (typecode_ < other.typecode_); } + //@} + + // BEGIN_WELL_KNOWN_TYPE_DECLARATIONS + // END_WELL_KNOWN_TYPE_DECLARATIONS + +private: + uint16_t typecode_; +}; + +// BEGIN_WELL_KNOWN_TYPE_DEFINITIONS +// END_WELL_KNOWN_TYPE_DEFINITIONS + +/// +/// \brief Insert the \c RRType as a string into stream. +/// +/// This method convert the \c rrtype 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 RRType objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrtype The \c RRType 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 RRType& rrtype); +} +} +#endif // RRTYPE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc new file mode 100644 index 0000000..242af51 --- /dev/null +++ b/src/lib/dns/rrtype.cc @@ -0,0 +1,65 @@ +// 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 <config.h> + +#include <stdint.h> + +#include <string> +#include <ostream> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrparamregistry.h> +#include <dns/rrtype.h> + +using namespace std; +using namespace isc::util; +using isc::dns::RRType; + +namespace isc { +namespace dns { + +RRType::RRType(const std::string& type_str) { + uint16_t typecode; + if (!RRParamRegistry::getRegistry().textToTypeCode(type_str, typecode)) { + isc_throw(InvalidRRType, + "Unrecognized RR type string: " + type_str); + } + typecode_ = typecode; +} + +RRType::RRType(InputBuffer& buffer) { + if (buffer.getLength() - buffer.getPosition() < sizeof(uint16_t)) { + isc_throw(IncompleteRRType, "incomplete wire-format RR type"); + } + typecode_ = buffer.readUint16(); +} + +const string +RRType::toText() const { + return (RRParamRegistry::getRegistry().codeToTypeText(typecode_)); +} + +void +RRType::toWire(OutputBuffer& buffer) const { + buffer.writeUint16(typecode_); +} + +void +RRType::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint16(typecode_); +} + +ostream& +operator<<(ostream& os, const RRType& rrtype) { + os << rrtype.toText(); + return (os); +} +} +} diff --git a/src/lib/dns/rrtype.h b/src/lib/dns/rrtype.h new file mode 100644 index 0000000..e752253 --- /dev/null +++ b/src/lib/dns/rrtype.h @@ -0,0 +1,748 @@ +/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +// 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/. + +#ifndef RRTYPE_H +#define RRTYPE_H 1 + +#include <stdint.h> + +#include <string> +#include <ostream> + +#include <dns/exceptions.h> + +// Solaris x86 defines DS in <sys/regset.h>, which gets pulled in by Boost +#if defined(__sun) && defined(DS) +# undef DS +#endif + +namespace isc { +namespace util { +class InputBuffer; +class OutputBuffer; +} + +namespace dns { + +// forward declarations +class AbstractMessageRenderer; + +/// +/// \brief A standard DNS module exception that is thrown if an RRType object +/// is being constructed from an unrecognized string. +/// +class InvalidRRType : public DNSTextError { +public: + InvalidRRType(const char* file, size_t line, const char* what) : + DNSTextError(file, line, what) {} +}; + +/// +/// \brief A standard DNS module exception that is thrown if an RRType object +/// is being constructed from a incomplete (too short) wire-format data. +/// +class IncompleteRRType : public isc::dns::Exception { +public: + IncompleteRRType(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +/// +/// The \c RRType class encapsulates DNS resource record types. +/// +/// This class manages the 16-bit integer type codes in quite a straightforward +/// way. The only non trivial task is to handle textual representations of +/// RR types, such as "A", "AAAA", or "TYPE65534". +/// +/// This class consults a helper \c RRParamRegistry class, which is a registry +/// of RR related parameters and has the singleton object. This registry +/// provides a mapping between RR type codes and their "well-known" textual +/// representations. +/// Parameters of RR types defined by DNS protocol standards are automatically +/// registered at initialization time and are ensured to be always available for +/// applications unless the application explicitly modifies the registry. +/// +/// For convenience, this class defines constant class objects corresponding to +/// standard RR types. These are generally referred to as the form of +/// <code>RRType::{type-text}()</code>. +/// For example, \c RRType::NS() is an \c RRType object corresponding to the NS +/// resource record (type code 2). +/// Note that these constants are used through a "proxy" function. +/// This is because they may be used to initialize another non-local (e.g. +/// global or namespace-scope) static object as follows: +/// +/// \code +/// namespace foo { +/// const RRType default_type = RRType::A(); +/// } \endcode +/// +/// In order to ensure that the constant RRType object has been initialized +/// before the initialization for \c default_type, we need help from +/// the proxy function. +/// +/// In the current implementation, the initialization of the well-known +/// static objects is not thread safe. The same consideration as the +/// \c RRParamRegistry class applies. We may extend the implementation so +/// that the initialization is ensured to be thread safe in a future version. +/// +/// Note to developers: since it's expected that some of these constant +/// \c RRType objects are frequently used in a performance sensitive path, +/// we define these proxy functions as inline. This makes sense only when +/// the corresponding static objects are defined only once even if they used +/// in different source files. Sufficiently modern compilers should meet +/// this assumption, but if we encounter memory bloat due to this problem with +/// particular compilers we need to revisit the design or think about +/// workaround. +class RRType { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// Constructor from an integer type code. + /// + /// This constructor never throws an exception. + /// + /// \param typecode An 16-bit integer code corresponding to the RRType. + explicit RRType(uint16_t typecode) : typecode_(typecode) {} + /// Constructor from a string. + /// + /// A valid string is one of "well-known" textual type representations + /// such as "A", "AAAA", or "NS", or in the standard format for "unknown" + /// RR types as defined in RFC3597, i.e., "TYPEnnnn". + /// + /// More precisely, the "well-known" representations are the ones stored + /// in the \c RRParamRegistry registry (see the class description). + /// + /// As for the format of "TYPEnnnn", "nnnn" must represent a valid 16-bit + /// unsigned integer, which may contain leading 0's as long as it consists + /// of at most 5 characters (inclusive). + /// For example, "TYPE1" and "TYPE001" are valid and represent the same + /// RR type, but "TYPE65536" and "TYPE000001" are invalid. + /// A "TYPEnnnn" representation is valid even if the corresponding type code + /// is registered in the \c RRParamRegistry object. For example, both + /// "A" and "TYPE1" are valid and represent the same RR type. + /// + /// All of these representations are case insensitive; "NS" and "ns", and + /// "TYPE1" and "type1" are all valid and represent the same RR types, + /// respectively. + /// + /// If the given string is not recognized as a valid representation of + /// an RR type, an exception of class \c InvalidRRType will be thrown. + /// + /// \param typestr A string representation of the \c RRType + explicit RRType(const std::string& typestr); + /// Constructor from wire-format data. + /// + /// The \c buffer parameter normally stores a complete DNS message + /// containing the RRType to be constructed. The current read position of + /// the buffer points to the head of the type. + /// + /// If the given data does not large enough to contain a 16-bit integer, + /// an exception of class \c IncompleteRRType will be thrown. + /// + /// \param buffer A buffer storing the wire format data. + explicit RRType(isc::util::InputBuffer& buffer); + /// + /// We use the default copy constructor intentionally. + //@} + /// We use the default copy assignment operator intentionally. + /// + + /// + /// \name Converter methods + /// + //@{ + /// \brief Convert the \c RRType to a string. + /// + /// If a "well known" textual representation for the type code is registered + /// in the RR parameter registry (see the class description), that will be + /// used as the return value of this method. Otherwise, this method creates + /// a new string for an "unknown" RR type in the format defined in RFC3597, + /// i.e., "TYPEnnnn", and returns it. + /// + /// If resource allocation for the string fails, a corresponding standard + /// exception will be thrown. + /// + /// \return A string representation of the \c RRType. + const std::string toText() const; + /// \brief Render the \c RRType in the wire format. + /// + /// This method renders the type code in network byte order via \c renderer, + /// which encapsulates output buffer and other rendering contexts. + /// + /// 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 in which the RRType is to be stored. + void toWire(AbstractMessageRenderer& renderer) const; + /// \brief Render the \c RRType in the wire format. + /// + /// This method renders the type code in network byte order into the + /// \c buffer. + /// + /// If resource allocation in rendering process fails, a corresponding + /// standard exception will be thrown. + /// + /// \param buffer An output buffer to store the wire data. + void toWire(isc::util::OutputBuffer& buffer) const; + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Returns the RR type code as a 16-bit unsigned integer. + /// + /// This method never throws an exception. + /// + /// \return An 16-bit integer code corresponding to the RRType. + uint16_t getCode() const { return (typecode_); } + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Return true iff two RRTypes are equal. + /// + /// Two RRTypes are equal iff their type codes are equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if the two RRTypes are equal; otherwise false. + bool equals(const RRType& other) const + { return (typecode_ == other.typecode_); } + /// \brief Same as \c equals(). + bool operator==(const RRType& other) const { return (equals(other)); } + + /// \brief Return true iff two RRTypes are not equal. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if the two RRTypes are not equal; otherwise false. + bool nequals(const RRType& other) const + { return (typecode_ != other.typecode_); } + /// \brief Same as \c nequals(). + bool operator!=(const RRType& other) const { return (nequals(other)); } + + /// \brief Less-than comparison for RRType against \c other + /// + /// We define the less-than relationship based on their type codes; + /// one RRType is less than the other iff the code of the former is less + /// than that of the other as unsigned integers. + /// The relationship is meaningless in terms of DNS protocol; the only + /// reason we define this method is that RRType objects can be stored in + /// STL containers without requiring user-defined less-than relationship. + /// We therefore don't define other comparison operators. + /// + /// This method never throws an exception. + /// + /// \param other the \c RRType object to compare against. + /// \return true if \c this RRType is less than the \c other; otherwise + /// false. + bool operator<(const RRType& other) const + { return (typecode_ < other.typecode_); } + //@} + + // BEGIN_WELL_KNOWN_TYPE_DECLARATIONS + static const RRType& TSIG(); + static const RRType& A(); + static const RRType& AFSDB(); + static const RRType& CAA(); + static const RRType& CNAME(); + static const RRType& DLV(); + static const RRType& DNAME(); + static const RRType& DNSKEY(); + static const RRType& DS(); + static const RRType& HINFO(); + static const RRType& MINFO(); + static const RRType& MX(); + static const RRType& NAPTR(); + static const RRType& NS(); + static const RRType& NSEC3(); + static const RRType& NSEC3PARAM(); + static const RRType& NSEC(); + static const RRType& OPT(); + static const RRType& PTR(); + static const RRType& RP(); + static const RRType& RRSIG(); + static const RRType& SOA(); + static const RRType& SPF(); + static const RRType& SSHFP(); + static const RRType& TKEY(); + static const RRType& TLSA(); + static const RRType& TXT(); + static const RRType& AAAA(); + static const RRType& DHCID(); + static const RRType& SRV(); + static const RRType& IXFR(); + static const RRType& AXFR(); + static const RRType& ANY(); + static const RRType& MD(); + static const RRType& MF(); + static const RRType& MB(); + static const RRType& MG(); + static const RRType& MR(); + static const RRType& NXT(); + static const RRType& A6(); + static const RRType& MAILA(); + static const RRType& Null(); + static const RRType& WKS(); + static const RRType& X25(); + static const RRType& RT(); + static const RRType& NSAP(); + static const RRType& NSAP_PTR(); + static const RRType& SIG(); + static const RRType& ISDN(); + static const RRType& KEY(); + static const RRType& PX(); + static const RRType& GPOS(); + static const RRType& LOC(); + static const RRType& KX(); + static const RRType& CERT(); + static const RRType& APL(); + static const RRType& IPSECKEY(); + static const RRType& HIP(); + static const RRType& UNSPEC(); + static const RRType& NID(); + static const RRType& L32(); + static const RRType& L64(); + static const RRType& LP(); + static const RRType& MAILB(); + static const RRType& URI(); + // END_WELL_KNOWN_TYPE_DECLARATIONS + +private: + uint16_t typecode_; +}; + +// BEGIN_WELL_KNOWN_TYPE_DEFINITIONS +inline const RRType& +RRType::TSIG() { + static RRType rrtype(250); + return (rrtype); +} + +inline const RRType& +RRType::A() { + static RRType rrtype(1); + return (rrtype); +} + +inline const RRType& +RRType::AFSDB() { + static RRType rrtype(18); + return (rrtype); +} + +inline const RRType& +RRType::CAA() { + static RRType rrtype(257); + return (rrtype); +} + +inline const RRType& +RRType::CNAME() { + static RRType rrtype(5); + return (rrtype); +} + +inline const RRType& +RRType::DLV() { + static RRType rrtype(32769); + return (rrtype); +} + +inline const RRType& +RRType::DNAME() { + static RRType rrtype(39); + return (rrtype); +} + +inline const RRType& +RRType::DNSKEY() { + static RRType rrtype(48); + return (rrtype); +} + +inline const RRType& +RRType::DS() { + static RRType rrtype(43); + return (rrtype); +} + +inline const RRType& +RRType::HINFO() { + static RRType rrtype(13); + return (rrtype); +} + +inline const RRType& +RRType::MINFO() { + static RRType rrtype(14); + return (rrtype); +} + +inline const RRType& +RRType::MX() { + static RRType rrtype(15); + return (rrtype); +} + +inline const RRType& +RRType::NAPTR() { + static RRType rrtype(35); + return (rrtype); +} + +inline const RRType& +RRType::NS() { + static RRType rrtype(2); + return (rrtype); +} + +inline const RRType& +RRType::NSEC3() { + static RRType rrtype(50); + return (rrtype); +} + +inline const RRType& +RRType::NSEC3PARAM() { + static RRType rrtype(51); + return (rrtype); +} + +inline const RRType& +RRType::NSEC() { + static RRType rrtype(47); + return (rrtype); +} + +inline const RRType& +RRType::OPT() { + static RRType rrtype(41); + return (rrtype); +} + +inline const RRType& +RRType::PTR() { + static RRType rrtype(12); + return (rrtype); +} + +inline const RRType& +RRType::RP() { + static RRType rrtype(17); + return (rrtype); +} + +inline const RRType& +RRType::RRSIG() { + static RRType rrtype(46); + return (rrtype); +} + +inline const RRType& +RRType::SOA() { + static RRType rrtype(6); + return (rrtype); +} + +inline const RRType& +RRType::SPF() { + static RRType rrtype(99); + return (rrtype); +} + +inline const RRType& +RRType::SSHFP() { + static RRType rrtype(44); + return (rrtype); +} + +inline const RRType& +RRType::TKEY() { + static RRType rrtype(249); + return (rrtype); +} + +inline const RRType& +RRType::TLSA() { + static RRType rrtype(52); + return (rrtype); +} + +inline const RRType& +RRType::TXT() { + static RRType rrtype(16); + return (rrtype); +} + +inline const RRType& +RRType::AAAA() { + static RRType rrtype(28); + return (rrtype); +} + +inline const RRType& +RRType::DHCID() { + static RRType rrtype(49); + return (rrtype); +} + +inline const RRType& +RRType::SRV() { + static RRType rrtype(33); + return (rrtype); +} + +inline const RRType& +RRType::IXFR() { + static RRType rrtype(251); + return (rrtype); +} + +inline const RRType& +RRType::AXFR() { + static RRType rrtype(252); + return (rrtype); +} + +inline const RRType& +RRType::ANY() { + static RRType rrtype(255); + return (rrtype); +} + +inline const RRType& +RRType::MD() { + static RRType rrtype(3); + return (rrtype); +} + +inline const RRType& +RRType::MF() { + static RRType rrtype(4); + return (rrtype); +} + +inline const RRType& +RRType::MB() { + static RRType rrtype(7); + return (rrtype); +} + +inline const RRType& +RRType::MG() { + static RRType rrtype(8); + return (rrtype); +} + +inline const RRType& +RRType::MR() { + static RRType rrtype(9); + return (rrtype); +} + +inline const RRType& +RRType::NXT() { + static RRType rrtype(30); + return (rrtype); +} + +inline const RRType& +RRType::A6() { + static RRType rrtype(38); + return (rrtype); +} + +inline const RRType& +RRType::MAILA() { + static RRType rrtype(254); + return (rrtype); +} + +inline const RRType& +RRType::Null() { + static RRType rrtype(10); + return (rrtype); +} + +inline const RRType& +RRType::WKS() { + static RRType rrtype(11); + return (rrtype); +} + +inline const RRType& +RRType::X25() { + static RRType rrtype(19); + return (rrtype); +} + +inline const RRType& +RRType::RT() { + static RRType rrtype(21); + return (rrtype); +} + +inline const RRType& +RRType::NSAP() { + static RRType rrtype(22); + return (rrtype); +} + +inline const RRType& +RRType::NSAP_PTR() { + static RRType rrtype(23); + return (rrtype); +} + +inline const RRType& +RRType::SIG() { + static RRType rrtype(24); + return (rrtype); +} + +inline const RRType& +RRType::ISDN() { + static RRType rrtype(20); + return (rrtype); +} + +inline const RRType& +RRType::KEY() { + static RRType rrtype(25); + return (rrtype); +} + +inline const RRType& +RRType::PX() { + static RRType rrtype(26); + return (rrtype); +} + +inline const RRType& +RRType::GPOS() { + static RRType rrtype(27); + return (rrtype); +} + +inline const RRType& +RRType::LOC() { + static RRType rrtype(29); + return (rrtype); +} + +inline const RRType& +RRType::KX() { + static RRType rrtype(36); + return (rrtype); +} + +inline const RRType& +RRType::CERT() { + static RRType rrtype(37); + return (rrtype); +} + +inline const RRType& +RRType::APL() { + static RRType rrtype(42); + return (rrtype); +} + +inline const RRType& +RRType::IPSECKEY() { + static RRType rrtype(45); + return (rrtype); +} + +inline const RRType& +RRType::HIP() { + static RRType rrtype(55); + return (rrtype); +} + +inline const RRType& +RRType::UNSPEC() { + static RRType rrtype(103); + return (rrtype); +} + +inline const RRType& +RRType::NID() { + static RRType rrtype(104); + return (rrtype); +} + +inline const RRType& +RRType::L32() { + static RRType rrtype(105); + return (rrtype); +} + +inline const RRType& +RRType::L64() { + static RRType rrtype(106); + return (rrtype); +} + +inline const RRType& +RRType::LP() { + static RRType rrtype(107); + return (rrtype); +} + +inline const RRType& +RRType::MAILB() { + static RRType rrtype(253); + return (rrtype); +} + +inline const RRType& +RRType::URI() { + static RRType rrtype(256); + return (rrtype); +} + +// END_WELL_KNOWN_TYPE_DEFINITIONS + +/// +/// \brief Insert the \c RRType as a string into stream. +/// +/// This method convert the \c rrtype 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 RRType objects. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param rrtype The \c RRType 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 RRType& rrtype); +} +} +#endif // RRTYPE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/serial.cc b/src/lib/dns/serial.cc new file mode 100644 index 0000000..842bff8 --- /dev/null +++ b/src/lib/dns/serial.cc @@ -0,0 +1,70 @@ +// Copyright (C) 2011-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 <config.h> + +#include <dns/serial.h> + +namespace isc { +namespace dns { + +bool +Serial::operator==(const Serial& other) const { + return (value_ == other.getValue()); +} + +bool +Serial::operator!=(const Serial& other) const { + return (value_ != other.getValue()); +} + +bool +Serial::operator<(const Serial& other) const { + uint32_t other_val = other.getValue(); + bool result = false; + if (value_ < other_val) { + result = ((other_val - value_) <= MAX_SERIAL_INCREMENT); + } else if (other_val < value_) { + result = ((value_ - other_val) > MAX_SERIAL_INCREMENT); + } + return (result); +} + +bool +Serial::operator<=(const Serial& other) const { + return (operator==(other) || operator<(other)); +} + +bool +Serial::operator>(const Serial& other) const { + return (!operator==(other) && !operator<(other)); +} + +bool +Serial::operator>=(const Serial& other) const { + return (!operator<(other)); +} + +Serial +Serial::operator+(uint32_t other_val) const { + uint64_t new_val = static_cast<uint64_t>(value_) + + static_cast<uint64_t>(other_val); + return Serial(static_cast<uint32_t>(new_val % MAX_SERIAL_VALUE)); +} + +Serial +Serial::operator+(const Serial& other) const { + return (operator+(other.getValue())); +} + +std::ostream& +operator<<(std::ostream& os, const Serial& serial) { + return (os << serial.getValue()); +} + +} // end namespace dns +} // end namespace isc + diff --git a/src/lib/dns/serial.h b/src/lib/dns/serial.h new file mode 100644 index 0000000..7c7e338 --- /dev/null +++ b/src/lib/dns/serial.h @@ -0,0 +1,150 @@ +// Copyright (C) 2011-2015,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 SERIAL_H +#define SERIAL_H 1 + +#include <stdint.h> +#include <iostream> + +namespace isc { +namespace dns { + +/// The maximum difference between two serial numbers. If the (plain uint32_t) +/// difference between two serials is greater than this number, the smaller one +/// is considered greater. +const uint32_t MAX_SERIAL_INCREMENT = 2147483647; + +/// Maximum value a serial can have, used in + operator. +const uint64_t MAX_SERIAL_VALUE = 4294967296ull; + +/// \brief This class defines DNS serial numbers and serial arithmetic. +/// +/// DNS Serial number are in essence unsigned 32-bits numbers, with one +/// catch; they should be compared using sequence space arithmetic. +/// So given that they are 32-bits; as soon as the difference between two +/// serial numbers is greater than 2147483647 (2^31 - 1), the lower number +/// (in plain comparison) is considered the higher one. +/// +/// In order to do this as transparently as possible, these numbers are +/// stored in the Serial class, which overrides the basic comparison operators. +/// +/// In this specific context, these operations are called 'serial number +/// arithmetic', and they are defined in RFC 1982. +/// +/// \note RFC 1982 defines everything based on the value SERIAL_BITS. Since +/// the serial number has a fixed length of 32 bits, the values we use are +/// hard-coded, and not computed based on variable bit lengths. +class Serial { +public: + /// \brief Constructor with value + /// + /// \param value The uint32_t value of the serial + explicit Serial(uint32_t value) : value_(value) {} + + /// \brief Copy constructor + Serial(const Serial& other) : value_(other.getValue()) {} + + /// \brief Direct assignment from other Serial + /// + /// \param other The Serial to assign the value from + Serial& operator=(const Serial& other) { + value_ = other.getValue(); + return (*this); + } + + /// \brief Direct assignment from value + /// + /// \param value the uint32_t value to assign + void operator=(uint32_t value) { value_ = value; } + + /// \brief Returns the uint32_t representation of this serial value + /// + /// \return The uint32_t value of this Serial + uint32_t getValue() const { return (value_); } + + /// \brief Returns true if the serial values are equal + /// + /// \return True if the values are equal + bool operator==(const Serial& other) const; + + /// \brief Returns true if the serial values are not equal + /// + /// \return True if the values are not equal + bool operator!=(const Serial& other) const; + + /// \brief Returns true if the serial value of this serial is smaller than + /// the other, according to serial arithmetic as described in RFC 1982 + /// + /// \param other The Serial to compare to + /// + /// \return True if this is smaller than the given value + bool operator<(const Serial& other) const; + + /// \brief Returns true if the serial value of this serial is equal to or + /// smaller than the other, according to serial arithmetic as described + /// in RFC 1982 + /// + /// \param other The Serial to compare to + /// + /// \return True if this is smaller than or equal to the given value + bool operator<=(const Serial& other) const; + + /// \brief Returns true if the serial value of this serial is greater than + /// the other, according to serial arithmetic as described in RFC 1982 + /// + /// \param other The Serial to compare to + /// + /// \return True if this is greater than the given value + bool operator>(const Serial& other) const; + + /// \brief Returns true if the serial value of this serial is equal to or + /// greater than the other, according to serial arithmetic as described in + /// RFC 1982 + /// + /// \param other The Serial to compare to + /// + /// \return True if this is greater than or equal to the given value + bool operator>=(const Serial& other) const; + + /// \brief Adds the given value to the serial number. If this would make + /// the number greater than 2^32-1, it is 'wrapped'. + /// \note According to the specification, an addition greater than + /// MAX_SERIAL_INCREMENT is undefined. We do NOT catch this error (so as not + /// to raise exceptions), but this behaviour remains undefined. + /// + /// \param other The Serial to add + /// + /// \return The result of the addition + Serial operator+(const Serial& other) const; + + /// \brief Adds the given value to the serial number. If this would make + /// the number greater than 2^32-1, it is 'wrapped'. + /// + /// \note According to the specification, an addition greater than + /// MAX_SERIAL_INCREMENT is undefined. We do NOT catch this error (so as not + /// to raise exceptions), but this behaviour remains undefined. + /// + /// \param other_val The uint32_t value to add + /// + /// \return The result of the addition + Serial operator+(uint32_t other_val) const; + +private: + uint32_t value_; +}; + +/// \brief Helper operator for output streams, writes the value to the stream +/// +/// \param os The ostream to write to +/// \param serial The Serial to write +/// \return the output stream +std::ostream& operator<<(std::ostream& os, const Serial& serial); + +} // end namespace dns +} // end namespace isc + +#endif // SERIAL_H diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am new file mode 100644 index 0000000..9b2e0fa --- /dev/null +++ b/src/lib/dns/tests/Makefile.am @@ -0,0 +1,95 @@ +SUBDIRS = testdata . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(abs_top_srcdir)/src/lib/dns/tests/testdata\" +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dns/tests/testdata\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += run_unittests +run_unittests_SOURCES = unittest_util.h unittest_util.cc +run_unittests_SOURCES += dns_exceptions_unittest.cc +run_unittests_SOURCES += edns_unittest.cc +run_unittests_SOURCES += master_lexer_inputsource_unittest.cc +run_unittests_SOURCES += labelsequence_unittest.cc +run_unittests_SOURCES += messagerenderer_unittest.cc +run_unittests_SOURCES += master_lexer_token_unittest.cc +run_unittests_SOURCES += master_lexer_unittest.cc +run_unittests_SOURCES += master_loader_unittest.cc +run_unittests_SOURCES += master_lexer_state_unittest.cc +run_unittests_SOURCES += name_unittest.cc +run_unittests_SOURCES += nsec3hash_unittest.cc +run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc +run_unittests_SOURCES += rrttl_unittest.cc +run_unittests_SOURCES += rrcollator_unittest.cc +run_unittests_SOURCES += opcode_unittest.cc +run_unittests_SOURCES += rcode_unittest.cc +run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc +run_unittests_SOURCES += rdatafields_unittest.cc +run_unittests_SOURCES += rdata_pimpl_holder_unittest.cc +run_unittests_SOURCES += rdata_char_string_unittest.cc +run_unittests_SOURCES += rdata_char_string_data_unittest.cc +run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc +run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc +run_unittests_SOURCES += rdata_txt_like_unittest.cc +run_unittests_SOURCES += rdata_mx_unittest.cc +run_unittests_SOURCES += rdata_sshfp_unittest.cc +run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc +run_unittests_SOURCES += rdata_dname_unittest.cc +run_unittests_SOURCES += rdata_afsdb_unittest.cc +run_unittests_SOURCES += rdata_opt_unittest.cc +run_unittests_SOURCES += rdata_dhcid_unittest.cc +run_unittests_SOURCES += rdata_dnskey_unittest.cc +run_unittests_SOURCES += rdata_ds_like_unittest.cc +run_unittests_SOURCES += rdata_nsec_unittest.cc +run_unittests_SOURCES += rdata_nsec3_unittest.cc +run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc +run_unittests_SOURCES += rdata_nsec3param_unittest.cc +run_unittests_SOURCES += rdata_nsec3param_like_unittest.cc +run_unittests_SOURCES += rdata_rrsig_unittest.cc +run_unittests_SOURCES += rdata_rp_unittest.cc +run_unittests_SOURCES += rdata_srv_unittest.cc +run_unittests_SOURCES += rdata_tlsa_unittest.cc +run_unittests_SOURCES += rdata_minfo_unittest.cc +run_unittests_SOURCES += rdata_tsig_unittest.cc +run_unittests_SOURCES += rdata_naptr_unittest.cc +run_unittests_SOURCES += rdata_hinfo_unittest.cc +run_unittests_SOURCES += rdata_caa_unittest.cc +run_unittests_SOURCES += rdata_tkey_unittest.cc +run_unittests_SOURCES += rrset_unittest.cc +run_unittests_SOURCES += qid_gen_unittest.cc +run_unittests_SOURCES += question_unittest.cc +run_unittests_SOURCES += rrparamregistry_unittest.cc +run_unittests_SOURCES += masterload_unittest.cc +run_unittests_SOURCES += message_unittest.cc +run_unittests_SOURCES += serial_unittest.cc +run_unittests_SOURCES += tsig_unittest.cc +run_unittests_SOURCES += tsigerror_unittest.cc +run_unittests_SOURCES += tsigkey_unittest.cc +run_unittests_SOURCES += tsigrecord_unittest.cc +run_unittests_SOURCES += master_loader_callbacks_test.cc +run_unittests_SOURCES += rrset_collection_unittest.cc +run_unittests_SOURCES += zone_checker_unittest.cc +run_unittests_SOURCES += run_unittests.cc +run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) +run_unittests_LDADD = $(top_builddir)/src/lib/dns/libkea-dns++.la +run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +run_unittests_LDADD += $(CRYPTO_LIBS) $(GTEST_LDADD) +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/dns/tests/Makefile.in b/src/lib/dns/tests/Makefile.in new file mode 100644 index 0000000..14bae9c --- /dev/null +++ b/src/lib/dns/tests/Makefile.in @@ -0,0 +1,2335 @@ +# 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@ +TESTS = $(am__EXEEXT_1) +@HAVE_GTEST_TRUE@am__append_1 = run_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/lib/dns/tests +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 $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__run_unittests_SOURCES_DIST = unittest_util.h unittest_util.cc \ + dns_exceptions_unittest.cc edns_unittest.cc \ + master_lexer_inputsource_unittest.cc labelsequence_unittest.cc \ + messagerenderer_unittest.cc master_lexer_token_unittest.cc \ + master_lexer_unittest.cc master_loader_unittest.cc \ + master_lexer_state_unittest.cc name_unittest.cc \ + nsec3hash_unittest.cc rrclass_unittest.cc rrtype_unittest.cc \ + rrttl_unittest.cc rrcollator_unittest.cc opcode_unittest.cc \ + rcode_unittest.cc rdata_unittest.h rdata_unittest.cc \ + rdatafields_unittest.cc rdata_pimpl_holder_unittest.cc \ + rdata_char_string_unittest.cc \ + rdata_char_string_data_unittest.cc rdata_in_a_unittest.cc \ + rdata_in_aaaa_unittest.cc rdata_ns_unittest.cc \ + rdata_soa_unittest.cc rdata_txt_like_unittest.cc \ + rdata_mx_unittest.cc rdata_sshfp_unittest.cc \ + rdata_ptr_unittest.cc rdata_cname_unittest.cc \ + rdata_dname_unittest.cc rdata_afsdb_unittest.cc \ + rdata_opt_unittest.cc rdata_dhcid_unittest.cc \ + rdata_dnskey_unittest.cc rdata_ds_like_unittest.cc \ + rdata_nsec_unittest.cc rdata_nsec3_unittest.cc \ + rdata_nsecbitmap_unittest.cc rdata_nsec3param_unittest.cc \ + rdata_nsec3param_like_unittest.cc rdata_rrsig_unittest.cc \ + rdata_rp_unittest.cc rdata_srv_unittest.cc \ + rdata_tlsa_unittest.cc rdata_minfo_unittest.cc \ + rdata_tsig_unittest.cc rdata_naptr_unittest.cc \ + rdata_hinfo_unittest.cc rdata_caa_unittest.cc \ + rdata_tkey_unittest.cc rrset_unittest.cc qid_gen_unittest.cc \ + question_unittest.cc rrparamregistry_unittest.cc \ + masterload_unittest.cc message_unittest.cc serial_unittest.cc \ + tsig_unittest.cc tsigerror_unittest.cc tsigkey_unittest.cc \ + tsigrecord_unittest.cc master_loader_callbacks_test.cc \ + rrset_collection_unittest.cc zone_checker_unittest.cc \ + run_unittests.cc +@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = \ +@HAVE_GTEST_TRUE@ run_unittests-unittest_util.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-dns_exceptions_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-edns_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_lexer_inputsource_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-labelsequence_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-messagerenderer_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_lexer_token_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_lexer_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_loader_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_lexer_state_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-name_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-nsec3hash_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrclass_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrtype_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrttl_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrcollator_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-opcode_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rcode_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdatafields_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_pimpl_holder_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_char_string_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_char_string_data_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_in_a_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_in_aaaa_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_ns_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_soa_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_txt_like_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_mx_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_sshfp_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_ptr_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_cname_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_dname_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_afsdb_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_opt_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_dhcid_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_dnskey_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_ds_like_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_nsec_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_nsec3_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_nsecbitmap_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_nsec3param_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_nsec3param_like_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_rrsig_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_rp_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_srv_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_tlsa_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_minfo_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_tsig_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_naptr_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_hinfo_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_caa_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rdata_tkey_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrset_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-qid_gen_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-question_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrparamregistry_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-masterload_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-message_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-serial_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-tsig_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-tsigerror_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-tsigkey_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-tsigrecord_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-master_loader_callbacks_test.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-rrset_collection_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-zone_checker_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) +run_unittests_OBJECTS = $(am_run_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +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 = +run_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(run_unittests_LDFLAGS) $(LDFLAGS) \ + -o $@ +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)/run_unittests-dns_exceptions_unittest.Po \ + ./$(DEPDIR)/run_unittests-edns_unittest.Po \ + ./$(DEPDIR)/run_unittests-labelsequence_unittest.Po \ + ./$(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po \ + ./$(DEPDIR)/run_unittests-master_lexer_state_unittest.Po \ + ./$(DEPDIR)/run_unittests-master_lexer_token_unittest.Po \ + ./$(DEPDIR)/run_unittests-master_lexer_unittest.Po \ + ./$(DEPDIR)/run_unittests-master_loader_callbacks_test.Po \ + ./$(DEPDIR)/run_unittests-master_loader_unittest.Po \ + ./$(DEPDIR)/run_unittests-masterload_unittest.Po \ + ./$(DEPDIR)/run_unittests-message_unittest.Po \ + ./$(DEPDIR)/run_unittests-messagerenderer_unittest.Po \ + ./$(DEPDIR)/run_unittests-name_unittest.Po \ + ./$(DEPDIR)/run_unittests-nsec3hash_unittest.Po \ + ./$(DEPDIR)/run_unittests-opcode_unittest.Po \ + ./$(DEPDIR)/run_unittests-qid_gen_unittest.Po \ + ./$(DEPDIR)/run_unittests-question_unittest.Po \ + ./$(DEPDIR)/run_unittests-rcode_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_caa_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_char_string_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_cname_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_dname_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_in_a_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_minfo_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_mx_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_naptr_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_ns_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_nsec_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_opt_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_ptr_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_rp_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_soa_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_srv_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_tkey_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_tsig_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdata_unittest.Po \ + ./$(DEPDIR)/run_unittests-rdatafields_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrclass_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrcollator_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrparamregistry_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrset_collection_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrset_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrttl_unittest.Po \ + ./$(DEPDIR)/run_unittests-rrtype_unittest.Po \ + ./$(DEPDIR)/run_unittests-run_unittests.Po \ + ./$(DEPDIR)/run_unittests-serial_unittest.Po \ + ./$(DEPDIR)/run_unittests-tsig_unittest.Po \ + ./$(DEPDIR)/run_unittests-tsigerror_unittest.Po \ + ./$(DEPDIR)/run_unittests-tsigkey_unittest.Po \ + ./$(DEPDIR)/run_unittests-tsigrecord_unittest.Po \ + ./$(DEPDIR)/run_unittests-unittest_util.Po \ + ./$(DEPDIR)/run_unittests-zone_checker_unittest.Po +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 = $(run_unittests_SOURCES) +DIST_SOURCES = $(am__run_unittests_SOURCES_DIST) +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 +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 +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.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@ +SUBDIRS = testdata . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) \ + -DTEST_DATA_SRCDIR=\"$(abs_top_srcdir)/src/lib/dns/tests/testdata\" \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dns/tests/testdata\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +@HAVE_GTEST_TRUE@run_unittests_SOURCES = unittest_util.h \ +@HAVE_GTEST_TRUE@ unittest_util.cc dns_exceptions_unittest.cc \ +@HAVE_GTEST_TRUE@ edns_unittest.cc \ +@HAVE_GTEST_TRUE@ master_lexer_inputsource_unittest.cc \ +@HAVE_GTEST_TRUE@ labelsequence_unittest.cc \ +@HAVE_GTEST_TRUE@ messagerenderer_unittest.cc \ +@HAVE_GTEST_TRUE@ master_lexer_token_unittest.cc \ +@HAVE_GTEST_TRUE@ master_lexer_unittest.cc \ +@HAVE_GTEST_TRUE@ master_loader_unittest.cc \ +@HAVE_GTEST_TRUE@ master_lexer_state_unittest.cc \ +@HAVE_GTEST_TRUE@ name_unittest.cc nsec3hash_unittest.cc \ +@HAVE_GTEST_TRUE@ rrclass_unittest.cc rrtype_unittest.cc \ +@HAVE_GTEST_TRUE@ rrttl_unittest.cc rrcollator_unittest.cc \ +@HAVE_GTEST_TRUE@ opcode_unittest.cc rcode_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_unittest.h rdata_unittest.cc \ +@HAVE_GTEST_TRUE@ rdatafields_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_pimpl_holder_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_char_string_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_char_string_data_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_in_a_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_in_aaaa_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_ns_unittest.cc rdata_soa_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_txt_like_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_mx_unittest.cc rdata_sshfp_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_ptr_unittest.cc rdata_cname_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_dname_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_afsdb_unittest.cc rdata_opt_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_dhcid_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_dnskey_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_ds_like_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_nsec_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_nsec3_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_nsecbitmap_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_nsec3param_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_nsec3param_like_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_rrsig_unittest.cc rdata_rp_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_srv_unittest.cc rdata_tlsa_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_minfo_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_tsig_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_naptr_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_hinfo_unittest.cc rdata_caa_unittest.cc \ +@HAVE_GTEST_TRUE@ rdata_tkey_unittest.cc rrset_unittest.cc \ +@HAVE_GTEST_TRUE@ qid_gen_unittest.cc question_unittest.cc \ +@HAVE_GTEST_TRUE@ rrparamregistry_unittest.cc \ +@HAVE_GTEST_TRUE@ masterload_unittest.cc message_unittest.cc \ +@HAVE_GTEST_TRUE@ serial_unittest.cc tsig_unittest.cc \ +@HAVE_GTEST_TRUE@ tsigerror_unittest.cc tsigkey_unittest.cc \ +@HAVE_GTEST_TRUE@ tsigrecord_unittest.cc \ +@HAVE_GTEST_TRUE@ master_loader_callbacks_test.cc \ +@HAVE_GTEST_TRUE@ rrset_collection_unittest.cc \ +@HAVE_GTEST_TRUE@ zone_checker_unittest.cc run_unittests.cc +@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@run_unittests_LDADD = \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(CRYPTO_LIBS) $(GTEST_LDADD) +all: 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/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/dns/tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +run_unittests$(EXEEXT): $(run_unittests_OBJECTS) $(run_unittests_DEPENDENCIES) $(EXTRA_run_unittests_DEPENDENCIES) + @rm -f run_unittests$(EXEEXT) + $(AM_V_CXXLD)$(run_unittests_LINK) $(run_unittests_OBJECTS) $(run_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-dns_exceptions_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-edns_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-labelsequence_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_lexer_state_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_lexer_token_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_lexer_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_loader_callbacks_test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-master_loader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-masterload_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-message_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-messagerenderer_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-name_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-nsec3hash_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-opcode_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-qid_gen_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-question_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rcode_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_caa_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_char_string_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_cname_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_dname_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_in_a_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_minfo_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_mx_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_naptr_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_ns_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_nsec_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_opt_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_ptr_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_rp_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_soa_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_srv_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_tkey_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_tsig_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdata_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rdatafields_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrclass_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrcollator_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrparamregistry_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrset_collection_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrset_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrttl_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-rrtype_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-serial_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tsig_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tsigerror_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tsigkey_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-tsigrecord_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-unittest_util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-zone_checker_unittest.Po@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)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.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 $@ $< + +run_unittests-unittest_util.o: unittest_util.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unittest_util.o -MD -MP -MF $(DEPDIR)/run_unittests-unittest_util.Tpo -c -o run_unittests-unittest_util.o `test -f 'unittest_util.cc' || echo '$(srcdir)/'`unittest_util.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unittest_util.Tpo $(DEPDIR)/run_unittests-unittest_util.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unittest_util.cc' object='run_unittests-unittest_util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-unittest_util.o `test -f 'unittest_util.cc' || echo '$(srcdir)/'`unittest_util.cc + +run_unittests-unittest_util.obj: unittest_util.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-unittest_util.obj -MD -MP -MF $(DEPDIR)/run_unittests-unittest_util.Tpo -c -o run_unittests-unittest_util.obj `if test -f 'unittest_util.cc'; then $(CYGPATH_W) 'unittest_util.cc'; else $(CYGPATH_W) '$(srcdir)/unittest_util.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-unittest_util.Tpo $(DEPDIR)/run_unittests-unittest_util.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unittest_util.cc' object='run_unittests-unittest_util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-unittest_util.obj `if test -f 'unittest_util.cc'; then $(CYGPATH_W) 'unittest_util.cc'; else $(CYGPATH_W) '$(srcdir)/unittest_util.cc'; fi` + +run_unittests-dns_exceptions_unittest.o: dns_exceptions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dns_exceptions_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-dns_exceptions_unittest.Tpo -c -o run_unittests-dns_exceptions_unittest.o `test -f 'dns_exceptions_unittest.cc' || echo '$(srcdir)/'`dns_exceptions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dns_exceptions_unittest.Tpo $(DEPDIR)/run_unittests-dns_exceptions_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_exceptions_unittest.cc' object='run_unittests-dns_exceptions_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-dns_exceptions_unittest.o `test -f 'dns_exceptions_unittest.cc' || echo '$(srcdir)/'`dns_exceptions_unittest.cc + +run_unittests-dns_exceptions_unittest.obj: dns_exceptions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-dns_exceptions_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-dns_exceptions_unittest.Tpo -c -o run_unittests-dns_exceptions_unittest.obj `if test -f 'dns_exceptions_unittest.cc'; then $(CYGPATH_W) 'dns_exceptions_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dns_exceptions_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-dns_exceptions_unittest.Tpo $(DEPDIR)/run_unittests-dns_exceptions_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_exceptions_unittest.cc' object='run_unittests-dns_exceptions_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-dns_exceptions_unittest.obj `if test -f 'dns_exceptions_unittest.cc'; then $(CYGPATH_W) 'dns_exceptions_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dns_exceptions_unittest.cc'; fi` + +run_unittests-edns_unittest.o: edns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-edns_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-edns_unittest.Tpo -c -o run_unittests-edns_unittest.o `test -f 'edns_unittest.cc' || echo '$(srcdir)/'`edns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-edns_unittest.Tpo $(DEPDIR)/run_unittests-edns_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='edns_unittest.cc' object='run_unittests-edns_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-edns_unittest.o `test -f 'edns_unittest.cc' || echo '$(srcdir)/'`edns_unittest.cc + +run_unittests-edns_unittest.obj: edns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-edns_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-edns_unittest.Tpo -c -o run_unittests-edns_unittest.obj `if test -f 'edns_unittest.cc'; then $(CYGPATH_W) 'edns_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/edns_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-edns_unittest.Tpo $(DEPDIR)/run_unittests-edns_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='edns_unittest.cc' object='run_unittests-edns_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-edns_unittest.obj `if test -f 'edns_unittest.cc'; then $(CYGPATH_W) 'edns_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/edns_unittest.cc'; fi` + +run_unittests-master_lexer_inputsource_unittest.o: master_lexer_inputsource_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_inputsource_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Tpo -c -o run_unittests-master_lexer_inputsource_unittest.o `test -f 'master_lexer_inputsource_unittest.cc' || echo '$(srcdir)/'`master_lexer_inputsource_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_inputsource_unittest.cc' object='run_unittests-master_lexer_inputsource_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_inputsource_unittest.o `test -f 'master_lexer_inputsource_unittest.cc' || echo '$(srcdir)/'`master_lexer_inputsource_unittest.cc + +run_unittests-master_lexer_inputsource_unittest.obj: master_lexer_inputsource_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_inputsource_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Tpo -c -o run_unittests-master_lexer_inputsource_unittest.obj `if test -f 'master_lexer_inputsource_unittest.cc'; then $(CYGPATH_W) 'master_lexer_inputsource_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_inputsource_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_inputsource_unittest.cc' object='run_unittests-master_lexer_inputsource_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_inputsource_unittest.obj `if test -f 'master_lexer_inputsource_unittest.cc'; then $(CYGPATH_W) 'master_lexer_inputsource_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_inputsource_unittest.cc'; fi` + +run_unittests-labelsequence_unittest.o: labelsequence_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-labelsequence_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-labelsequence_unittest.Tpo -c -o run_unittests-labelsequence_unittest.o `test -f 'labelsequence_unittest.cc' || echo '$(srcdir)/'`labelsequence_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-labelsequence_unittest.Tpo $(DEPDIR)/run_unittests-labelsequence_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='labelsequence_unittest.cc' object='run_unittests-labelsequence_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-labelsequence_unittest.o `test -f 'labelsequence_unittest.cc' || echo '$(srcdir)/'`labelsequence_unittest.cc + +run_unittests-labelsequence_unittest.obj: labelsequence_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-labelsequence_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-labelsequence_unittest.Tpo -c -o run_unittests-labelsequence_unittest.obj `if test -f 'labelsequence_unittest.cc'; then $(CYGPATH_W) 'labelsequence_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/labelsequence_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-labelsequence_unittest.Tpo $(DEPDIR)/run_unittests-labelsequence_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='labelsequence_unittest.cc' object='run_unittests-labelsequence_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-labelsequence_unittest.obj `if test -f 'labelsequence_unittest.cc'; then $(CYGPATH_W) 'labelsequence_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/labelsequence_unittest.cc'; fi` + +run_unittests-messagerenderer_unittest.o: messagerenderer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-messagerenderer_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-messagerenderer_unittest.Tpo -c -o run_unittests-messagerenderer_unittest.o `test -f 'messagerenderer_unittest.cc' || echo '$(srcdir)/'`messagerenderer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-messagerenderer_unittest.Tpo $(DEPDIR)/run_unittests-messagerenderer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='messagerenderer_unittest.cc' object='run_unittests-messagerenderer_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-messagerenderer_unittest.o `test -f 'messagerenderer_unittest.cc' || echo '$(srcdir)/'`messagerenderer_unittest.cc + +run_unittests-messagerenderer_unittest.obj: messagerenderer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-messagerenderer_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-messagerenderer_unittest.Tpo -c -o run_unittests-messagerenderer_unittest.obj `if test -f 'messagerenderer_unittest.cc'; then $(CYGPATH_W) 'messagerenderer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/messagerenderer_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-messagerenderer_unittest.Tpo $(DEPDIR)/run_unittests-messagerenderer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='messagerenderer_unittest.cc' object='run_unittests-messagerenderer_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-messagerenderer_unittest.obj `if test -f 'messagerenderer_unittest.cc'; then $(CYGPATH_W) 'messagerenderer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/messagerenderer_unittest.cc'; fi` + +run_unittests-master_lexer_token_unittest.o: master_lexer_token_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_token_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_token_unittest.Tpo -c -o run_unittests-master_lexer_token_unittest.o `test -f 'master_lexer_token_unittest.cc' || echo '$(srcdir)/'`master_lexer_token_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_token_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_token_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_token_unittest.cc' object='run_unittests-master_lexer_token_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_token_unittest.o `test -f 'master_lexer_token_unittest.cc' || echo '$(srcdir)/'`master_lexer_token_unittest.cc + +run_unittests-master_lexer_token_unittest.obj: master_lexer_token_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_token_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_token_unittest.Tpo -c -o run_unittests-master_lexer_token_unittest.obj `if test -f 'master_lexer_token_unittest.cc'; then $(CYGPATH_W) 'master_lexer_token_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_token_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_token_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_token_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_token_unittest.cc' object='run_unittests-master_lexer_token_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_token_unittest.obj `if test -f 'master_lexer_token_unittest.cc'; then $(CYGPATH_W) 'master_lexer_token_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_token_unittest.cc'; fi` + +run_unittests-master_lexer_unittest.o: master_lexer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_unittest.Tpo -c -o run_unittests-master_lexer_unittest.o `test -f 'master_lexer_unittest.cc' || echo '$(srcdir)/'`master_lexer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_unittest.cc' object='run_unittests-master_lexer_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_unittest.o `test -f 'master_lexer_unittest.cc' || echo '$(srcdir)/'`master_lexer_unittest.cc + +run_unittests-master_lexer_unittest.obj: master_lexer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_unittest.Tpo -c -o run_unittests-master_lexer_unittest.obj `if test -f 'master_lexer_unittest.cc'; then $(CYGPATH_W) 'master_lexer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_unittest.cc' object='run_unittests-master_lexer_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_unittest.obj `if test -f 'master_lexer_unittest.cc'; then $(CYGPATH_W) 'master_lexer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_unittest.cc'; fi` + +run_unittests-master_loader_unittest.o: master_loader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_loader_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-master_loader_unittest.Tpo -c -o run_unittests-master_loader_unittest.o `test -f 'master_loader_unittest.cc' || echo '$(srcdir)/'`master_loader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_loader_unittest.Tpo $(DEPDIR)/run_unittests-master_loader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader_unittest.cc' object='run_unittests-master_loader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_loader_unittest.o `test -f 'master_loader_unittest.cc' || echo '$(srcdir)/'`master_loader_unittest.cc + +run_unittests-master_loader_unittest.obj: master_loader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_loader_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_loader_unittest.Tpo -c -o run_unittests-master_loader_unittest.obj `if test -f 'master_loader_unittest.cc'; then $(CYGPATH_W) 'master_loader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_loader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_loader_unittest.Tpo $(DEPDIR)/run_unittests-master_loader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader_unittest.cc' object='run_unittests-master_loader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_loader_unittest.obj `if test -f 'master_loader_unittest.cc'; then $(CYGPATH_W) 'master_loader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_loader_unittest.cc'; fi` + +run_unittests-master_lexer_state_unittest.o: master_lexer_state_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_state_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_state_unittest.Tpo -c -o run_unittests-master_lexer_state_unittest.o `test -f 'master_lexer_state_unittest.cc' || echo '$(srcdir)/'`master_lexer_state_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_state_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_state_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_state_unittest.cc' object='run_unittests-master_lexer_state_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_state_unittest.o `test -f 'master_lexer_state_unittest.cc' || echo '$(srcdir)/'`master_lexer_state_unittest.cc + +run_unittests-master_lexer_state_unittest.obj: master_lexer_state_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_lexer_state_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_lexer_state_unittest.Tpo -c -o run_unittests-master_lexer_state_unittest.obj `if test -f 'master_lexer_state_unittest.cc'; then $(CYGPATH_W) 'master_lexer_state_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_state_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_lexer_state_unittest.Tpo $(DEPDIR)/run_unittests-master_lexer_state_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_lexer_state_unittest.cc' object='run_unittests-master_lexer_state_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_lexer_state_unittest.obj `if test -f 'master_lexer_state_unittest.cc'; then $(CYGPATH_W) 'master_lexer_state_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/master_lexer_state_unittest.cc'; fi` + +run_unittests-name_unittest.o: name_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-name_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-name_unittest.Tpo -c -o run_unittests-name_unittest.o `test -f 'name_unittest.cc' || echo '$(srcdir)/'`name_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-name_unittest.Tpo $(DEPDIR)/run_unittests-name_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='name_unittest.cc' object='run_unittests-name_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-name_unittest.o `test -f 'name_unittest.cc' || echo '$(srcdir)/'`name_unittest.cc + +run_unittests-name_unittest.obj: name_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-name_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-name_unittest.Tpo -c -o run_unittests-name_unittest.obj `if test -f 'name_unittest.cc'; then $(CYGPATH_W) 'name_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/name_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-name_unittest.Tpo $(DEPDIR)/run_unittests-name_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='name_unittest.cc' object='run_unittests-name_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-name_unittest.obj `if test -f 'name_unittest.cc'; then $(CYGPATH_W) 'name_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/name_unittest.cc'; fi` + +run_unittests-nsec3hash_unittest.o: nsec3hash_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-nsec3hash_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-nsec3hash_unittest.Tpo -c -o run_unittests-nsec3hash_unittest.o `test -f 'nsec3hash_unittest.cc' || echo '$(srcdir)/'`nsec3hash_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-nsec3hash_unittest.Tpo $(DEPDIR)/run_unittests-nsec3hash_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nsec3hash_unittest.cc' object='run_unittests-nsec3hash_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-nsec3hash_unittest.o `test -f 'nsec3hash_unittest.cc' || echo '$(srcdir)/'`nsec3hash_unittest.cc + +run_unittests-nsec3hash_unittest.obj: nsec3hash_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-nsec3hash_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-nsec3hash_unittest.Tpo -c -o run_unittests-nsec3hash_unittest.obj `if test -f 'nsec3hash_unittest.cc'; then $(CYGPATH_W) 'nsec3hash_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/nsec3hash_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-nsec3hash_unittest.Tpo $(DEPDIR)/run_unittests-nsec3hash_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nsec3hash_unittest.cc' object='run_unittests-nsec3hash_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-nsec3hash_unittest.obj `if test -f 'nsec3hash_unittest.cc'; then $(CYGPATH_W) 'nsec3hash_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/nsec3hash_unittest.cc'; fi` + +run_unittests-rrclass_unittest.o: rrclass_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrclass_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrclass_unittest.Tpo -c -o run_unittests-rrclass_unittest.o `test -f 'rrclass_unittest.cc' || echo '$(srcdir)/'`rrclass_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrclass_unittest.Tpo $(DEPDIR)/run_unittests-rrclass_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrclass_unittest.cc' object='run_unittests-rrclass_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrclass_unittest.o `test -f 'rrclass_unittest.cc' || echo '$(srcdir)/'`rrclass_unittest.cc + +run_unittests-rrclass_unittest.obj: rrclass_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrclass_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrclass_unittest.Tpo -c -o run_unittests-rrclass_unittest.obj `if test -f 'rrclass_unittest.cc'; then $(CYGPATH_W) 'rrclass_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrclass_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrclass_unittest.Tpo $(DEPDIR)/run_unittests-rrclass_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrclass_unittest.cc' object='run_unittests-rrclass_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrclass_unittest.obj `if test -f 'rrclass_unittest.cc'; then $(CYGPATH_W) 'rrclass_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrclass_unittest.cc'; fi` + +run_unittests-rrtype_unittest.o: rrtype_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrtype_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrtype_unittest.Tpo -c -o run_unittests-rrtype_unittest.o `test -f 'rrtype_unittest.cc' || echo '$(srcdir)/'`rrtype_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrtype_unittest.Tpo $(DEPDIR)/run_unittests-rrtype_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrtype_unittest.cc' object='run_unittests-rrtype_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrtype_unittest.o `test -f 'rrtype_unittest.cc' || echo '$(srcdir)/'`rrtype_unittest.cc + +run_unittests-rrtype_unittest.obj: rrtype_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrtype_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrtype_unittest.Tpo -c -o run_unittests-rrtype_unittest.obj `if test -f 'rrtype_unittest.cc'; then $(CYGPATH_W) 'rrtype_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrtype_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrtype_unittest.Tpo $(DEPDIR)/run_unittests-rrtype_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrtype_unittest.cc' object='run_unittests-rrtype_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrtype_unittest.obj `if test -f 'rrtype_unittest.cc'; then $(CYGPATH_W) 'rrtype_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrtype_unittest.cc'; fi` + +run_unittests-rrttl_unittest.o: rrttl_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrttl_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrttl_unittest.Tpo -c -o run_unittests-rrttl_unittest.o `test -f 'rrttl_unittest.cc' || echo '$(srcdir)/'`rrttl_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrttl_unittest.Tpo $(DEPDIR)/run_unittests-rrttl_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrttl_unittest.cc' object='run_unittests-rrttl_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrttl_unittest.o `test -f 'rrttl_unittest.cc' || echo '$(srcdir)/'`rrttl_unittest.cc + +run_unittests-rrttl_unittest.obj: rrttl_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrttl_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrttl_unittest.Tpo -c -o run_unittests-rrttl_unittest.obj `if test -f 'rrttl_unittest.cc'; then $(CYGPATH_W) 'rrttl_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrttl_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrttl_unittest.Tpo $(DEPDIR)/run_unittests-rrttl_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrttl_unittest.cc' object='run_unittests-rrttl_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrttl_unittest.obj `if test -f 'rrttl_unittest.cc'; then $(CYGPATH_W) 'rrttl_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrttl_unittest.cc'; fi` + +run_unittests-rrcollator_unittest.o: rrcollator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrcollator_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrcollator_unittest.Tpo -c -o run_unittests-rrcollator_unittest.o `test -f 'rrcollator_unittest.cc' || echo '$(srcdir)/'`rrcollator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrcollator_unittest.Tpo $(DEPDIR)/run_unittests-rrcollator_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrcollator_unittest.cc' object='run_unittests-rrcollator_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrcollator_unittest.o `test -f 'rrcollator_unittest.cc' || echo '$(srcdir)/'`rrcollator_unittest.cc + +run_unittests-rrcollator_unittest.obj: rrcollator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrcollator_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrcollator_unittest.Tpo -c -o run_unittests-rrcollator_unittest.obj `if test -f 'rrcollator_unittest.cc'; then $(CYGPATH_W) 'rrcollator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrcollator_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrcollator_unittest.Tpo $(DEPDIR)/run_unittests-rrcollator_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrcollator_unittest.cc' object='run_unittests-rrcollator_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrcollator_unittest.obj `if test -f 'rrcollator_unittest.cc'; then $(CYGPATH_W) 'rrcollator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrcollator_unittest.cc'; fi` + +run_unittests-opcode_unittest.o: opcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-opcode_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-opcode_unittest.Tpo -c -o run_unittests-opcode_unittest.o `test -f 'opcode_unittest.cc' || echo '$(srcdir)/'`opcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-opcode_unittest.Tpo $(DEPDIR)/run_unittests-opcode_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='opcode_unittest.cc' object='run_unittests-opcode_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-opcode_unittest.o `test -f 'opcode_unittest.cc' || echo '$(srcdir)/'`opcode_unittest.cc + +run_unittests-opcode_unittest.obj: opcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-opcode_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-opcode_unittest.Tpo -c -o run_unittests-opcode_unittest.obj `if test -f 'opcode_unittest.cc'; then $(CYGPATH_W) 'opcode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/opcode_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-opcode_unittest.Tpo $(DEPDIR)/run_unittests-opcode_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='opcode_unittest.cc' object='run_unittests-opcode_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-opcode_unittest.obj `if test -f 'opcode_unittest.cc'; then $(CYGPATH_W) 'opcode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/opcode_unittest.cc'; fi` + +run_unittests-rcode_unittest.o: rcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rcode_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rcode_unittest.Tpo -c -o run_unittests-rcode_unittest.o `test -f 'rcode_unittest.cc' || echo '$(srcdir)/'`rcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rcode_unittest.Tpo $(DEPDIR)/run_unittests-rcode_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rcode_unittest.cc' object='run_unittests-rcode_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rcode_unittest.o `test -f 'rcode_unittest.cc' || echo '$(srcdir)/'`rcode_unittest.cc + +run_unittests-rcode_unittest.obj: rcode_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rcode_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rcode_unittest.Tpo -c -o run_unittests-rcode_unittest.obj `if test -f 'rcode_unittest.cc'; then $(CYGPATH_W) 'rcode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rcode_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rcode_unittest.Tpo $(DEPDIR)/run_unittests-rcode_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rcode_unittest.cc' object='run_unittests-rcode_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rcode_unittest.obj `if test -f 'rcode_unittest.cc'; then $(CYGPATH_W) 'rcode_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rcode_unittest.cc'; fi` + +run_unittests-rdata_unittest.o: rdata_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_unittest.Tpo -c -o run_unittests-rdata_unittest.o `test -f 'rdata_unittest.cc' || echo '$(srcdir)/'`rdata_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_unittest.Tpo $(DEPDIR)/run_unittests-rdata_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_unittest.cc' object='run_unittests-rdata_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_unittest.o `test -f 'rdata_unittest.cc' || echo '$(srcdir)/'`rdata_unittest.cc + +run_unittests-rdata_unittest.obj: rdata_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_unittest.Tpo -c -o run_unittests-rdata_unittest.obj `if test -f 'rdata_unittest.cc'; then $(CYGPATH_W) 'rdata_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_unittest.Tpo $(DEPDIR)/run_unittests-rdata_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_unittest.cc' object='run_unittests-rdata_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_unittest.obj `if test -f 'rdata_unittest.cc'; then $(CYGPATH_W) 'rdata_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_unittest.cc'; fi` + +run_unittests-rdatafields_unittest.o: rdatafields_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdatafields_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdatafields_unittest.Tpo -c -o run_unittests-rdatafields_unittest.o `test -f 'rdatafields_unittest.cc' || echo '$(srcdir)/'`rdatafields_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdatafields_unittest.Tpo $(DEPDIR)/run_unittests-rdatafields_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdatafields_unittest.cc' object='run_unittests-rdatafields_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdatafields_unittest.o `test -f 'rdatafields_unittest.cc' || echo '$(srcdir)/'`rdatafields_unittest.cc + +run_unittests-rdatafields_unittest.obj: rdatafields_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdatafields_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdatafields_unittest.Tpo -c -o run_unittests-rdatafields_unittest.obj `if test -f 'rdatafields_unittest.cc'; then $(CYGPATH_W) 'rdatafields_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdatafields_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdatafields_unittest.Tpo $(DEPDIR)/run_unittests-rdatafields_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdatafields_unittest.cc' object='run_unittests-rdatafields_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdatafields_unittest.obj `if test -f 'rdatafields_unittest.cc'; then $(CYGPATH_W) 'rdatafields_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdatafields_unittest.cc'; fi` + +run_unittests-rdata_pimpl_holder_unittest.o: rdata_pimpl_holder_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_pimpl_holder_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Tpo -c -o run_unittests-rdata_pimpl_holder_unittest.o `test -f 'rdata_pimpl_holder_unittest.cc' || echo '$(srcdir)/'`rdata_pimpl_holder_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Tpo $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_pimpl_holder_unittest.cc' object='run_unittests-rdata_pimpl_holder_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_pimpl_holder_unittest.o `test -f 'rdata_pimpl_holder_unittest.cc' || echo '$(srcdir)/'`rdata_pimpl_holder_unittest.cc + +run_unittests-rdata_pimpl_holder_unittest.obj: rdata_pimpl_holder_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_pimpl_holder_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Tpo -c -o run_unittests-rdata_pimpl_holder_unittest.obj `if test -f 'rdata_pimpl_holder_unittest.cc'; then $(CYGPATH_W) 'rdata_pimpl_holder_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_pimpl_holder_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Tpo $(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_pimpl_holder_unittest.cc' object='run_unittests-rdata_pimpl_holder_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_pimpl_holder_unittest.obj `if test -f 'rdata_pimpl_holder_unittest.cc'; then $(CYGPATH_W) 'rdata_pimpl_holder_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_pimpl_holder_unittest.cc'; fi` + +run_unittests-rdata_char_string_unittest.o: rdata_char_string_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_char_string_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_char_string_unittest.Tpo -c -o run_unittests-rdata_char_string_unittest.o `test -f 'rdata_char_string_unittest.cc' || echo '$(srcdir)/'`rdata_char_string_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_char_string_unittest.Tpo $(DEPDIR)/run_unittests-rdata_char_string_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_char_string_unittest.cc' object='run_unittests-rdata_char_string_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_char_string_unittest.o `test -f 'rdata_char_string_unittest.cc' || echo '$(srcdir)/'`rdata_char_string_unittest.cc + +run_unittests-rdata_char_string_unittest.obj: rdata_char_string_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_char_string_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_char_string_unittest.Tpo -c -o run_unittests-rdata_char_string_unittest.obj `if test -f 'rdata_char_string_unittest.cc'; then $(CYGPATH_W) 'rdata_char_string_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_char_string_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_char_string_unittest.Tpo $(DEPDIR)/run_unittests-rdata_char_string_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_char_string_unittest.cc' object='run_unittests-rdata_char_string_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_char_string_unittest.obj `if test -f 'rdata_char_string_unittest.cc'; then $(CYGPATH_W) 'rdata_char_string_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_char_string_unittest.cc'; fi` + +run_unittests-rdata_char_string_data_unittest.o: rdata_char_string_data_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_char_string_data_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Tpo -c -o run_unittests-rdata_char_string_data_unittest.o `test -f 'rdata_char_string_data_unittest.cc' || echo '$(srcdir)/'`rdata_char_string_data_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Tpo $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_char_string_data_unittest.cc' object='run_unittests-rdata_char_string_data_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_char_string_data_unittest.o `test -f 'rdata_char_string_data_unittest.cc' || echo '$(srcdir)/'`rdata_char_string_data_unittest.cc + +run_unittests-rdata_char_string_data_unittest.obj: rdata_char_string_data_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_char_string_data_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Tpo -c -o run_unittests-rdata_char_string_data_unittest.obj `if test -f 'rdata_char_string_data_unittest.cc'; then $(CYGPATH_W) 'rdata_char_string_data_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_char_string_data_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Tpo $(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_char_string_data_unittest.cc' object='run_unittests-rdata_char_string_data_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_char_string_data_unittest.obj `if test -f 'rdata_char_string_data_unittest.cc'; then $(CYGPATH_W) 'rdata_char_string_data_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_char_string_data_unittest.cc'; fi` + +run_unittests-rdata_in_a_unittest.o: rdata_in_a_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_in_a_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_in_a_unittest.Tpo -c -o run_unittests-rdata_in_a_unittest.o `test -f 'rdata_in_a_unittest.cc' || echo '$(srcdir)/'`rdata_in_a_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_in_a_unittest.Tpo $(DEPDIR)/run_unittests-rdata_in_a_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_in_a_unittest.cc' object='run_unittests-rdata_in_a_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_in_a_unittest.o `test -f 'rdata_in_a_unittest.cc' || echo '$(srcdir)/'`rdata_in_a_unittest.cc + +run_unittests-rdata_in_a_unittest.obj: rdata_in_a_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_in_a_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_in_a_unittest.Tpo -c -o run_unittests-rdata_in_a_unittest.obj `if test -f 'rdata_in_a_unittest.cc'; then $(CYGPATH_W) 'rdata_in_a_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_in_a_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_in_a_unittest.Tpo $(DEPDIR)/run_unittests-rdata_in_a_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_in_a_unittest.cc' object='run_unittests-rdata_in_a_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_in_a_unittest.obj `if test -f 'rdata_in_a_unittest.cc'; then $(CYGPATH_W) 'rdata_in_a_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_in_a_unittest.cc'; fi` + +run_unittests-rdata_in_aaaa_unittest.o: rdata_in_aaaa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_in_aaaa_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Tpo -c -o run_unittests-rdata_in_aaaa_unittest.o `test -f 'rdata_in_aaaa_unittest.cc' || echo '$(srcdir)/'`rdata_in_aaaa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_in_aaaa_unittest.cc' object='run_unittests-rdata_in_aaaa_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_in_aaaa_unittest.o `test -f 'rdata_in_aaaa_unittest.cc' || echo '$(srcdir)/'`rdata_in_aaaa_unittest.cc + +run_unittests-rdata_in_aaaa_unittest.obj: rdata_in_aaaa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_in_aaaa_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Tpo -c -o run_unittests-rdata_in_aaaa_unittest.obj `if test -f 'rdata_in_aaaa_unittest.cc'; then $(CYGPATH_W) 'rdata_in_aaaa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_in_aaaa_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_in_aaaa_unittest.cc' object='run_unittests-rdata_in_aaaa_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_in_aaaa_unittest.obj `if test -f 'rdata_in_aaaa_unittest.cc'; then $(CYGPATH_W) 'rdata_in_aaaa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_in_aaaa_unittest.cc'; fi` + +run_unittests-rdata_ns_unittest.o: rdata_ns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ns_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ns_unittest.Tpo -c -o run_unittests-rdata_ns_unittest.o `test -f 'rdata_ns_unittest.cc' || echo '$(srcdir)/'`rdata_ns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ns_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ns_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ns_unittest.cc' object='run_unittests-rdata_ns_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ns_unittest.o `test -f 'rdata_ns_unittest.cc' || echo '$(srcdir)/'`rdata_ns_unittest.cc + +run_unittests-rdata_ns_unittest.obj: rdata_ns_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ns_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ns_unittest.Tpo -c -o run_unittests-rdata_ns_unittest.obj `if test -f 'rdata_ns_unittest.cc'; then $(CYGPATH_W) 'rdata_ns_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ns_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ns_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ns_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ns_unittest.cc' object='run_unittests-rdata_ns_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ns_unittest.obj `if test -f 'rdata_ns_unittest.cc'; then $(CYGPATH_W) 'rdata_ns_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ns_unittest.cc'; fi` + +run_unittests-rdata_soa_unittest.o: rdata_soa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_soa_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_soa_unittest.Tpo -c -o run_unittests-rdata_soa_unittest.o `test -f 'rdata_soa_unittest.cc' || echo '$(srcdir)/'`rdata_soa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_soa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_soa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_soa_unittest.cc' object='run_unittests-rdata_soa_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_soa_unittest.o `test -f 'rdata_soa_unittest.cc' || echo '$(srcdir)/'`rdata_soa_unittest.cc + +run_unittests-rdata_soa_unittest.obj: rdata_soa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_soa_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_soa_unittest.Tpo -c -o run_unittests-rdata_soa_unittest.obj `if test -f 'rdata_soa_unittest.cc'; then $(CYGPATH_W) 'rdata_soa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_soa_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_soa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_soa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_soa_unittest.cc' object='run_unittests-rdata_soa_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_soa_unittest.obj `if test -f 'rdata_soa_unittest.cc'; then $(CYGPATH_W) 'rdata_soa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_soa_unittest.cc'; fi` + +run_unittests-rdata_txt_like_unittest.o: rdata_txt_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_txt_like_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Tpo -c -o run_unittests-rdata_txt_like_unittest.o `test -f 'rdata_txt_like_unittest.cc' || echo '$(srcdir)/'`rdata_txt_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_txt_like_unittest.cc' object='run_unittests-rdata_txt_like_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_txt_like_unittest.o `test -f 'rdata_txt_like_unittest.cc' || echo '$(srcdir)/'`rdata_txt_like_unittest.cc + +run_unittests-rdata_txt_like_unittest.obj: rdata_txt_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_txt_like_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Tpo -c -o run_unittests-rdata_txt_like_unittest.obj `if test -f 'rdata_txt_like_unittest.cc'; then $(CYGPATH_W) 'rdata_txt_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_txt_like_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_txt_like_unittest.cc' object='run_unittests-rdata_txt_like_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_txt_like_unittest.obj `if test -f 'rdata_txt_like_unittest.cc'; then $(CYGPATH_W) 'rdata_txt_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_txt_like_unittest.cc'; fi` + +run_unittests-rdata_mx_unittest.o: rdata_mx_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_mx_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_mx_unittest.Tpo -c -o run_unittests-rdata_mx_unittest.o `test -f 'rdata_mx_unittest.cc' || echo '$(srcdir)/'`rdata_mx_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_mx_unittest.Tpo $(DEPDIR)/run_unittests-rdata_mx_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_mx_unittest.cc' object='run_unittests-rdata_mx_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_mx_unittest.o `test -f 'rdata_mx_unittest.cc' || echo '$(srcdir)/'`rdata_mx_unittest.cc + +run_unittests-rdata_mx_unittest.obj: rdata_mx_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_mx_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_mx_unittest.Tpo -c -o run_unittests-rdata_mx_unittest.obj `if test -f 'rdata_mx_unittest.cc'; then $(CYGPATH_W) 'rdata_mx_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_mx_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_mx_unittest.Tpo $(DEPDIR)/run_unittests-rdata_mx_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_mx_unittest.cc' object='run_unittests-rdata_mx_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_mx_unittest.obj `if test -f 'rdata_mx_unittest.cc'; then $(CYGPATH_W) 'rdata_mx_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_mx_unittest.cc'; fi` + +run_unittests-rdata_sshfp_unittest.o: rdata_sshfp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_sshfp_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Tpo -c -o run_unittests-rdata_sshfp_unittest.o `test -f 'rdata_sshfp_unittest.cc' || echo '$(srcdir)/'`rdata_sshfp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Tpo $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_sshfp_unittest.cc' object='run_unittests-rdata_sshfp_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_sshfp_unittest.o `test -f 'rdata_sshfp_unittest.cc' || echo '$(srcdir)/'`rdata_sshfp_unittest.cc + +run_unittests-rdata_sshfp_unittest.obj: rdata_sshfp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_sshfp_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Tpo -c -o run_unittests-rdata_sshfp_unittest.obj `if test -f 'rdata_sshfp_unittest.cc'; then $(CYGPATH_W) 'rdata_sshfp_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_sshfp_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Tpo $(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_sshfp_unittest.cc' object='run_unittests-rdata_sshfp_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_sshfp_unittest.obj `if test -f 'rdata_sshfp_unittest.cc'; then $(CYGPATH_W) 'rdata_sshfp_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_sshfp_unittest.cc'; fi` + +run_unittests-rdata_ptr_unittest.o: rdata_ptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ptr_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ptr_unittest.Tpo -c -o run_unittests-rdata_ptr_unittest.o `test -f 'rdata_ptr_unittest.cc' || echo '$(srcdir)/'`rdata_ptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ptr_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ptr_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ptr_unittest.cc' object='run_unittests-rdata_ptr_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ptr_unittest.o `test -f 'rdata_ptr_unittest.cc' || echo '$(srcdir)/'`rdata_ptr_unittest.cc + +run_unittests-rdata_ptr_unittest.obj: rdata_ptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ptr_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ptr_unittest.Tpo -c -o run_unittests-rdata_ptr_unittest.obj `if test -f 'rdata_ptr_unittest.cc'; then $(CYGPATH_W) 'rdata_ptr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ptr_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ptr_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ptr_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ptr_unittest.cc' object='run_unittests-rdata_ptr_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ptr_unittest.obj `if test -f 'rdata_ptr_unittest.cc'; then $(CYGPATH_W) 'rdata_ptr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ptr_unittest.cc'; fi` + +run_unittests-rdata_cname_unittest.o: rdata_cname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_cname_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_cname_unittest.Tpo -c -o run_unittests-rdata_cname_unittest.o `test -f 'rdata_cname_unittest.cc' || echo '$(srcdir)/'`rdata_cname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_cname_unittest.Tpo $(DEPDIR)/run_unittests-rdata_cname_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_cname_unittest.cc' object='run_unittests-rdata_cname_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_cname_unittest.o `test -f 'rdata_cname_unittest.cc' || echo '$(srcdir)/'`rdata_cname_unittest.cc + +run_unittests-rdata_cname_unittest.obj: rdata_cname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_cname_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_cname_unittest.Tpo -c -o run_unittests-rdata_cname_unittest.obj `if test -f 'rdata_cname_unittest.cc'; then $(CYGPATH_W) 'rdata_cname_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_cname_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_cname_unittest.Tpo $(DEPDIR)/run_unittests-rdata_cname_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_cname_unittest.cc' object='run_unittests-rdata_cname_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_cname_unittest.obj `if test -f 'rdata_cname_unittest.cc'; then $(CYGPATH_W) 'rdata_cname_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_cname_unittest.cc'; fi` + +run_unittests-rdata_dname_unittest.o: rdata_dname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dname_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dname_unittest.Tpo -c -o run_unittests-rdata_dname_unittest.o `test -f 'rdata_dname_unittest.cc' || echo '$(srcdir)/'`rdata_dname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dname_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dname_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dname_unittest.cc' object='run_unittests-rdata_dname_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dname_unittest.o `test -f 'rdata_dname_unittest.cc' || echo '$(srcdir)/'`rdata_dname_unittest.cc + +run_unittests-rdata_dname_unittest.obj: rdata_dname_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dname_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dname_unittest.Tpo -c -o run_unittests-rdata_dname_unittest.obj `if test -f 'rdata_dname_unittest.cc'; then $(CYGPATH_W) 'rdata_dname_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dname_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dname_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dname_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dname_unittest.cc' object='run_unittests-rdata_dname_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dname_unittest.obj `if test -f 'rdata_dname_unittest.cc'; then $(CYGPATH_W) 'rdata_dname_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dname_unittest.cc'; fi` + +run_unittests-rdata_afsdb_unittest.o: rdata_afsdb_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_afsdb_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Tpo -c -o run_unittests-rdata_afsdb_unittest.o `test -f 'rdata_afsdb_unittest.cc' || echo '$(srcdir)/'`rdata_afsdb_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Tpo $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_afsdb_unittest.cc' object='run_unittests-rdata_afsdb_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_afsdb_unittest.o `test -f 'rdata_afsdb_unittest.cc' || echo '$(srcdir)/'`rdata_afsdb_unittest.cc + +run_unittests-rdata_afsdb_unittest.obj: rdata_afsdb_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_afsdb_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Tpo -c -o run_unittests-rdata_afsdb_unittest.obj `if test -f 'rdata_afsdb_unittest.cc'; then $(CYGPATH_W) 'rdata_afsdb_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_afsdb_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Tpo $(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_afsdb_unittest.cc' object='run_unittests-rdata_afsdb_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_afsdb_unittest.obj `if test -f 'rdata_afsdb_unittest.cc'; then $(CYGPATH_W) 'rdata_afsdb_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_afsdb_unittest.cc'; fi` + +run_unittests-rdata_opt_unittest.o: rdata_opt_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_opt_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_opt_unittest.Tpo -c -o run_unittests-rdata_opt_unittest.o `test -f 'rdata_opt_unittest.cc' || echo '$(srcdir)/'`rdata_opt_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_opt_unittest.Tpo $(DEPDIR)/run_unittests-rdata_opt_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_opt_unittest.cc' object='run_unittests-rdata_opt_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_opt_unittest.o `test -f 'rdata_opt_unittest.cc' || echo '$(srcdir)/'`rdata_opt_unittest.cc + +run_unittests-rdata_opt_unittest.obj: rdata_opt_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_opt_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_opt_unittest.Tpo -c -o run_unittests-rdata_opt_unittest.obj `if test -f 'rdata_opt_unittest.cc'; then $(CYGPATH_W) 'rdata_opt_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_opt_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_opt_unittest.Tpo $(DEPDIR)/run_unittests-rdata_opt_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_opt_unittest.cc' object='run_unittests-rdata_opt_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_opt_unittest.obj `if test -f 'rdata_opt_unittest.cc'; then $(CYGPATH_W) 'rdata_opt_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_opt_unittest.cc'; fi` + +run_unittests-rdata_dhcid_unittest.o: rdata_dhcid_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dhcid_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Tpo -c -o run_unittests-rdata_dhcid_unittest.o `test -f 'rdata_dhcid_unittest.cc' || echo '$(srcdir)/'`rdata_dhcid_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dhcid_unittest.cc' object='run_unittests-rdata_dhcid_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dhcid_unittest.o `test -f 'rdata_dhcid_unittest.cc' || echo '$(srcdir)/'`rdata_dhcid_unittest.cc + +run_unittests-rdata_dhcid_unittest.obj: rdata_dhcid_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dhcid_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Tpo -c -o run_unittests-rdata_dhcid_unittest.obj `if test -f 'rdata_dhcid_unittest.cc'; then $(CYGPATH_W) 'rdata_dhcid_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dhcid_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dhcid_unittest.cc' object='run_unittests-rdata_dhcid_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dhcid_unittest.obj `if test -f 'rdata_dhcid_unittest.cc'; then $(CYGPATH_W) 'rdata_dhcid_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dhcid_unittest.cc'; fi` + +run_unittests-rdata_dnskey_unittest.o: rdata_dnskey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dnskey_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Tpo -c -o run_unittests-rdata_dnskey_unittest.o `test -f 'rdata_dnskey_unittest.cc' || echo '$(srcdir)/'`rdata_dnskey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dnskey_unittest.cc' object='run_unittests-rdata_dnskey_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dnskey_unittest.o `test -f 'rdata_dnskey_unittest.cc' || echo '$(srcdir)/'`rdata_dnskey_unittest.cc + +run_unittests-rdata_dnskey_unittest.obj: rdata_dnskey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_dnskey_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Tpo -c -o run_unittests-rdata_dnskey_unittest.obj `if test -f 'rdata_dnskey_unittest.cc'; then $(CYGPATH_W) 'rdata_dnskey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dnskey_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Tpo $(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_dnskey_unittest.cc' object='run_unittests-rdata_dnskey_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_dnskey_unittest.obj `if test -f 'rdata_dnskey_unittest.cc'; then $(CYGPATH_W) 'rdata_dnskey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_dnskey_unittest.cc'; fi` + +run_unittests-rdata_ds_like_unittest.o: rdata_ds_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ds_like_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Tpo -c -o run_unittests-rdata_ds_like_unittest.o `test -f 'rdata_ds_like_unittest.cc' || echo '$(srcdir)/'`rdata_ds_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ds_like_unittest.cc' object='run_unittests-rdata_ds_like_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ds_like_unittest.o `test -f 'rdata_ds_like_unittest.cc' || echo '$(srcdir)/'`rdata_ds_like_unittest.cc + +run_unittests-rdata_ds_like_unittest.obj: rdata_ds_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_ds_like_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Tpo -c -o run_unittests-rdata_ds_like_unittest.obj `if test -f 'rdata_ds_like_unittest.cc'; then $(CYGPATH_W) 'rdata_ds_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ds_like_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_ds_like_unittest.cc' object='run_unittests-rdata_ds_like_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_ds_like_unittest.obj `if test -f 'rdata_ds_like_unittest.cc'; then $(CYGPATH_W) 'rdata_ds_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_ds_like_unittest.cc'; fi` + +run_unittests-rdata_nsec_unittest.o: rdata_nsec_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec_unittest.Tpo -c -o run_unittests-rdata_nsec_unittest.o `test -f 'rdata_nsec_unittest.cc' || echo '$(srcdir)/'`rdata_nsec_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec_unittest.cc' object='run_unittests-rdata_nsec_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec_unittest.o `test -f 'rdata_nsec_unittest.cc' || echo '$(srcdir)/'`rdata_nsec_unittest.cc + +run_unittests-rdata_nsec_unittest.obj: rdata_nsec_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec_unittest.Tpo -c -o run_unittests-rdata_nsec_unittest.obj `if test -f 'rdata_nsec_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec_unittest.cc' object='run_unittests-rdata_nsec_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec_unittest.obj `if test -f 'rdata_nsec_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec_unittest.cc'; fi` + +run_unittests-rdata_nsec3_unittest.o: rdata_nsec3_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Tpo -c -o run_unittests-rdata_nsec3_unittest.o `test -f 'rdata_nsec3_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3_unittest.cc' object='run_unittests-rdata_nsec3_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3_unittest.o `test -f 'rdata_nsec3_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3_unittest.cc + +run_unittests-rdata_nsec3_unittest.obj: rdata_nsec3_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Tpo -c -o run_unittests-rdata_nsec3_unittest.obj `if test -f 'rdata_nsec3_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3_unittest.cc' object='run_unittests-rdata_nsec3_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3_unittest.obj `if test -f 'rdata_nsec3_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3_unittest.cc'; fi` + +run_unittests-rdata_nsecbitmap_unittest.o: rdata_nsecbitmap_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsecbitmap_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Tpo -c -o run_unittests-rdata_nsecbitmap_unittest.o `test -f 'rdata_nsecbitmap_unittest.cc' || echo '$(srcdir)/'`rdata_nsecbitmap_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsecbitmap_unittest.cc' object='run_unittests-rdata_nsecbitmap_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsecbitmap_unittest.o `test -f 'rdata_nsecbitmap_unittest.cc' || echo '$(srcdir)/'`rdata_nsecbitmap_unittest.cc + +run_unittests-rdata_nsecbitmap_unittest.obj: rdata_nsecbitmap_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsecbitmap_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Tpo -c -o run_unittests-rdata_nsecbitmap_unittest.obj `if test -f 'rdata_nsecbitmap_unittest.cc'; then $(CYGPATH_W) 'rdata_nsecbitmap_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsecbitmap_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsecbitmap_unittest.cc' object='run_unittests-rdata_nsecbitmap_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsecbitmap_unittest.obj `if test -f 'rdata_nsecbitmap_unittest.cc'; then $(CYGPATH_W) 'rdata_nsecbitmap_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsecbitmap_unittest.cc'; fi` + +run_unittests-rdata_nsec3param_unittest.o: rdata_nsec3param_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3param_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Tpo -c -o run_unittests-rdata_nsec3param_unittest.o `test -f 'rdata_nsec3param_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3param_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3param_unittest.cc' object='run_unittests-rdata_nsec3param_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3param_unittest.o `test -f 'rdata_nsec3param_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3param_unittest.cc + +run_unittests-rdata_nsec3param_unittest.obj: rdata_nsec3param_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3param_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Tpo -c -o run_unittests-rdata_nsec3param_unittest.obj `if test -f 'rdata_nsec3param_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3param_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3param_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3param_unittest.cc' object='run_unittests-rdata_nsec3param_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3param_unittest.obj `if test -f 'rdata_nsec3param_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3param_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3param_unittest.cc'; fi` + +run_unittests-rdata_nsec3param_like_unittest.o: rdata_nsec3param_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3param_like_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Tpo -c -o run_unittests-rdata_nsec3param_like_unittest.o `test -f 'rdata_nsec3param_like_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3param_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3param_like_unittest.cc' object='run_unittests-rdata_nsec3param_like_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3param_like_unittest.o `test -f 'rdata_nsec3param_like_unittest.cc' || echo '$(srcdir)/'`rdata_nsec3param_like_unittest.cc + +run_unittests-rdata_nsec3param_like_unittest.obj: rdata_nsec3param_like_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_nsec3param_like_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Tpo -c -o run_unittests-rdata_nsec3param_like_unittest.obj `if test -f 'rdata_nsec3param_like_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3param_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3param_like_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Tpo $(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_nsec3param_like_unittest.cc' object='run_unittests-rdata_nsec3param_like_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_nsec3param_like_unittest.obj `if test -f 'rdata_nsec3param_like_unittest.cc'; then $(CYGPATH_W) 'rdata_nsec3param_like_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_nsec3param_like_unittest.cc'; fi` + +run_unittests-rdata_rrsig_unittest.o: rdata_rrsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_rrsig_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Tpo -c -o run_unittests-rdata_rrsig_unittest.o `test -f 'rdata_rrsig_unittest.cc' || echo '$(srcdir)/'`rdata_rrsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Tpo $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_rrsig_unittest.cc' object='run_unittests-rdata_rrsig_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_rrsig_unittest.o `test -f 'rdata_rrsig_unittest.cc' || echo '$(srcdir)/'`rdata_rrsig_unittest.cc + +run_unittests-rdata_rrsig_unittest.obj: rdata_rrsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_rrsig_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Tpo -c -o run_unittests-rdata_rrsig_unittest.obj `if test -f 'rdata_rrsig_unittest.cc'; then $(CYGPATH_W) 'rdata_rrsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_rrsig_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Tpo $(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_rrsig_unittest.cc' object='run_unittests-rdata_rrsig_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_rrsig_unittest.obj `if test -f 'rdata_rrsig_unittest.cc'; then $(CYGPATH_W) 'rdata_rrsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_rrsig_unittest.cc'; fi` + +run_unittests-rdata_rp_unittest.o: rdata_rp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_rp_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_rp_unittest.Tpo -c -o run_unittests-rdata_rp_unittest.o `test -f 'rdata_rp_unittest.cc' || echo '$(srcdir)/'`rdata_rp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_rp_unittest.Tpo $(DEPDIR)/run_unittests-rdata_rp_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_rp_unittest.cc' object='run_unittests-rdata_rp_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_rp_unittest.o `test -f 'rdata_rp_unittest.cc' || echo '$(srcdir)/'`rdata_rp_unittest.cc + +run_unittests-rdata_rp_unittest.obj: rdata_rp_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_rp_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_rp_unittest.Tpo -c -o run_unittests-rdata_rp_unittest.obj `if test -f 'rdata_rp_unittest.cc'; then $(CYGPATH_W) 'rdata_rp_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_rp_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_rp_unittest.Tpo $(DEPDIR)/run_unittests-rdata_rp_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_rp_unittest.cc' object='run_unittests-rdata_rp_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_rp_unittest.obj `if test -f 'rdata_rp_unittest.cc'; then $(CYGPATH_W) 'rdata_rp_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_rp_unittest.cc'; fi` + +run_unittests-rdata_srv_unittest.o: rdata_srv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_srv_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_srv_unittest.Tpo -c -o run_unittests-rdata_srv_unittest.o `test -f 'rdata_srv_unittest.cc' || echo '$(srcdir)/'`rdata_srv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_srv_unittest.Tpo $(DEPDIR)/run_unittests-rdata_srv_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_srv_unittest.cc' object='run_unittests-rdata_srv_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_srv_unittest.o `test -f 'rdata_srv_unittest.cc' || echo '$(srcdir)/'`rdata_srv_unittest.cc + +run_unittests-rdata_srv_unittest.obj: rdata_srv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_srv_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_srv_unittest.Tpo -c -o run_unittests-rdata_srv_unittest.obj `if test -f 'rdata_srv_unittest.cc'; then $(CYGPATH_W) 'rdata_srv_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_srv_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_srv_unittest.Tpo $(DEPDIR)/run_unittests-rdata_srv_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_srv_unittest.cc' object='run_unittests-rdata_srv_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_srv_unittest.obj `if test -f 'rdata_srv_unittest.cc'; then $(CYGPATH_W) 'rdata_srv_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_srv_unittest.cc'; fi` + +run_unittests-rdata_tlsa_unittest.o: rdata_tlsa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tlsa_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Tpo -c -o run_unittests-rdata_tlsa_unittest.o `test -f 'rdata_tlsa_unittest.cc' || echo '$(srcdir)/'`rdata_tlsa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tlsa_unittest.cc' object='run_unittests-rdata_tlsa_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tlsa_unittest.o `test -f 'rdata_tlsa_unittest.cc' || echo '$(srcdir)/'`rdata_tlsa_unittest.cc + +run_unittests-rdata_tlsa_unittest.obj: rdata_tlsa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tlsa_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Tpo -c -o run_unittests-rdata_tlsa_unittest.obj `if test -f 'rdata_tlsa_unittest.cc'; then $(CYGPATH_W) 'rdata_tlsa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tlsa_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tlsa_unittest.cc' object='run_unittests-rdata_tlsa_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tlsa_unittest.obj `if test -f 'rdata_tlsa_unittest.cc'; then $(CYGPATH_W) 'rdata_tlsa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tlsa_unittest.cc'; fi` + +run_unittests-rdata_minfo_unittest.o: rdata_minfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_minfo_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_minfo_unittest.Tpo -c -o run_unittests-rdata_minfo_unittest.o `test -f 'rdata_minfo_unittest.cc' || echo '$(srcdir)/'`rdata_minfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_minfo_unittest.Tpo $(DEPDIR)/run_unittests-rdata_minfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_minfo_unittest.cc' object='run_unittests-rdata_minfo_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_minfo_unittest.o `test -f 'rdata_minfo_unittest.cc' || echo '$(srcdir)/'`rdata_minfo_unittest.cc + +run_unittests-rdata_minfo_unittest.obj: rdata_minfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_minfo_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_minfo_unittest.Tpo -c -o run_unittests-rdata_minfo_unittest.obj `if test -f 'rdata_minfo_unittest.cc'; then $(CYGPATH_W) 'rdata_minfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_minfo_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_minfo_unittest.Tpo $(DEPDIR)/run_unittests-rdata_minfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_minfo_unittest.cc' object='run_unittests-rdata_minfo_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_minfo_unittest.obj `if test -f 'rdata_minfo_unittest.cc'; then $(CYGPATH_W) 'rdata_minfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_minfo_unittest.cc'; fi` + +run_unittests-rdata_tsig_unittest.o: rdata_tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tsig_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tsig_unittest.Tpo -c -o run_unittests-rdata_tsig_unittest.o `test -f 'rdata_tsig_unittest.cc' || echo '$(srcdir)/'`rdata_tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tsig_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tsig_unittest.cc' object='run_unittests-rdata_tsig_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tsig_unittest.o `test -f 'rdata_tsig_unittest.cc' || echo '$(srcdir)/'`rdata_tsig_unittest.cc + +run_unittests-rdata_tsig_unittest.obj: rdata_tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tsig_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tsig_unittest.Tpo -c -o run_unittests-rdata_tsig_unittest.obj `if test -f 'rdata_tsig_unittest.cc'; then $(CYGPATH_W) 'rdata_tsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tsig_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tsig_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tsig_unittest.cc' object='run_unittests-rdata_tsig_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tsig_unittest.obj `if test -f 'rdata_tsig_unittest.cc'; then $(CYGPATH_W) 'rdata_tsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tsig_unittest.cc'; fi` + +run_unittests-rdata_naptr_unittest.o: rdata_naptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_naptr_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_naptr_unittest.Tpo -c -o run_unittests-rdata_naptr_unittest.o `test -f 'rdata_naptr_unittest.cc' || echo '$(srcdir)/'`rdata_naptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_naptr_unittest.Tpo $(DEPDIR)/run_unittests-rdata_naptr_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_naptr_unittest.cc' object='run_unittests-rdata_naptr_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_naptr_unittest.o `test -f 'rdata_naptr_unittest.cc' || echo '$(srcdir)/'`rdata_naptr_unittest.cc + +run_unittests-rdata_naptr_unittest.obj: rdata_naptr_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_naptr_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_naptr_unittest.Tpo -c -o run_unittests-rdata_naptr_unittest.obj `if test -f 'rdata_naptr_unittest.cc'; then $(CYGPATH_W) 'rdata_naptr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_naptr_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_naptr_unittest.Tpo $(DEPDIR)/run_unittests-rdata_naptr_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_naptr_unittest.cc' object='run_unittests-rdata_naptr_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_naptr_unittest.obj `if test -f 'rdata_naptr_unittest.cc'; then $(CYGPATH_W) 'rdata_naptr_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_naptr_unittest.cc'; fi` + +run_unittests-rdata_hinfo_unittest.o: rdata_hinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_hinfo_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Tpo -c -o run_unittests-rdata_hinfo_unittest.o `test -f 'rdata_hinfo_unittest.cc' || echo '$(srcdir)/'`rdata_hinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Tpo $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_hinfo_unittest.cc' object='run_unittests-rdata_hinfo_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_hinfo_unittest.o `test -f 'rdata_hinfo_unittest.cc' || echo '$(srcdir)/'`rdata_hinfo_unittest.cc + +run_unittests-rdata_hinfo_unittest.obj: rdata_hinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_hinfo_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Tpo -c -o run_unittests-rdata_hinfo_unittest.obj `if test -f 'rdata_hinfo_unittest.cc'; then $(CYGPATH_W) 'rdata_hinfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_hinfo_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Tpo $(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_hinfo_unittest.cc' object='run_unittests-rdata_hinfo_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_hinfo_unittest.obj `if test -f 'rdata_hinfo_unittest.cc'; then $(CYGPATH_W) 'rdata_hinfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_hinfo_unittest.cc'; fi` + +run_unittests-rdata_caa_unittest.o: rdata_caa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_caa_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_caa_unittest.Tpo -c -o run_unittests-rdata_caa_unittest.o `test -f 'rdata_caa_unittest.cc' || echo '$(srcdir)/'`rdata_caa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_caa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_caa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_caa_unittest.cc' object='run_unittests-rdata_caa_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_caa_unittest.o `test -f 'rdata_caa_unittest.cc' || echo '$(srcdir)/'`rdata_caa_unittest.cc + +run_unittests-rdata_caa_unittest.obj: rdata_caa_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_caa_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_caa_unittest.Tpo -c -o run_unittests-rdata_caa_unittest.obj `if test -f 'rdata_caa_unittest.cc'; then $(CYGPATH_W) 'rdata_caa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_caa_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_caa_unittest.Tpo $(DEPDIR)/run_unittests-rdata_caa_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_caa_unittest.cc' object='run_unittests-rdata_caa_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_caa_unittest.obj `if test -f 'rdata_caa_unittest.cc'; then $(CYGPATH_W) 'rdata_caa_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_caa_unittest.cc'; fi` + +run_unittests-rdata_tkey_unittest.o: rdata_tkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tkey_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tkey_unittest.Tpo -c -o run_unittests-rdata_tkey_unittest.o `test -f 'rdata_tkey_unittest.cc' || echo '$(srcdir)/'`rdata_tkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tkey_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tkey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tkey_unittest.cc' object='run_unittests-rdata_tkey_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tkey_unittest.o `test -f 'rdata_tkey_unittest.cc' || echo '$(srcdir)/'`rdata_tkey_unittest.cc + +run_unittests-rdata_tkey_unittest.obj: rdata_tkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rdata_tkey_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rdata_tkey_unittest.Tpo -c -o run_unittests-rdata_tkey_unittest.obj `if test -f 'rdata_tkey_unittest.cc'; then $(CYGPATH_W) 'rdata_tkey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tkey_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rdata_tkey_unittest.Tpo $(DEPDIR)/run_unittests-rdata_tkey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rdata_tkey_unittest.cc' object='run_unittests-rdata_tkey_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rdata_tkey_unittest.obj `if test -f 'rdata_tkey_unittest.cc'; then $(CYGPATH_W) 'rdata_tkey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rdata_tkey_unittest.cc'; fi` + +run_unittests-rrset_unittest.o: rrset_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrset_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrset_unittest.Tpo -c -o run_unittests-rrset_unittest.o `test -f 'rrset_unittest.cc' || echo '$(srcdir)/'`rrset_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrset_unittest.Tpo $(DEPDIR)/run_unittests-rrset_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset_unittest.cc' object='run_unittests-rrset_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrset_unittest.o `test -f 'rrset_unittest.cc' || echo '$(srcdir)/'`rrset_unittest.cc + +run_unittests-rrset_unittest.obj: rrset_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrset_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrset_unittest.Tpo -c -o run_unittests-rrset_unittest.obj `if test -f 'rrset_unittest.cc'; then $(CYGPATH_W) 'rrset_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrset_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrset_unittest.Tpo $(DEPDIR)/run_unittests-rrset_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset_unittest.cc' object='run_unittests-rrset_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrset_unittest.obj `if test -f 'rrset_unittest.cc'; then $(CYGPATH_W) 'rrset_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrset_unittest.cc'; fi` + +run_unittests-qid_gen_unittest.o: qid_gen_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-qid_gen_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-qid_gen_unittest.Tpo -c -o run_unittests-qid_gen_unittest.o `test -f 'qid_gen_unittest.cc' || echo '$(srcdir)/'`qid_gen_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-qid_gen_unittest.Tpo $(DEPDIR)/run_unittests-qid_gen_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='qid_gen_unittest.cc' object='run_unittests-qid_gen_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-qid_gen_unittest.o `test -f 'qid_gen_unittest.cc' || echo '$(srcdir)/'`qid_gen_unittest.cc + +run_unittests-qid_gen_unittest.obj: qid_gen_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-qid_gen_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-qid_gen_unittest.Tpo -c -o run_unittests-qid_gen_unittest.obj `if test -f 'qid_gen_unittest.cc'; then $(CYGPATH_W) 'qid_gen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/qid_gen_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-qid_gen_unittest.Tpo $(DEPDIR)/run_unittests-qid_gen_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='qid_gen_unittest.cc' object='run_unittests-qid_gen_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-qid_gen_unittest.obj `if test -f 'qid_gen_unittest.cc'; then $(CYGPATH_W) 'qid_gen_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/qid_gen_unittest.cc'; fi` + +run_unittests-question_unittest.o: question_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-question_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-question_unittest.Tpo -c -o run_unittests-question_unittest.o `test -f 'question_unittest.cc' || echo '$(srcdir)/'`question_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-question_unittest.Tpo $(DEPDIR)/run_unittests-question_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='question_unittest.cc' object='run_unittests-question_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-question_unittest.o `test -f 'question_unittest.cc' || echo '$(srcdir)/'`question_unittest.cc + +run_unittests-question_unittest.obj: question_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-question_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-question_unittest.Tpo -c -o run_unittests-question_unittest.obj `if test -f 'question_unittest.cc'; then $(CYGPATH_W) 'question_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/question_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-question_unittest.Tpo $(DEPDIR)/run_unittests-question_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='question_unittest.cc' object='run_unittests-question_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-question_unittest.obj `if test -f 'question_unittest.cc'; then $(CYGPATH_W) 'question_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/question_unittest.cc'; fi` + +run_unittests-rrparamregistry_unittest.o: rrparamregistry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrparamregistry_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrparamregistry_unittest.Tpo -c -o run_unittests-rrparamregistry_unittest.o `test -f 'rrparamregistry_unittest.cc' || echo '$(srcdir)/'`rrparamregistry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrparamregistry_unittest.Tpo $(DEPDIR)/run_unittests-rrparamregistry_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrparamregistry_unittest.cc' object='run_unittests-rrparamregistry_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrparamregistry_unittest.o `test -f 'rrparamregistry_unittest.cc' || echo '$(srcdir)/'`rrparamregistry_unittest.cc + +run_unittests-rrparamregistry_unittest.obj: rrparamregistry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrparamregistry_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrparamregistry_unittest.Tpo -c -o run_unittests-rrparamregistry_unittest.obj `if test -f 'rrparamregistry_unittest.cc'; then $(CYGPATH_W) 'rrparamregistry_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrparamregistry_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrparamregistry_unittest.Tpo $(DEPDIR)/run_unittests-rrparamregistry_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrparamregistry_unittest.cc' object='run_unittests-rrparamregistry_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrparamregistry_unittest.obj `if test -f 'rrparamregistry_unittest.cc'; then $(CYGPATH_W) 'rrparamregistry_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrparamregistry_unittest.cc'; fi` + +run_unittests-masterload_unittest.o: masterload_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-masterload_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-masterload_unittest.Tpo -c -o run_unittests-masterload_unittest.o `test -f 'masterload_unittest.cc' || echo '$(srcdir)/'`masterload_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-masterload_unittest.Tpo $(DEPDIR)/run_unittests-masterload_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='masterload_unittest.cc' object='run_unittests-masterload_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-masterload_unittest.o `test -f 'masterload_unittest.cc' || echo '$(srcdir)/'`masterload_unittest.cc + +run_unittests-masterload_unittest.obj: masterload_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-masterload_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-masterload_unittest.Tpo -c -o run_unittests-masterload_unittest.obj `if test -f 'masterload_unittest.cc'; then $(CYGPATH_W) 'masterload_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/masterload_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-masterload_unittest.Tpo $(DEPDIR)/run_unittests-masterload_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='masterload_unittest.cc' object='run_unittests-masterload_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-masterload_unittest.obj `if test -f 'masterload_unittest.cc'; then $(CYGPATH_W) 'masterload_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/masterload_unittest.cc'; fi` + +run_unittests-message_unittest.o: message_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-message_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-message_unittest.Tpo -c -o run_unittests-message_unittest.o `test -f 'message_unittest.cc' || echo '$(srcdir)/'`message_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-message_unittest.Tpo $(DEPDIR)/run_unittests-message_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='message_unittest.cc' object='run_unittests-message_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-message_unittest.o `test -f 'message_unittest.cc' || echo '$(srcdir)/'`message_unittest.cc + +run_unittests-message_unittest.obj: message_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-message_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-message_unittest.Tpo -c -o run_unittests-message_unittest.obj `if test -f 'message_unittest.cc'; then $(CYGPATH_W) 'message_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/message_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-message_unittest.Tpo $(DEPDIR)/run_unittests-message_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='message_unittest.cc' object='run_unittests-message_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-message_unittest.obj `if test -f 'message_unittest.cc'; then $(CYGPATH_W) 'message_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/message_unittest.cc'; fi` + +run_unittests-serial_unittest.o: serial_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-serial_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-serial_unittest.Tpo -c -o run_unittests-serial_unittest.o `test -f 'serial_unittest.cc' || echo '$(srcdir)/'`serial_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-serial_unittest.Tpo $(DEPDIR)/run_unittests-serial_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='serial_unittest.cc' object='run_unittests-serial_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-serial_unittest.o `test -f 'serial_unittest.cc' || echo '$(srcdir)/'`serial_unittest.cc + +run_unittests-serial_unittest.obj: serial_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-serial_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-serial_unittest.Tpo -c -o run_unittests-serial_unittest.obj `if test -f 'serial_unittest.cc'; then $(CYGPATH_W) 'serial_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/serial_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-serial_unittest.Tpo $(DEPDIR)/run_unittests-serial_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='serial_unittest.cc' object='run_unittests-serial_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-serial_unittest.obj `if test -f 'serial_unittest.cc'; then $(CYGPATH_W) 'serial_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/serial_unittest.cc'; fi` + +run_unittests-tsig_unittest.o: tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsig_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tsig_unittest.Tpo -c -o run_unittests-tsig_unittest.o `test -f 'tsig_unittest.cc' || echo '$(srcdir)/'`tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsig_unittest.Tpo $(DEPDIR)/run_unittests-tsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsig_unittest.cc' object='run_unittests-tsig_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsig_unittest.o `test -f 'tsig_unittest.cc' || echo '$(srcdir)/'`tsig_unittest.cc + +run_unittests-tsig_unittest.obj: tsig_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsig_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tsig_unittest.Tpo -c -o run_unittests-tsig_unittest.obj `if test -f 'tsig_unittest.cc'; then $(CYGPATH_W) 'tsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsig_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsig_unittest.Tpo $(DEPDIR)/run_unittests-tsig_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsig_unittest.cc' object='run_unittests-tsig_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsig_unittest.obj `if test -f 'tsig_unittest.cc'; then $(CYGPATH_W) 'tsig_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsig_unittest.cc'; fi` + +run_unittests-tsigerror_unittest.o: tsigerror_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigerror_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tsigerror_unittest.Tpo -c -o run_unittests-tsigerror_unittest.o `test -f 'tsigerror_unittest.cc' || echo '$(srcdir)/'`tsigerror_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigerror_unittest.Tpo $(DEPDIR)/run_unittests-tsigerror_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigerror_unittest.cc' object='run_unittests-tsigerror_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigerror_unittest.o `test -f 'tsigerror_unittest.cc' || echo '$(srcdir)/'`tsigerror_unittest.cc + +run_unittests-tsigerror_unittest.obj: tsigerror_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigerror_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tsigerror_unittest.Tpo -c -o run_unittests-tsigerror_unittest.obj `if test -f 'tsigerror_unittest.cc'; then $(CYGPATH_W) 'tsigerror_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigerror_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigerror_unittest.Tpo $(DEPDIR)/run_unittests-tsigerror_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigerror_unittest.cc' object='run_unittests-tsigerror_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigerror_unittest.obj `if test -f 'tsigerror_unittest.cc'; then $(CYGPATH_W) 'tsigerror_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigerror_unittest.cc'; fi` + +run_unittests-tsigkey_unittest.o: tsigkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigkey_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tsigkey_unittest.Tpo -c -o run_unittests-tsigkey_unittest.o `test -f 'tsigkey_unittest.cc' || echo '$(srcdir)/'`tsigkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigkey_unittest.Tpo $(DEPDIR)/run_unittests-tsigkey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigkey_unittest.cc' object='run_unittests-tsigkey_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigkey_unittest.o `test -f 'tsigkey_unittest.cc' || echo '$(srcdir)/'`tsigkey_unittest.cc + +run_unittests-tsigkey_unittest.obj: tsigkey_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigkey_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tsigkey_unittest.Tpo -c -o run_unittests-tsigkey_unittest.obj `if test -f 'tsigkey_unittest.cc'; then $(CYGPATH_W) 'tsigkey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigkey_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigkey_unittest.Tpo $(DEPDIR)/run_unittests-tsigkey_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigkey_unittest.cc' object='run_unittests-tsigkey_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigkey_unittest.obj `if test -f 'tsigkey_unittest.cc'; then $(CYGPATH_W) 'tsigkey_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigkey_unittest.cc'; fi` + +run_unittests-tsigrecord_unittest.o: tsigrecord_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigrecord_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-tsigrecord_unittest.Tpo -c -o run_unittests-tsigrecord_unittest.o `test -f 'tsigrecord_unittest.cc' || echo '$(srcdir)/'`tsigrecord_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigrecord_unittest.Tpo $(DEPDIR)/run_unittests-tsigrecord_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigrecord_unittest.cc' object='run_unittests-tsigrecord_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigrecord_unittest.o `test -f 'tsigrecord_unittest.cc' || echo '$(srcdir)/'`tsigrecord_unittest.cc + +run_unittests-tsigrecord_unittest.obj: tsigrecord_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-tsigrecord_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-tsigrecord_unittest.Tpo -c -o run_unittests-tsigrecord_unittest.obj `if test -f 'tsigrecord_unittest.cc'; then $(CYGPATH_W) 'tsigrecord_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigrecord_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-tsigrecord_unittest.Tpo $(DEPDIR)/run_unittests-tsigrecord_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='tsigrecord_unittest.cc' object='run_unittests-tsigrecord_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-tsigrecord_unittest.obj `if test -f 'tsigrecord_unittest.cc'; then $(CYGPATH_W) 'tsigrecord_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/tsigrecord_unittest.cc'; fi` + +run_unittests-master_loader_callbacks_test.o: master_loader_callbacks_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_loader_callbacks_test.o -MD -MP -MF $(DEPDIR)/run_unittests-master_loader_callbacks_test.Tpo -c -o run_unittests-master_loader_callbacks_test.o `test -f 'master_loader_callbacks_test.cc' || echo '$(srcdir)/'`master_loader_callbacks_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_loader_callbacks_test.Tpo $(DEPDIR)/run_unittests-master_loader_callbacks_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader_callbacks_test.cc' object='run_unittests-master_loader_callbacks_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_loader_callbacks_test.o `test -f 'master_loader_callbacks_test.cc' || echo '$(srcdir)/'`master_loader_callbacks_test.cc + +run_unittests-master_loader_callbacks_test.obj: master_loader_callbacks_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-master_loader_callbacks_test.obj -MD -MP -MF $(DEPDIR)/run_unittests-master_loader_callbacks_test.Tpo -c -o run_unittests-master_loader_callbacks_test.obj `if test -f 'master_loader_callbacks_test.cc'; then $(CYGPATH_W) 'master_loader_callbacks_test.cc'; else $(CYGPATH_W) '$(srcdir)/master_loader_callbacks_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-master_loader_callbacks_test.Tpo $(DEPDIR)/run_unittests-master_loader_callbacks_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='master_loader_callbacks_test.cc' object='run_unittests-master_loader_callbacks_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-master_loader_callbacks_test.obj `if test -f 'master_loader_callbacks_test.cc'; then $(CYGPATH_W) 'master_loader_callbacks_test.cc'; else $(CYGPATH_W) '$(srcdir)/master_loader_callbacks_test.cc'; fi` + +run_unittests-rrset_collection_unittest.o: rrset_collection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrset_collection_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-rrset_collection_unittest.Tpo -c -o run_unittests-rrset_collection_unittest.o `test -f 'rrset_collection_unittest.cc' || echo '$(srcdir)/'`rrset_collection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrset_collection_unittest.Tpo $(DEPDIR)/run_unittests-rrset_collection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset_collection_unittest.cc' object='run_unittests-rrset_collection_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrset_collection_unittest.o `test -f 'rrset_collection_unittest.cc' || echo '$(srcdir)/'`rrset_collection_unittest.cc + +run_unittests-rrset_collection_unittest.obj: rrset_collection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-rrset_collection_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-rrset_collection_unittest.Tpo -c -o run_unittests-rrset_collection_unittest.obj `if test -f 'rrset_collection_unittest.cc'; then $(CYGPATH_W) 'rrset_collection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrset_collection_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-rrset_collection_unittest.Tpo $(DEPDIR)/run_unittests-rrset_collection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='rrset_collection_unittest.cc' object='run_unittests-rrset_collection_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-rrset_collection_unittest.obj `if test -f 'rrset_collection_unittest.cc'; then $(CYGPATH_W) 'rrset_collection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/rrset_collection_unittest.cc'; fi` + +run_unittests-zone_checker_unittest.o: zone_checker_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-zone_checker_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-zone_checker_unittest.Tpo -c -o run_unittests-zone_checker_unittest.o `test -f 'zone_checker_unittest.cc' || echo '$(srcdir)/'`zone_checker_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-zone_checker_unittest.Tpo $(DEPDIR)/run_unittests-zone_checker_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='zone_checker_unittest.cc' object='run_unittests-zone_checker_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-zone_checker_unittest.o `test -f 'zone_checker_unittest.cc' || echo '$(srcdir)/'`zone_checker_unittest.cc + +run_unittests-zone_checker_unittest.obj: zone_checker_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-zone_checker_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-zone_checker_unittest.Tpo -c -o run_unittests-zone_checker_unittest.obj `if test -f 'zone_checker_unittest.cc'; then $(CYGPATH_W) 'zone_checker_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/zone_checker_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-zone_checker_unittest.Tpo $(DEPDIR)/run_unittests-zone_checker_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='zone_checker_unittest.cc' object='run_unittests-zone_checker_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-zone_checker_unittest.obj `if test -f 'zone_checker_unittest.cc'; then $(CYGPATH_W) 'zone_checker_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/zone_checker_unittest.cc'; fi` + +run_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +run_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# 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 + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +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 + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: 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) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/run_unittests-dns_exceptions_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-edns_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-labelsequence_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_state_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_token_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_loader_callbacks_test.Po + -rm -f ./$(DEPDIR)/run_unittests-master_loader_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-masterload_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-message_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-messagerenderer_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-name_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-nsec3hash_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-opcode_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-qid_gen_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-question_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rcode_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_caa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_char_string_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_cname_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dname_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_in_a_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_minfo_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_mx_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_naptr_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ns_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_opt_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ptr_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_rp_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_soa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_srv_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tkey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdatafields_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrclass_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrcollator_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrparamregistry_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrset_collection_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrset_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrttl_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrtype_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/run_unittests-serial_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigerror_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigkey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigrecord_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-unittest_util.Po + -rm -f ./$(DEPDIR)/run_unittests-zone_checker_unittest.Po + -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-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +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)/run_unittests-dns_exceptions_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-edns_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-labelsequence_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_inputsource_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_state_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_token_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_lexer_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-master_loader_callbacks_test.Po + -rm -f ./$(DEPDIR)/run_unittests-master_loader_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-masterload_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-message_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-messagerenderer_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-name_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-nsec3hash_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-opcode_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-qid_gen_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-question_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rcode_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_afsdb_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_caa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_char_string_data_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_char_string_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_cname_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dhcid_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dname_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_dnskey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ds_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_hinfo_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_in_a_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_in_aaaa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_minfo_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_mx_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_naptr_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ns_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3param_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec3param_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsec_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_nsecbitmap_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_opt_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_pimpl_holder_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_ptr_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_rp_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_rrsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_soa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_srv_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_sshfp_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tkey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tlsa_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_tsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_txt_like_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdata_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rdatafields_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrclass_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrcollator_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrparamregistry_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrset_collection_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrset_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrttl_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-rrtype_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/run_unittests-serial_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsig_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigerror_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigkey_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-tsigrecord_unittest.Po + -rm -f ./$(DEPDIR)/run_unittests-unittest_util.Po + -rm -f ./$(DEPDIR)/run_unittests-zone_checker_unittest.Po + -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: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs 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 + +.PRECIOUS: Makefile + + +# 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/tests/dns_exceptions_unittest.cc b/src/lib/dns/tests/dns_exceptions_unittest.cc new file mode 100644 index 0000000..a8d2590 --- /dev/null +++ b/src/lib/dns/tests/dns_exceptions_unittest.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2014-2015,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/. + +#include <config.h> + +#include <dns/exceptions.h> + +#include <gtest/gtest.h> + +namespace { // begin unnamed namespace + +TEST(DNSExceptionsTest, checkExceptionsHierarchy) { + EXPECT_NO_THROW({ + const isc::dns::Exception exception("", 0, ""); + const isc::Exception& exception_cast = + dynamic_cast<const isc::Exception&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSTextError exception("", 0, ""); + const isc::dns::Exception& exception_cast = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::NameParserException exception("", 0, ""); + const isc::dns::DNSTextError& exception_cast = + dynamic_cast<const isc::dns::DNSTextError&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSMessageFORMERR exception("", 0, ""); + const isc::dns::DNSProtocolError& exception_cast = + dynamic_cast<const isc::dns::DNSProtocolError&>(exception); + const isc::dns::Exception& exception_cast2 = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.getRcode(); + exception_cast.what(); + exception_cast2.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSMessageBADVERS exception("", 0, ""); + const isc::dns::DNSProtocolError& exception_cast = + dynamic_cast<const isc::dns::DNSProtocolError&>(exception); + const isc::dns::Exception& exception_cast2 = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.getRcode(); + exception_cast.what(); + exception_cast2.what(); + }); +} + +} // end unnamed namespace diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc new file mode 100644 index 0000000..dfc68cc --- /dev/null +++ b/src/lib/dns/tests/edns_unittest.cc @@ -0,0 +1,258 @@ +// 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 <config.h> + +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rcode.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +const uint8_t EDNS::SUPPORTED_VERSION; + +namespace { +class EDNSTest : public ::testing::Test { +protected: + EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0), rcode(0) { + opt_rdata = ConstRdataPtr(new generic::OPT()); + edns_base.setUDPSize(4096); + } + RRType rrtype; + EDNS edns_base; + ConstEDNSPtr edns; + InputBuffer buffer; + OutputBuffer obuffer; + MessageRenderer renderer; + ConstRdataPtr opt_rdata; + Rcode rcode; + vector<unsigned char> wiredata; +}; + +// RRClass of EDNS OPT means UDP buffer size +const RRClass rrclass(4096); +// RRTTL of EDNS OPT encodes extended-rcode, version, and DO bit +const RRTTL rrttl_do_on(0x00008000); // DO=on +const RRTTL rrttl_do_off(0); // DO=off +const RRTTL rrttl_badver(0x00018000); // version=1, DO=on + +TEST_F(EDNSTest, badVerConstruct) { + EXPECT_THROW(EDNS(1), isc::InvalidParameter); +} + +TEST_F(EDNSTest, DNSSECDOBit) { + // tests for EDNS from RR + + // DO bit is on, so DNSSEC should be considered to be supported. + EXPECT_TRUE(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_on, *opt_rdata).getDNSSECAwareness()); + + // DO bit is off. DNSSEC should be considered to be unsupported. + EXPECT_FALSE(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_off, *opt_rdata).getDNSSECAwareness()); + + // tests for EDNS constructed by hand + EXPECT_FALSE(edns_base.getDNSSECAwareness()); // false by default + edns_base.setDNSSECAwareness(true); // enable by hand + EXPECT_TRUE(edns_base.getDNSSECAwareness()); + edns_base.setDNSSECAwareness(false); // disable by hand + EXPECT_FALSE(edns_base.getDNSSECAwareness()); +} + +TEST_F(EDNSTest, UDPSize) { + EXPECT_EQ(4096, EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).getUDPSize()); + + // EDNS UDP size smaller than the traditional max, 512. Unusual, but + // not prohibited. + edns_base.setUDPSize(511); + EXPECT_EQ(511, edns_base.getUDPSize()); + + // Even 0 is okay. + edns_base.setUDPSize(0); + EXPECT_EQ(0, edns_base.getUDPSize()); + + // Possible max value is also okay, although the higher layer app may + // adjust it to a reasonably lower value + edns_base.setUDPSize(65535); + EXPECT_EQ(65535, edns_base.getUDPSize()); +} + +TEST_F(EDNSTest, getVersion) { + // Constructed by hand + EXPECT_EQ(EDNS::SUPPORTED_VERSION, EDNS().getVersion()); + + // From RR + EXPECT_EQ(EDNS::SUPPORTED_VERSION, + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).getVersion()); +} + +TEST_F(EDNSTest, BadWireData) { + // Incompatible RR type + EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, RRType::A(), + rrttl_do_on, *opt_rdata), isc::InvalidParameter); + + // OPT RR of a non root name + EXPECT_THROW(EDNS(Name("example.com"), rrclass, rrtype, + rrttl_do_on, *opt_rdata), DNSMessageFORMERR); + + // Unsupported Version + EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_badver, *opt_rdata), DNSMessageBADVERS); +} + +TEST_F(EDNSTest, toText) { + // Typical case, disabling DNSSEC + EXPECT_EQ("; EDNS: version: 0, flags:; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_off, + *opt_rdata).toText()); + + // Typical case, enabling DNSSEC + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).toText()); + + // Non-0 extended Rcode: ignored in the toText() output. + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x01008000), *opt_rdata).toText()); + + // Unknown flag: ignored in the toText() output. + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x00008001), *opt_rdata).toText()); +} + +TEST_F(EDNSTest, toWireRenderer) { + // Typical case, (explicitly) disabling DNSSEC + edns_base.setDNSSECAwareness(false); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire1.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Typical case, enabling DNSSEC + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire2.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Non-0 extended Rcode + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::BADVERS().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire3.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Uncommon UDP buffer size + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + edns_base.setUDPSize(511); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire4.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // From RR with unknown flag. If used for toWire(), the unknown flag + // should disappear. + renderer.clear(); + wiredata.clear(); + EXPECT_EQ(1, EDNS(Name::ROOT_NAME(), rrclass, rrtype, RRTTL(0x00008001), + *opt_rdata).toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire2.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // If the available length in the renderer is not sufficient for the OPT + // RR, it shouldn't be inserted. + renderer.clear(); + renderer.setLengthLimit(10); // 10 = minimum length of OPT RR - 1 + edns_base.setDNSSECAwareness(true); + edns_base.setUDPSize(4096); + EXPECT_EQ(0, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + // renderer should be intact + EXPECT_EQ(0, renderer.getLength()); +} + +TEST_F(EDNSTest, toWireBuffer) { + // "to renderer" and "to buffer" should generally produce the same result. + // for simplicity we only check one typical case to confirm that. + EXPECT_EQ(1, edns_base.toWire(obuffer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire1.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(EDNSTest, createFromRR) { + uint8_t extended_rcode; + + // normal case + edns = ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_on, *opt_rdata, + extended_rcode)); + EXPECT_EQ(EDNS::SUPPORTED_VERSION, edns->getVersion()); + EXPECT_TRUE(edns->getDNSSECAwareness()); + EXPECT_EQ(4096, edns->getUDPSize()); + EXPECT_EQ(0, static_cast<int>(extended_rcode)); + + // non-0 extended rcode + extended_rcode = 0; + ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x01008000), *opt_rdata, + extended_rcode)); + EXPECT_EQ(1, static_cast<int>(extended_rcode)); + + // creation triggers an exception. extended_rcode must be intact. + extended_rcode = 0; + EXPECT_THROW(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_badver, *opt_rdata, extended_rcode), + DNSMessageBADVERS); + EXPECT_EQ(0, static_cast<int>(extended_rcode)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(EDNSTest, LeftShiftOperator) { + ostringstream oss; + oss << edns_base; + EXPECT_EQ(edns_base.toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc new file mode 100644 index 0000000..15650fa --- /dev/null +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -0,0 +1,1243 @@ +// 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 <config.h> + +#include <util/buffer.h> + +#include <dns/labelsequence.h> +#include <dns/name.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <boost/functional/hash.hpp> + +#include <string> +#include <vector> +#include <utility> +#include <set> + +using namespace isc::dns; +using namespace std; + +// XXX: this is defined as class static constants, but some compilers +// seemingly cannot find the symbols when used in the EXPECT_xxx macros. +const size_t LabelSequence::MAX_SERIALIZED_LENGTH; + +namespace { + +// Common check that two labelsequences are equal +void check_equal(const LabelSequence& ls1, const LabelSequence& ls2) { + NameComparisonResult result = ls1.compare(ls2); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()) << ls1.toText() << " != " << ls2.toText(); + EXPECT_EQ(0, result.getOrder()) << ls1.toText() << " != " << ls2.toText(); + EXPECT_EQ(ls1.getLabelCount(), result.getCommonLabels()); +} + +// Common check for general comparison of two labelsequences +void check_compare(const LabelSequence& ls1, const LabelSequence& ls2, + isc::dns::NameComparisonResult::NameRelation relation, + size_t common_labels, bool check_order, int order=0) { + NameComparisonResult result = ls1.compare(ls2); + EXPECT_EQ(relation, result.getRelation()); + EXPECT_EQ(common_labels, result.getCommonLabels()); + if (check_order) { + EXPECT_EQ(order, result.getOrder()); + } +} + +class LabelSequenceTest : public ::testing::Test { +public: + LabelSequenceTest() : n1("example.org"), n2("example.com"), + n3("example.org"), n4("foo.bar.test.example"), + n5("example.ORG"), n6("ExAmPlE.org"), + n7("."), n8("foo.example.org.bar"), + n9("\\000xample.org"), + n10("\\000xample.org"), + n11("\\000xample.com"), + n12("\\000xamplE.com"), + n_maxlabel("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6"), + ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5), + ls6(n6), ls7(n7), ls8(n8), + ls9(n9), ls10(n10), ls11(n11), ls12(n12) + {}; + // Need to keep names in scope for at least the lifetime of + // the labelsequences + Name n1, n2, n3, n4, n5, n6, n7, n8; + Name n9, n10, n11, n12; + const Name n_maxlabel; + + LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8; + LabelSequence ls9, ls10, ls11, ls12; +}; + +// Check the assignment operator. +TEST_F(LabelSequenceTest, assign) { + // Create the label sequence with example.org (n1 name). + LabelSequence ls(n1); + EXPECT_TRUE(ls == ls1); + + // Assign the label sequence to example.com (n2 name). + ls = ls2; + EXPECT_FALSE(ls == ls1); + EXPECT_TRUE(ls == ls2); +} + +// Basic equality tests +TEST_F(LabelSequenceTest, equals_sensitive) { + EXPECT_TRUE(ls1.equals(ls1, true)); + EXPECT_FALSE(ls1.equals(ls2, true)); + EXPECT_TRUE(ls1.equals(ls3, true)); + EXPECT_FALSE(ls1.equals(ls4, true)); + EXPECT_FALSE(ls1.equals(ls5, true)); + EXPECT_FALSE(ls1.equals(ls6, true)); + EXPECT_FALSE(ls1.equals(ls7, true)); + EXPECT_FALSE(ls1.equals(ls8, true)); + + EXPECT_FALSE(ls2.equals(ls1, true)); + EXPECT_TRUE(ls2.equals(ls2, true)); + EXPECT_FALSE(ls2.equals(ls3, true)); + EXPECT_FALSE(ls2.equals(ls4, true)); + EXPECT_FALSE(ls2.equals(ls5, true)); + EXPECT_FALSE(ls2.equals(ls6, true)); + EXPECT_FALSE(ls2.equals(ls7, true)); + EXPECT_FALSE(ls2.equals(ls8, true)); + + EXPECT_FALSE(ls4.equals(ls1, true)); + EXPECT_FALSE(ls4.equals(ls2, true)); + EXPECT_FALSE(ls4.equals(ls3, true)); + EXPECT_TRUE(ls4.equals(ls4, true)); + EXPECT_FALSE(ls4.equals(ls5, true)); + EXPECT_FALSE(ls4.equals(ls6, true)); + EXPECT_FALSE(ls4.equals(ls7, true)); + EXPECT_FALSE(ls4.equals(ls8, true)); + + EXPECT_FALSE(ls5.equals(ls1, true)); + EXPECT_FALSE(ls5.equals(ls2, true)); + EXPECT_FALSE(ls5.equals(ls3, true)); + EXPECT_FALSE(ls5.equals(ls4, true)); + EXPECT_TRUE(ls5.equals(ls5, true)); + EXPECT_FALSE(ls5.equals(ls6, true)); + EXPECT_FALSE(ls5.equals(ls7, true)); + EXPECT_FALSE(ls5.equals(ls8, true)); + + EXPECT_TRUE(ls9.equals(ls10, true)); + EXPECT_FALSE(ls9.equals(ls11, true)); + EXPECT_FALSE(ls9.equals(ls12, true)); + EXPECT_FALSE(ls11.equals(ls12, true)); +} + +TEST_F(LabelSequenceTest, equals_insensitive) { + EXPECT_TRUE(ls1.equals(ls1)); + EXPECT_FALSE(ls1.equals(ls2)); + EXPECT_TRUE(ls1.equals(ls3)); + EXPECT_FALSE(ls1.equals(ls4)); + EXPECT_TRUE(ls1.equals(ls5)); + EXPECT_TRUE(ls1.equals(ls6)); + EXPECT_FALSE(ls1.equals(ls7)); + + EXPECT_FALSE(ls2.equals(ls1)); + EXPECT_TRUE(ls2.equals(ls2)); + EXPECT_FALSE(ls2.equals(ls3)); + EXPECT_FALSE(ls2.equals(ls4)); + EXPECT_FALSE(ls2.equals(ls5)); + EXPECT_FALSE(ls2.equals(ls6)); + EXPECT_FALSE(ls2.equals(ls7)); + + EXPECT_TRUE(ls3.equals(ls1)); + EXPECT_FALSE(ls3.equals(ls2)); + EXPECT_TRUE(ls3.equals(ls3)); + EXPECT_FALSE(ls3.equals(ls4)); + EXPECT_TRUE(ls3.equals(ls5)); + EXPECT_TRUE(ls3.equals(ls6)); + EXPECT_FALSE(ls3.equals(ls7)); + + EXPECT_FALSE(ls4.equals(ls1)); + EXPECT_FALSE(ls4.equals(ls2)); + EXPECT_FALSE(ls4.equals(ls3)); + EXPECT_TRUE(ls4.equals(ls4)); + EXPECT_FALSE(ls4.equals(ls5)); + EXPECT_FALSE(ls4.equals(ls6)); + EXPECT_FALSE(ls4.equals(ls7)); + + EXPECT_TRUE(ls5.equals(ls1)); + EXPECT_FALSE(ls5.equals(ls2)); + EXPECT_TRUE(ls5.equals(ls3)); + EXPECT_FALSE(ls5.equals(ls4)); + EXPECT_TRUE(ls5.equals(ls5)); + EXPECT_TRUE(ls5.equals(ls6)); + EXPECT_FALSE(ls5.equals(ls7)); + + EXPECT_TRUE(ls9.equals(ls10)); + EXPECT_FALSE(ls9.equals(ls11)); + EXPECT_FALSE(ls9.equals(ls12)); + EXPECT_TRUE(ls11.equals(ls12)); +} + +// operator==(). This is mostly trivial wrapper, so it should suffice to +// check some basic cases. +TEST_F(LabelSequenceTest, operatorEqual) { + // cppcheck-suppress duplicateExpression + EXPECT_TRUE(ls1 == ls1); // self equivalence + EXPECT_TRUE(ls1 == LabelSequence(n1)); // equivalent two different objects + EXPECT_FALSE(ls1 == ls2); // non equivalent objects + EXPECT_TRUE(ls1 == ls5); // it's always case insensitive +} + +// Compare tests +TEST_F(LabelSequenceTest, compare) { + // "example.org." and "example.org.", case sensitive + NameComparisonResult result = ls1.compare(ls3, true); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "example.ORG.", case sensitive + result = ls3.compare(ls5, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + // "example.org." and "example.ORG.", case in-sensitive + result = ls3.compare(ls5); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + Name na("a.example.org"); + Name nb("b.example.org"); + LabelSequence lsa(na); + LabelSequence lsb(nb); + + // "a.example.org." and "b.example.org.", case in-sensitive + result = lsa.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "b.example.org.", case in-sensitive + lsa.stripLeft(1); + result = lsa.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + Name nc("g.f.e.d.c.example.org"); + LabelSequence lsc(nc); + + // "g.f.e.d.c.example.org." and "b.example.org" (not absolute), case + // in-sensitive; the absolute one is always smaller. + lsb.stripRight(1); + result = lsc.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // "g.f.e.d.c.example.org." and "example.org.", case in-sensitive + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "e.d.c.example.org." and "example.org.", case in-sensitive + lsc.stripLeft(2); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "example.org.", case in-sensitive + lsc.stripLeft(3); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "." and "example.org.", case in-sensitive + lsc.stripLeft(2); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + Name nd("a.b.c.isc.example.org"); + LabelSequence lsd(nd); + Name ne("w.x.y.isc.EXAMPLE.org"); + LabelSequence lse(ne); + + // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.", + // case sensitive + result = lsd.compare(lse, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.", + // case in-sensitive + result = lsd.compare(lse); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(4, result.getCommonLabels()); + + // "isc.example.org." and "isc.EXAMPLE.org.", case sensitive + lsd.stripLeft(3); + lse.stripLeft(3); + result = lsd.compare(lse, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "isc.example.org." and "isc.EXAMPLE.org.", case in-sensitive + result = lsd.compare(lse); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(4, result.getCommonLabels()); + + Name nf("a.b.c.isc.example.org"); + LabelSequence lsf(nf); + Name ng("w.x.y.isc.EXAMPLE.org"); + LabelSequence lsg(ng); + + // lsf: "a.b.c.isc.example.org." + // lsg: "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive. + // the absolute one is always smaller. + lsg.stripRight(1); + result = lsg.compare(lsf); // lsg > lsf + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // "a.b.c.isc.example.org" (not absolute) and + // "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive + lsf.stripRight(1); + result = lsg.compare(lsf); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "a.b.c.isc.example" (not absolute) and + // "w.x.y.isc.EXAMPLE" (not absolute), case in-sensitive + lsf.stripRight(1); + lsg.stripRight(1); + result = lsg.compare(lsf); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // lsf: "a.b.c" (not absolute) and + // lsg: "w.x.y" (not absolute), case in-sensitive; a.b.c < w.x.y; + // no common labels. + lsf.stripRight(2); + lsg.stripRight(2); + result = lsf.compare(lsg); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // lsf2: a.b.cc (not absolute); a.b.c < a.b.cc, no common labels. + const Name nf2("a.b.cc"); + LabelSequence lsf2(nf2); + lsf2.stripRight(1); + result = lsf.compare(lsf2); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + Name nh("aexample.org"); + LabelSequence lsh(nh); + Name ni("bexample.org"); + LabelSequence lsi(ni); + + // "aexample.org" (not absolute) and + // "bexample.org" (not absolute), case in-sensitive + lsh.stripRight(1); + lsi.stripRight(1); + result = lsh.compare(lsi); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + // "aexample" (not absolute) and + // "bexample" (not absolute), case in-sensitive; + // aexample < bexample; no common labels. + lsh.stripRight(1); + lsi.stripRight(1); + result = lsh.compare(lsi); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + Name nj("example.org"); + LabelSequence lsj(nj); + Name nk("example.org"); + LabelSequence lsk(nk); + + // "example.org" (not absolute) and + // "example.org" (not absolute), case in-sensitive + lsj.stripRight(1); + lsk.stripRight(1); + result = lsj.compare(lsk); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "example" (not absolute) and + // "example" (not absolute), case in-sensitive + lsj.stripRight(1); + lsk.stripRight(1); + result = lsj.compare(lsk); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); +} + +void +getDataCheck(const uint8_t* expected_data, size_t expected_len, + const LabelSequence& ls) +{ + size_t len; + const uint8_t* data = ls.getData(&len); + ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data << + ", label sequence: " << ls; + EXPECT_EQ(expected_len, ls.getDataLength()) << + "Expected data: " << expected_data << + ", label sequence: " << ls; + for (size_t i = 0; i < len; ++i) { + EXPECT_EQ(expected_data[i], data[i]) << + "Difference at pos " << i << ": Expected data: " << expected_data << + ", label sequence: " << ls; + } +} + +// Convenient data converter for expected data. Label data must be of +// uint8_t*, while it's convenient if we can specify some test data in +// plain string (which is of char*). This wrapper converts the latter to +// the former in a safer way. +void +getDataCheck(const char* expected_char_data, size_t expected_len, + const LabelSequence& ls) +{ + const vector<uint8_t> expected_data(expected_char_data, + expected_char_data + expected_len); + getDataCheck(&expected_data[0], expected_len, ls); +} + +TEST_F(LabelSequenceTest, getData) { + getDataCheck("\007example\003org\000", 13, ls1); + getDataCheck("\007example\003com\000", 13, ls2); + getDataCheck("\007example\003org\000", 13, ls3); + getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4); + getDataCheck("\007example\003ORG\000", 13, ls5); + getDataCheck("\007ExAmPlE\003org\000", 13, ls6); + getDataCheck("\000", 1, ls7); +}; + +TEST_F(LabelSequenceTest, stripLeft) { + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripLeft(0); + getDataCheck("\007example\003org\000", 13, ls1); + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripLeft(1); + getDataCheck("\003org\000", 5, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + ls1.stripLeft(1); + getDataCheck("\000", 1, ls1); + EXPECT_TRUE(ls1.equals(ls7)); + + ls2.stripLeft(2); + getDataCheck("\000", 1, ls2); + EXPECT_TRUE(ls2.equals(ls7)); +} + +TEST_F(LabelSequenceTest, stripRight) { + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripRight(1); + getDataCheck("\007example\003org", 12, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + ls1.stripRight(1); + getDataCheck("\007example", 8, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + + ASSERT_FALSE(ls1.equals(ls2)); + ls2.stripRight(2); + getDataCheck("\007example", 8, ls2); + EXPECT_TRUE(ls1.equals(ls2)); +} + +TEST_F(LabelSequenceTest, stripOutOfRange) { + EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange); + getDataCheck("\007example\003org\000", 13, ls1); + + EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange); + getDataCheck("\007example\003org\000", 13, ls1); +} + +TEST_F(LabelSequenceTest, getLabelCount) { + EXPECT_EQ(3, ls1.getLabelCount()); + ls1.stripLeft(0); + EXPECT_EQ(3, ls1.getLabelCount()); + ls1.stripLeft(1); + EXPECT_EQ(2, ls1.getLabelCount()); + ls1.stripLeft(1); + EXPECT_EQ(1, ls1.getLabelCount()); + + EXPECT_EQ(3, ls2.getLabelCount()); + ls2.stripRight(1); + EXPECT_EQ(2, ls2.getLabelCount()); + ls2.stripRight(1); + EXPECT_EQ(1, ls2.getLabelCount()); + + EXPECT_EQ(3, ls3.getLabelCount()); + ls3.stripRight(2); + EXPECT_EQ(1, ls3.getLabelCount()); + + EXPECT_EQ(5, ls4.getLabelCount()); + ls4.stripRight(3); + EXPECT_EQ(2, ls4.getLabelCount()); + + EXPECT_EQ(3, ls5.getLabelCount()); + ls5.stripLeft(2); + EXPECT_EQ(1, ls5.getLabelCount()); +} + +TEST_F(LabelSequenceTest, comparePart) { + EXPECT_FALSE(ls1.equals(ls8)); + + // strip root label from example.org. + ls1.stripRight(1); + // strip foo from foo.example.org.bar. + ls8.stripLeft(1); + // strip bar. (i.e. bar and root) too + ls8.stripRight(2); + + EXPECT_TRUE(ls1.equals(ls8)); + + // Data comparison + size_t len; + const uint8_t* data = ls1.getData(&len); + getDataCheck(data, len, ls8); +} + +TEST_F(LabelSequenceTest, isAbsolute) { + ASSERT_TRUE(ls1.isAbsolute()); + + ls1.stripLeft(1); + ASSERT_TRUE(ls1.isAbsolute()); + ls1.stripRight(1); + ASSERT_FALSE(ls1.isAbsolute()); + + ASSERT_TRUE(ls2.isAbsolute()); + ls2.stripRight(1); + ASSERT_FALSE(ls2.isAbsolute()); + + ASSERT_TRUE(ls3.isAbsolute()); + ls3.stripLeft(2); + ASSERT_TRUE(ls3.isAbsolute()); +} + +TEST_F(LabelSequenceTest, toText) { + EXPECT_EQ(".", ls7.toText()); + + EXPECT_EQ("example.org.", ls1.toText()); + ls1.stripLeft(1); + EXPECT_EQ("org.", ls1.toText()); + ls1.stripLeft(1); + EXPECT_EQ(".", ls1.toText()); + + EXPECT_EQ("example.com.", ls2.toText()); + ls2.stripRight(1); + EXPECT_EQ("example.com", ls2.toText()); + ls2.stripRight(1); + EXPECT_EQ("example", ls2.toText()); + + EXPECT_EQ("foo.example.org.bar.", ls8.toText()); + ls8.stripRight(2); + EXPECT_EQ("foo.example.org", ls8.toText()); + + EXPECT_EQ(".", ls7.toText()); + EXPECT_THROW(ls7.stripLeft(1), isc::OutOfRange); + + Name n_long1("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890"); + LabelSequence ls_long1(n_long1); + + EXPECT_EQ("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890.", ls_long1.toText()); + ls_long1.stripRight(1); + EXPECT_EQ("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890", ls_long1.toText()); + + LabelSequence ls_long2(n_maxlabel); + + EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.", ls_long2.toText()); + ls_long2.stripRight(1); + EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6", ls_long2.toText()); + ls_long2.stripRight(125); + EXPECT_EQ("0.1", ls_long2.toText()); +} + +// The following verifies that toRawText() returns a string +// actual characters in place of escape sequences. We do not +// bother with an exhaustive set of tests here as this is +// not a primary use case. +TEST_F(LabelSequenceTest, toRawText) { + Name n("a bc.$exa(m)ple.@org"); + LabelSequence l(n); + EXPECT_EQ("a bc.$exa(m)ple.@org", l.toRawText(true)); + EXPECT_EQ("a bc.$exa(m)ple.@org.", l.toRawText(false)); + + // toRawText is not supposed to do any sanity checks. + // Let's try with a very weird name. + Name n2("xtra\tchars\n.in.name"); + LabelSequence l2(n2); + EXPECT_EQ("xtra\tchars\n.in.name.", l2.toRawText(false)); +} + +// The following are test data used in the getHash test below. Normally +// we use example/documentation domain names for testing, but in this case +// we'd specifically like to use more realistic data, and are intentionally +// using real-world samples: They are the NS names of root and some top level +// domains as of this test. +const char* const root_servers[] = { + "a.root-servers.net", "b.root-servers.net", "c.root-servers.net", + "d.root-servers.net", "e.root-servers.net", "f.root-servers.net", + "g.root-servers.net", "h.root-servers.net", "i.root-servers.net", + "j.root-servers.net", "k.root-servers.net", "l.root-servers.net", + "m.root-servers.net", NULL +}; + +const char* const jp_servers[] = { + "a.dns.jp", "b.dns.jp", "c.dns.jp", "d.dns.jp", "e.dns.jp", + "f.dns.jp", "g.dns.jp", NULL +}; +const char* const cn_servers[] = { + "a.dns.cn", "b.dns.cn", "c.dns.cn", "d.dns.cn", "e.dns.cn", + "ns.cernet.net", NULL +}; +const char* const ca_servers[] = { + "k.ca-servers.ca", "e.ca-servers.ca", "a.ca-servers.ca", "z.ca-servers.ca", + "tld.isc-sns.net", "c.ca-servers.ca", "j.ca-servers.ca", "l.ca-servers.ca", + "sns-pb.isc.org", "f.ca-servers.ca", NULL +}; + +// A helper function used in the getHash test below. +void +hashDistributionCheck(const char* const* servers) { + const size_t BUCKETS = 64; // constant used in the MessageRenderer + set<Name> names; + vector<size_t> hash_counts(BUCKETS); + + // Store all test names and their super domain names (excluding the + // "root" label) in the set, calculates their hash values, and increments + // the counter for the corresponding hash "bucket". + for (size_t i = 0; servers[i] != NULL; ++i) { + const Name name(servers[i]); + for (size_t l = 0; l < name.getLabelCount() - 1; ++l) { + pair<set<Name>::const_iterator, bool> ret = + names.insert(name.split(l)); + if (ret.second) { + hash_counts[LabelSequence((*ret.first)).getHash(false) % + BUCKETS]++; + } + } + } + + // See how many conflicts we have in the buckets. For the testing purpose + // we expect there's at most 2 conflicts in each set, which is an + // arbitrary choice (it should happen to succeed with the hash function + // and data we are using; if it's not the case, maybe with an update to + // the hash implementation, we should revise the test). + for (size_t i = 0; i < BUCKETS; ++i) { + EXPECT_GE(3, hash_counts[i]); + } +} + +TEST_F(LabelSequenceTest, getHash) { + // Trivial case. The same sequence should have the same hash. + EXPECT_EQ(ls1.getHash(true), ls1.getHash(true)); + + // Check the case-insensitive mode behavior. + EXPECT_EQ(ls1.getHash(false), ls5.getHash(false)); + + // Check that the distribution of hash values is "not too bad" (such as + // everything has the same hash value due to a stupid bug). It's + // difficult to check such things reliably. We do some ad hoc tests here. + hashDistributionCheck(root_servers); + hashDistributionCheck(jp_servers); + hashDistributionCheck(cn_servers); + hashDistributionCheck(ca_servers); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(LabelSequenceTest, LeftShiftOperator) { + ostringstream oss; + oss << ls1; + EXPECT_EQ(ls1.toText(), oss.str()); +} + +TEST_F(LabelSequenceTest, serialize) { + // placeholder for serialized data. We use a sufficiently large space + // for testing the overwrapping cases below. + uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH * 3]; + + // vector to store expected and actual data + vector<LabelSequence> actual_labelseqs; + typedef pair<size_t, const uint8_t*> DataPair; + vector<DataPair> expected; + + // An absolute sequence directly constructed from a valid name. + // labels = 3, offset sequence = 0, 8, 12, data = "example.com." + actual_labelseqs.push_back(ls1); + const uint8_t expected_data1[] = { + 3, 0, 8, 12, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 3, 'o', 'r', 'g', 0 }; + expected.push_back(DataPair(sizeof(expected_data1), expected_data1)); + + // Strip the original one from right. + // labels = 2, offset sequence = 0, 8, data = "example.com" (non absolute) + LabelSequence ls_rstripped = ls1; + ls_rstripped.stripRight(1); + actual_labelseqs.push_back(ls_rstripped); + const uint8_t expected_data2[] = { + 2, 0, 8, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 3, 'o', 'r', 'g'}; + expected.push_back(DataPair(sizeof(expected_data2), expected_data2)); + + // Strip the original one from left. + // labels = 2, offset sequence = 0, 4, data = "com." + // Note that offsets are adjusted so that they begin with 0. + LabelSequence ls_lstripped = ls1; + ls_lstripped.stripLeft(1); + actual_labelseqs.push_back(ls_lstripped); + const uint8_t expected_data3[] = { 2, 0, 4, 3, 'o', 'r', 'g', 0 }; + expected.push_back(DataPair(sizeof(expected_data3), expected_data3)); + + // Root label. + LabelSequence ls_root(Name::ROOT_NAME()); + actual_labelseqs.push_back(ls_root); + const uint8_t expected_data4[] = { 1, 0, 0 }; + expected.push_back(DataPair(sizeof(expected_data4), expected_data4)); + + // Non absolute single-label. + LabelSequence ls_single = ls_rstripped; + ls_single.stripRight(1); + actual_labelseqs.push_back(ls_single); + const uint8_t expected_data5[] = { + 1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' }; + expected.push_back(DataPair(sizeof(expected_data5), expected_data5)); + + // Labels containing a longest possible label + const Name name_longlabel(std::string(63, 'x')); // 63 'x's + LabelSequence ls_longlabel(name_longlabel); + actual_labelseqs.push_back(ls_longlabel); + vector<uint8_t> expected_data6; + expected_data6.push_back(2); // 2 labels + expected_data6.push_back(0); // 1st offset + expected_data6.push_back(64); // 2nd offset + expected_data6.push_back(63); // 1st label length + expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's + expected_data6.push_back(0); // 2nd label: trailing 0 + expected.push_back(DataPair(expected_data6.size(), &expected_data6[0])); + + // Max number of labels and longest possible name + EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength()); + LabelSequence ls_maxlabel(n_maxlabel); + actual_labelseqs.push_back(ls_maxlabel); + vector<uint8_t> expected_data7; + expected_data7.push_back(Name::MAX_LABELS); // number of labels + for (size_t i = 0; i < Name::MAX_LABELS; ++i) { + expected_data7.push_back(i * 2); // each label has length and 1 byte + } + // Copy wire data of the name + isc::util::OutputBuffer ob(0); + n_maxlabel.toWire(ob); + expected_data7.insert(expected_data7.end(), + static_cast<const uint8_t*>(ob.getData()), + static_cast<const uint8_t*>(ob.getData()) + + ob.getLength()); + expected.push_back(DataPair(expected_data7.size(), &expected_data7[0])); + + // For each data set, serialize the labels and compare the data to the + // expected one. + vector<DataPair>::const_iterator it = expected.begin(); + vector<LabelSequence>::const_iterator itl = actual_labelseqs.begin(); + for (; it != expected.end(); ++it, ++itl) { + SCOPED_TRACE(itl->toText()); + + const size_t serialized_len = itl->getSerializedLength(); + + ASSERT_GE(LabelSequence::MAX_SERIALIZED_LENGTH, serialized_len); + itl->serialize(labels_buf, serialized_len); + EXPECT_EQ(it->first, serialized_len); + EXPECT_EQ(0, memcmp(it->second, labels_buf, serialized_len)); + + EXPECT_EQ(NameComparisonResult::EQUAL, + LabelSequence(labels_buf).compare(*itl).getRelation()); + + // Shift the data to the middle of the buffer for overwrap check + uint8_t* const bp = labels_buf; + std::memcpy(bp + serialized_len, bp, serialized_len); + // Memory layout is now as follows: + // <- ser_len -> <- ser_len ------> + // bp bp+ser_len bp+(ser_len*2) + // olen,odata,ndata + + // end of buffer would be the first byte of offsets: invalid. + EXPECT_THROW(LabelSequence(bp + serialized_len). + serialize(bp + 2, serialized_len), + isc::BadValue); + // begin of buffer would be the last byte of ndata: invalid. + EXPECT_THROW(LabelSequence(bp + serialized_len). + serialize(bp + (2 * serialized_len) - 1, serialized_len), + isc::BadValue); + // A boundary safe case: buffer is placed after the sequence data. + // should cause no disruption. + LabelSequence(bp + serialized_len). + serialize(bp + 2 * serialized_len, serialized_len); + // A boundary safe case: buffer is placed before the sequence data + // should cause no disruption. (but the original serialized data will + // be overridden, so it can't be used any more) + LabelSequence(bp + serialized_len). + serialize(bp + 1, serialized_len); + } + + EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1), + isc::BadValue); +} + +#ifdef ENABLE_DEBUG + +// These checks are enabled only in debug mode in the LabelSequence +// class. +TEST_F(LabelSequenceTest, badDeserialize) { + EXPECT_THROW(LabelSequence(NULL), isc::BadValue); + const uint8_t zero_offsets[] = { 0 }; + EXPECT_THROW(LabelSequence ls(zero_offsets), isc::BadValue); + const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 }; + EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue); + + // (second) offset does not match actual label length + const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 }; + EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue); + + // offset matches, but exceeds MAX_LABEL_LEN + const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 }; + EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue); + + // Inconsistent data: an offset is lower than the previous offset + const uint8_t offsets_lower[] = { 3, // # of offsets + 0, 2, 1, // offsets + 1, 'a', 1, 'b', 0}; + EXPECT_THROW(LabelSequence ls(offsets_lower), isc::BadValue); + + // Inconsistent data: an offset is equal to the previous offset + const uint8_t offsets_noincrease[] = { 2, 0, 0, 0, 0 }; + EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue); +} + +#endif + +namespace { + +// Helper function; repeatedly calls +// - Initially, all three labelsequences should be the same +// - repeatedly performs: +// - checks all three are equal +// - stripLeft on ls1 +// - checks ls1 and ls2 are different, and ls2 and ls3 are equal +// - stripLeft on ls2 +// - checks ls1 and ls2 are equal, and ls2 and ls3 are different +// - stripLeft on ls3 +// +// (this test makes sure the stripLeft of one has no effect on the other +// two, and that the strip properties hold regardless of how they were +// constructed) +// +void stripLeftCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) { + ASSERT_LT(1, ls1.getLabelCount()); + while (ls1.getLabelCount() > 1) { + check_equal(ls1, ls2); + check_equal(ls2, ls3); + + ls1.stripLeft(1); + check_compare(ls1, ls2, isc::dns::NameComparisonResult::SUPERDOMAIN, + ls1.getLabelCount(), true, -1); + check_equal(ls2, ls3); + + ls2.stripLeft(1); + check_equal(ls1, ls2); + check_compare(ls2, ls3, isc::dns::NameComparisonResult::SUPERDOMAIN, + ls1.getLabelCount(), true, -1); + + ls3.stripLeft(1); + } +} + +// Similar to stripLeftCheck, but using stripRight() +void stripRightCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) { + ASSERT_LT(1, ls1.getLabelCount()); + while (ls1.getLabelCount() > 1) { + check_equal(ls1, ls2); + check_equal(ls2, ls3); + + ls1.stripRight(1); + check_compare(ls1, ls2, isc::dns::NameComparisonResult::NONE, 0, + false); + check_equal(ls2, ls3); + + ls2.stripRight(1); + check_equal(ls1, ls2); + check_compare(ls2, ls3, isc::dns::NameComparisonResult::NONE, 0, + false); + + ls3.stripRight(1); + } +} + +} // end anonymous namespace + +class ExtendableLabelSequenceTest : public ::testing::Test { +public: + ExtendableLabelSequenceTest() : bar("bar."), + example_org("example.org"), + foo("foo."), + foo_bar("foo.bar."), + foo_bar_example_org("foo.bar.example.org."), + foo_bar_foo_bar("foo.bar.foo.bar."), + foo_example("foo.example."), + org("org") + { + // explicitly set to non-zero data, to make sure + // we don't try to use data we don't set + memset(buf, 0xff, LabelSequence::MAX_SERIALIZED_LENGTH); + } + + Name bar; + Name example_org; + Name foo; + Name foo_bar; + Name foo_bar_example_org; + Name foo_bar_foo_bar; + Name foo_example; + Name org; + + uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH]; +}; + +// Test that 'extendable' labelsequences behave correctly when using +// stripLeft() and stripRight() +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequence) { + LabelSequence ls1(example_org); + LabelSequence ls2(example_org); + + LabelSequence els(ls1, buf); + // ls1 is absolute, so els should be too + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + + // Creating an extendable labelsequence from a non-absolute + // label sequence should result in a non-absolute label sequence + ls1.stripRight(1); + els = LabelSequence(ls1, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls1, els); + + // and extending with the root label should make it absolute again + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Test that 'extendable' LabelSequences behave correctly when initialized +// with a stripped source LabelSequence +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceLeftStrippedSource) { + LabelSequence ls1(foo_bar_example_org); + LabelSequence ls2(foo_bar_example_org); + + while (ls1.getLabelCount() > 2) { + ls1.stripLeft(1); + ls2.stripLeft(1); + + LabelSequence els(ls1, buf); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + } +} + +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceRightStrippedSource) { + LabelSequence ls1(foo_bar_example_org); + LabelSequence ls2(foo_bar_example_org); + + while (ls1.getLabelCount() > 2) { + ls1.stripRight(1); + ls2.stripRight(1); + + LabelSequence els(ls1, buf); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + } +} + +// Check some basic 'extend' functionality +TEST_F(ExtendableLabelSequenceTest, extend) { + LabelSequence ls1(foo_bar); + LabelSequence ls2(foo); + LabelSequence ls3(bar); + LabelSequence ls4(foo_bar); + + LabelSequence els(ls2, buf); + + check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 1, + true, -4); + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + + check_equal(ls1, els); + stripLeftCheck(ls1, els, ls4); + stripRightCheck(ls1, els, ls4); + + // strip, then extend again + els.stripRight(2); // (2, 1 for root label, 1 for last label) + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + // Extending again should make it different + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 2, + true, 4); + + // Extending with a non-absolute name should make it non-absolute as well + ls3.stripRight(1); + els.extend(ls3, buf); + EXPECT_FALSE(els.isAbsolute()); + + Name check_name("foo.bar.bar.bar"); + LabelSequence check_ls(check_name); + check_ls.stripRight(1); + check_equal(check_ls, els); + + // And try extending when both are not absolute + els.stripRight(3); + ls1.stripRight(1); + EXPECT_FALSE(els.isAbsolute()); + els.extend(ls3, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls1, els); + + // Extending non-absolute with absolute should make it absolute again + EXPECT_FALSE(els.isAbsolute()); + els.extend(LabelSequence(Name("absolute.")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(LabelSequence(Name("foo.bar.absolute")), els); +} + +TEST_F(ExtendableLabelSequenceTest, extendLeftStripped) { + LabelSequence ls1(foo_example); + LabelSequence ls2(example_org); + LabelSequence ls3(org); + + LabelSequence els(ls1, buf); + + els.stripLeft(1); + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Check that when extending with itself, it does not cause horrible failures +TEST_F(ExtendableLabelSequenceTest, extendWithItself) { + LabelSequence ls1(foo_bar); + LabelSequence ls2(foo_bar_foo_bar); + + LabelSequence els(ls1, buf); + + els.extend(els, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); + + // Also try for non-absolute names + ls2.stripRight(1); + els = LabelSequence(ls1, buf); + els.stripRight(1); + els.extend(els, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls2, els); + + // Once more, now start out with non-absolute labelsequence + ls1.stripRight(1); + els = LabelSequence(ls1, buf); + els.extend(els, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Test that 'extending' with just a root label is a no-op, iff the original +// was already absolute +TEST_F(ExtendableLabelSequenceTest, extendWithRoot) { + LabelSequence ls1(example_org); + + LabelSequence els(LabelSequence(ls1, buf)); + check_equal(ls1, els); + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + // but not if the original was not absolute (it will be equal to + // the original labelsequence used above, but not the one it was based + // on). + LabelSequence ls2(example_org); + ls2.stripRight(1); + els = LabelSequence(ls2, buf); + EXPECT_FALSE(els.isAbsolute()); + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + check_compare(ls2, els, isc::dns::NameComparisonResult::NONE, 0, true, 3); +} + +// Check possible failure modes of extend() +TEST_F(ExtendableLabelSequenceTest, extendBadData) { + LabelSequence ls1(example_org); + + LabelSequence els(ls1, buf); + + // try use with unrelated labelsequence + EXPECT_THROW(ls1.extend(ls1, buf), isc::BadValue); + + // Create a long name, but so that we can still extend once + Name longlabel("1234567890123456789012345678901234567890" + "12345678901234567890"); + LabelSequence long_ls(longlabel); + els = LabelSequence(long_ls, buf); + els.extend(els, buf); + els.extend(long_ls, buf); + els.extend(long_ls, buf); + ASSERT_EQ(245, els.getDataLength()); + // Extending once more with 10 bytes should still work + els.extend(LabelSequence(Name("123456789")), buf); + EXPECT_TRUE(els.isAbsolute()); + + // Extended label sequence should now look like + const Name full_name( + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789."); + const LabelSequence full_ls(full_name); + check_equal(full_ls, els); + + // But now, even the shortest extension should fail + EXPECT_THROW(els.extend(LabelSequence(Name("1")), buf), isc::BadValue); + + // Check it hasn't been changed + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls, els); + + // Also check that extending past MAX_LABELS is not possible + Name shortname("1."); + LabelSequence short_ls(shortname); + els = LabelSequence(short_ls, buf); + for (size_t i=0; i < 126; ++i) { + els.extend(short_ls, buf); + } + + // Should now look like this + const Name full_name2( + "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."); + const LabelSequence full_ls2(full_name2); + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls2, els); + + EXPECT_THROW(els.extend(short_ls, buf), isc::BadValue); + + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls2, els); +} + +// Check the static fixed 'wildcard' LabelSequence +TEST(WildCardLabelSequence, wildcard) { + ASSERT_FALSE(LabelSequence::WILDCARD().isAbsolute()); + ASSERT_EQ("*", LabelSequence::WILDCARD().toText()); +} + +} diff --git a/src/lib/dns/tests/master_lexer_inputsource_unittest.cc b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc new file mode 100644 index 0000000..c5f618a --- /dev/null +++ b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc @@ -0,0 +1,368 @@ +// 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 <config.h> + +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <iostream> +#include <sstream> +#include <string> + +#include <string.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::master_lexer_internal; + +namespace { + +const char* const test_input = + "Line1 to scan.\nLine2 to scan.\nLine3 to scan.\n"; + +class InputSourceTest : public ::testing::Test { +protected: + InputSourceTest() : + str_(test_input), + str_length_(strlen(str_)), + iss_(str_), + source_(iss_) + {} + + const char* str_; + const size_t str_length_; + stringstream iss_; + InputSource source_; +}; + +// Test the default return values set during InputSource construction. +TEST_F(InputSourceTest, defaults) { + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); +} + +// getName() on file and stream sources +TEST_F(InputSourceTest, getName) { + EXPECT_EQ(0, source_.getName().find("stream-")); + + // Use some file; doesn't really matter what. + InputSource source2(TEST_DATA_SRCDIR "/masterload.txt"); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", source2.getName()); +} + +TEST_F(InputSourceTest, nonExistentFile) { + EXPECT_THROW({ + InputSource source(TEST_DATA_SRCDIR "/does-not-exist"); + }, InputSource::OpenError); +} + +// getChar() should return characters from the input stream in +// sequence. ungetChar() should skip backwards. +void +checkGetAndUngetChar(InputSource& source, + const char* str, const size_t str_length) +{ + for (size_t i = 0; i < str_length; ++i) { + EXPECT_EQ(str[i], source.getChar()); + EXPECT_EQ(i + 1, source.getPosition()); + EXPECT_FALSE(source.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source.atEOF()); + + // It doesn't increase the position count. + EXPECT_EQ(str_length, source.getPosition()); + EXPECT_EQ(str_length, source.getSize()); // this should be == getSize(). + + // Now, let's go backwards. This should cause the EOF to be set to + // false. + source.ungetChar(); + + // Now, EOF should be false. + EXPECT_FALSE(source.atEOF()); + + // But the position shouldn't change. + EXPECT_EQ(str_length, source.getPosition()); + + // This should cause EOF to be set again. + EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source.atEOF()); + + // Now, let's go backwards in a loop. Start by skipping the EOF. + source.ungetChar(); + + for (size_t i = 0; i < str_length; ++i) { + const size_t index = str_length - 1 - i; + // Skip one character. + source.ungetChar(); + EXPECT_EQ(str[index], source.getChar()); + EXPECT_EQ(index + 1, source.getPosition()); + // Skip the character we received again. + source.ungetChar(); + } + + // Skipping past the start of buffer should throw. + EXPECT_THROW(source.ungetChar(), InputSource::UngetBeforeBeginning); +} + +TEST_F(InputSourceTest, stream) { + checkGetAndUngetChar(source_, str_, str_length_); +} + +TEST_F(InputSourceTest, file) { + std::ifstream fs(TEST_DATA_SRCDIR "/masterload.txt"); + const std::string str((std::istreambuf_iterator<char>(fs)), + std::istreambuf_iterator<char>()); + fs.close(); + + InputSource source(TEST_DATA_SRCDIR "/masterload.txt"); + checkGetAndUngetChar(source, str.c_str(), str.size()); +} + +// ungetAll() should skip back to the place where the InputSource +// started at construction, or the last saved start of line. +TEST_F(InputSourceTest, ungetAll) { + while (!source_.atEOF()) { + source_.getChar(); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + source_.ungetAll(); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(0, source_.getPosition()); +} + +TEST_F(InputSourceTest, compact) { + // Compact at the start + source_.compact(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 0; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Compact again + source_.compact(); + + // We are still at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // compact shouldn't change the position count. + EXPECT_EQ(source_.getSize(), source_.getPosition()); + + // Skip the EOF. + source_.ungetChar(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + EXPECT_TRUE(source_.atEOF()); +} + +TEST_F(InputSourceTest, markDuring) { + // First, skip to line 2. + while (!source_.atEOF() && + (source_.getCurrentLine() != 2)) { + source_.getChar(); + } + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(2, source_.getCurrentLine()); + + // Now, unget a couple of characters. This should cause the + // buffer_pos_ to be not equal to the size of the buffer. + source_.ungetChar(); + source_.ungetChar(); + + // Now "mark" the source, meaning that we save line number and also + // compact the internal buffer at this stage. + source_.mark(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 13; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); + + // Now, ungetAll() and check where it goes back. + source_.ungetAll(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 13; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); +} + +// Test line counters. +TEST_F(InputSourceTest, lines) { + size_t line = 1; + while (!source_.atEOF()) { + if (source_.getChar() == '\n') { + ++line; + } + EXPECT_EQ(line, source_.getCurrentLine()); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Go backwards 2 characters, skipping the last EOF and '\n'. + source_.ungetChar(); + source_.ungetChar(); + + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(3, source_.getCurrentLine()); + + source_.ungetAll(); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); + + // Now check that line numbers are decremented properly (as much as + // possible using the available API). + while (!source_.atEOF()) { + source_.getChar(); + } + line = source_.getCurrentLine(); + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, line); + + EXPECT_THROW({ + while (true) { + source_.ungetChar(); + EXPECT_TRUE(((line == source_.getCurrentLine()) || + ((line - 1) == source_.getCurrentLine()))); + line = source_.getCurrentLine(); + } + }, InputSource::UngetBeforeBeginning); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); +} + +// ungetAll() after saveLine() should skip back to the last-saved place. +TEST_F(InputSourceTest, saveLine) { + // First, skip to line 2. + while (!source_.atEOF() && + (source_.getCurrentLine() != 2)) { + source_.getChar(); + } + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(2, source_.getCurrentLine()); + + // Now, save the line. + source_.saveLine(); + + // Now, go to EOF + while (!source_.atEOF()) { + source_.getChar(); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Now, ungetAll() and check where it goes back. + source_.ungetAll(); + + // Now we are back to where we last-saved. + EXPECT_EQ(2, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); +} + +TEST_F(InputSourceTest, getSize) { + // A simple case using string stream + EXPECT_EQ(strlen(test_input), source_.getSize()); + + // Check it works with an empty input + istringstream iss(""); + EXPECT_EQ(0, InputSource(iss).getSize()); + + // Pretend there's an error in seeking in the stream. It will be + // considered a seek specific error, and getSize() returns "unknown". + iss.setstate(std::ios_base::failbit); + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, InputSource(iss).getSize()); + // The fail bit should have been cleared. + EXPECT_FALSE(iss.fail()); + + // Pretend there's a *critical* error in the stream. The constructor will + // throw in the attempt of getting the input size. + iss.setstate(std::ios_base::badbit); + EXPECT_THROW(InputSource isrc(iss), InputSource::OpenError); + + // Check with input source from file name. We hardcode the file size + // for simplicity. It won't change too often. + EXPECT_EQ(143, InputSource(TEST_DATA_SRCDIR "/masterload.txt").getSize()); +} + +TEST_F(InputSourceTest, getPosition) { + // Initially the position is set to 0. Other cases are tested in tests + // for get and unget. + EXPECT_EQ(0, source_.getPosition()); + EXPECT_EQ(0, InputSource(TEST_DATA_SRCDIR "/masterload.txt").getPosition()); +} + +} // end namespace diff --git a/src/lib/dns/tests/master_lexer_state_unittest.cc b/src/lib/dns/tests/master_lexer_state_unittest.cc new file mode 100644 index 0000000..e810136 --- /dev/null +++ b/src/lib/dns/tests/master_lexer_state_unittest.cc @@ -0,0 +1,607 @@ +// 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 <config.h> + +#include <dns/master_lexer.h> +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer_state.h> + +#include <gtest/gtest.h> + +#include <sstream> + +using namespace isc::dns; +using namespace master_lexer_internal; + +namespace { +typedef MasterToken Token; // shortcut + +class MasterLexerStateTest : public ::testing::Test { +protected: + MasterLexerStateTest() : common_options(MasterLexer::INITIAL_WS), + s_null(NULL), + s_crlf(State::getInstance(State::CRLF)), + s_string(State::getInstance(State::String)), + s_qstring(State::getInstance(State::QString)), + s_number(State::getInstance(State::Number)), + options(MasterLexer::NONE), + orig_options(options) + {} + + // Specify INITIAL_WS as common initial options. + const MasterLexer::Options common_options; + MasterLexer lexer; + const State* const s_null; + const State& s_crlf; + const State& s_string; + const State& s_qstring; + const State& s_number; + std::stringstream ss; + MasterLexer::Options options, orig_options; +}; + +// Common check for the end-of-file condition. +// Token is set to END_OF_FILE, and the lexer was NOT last eol state. +// Passed state can be any valid one; they are stateless, just providing the +// interface for inspection. +void +eofCheck(const State& state, MasterLexer& lexer) { + EXPECT_EQ(Token::END_OF_FILE, state.getToken(lexer).getType()); + EXPECT_FALSE(state.wasLastEOL(lexer)); +} + +TEST_F(MasterLexerStateTest, startAndEnd) { + // A simple case: the input is empty, so we begin with start and + // are immediately done. + lexer.pushSource(ss); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + eofCheck(s_crlf, lexer); +} + +TEST_F(MasterLexerStateTest, startToEOL) { + ss << "\n"; + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // The next lexer session will reach EOF. Same eof check should pass. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + eofCheck(s_crlf, lexer); +} + +TEST_F(MasterLexerStateTest, space) { + // repeat '\t\n' twice (see below), then space after EOL + ss << " \t\n\t\n "; + lexer.pushSource(ss); + + // by default space characters and tabs will be ignored. We check this + // twice; at the second iteration, it's a white space at the beginning + // of line, but since we don't specify INITIAL_WS option, it's treated as + // normal space and ignored. + for (size_t i = 0; i < 2; ++i) { + EXPECT_EQ(s_null, State::start(lexer, MasterLexer::NONE)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + } + + // Now we specify the INITIAL_WS option. It will be recognized and the + // corresponding token will be returned. + EXPECT_EQ(s_null, State::start(lexer, MasterLexer::INITIAL_WS)); + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, parentheses) { + ss << "\n(\na\n )\n "; // 1st \n is to check if 'was EOL' is set to false + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); // handle \n + + // Now handle '('. It skips \n and recognize 'a' as string + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); // check pre condition + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); // check post condition + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + + // skip 'a' + s_string.handle(lexer); + + // Then handle ')'. '\n' before ')' isn't recognized because + // it's canceled due to the '('. Likewise, the space after the '\n' + // shouldn't be recognized but should be just ignored. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + + // Now, temporarily disabled options are restored: Both EOL and the + // initial WS are recognized + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, nestedParentheses) { + // This is an unusual, but allowed (in this implementation) case. + ss << "(a(b)\n c)\n "; + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'a' + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'b' + EXPECT_EQ(2, s_crlf.getParenCount(lexer)); // now the count is 2 + + // Close the inner most parentheses. count will be decreased, but option + // shouldn't be restored yet, so the intermediate EOL or initial WS won't + // be recognized. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume ')' + s_string.handle(lexer); // consume 'c' + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); + + // Close the outermost parentheses. count will be reset to 0, and original + // options are restored. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + + // Now, temporarily disabled options are restored: Both EOL and the + // initial WS are recognized + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, unbalancedParentheses) { + // Only closing paren is provided. We prepend a \n to check if it's + // correctly canceled after detecting the error. + ss << "\n)"; + ss << "(a"; + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); // consume '\n' + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); // this \n was remembered + + // Now checking ')'. The result should be error, count shouldn't be + // changed. "last EOL" should be canceled. + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + ASSERT_EQ(Token::ERROR, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_PAREN, s_crlf.getToken(lexer).getErrorCode()); + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + + // Reach EOF with a dangling open parenthesis. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'a' + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // reach EOF + ASSERT_EQ(Token::ERROR, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_PAREN, s_crlf.getToken(lexer).getErrorCode()); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); // should be reset to 0 +} + +TEST_F(MasterLexerStateTest, startToComment) { + // Begin with 'start', detect space, then encounter a comment. Skip + // the rest of the line, and recognize the new line. Note that the + // second ';' is simply ignored. + ss << " ;a;\n"; + ss << ";a;"; // Likewise, but the comment ends with EOF. + lexer.pushSource(ss); + + // Initial whitespace (asked for in common_options) + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); + // Comment ending with EOL + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // Comment ending with EOF + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, commentAfterParen) { + // comment after an opening parenthesis. The code that is tested by + // other tests should also ensure that it works correctly, but we + // check it explicitly. + ss << "( ;this is a comment\na)\n"; + lexer.pushSource(ss); + + // consume '(', skip comments, consume 'a', then consume ')' + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, crlf) { + ss << "\r\n"; // case 1 + ss << "\r "; // case 2 + ss << "\r;comment\na"; // case 3 + ss << "\r"; // case 4 + lexer.pushSource(ss); + + // 1. A sequence of \r, \n is recognized as a single 'end-of-line' + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + s_crlf.handle(lexer); // recognize '\n' + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + + // 2. Single '\r' (not followed by \n) is recognized as a single + // 'end-of-line'. then there will be "initial WS" + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // see ' ', "unget" it + s_crlf.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // recognize ' ' + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); + + // 3. comment between \r and \n + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // skip comments, recognize '\n' + s_crlf.handle(lexer); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // skip 'a' + + // 4. \r then EOF + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // see EOF, then "unget" it + s_crlf.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // recognize EOF + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +// Commonly used check for string related test cases, checking if the given +// token has expected values. +void +stringTokenCheck(const std::string& expected, const MasterToken& token, + bool quoted = false) +{ + EXPECT_EQ(quoted ? Token::QSTRING : Token::STRING, token.getType()); + EXPECT_EQ(expected, token.getString()); + const std::string actual(token.getStringRegion().beg, + token.getStringRegion().beg + + token.getStringRegion().len); + EXPECT_EQ(expected, actual); + + // There should be "hidden" nul-terminator after the string data. + ASSERT_NE(static_cast<const char*>(NULL), token.getStringRegion().beg); + EXPECT_EQ(0, *(token.getStringRegion().beg + token.getStringRegion().len)); +} + +TEST_F(MasterLexerStateTest, string) { + // Check with simple strings followed by separate characters + ss << "followed-by-EOL\n"; + ss << "followed-by-CR\r"; + ss << "followed-by-space "; + ss << "followed-by-tab\t"; + ss << "followed-by-comment;this is comment and ignored\n"; + ss << "followed-by-paren(closing)"; + ss << "followed-by-EOF"; + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \n + EXPECT_FALSE(s_string.wasLastEOL(lexer)); + stringTokenCheck("followed-by-EOL", s_string.getToken(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \r + stringTokenCheck("followed-by-CR", s_string.getToken(lexer)); + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // handle \r... + s_crlf.handle(lexer); // ...and skip it + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' + stringTokenCheck("followed-by-space", s_string.getToken(lexer)); + + // skip ' ', then recognize the next string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \t + stringTokenCheck("followed-by-tab", s_string.getToken(lexer)); + + // skip \t, then recognize the next string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see comment + stringTokenCheck("followed-by-comment", s_string.getToken(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n after it + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see '(' + stringTokenCheck("followed-by-paren", s_string.getToken(lexer)); + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // str in () + s_string.handle(lexer); // recognize the str, see ')' + stringTokenCheck("closing", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see EOF + stringTokenCheck("followed-by-EOF", s_string.getToken(lexer)); +} + +TEST_F(MasterLexerStateTest, stringEscape) { + // some of the separate characters should be considered part of the + // string if escaped. + ss << "escaped\\ space "; + ss << "escaped\\\ttab "; + ss << "escaped\\(paren "; + ss << "escaped\\)close "; + ss << "escaped\\;comment "; + ss << "escaped\\\\ backslash "; // second '\' shouldn't escape ' ' + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\ space", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\\ttab", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\(paren", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\)close", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\;comment", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' in mid + stringTokenCheck("escaped\\\\", s_string.getToken(lexer)); + + // Confirm the word that follows the escaped '\' is correctly recognized. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("backslash", s_string.getToken(lexer)); +} + +TEST_F(MasterLexerStateTest, quotedString) { + ss << "\"ignore-quotes\"\n"; + ss << "\"quoted string\" "; // space is part of the qstring + ss << "\"\" "; // empty quoted string + // also check other separator characters. note that \r doesn't cause + // UNBALANCED_QUOTES. Not sure if it's intentional, but that's how the + // BIND 9 version works, so we follow it (it should be too minor to matter + // in practice anyway) + ss << "\"quoted()\t\rstring\" "; + ss << "\"escape\\ in quote\" "; + ss << "\"escaped\\\"\" "; + ss << "\"escaped backslash\\\\\" "; + ss << "\"no;comment\""; + lexer.pushSource(ss); + + // by default, '"' is unexpected (when QSTRING is not specified), + // and it returns MasterToken::UNEXPECTED_QUOTES. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::UNEXPECTED_QUOTES, s_string.getToken(lexer).getErrorCode()); + // Read it as a QSTRING. + s_qstring.handle(lexer); // recognize quoted str, see \n + stringTokenCheck("ignore-quotes", s_qstring.getToken(lexer), true); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n after it + EXPECT_TRUE(s_string.wasLastEOL(lexer)); + + // If QSTRING is specified in option, '"' is regarded as a beginning of + // a quoted string. + const MasterLexer::Options options = common_options | MasterLexer::QSTRING; + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + EXPECT_FALSE(s_string.wasLastEOL(lexer)); // EOL is canceled due to '"' + s_qstring.handle(lexer); + stringTokenCheck("quoted string", s_string.getToken(lexer), true); + + // Empty string is okay as qstring + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("", s_string.getToken(lexer), true); + + // Also checks other separator characters within a qstring + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("quoted()\t\rstring", s_string.getToken(lexer), true); + + // escape character mostly doesn't have any effect in the qstring + // processing + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escape\\ in quote", s_string.getToken(lexer), true); + + // The only exception is the quotation mark itself. Note that the escape + // only works on the quotation mark immediately after it. + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escaped\"", s_string.getToken(lexer), true); + + // quoted '\' then '"'. Unlike the previous case '"' shouldn't be + // escaped. + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escaped backslash\\\\", s_string.getToken(lexer), true); + + // ';' has no meaning in a quoted string (not indicating a comment) + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("no;comment", s_string.getToken(lexer), true); +} + +TEST_F(MasterLexerStateTest, brokenQuotedString) { + ss << "\"unbalanced-quote\n"; + ss << "\"quoted\\\n\" "; + ss << "\"unclosed quote and EOF"; + lexer.pushSource(ss); + + // EOL is encountered without closing the quote + const MasterLexer::Options options = common_options | MasterLexer::QSTRING; + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + ASSERT_EQ(Token::ERROR, s_qstring.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_QUOTES, + s_qstring.getToken(lexer).getErrorCode()); + // We can resume after the error from the '\n' + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // \n is okay in a quoted string if escaped + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("quoted\\\n", s_string.getToken(lexer), true); + + // EOF is encountered without closing the quote + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + ASSERT_EQ(Token::ERROR, s_qstring.getToken(lexer).getType()); + EXPECT_EQ(Token::UNEXPECTED_END, s_qstring.getToken(lexer).getErrorCode()); + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, basicNumbers) { + ss << "0 "; + ss << "1 "; + ss << "12345 "; + ss << "4294967295 "; // 2^32-1 + ss << "4294967296 "; // Out of range + ss << "340282366920938463463374607431768211456 "; + // Very much out of range (2^128) + ss << "005 "; // Leading zeroes are ignored + ss << "42;asdf\n"; // Number with comment + ss << "37"; // Simple number again, here to make + // sure none of the above messed up + // the tokenizer + lexer.pushSource(ss); + + // Ask the lexer to recognize numbers as well + const MasterLexer::Options options = common_options | MasterLexer::NUMBER; + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(0, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(1, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(12345, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(4294967295u, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(Token::NUMBER_OUT_OF_RANGE, + s_number.getToken(lexer).getErrorCode()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(Token::NUMBER_OUT_OF_RANGE, + s_number.getToken(lexer).getErrorCode()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(5, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(42, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(37, s_number.getToken(lexer).getNumber()); + + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +// Test tokens that look like (or start out as) numbers, +// but turn out to be strings. Tests include escaped characters. +TEST_F(MasterLexerStateTest, stringNumbers) { + ss << "123 "; // Should be read as a string if the + // NUMBER option is not given + ss << "-1 "; // Negative numbers are interpreted + // as strings (unsigned integers only) + ss << "123abc456 "; // 'Numbers' containing non-digits should + // be interpreted as strings + ss << "123\\456 "; // Numbers containing escaped digits are + // interpreted as strings + ss << "3scaped\\ space "; + ss << "3scaped\\\ttab "; + ss << "3scaped\\(paren "; + ss << "3scaped\\)close "; + ss << "3scaped\\;comment "; + ss << "3scaped\\\\ 8ackslash "; // second '\' shouldn't escape ' ' + + lexer.pushSource(ss); + + // Note that common_options does not include MasterLexer::NUMBER, + // so the token should be recognized as a string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); + stringTokenCheck("123", s_string.getToken(lexer), false); + + // Ask the lexer to recognize numbers as well + const MasterLexer::Options options = common_options | MasterLexer::NUMBER; + + EXPECT_EQ(&s_string, State::start(lexer, options)); + s_string.handle(lexer); + stringTokenCheck("-1", s_string.getToken(lexer), false); + + // Starts out as a number, but ends up being a string + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + stringTokenCheck("123abc456", s_number.getToken(lexer), false); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + stringTokenCheck("123\\456", s_number.getToken(lexer), false); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\ space", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\\ttab", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\(paren", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\)close", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\;comment", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' in mid + stringTokenCheck("3scaped\\\\", s_number.getToken(lexer)); + + // Confirm the word that follows the escaped '\' is correctly recognized. + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("8ackslash", s_number.getToken(lexer)); + + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +} // end anonymous namespace + diff --git a/src/lib/dns/tests/master_lexer_token_unittest.cc b/src/lib/dns/tests/master_lexer_token_unittest.cc new file mode 100644 index 0000000..2167a9f --- /dev/null +++ b/src/lib/dns/tests/master_lexer_token_unittest.cc @@ -0,0 +1,162 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/master_lexer.h> + +#include <gtest/gtest.h> + +#include <string> + +using namespace isc::dns; + +namespace { + +const char TEST_STRING[] = "string token"; +// This excludes the ending \0 character +const size_t TEST_STRING_LEN = sizeof(TEST_STRING) - 1; + +class MasterLexerTokenTest : public ::testing::Test { +protected: + MasterLexerTokenTest() : + token_eof(MasterToken::END_OF_FILE), + token_str(TEST_STRING, TEST_STRING_LEN), + token_num(42), + token_err(MasterToken::UNEXPECTED_END) + {} + + const MasterToken token_eof; // an example of non-value type token + const MasterToken token_str; + const MasterToken token_num; + const MasterToken token_err; +}; + + +TEST_F(MasterLexerTokenTest, strings) { + // basic construction and getter checks + EXPECT_EQ(MasterToken::STRING, token_str.getType()); + EXPECT_EQ(std::string("string token"), token_str.getString()); + std::string strval = "dummy"; // this should be replaced + token_str.getString(strval); + EXPECT_EQ(std::string("string token"), strval); + const MasterToken::StringRegion str_region = + token_str.getStringRegion(); + EXPECT_EQ(TEST_STRING, str_region.beg); + EXPECT_EQ(TEST_STRING_LEN, str_region.len); + + // Even if the stored string contains a nul character (in this case, + // it happens to be at the end of the string, but could be in the middle), + // getString() should return a string object containing the nul. + std::string expected_str("string token"); + expected_str.push_back('\0'); + EXPECT_EQ(expected_str, + MasterToken(TEST_STRING, TEST_STRING_LEN + 1).getString()); + MasterToken(TEST_STRING, TEST_STRING_LEN + 1).getString(strval); + EXPECT_EQ(expected_str, strval); + + // Construct type of qstring + EXPECT_EQ(MasterToken::QSTRING, + MasterToken(TEST_STRING, sizeof(TEST_STRING), true). + getType()); + // if we explicitly set 'quoted' to false, it should be normal string + EXPECT_EQ(MasterToken::STRING, + MasterToken(TEST_STRING, sizeof(TEST_STRING), false). + getType()); + + // getString/StringRegion() aren't allowed for non string(-variant) types + EXPECT_THROW(token_eof.getString(), isc::InvalidOperation); + EXPECT_THROW(token_eof.getString(strval), isc::InvalidOperation); + EXPECT_THROW(token_num.getString(), isc::InvalidOperation); + EXPECT_THROW(token_num.getString(strval), isc::InvalidOperation); + EXPECT_THROW(token_eof.getStringRegion(), isc::InvalidOperation); + EXPECT_THROW(token_num.getStringRegion(), isc::InvalidOperation); +} + +TEST_F(MasterLexerTokenTest, numbers) { + EXPECT_EQ(42, token_num.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token_num.getType()); + + // It's copyable and assignable. + MasterToken token(token_num); + EXPECT_EQ(42, token.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token.getType()); + + token = token_num; + EXPECT_EQ(42, token.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token.getType()); + + // it's okay to replace it with a different type of token + token = token_eof; + EXPECT_EQ(MasterToken::END_OF_FILE, token.getType()); + + // Possible max value + token = MasterToken(0xffffffff); + EXPECT_EQ(4294967295u, token.getNumber()); + + // getNumber() isn't allowed for non number types + EXPECT_THROW(token_eof.getNumber(), isc::InvalidOperation); + EXPECT_THROW(token_str.getNumber(), isc::InvalidOperation); +} + +TEST_F(MasterLexerTokenTest, novalues) { + // Just checking we can construct them and getType() returns correct value. + EXPECT_EQ(MasterToken::END_OF_FILE, token_eof.getType()); + EXPECT_EQ(MasterToken::END_OF_LINE, + MasterToken(MasterToken::END_OF_LINE).getType()); + EXPECT_EQ(MasterToken::INITIAL_WS, + MasterToken(MasterToken::INITIAL_WS).getType()); + + // Special types of tokens cannot have value-based types + EXPECT_THROW(MasterToken t(MasterToken::STRING), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::QSTRING), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::NUMBER), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::ERROR), isc::InvalidParameter); +} + +TEST_F(MasterLexerTokenTest, errors) { + EXPECT_EQ(MasterToken::ERROR, token_err.getType()); + EXPECT_EQ(MasterToken::UNEXPECTED_END, token_err.getErrorCode()); + EXPECT_EQ("unexpected end of input", token_err.getErrorText()); + EXPECT_EQ("lexer not started", MasterToken(MasterToken::NOT_STARTED). + getErrorText()); + EXPECT_EQ("unbalanced parentheses", + MasterToken(MasterToken::UNBALANCED_PAREN). + getErrorText()); + EXPECT_EQ("unbalanced quotes", MasterToken(MasterToken::UNBALANCED_QUOTES). + getErrorText()); + EXPECT_EQ("no token produced", MasterToken(MasterToken::NO_TOKEN_PRODUCED). + getErrorText()); + EXPECT_EQ("number out of range", + MasterToken(MasterToken::NUMBER_OUT_OF_RANGE). + getErrorText()); + EXPECT_EQ("not a valid number", + MasterToken(MasterToken::BAD_NUMBER).getErrorText()); + EXPECT_EQ("unexpected quotes", + MasterToken(MasterToken::UNEXPECTED_QUOTES).getErrorText()); + + // getErrorCode/Text() isn't allowed for non number types + EXPECT_THROW(token_num.getErrorCode(), isc::InvalidOperation); + EXPECT_THROW(token_num.getErrorText(), isc::InvalidOperation); + + // Only the pre-defined error code is accepted. Hardcoding '8' (max code + // + 1) is intentional; it'd be actually better if we notice it when we + // update the enum list (which shouldn't happen too often). + // + // Note: if you fix this testcase, you probably want to update the + // getErrorText() tests above too. + EXPECT_THROW(MasterToken(MasterToken::ErrorCode(8)), + isc::InvalidParameter); + + // Check the coexistence of "from number" and "from error-code" + // constructors won't cause confusion. + EXPECT_EQ(MasterToken::NUMBER, + MasterToken(static_cast<uint32_t>(MasterToken::NOT_STARTED)). + getType()); +} +} diff --git a/src/lib/dns/tests/master_lexer_unittest.cc b/src/lib/dns/tests/master_lexer_unittest.cc new file mode 100644 index 0000000..7bebb48 --- /dev/null +++ b/src/lib/dns/tests/master_lexer_unittest.cc @@ -0,0 +1,521 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/master_lexer.h> +#include <dns/master_lexer_state.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> +#include <boost/scoped_ptr.hpp> + +#include <string> +#include <sstream> + +using namespace isc::dns; +using std::string; +using std::stringstream; +using boost::lexical_cast; +using boost::scoped_ptr; +using master_lexer_internal::State; + +namespace { + +class MasterLexerTest : public ::testing::Test { +protected: + MasterLexerTest() : + expected_stream_name("stream-" + lexical_cast<string>(&ss)) + {} + + MasterLexer lexer; + stringstream ss; + const string expected_stream_name; +}; + +// Commonly used check case where the input sources stack is empty. +void +checkEmptySource(const MasterLexer& lexer) { + EXPECT_TRUE(lexer.getSourceName().empty()); + EXPECT_EQ(0, lexer.getSourceLine()); + EXPECT_EQ(0, lexer.getPosition()); +} + +TEST_F(MasterLexerTest, preOpen) { + // Initially sources stack is empty. + checkEmptySource(lexer); +} + +TEST_F(MasterLexerTest, pushStream) { + EXPECT_EQ(0, lexer.getSourceCount()); + ss << "test"; + lexer.pushSource(ss); + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + EXPECT_EQ(1, lexer.getSourceCount()); + EXPECT_EQ(4, lexer.getTotalSourceSize()); // 4 = len("test") + + // From the point of view of this test, we only have to check (though + // indirectly) getSourceLine calls InputSource::getCurrentLine. It should + // return 1 initially. + EXPECT_EQ(1, lexer.getSourceLine()); + + // By popping it the stack will be empty again. + lexer.popSource(); + EXPECT_EQ(0, lexer.getSourceCount()); + checkEmptySource(lexer); + EXPECT_EQ(4, lexer.getTotalSourceSize()); // this shouldn't change +} + +TEST_F(MasterLexerTest, pushStreamFail) { + // Pretend a "bad" thing happened in the stream. This will make the + // initialization throw an exception. + ss << "test"; + ss.setstate(std::ios_base::badbit); + + EXPECT_THROW(lexer.pushSource(ss), isc::Unexpected); +} + +TEST_F(MasterLexerTest, pushFile) { + // We use zone file (-like) data, but in this test that actually doesn't + // matter. + EXPECT_EQ(0, lexer.getSourceCount()); + EXPECT_TRUE(lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt")); + EXPECT_EQ(1, lexer.getSourceCount()); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", lexer.getSourceName()); + EXPECT_EQ(1, lexer.getSourceLine()); + + // 143 = size of the test zone file. hardcode it assuming it won't change + // too often. + EXPECT_EQ(143, lexer.getTotalSourceSize()); + + lexer.popSource(); + checkEmptySource(lexer); + EXPECT_EQ(0, lexer.getSourceCount()); + EXPECT_EQ(143, lexer.getTotalSourceSize()); // this shouldn't change + + // If we give a non NULL string pointer, its content will be intact + // if pushSource succeeds. + std::string error_txt = "dummy"; + EXPECT_TRUE(lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt", + &error_txt)); + EXPECT_EQ("dummy", error_txt); +} + +TEST_F(MasterLexerTest, pushBadFileName) { + EXPECT_THROW(lexer.pushSource(NULL), isc::InvalidParameter); +} + +TEST_F(MasterLexerTest, pushFileFail) { + // The file to be pushed doesn't exist. pushSource() fails and + // some non empty error string should be set. + std::string error_txt; + EXPECT_TRUE(error_txt.empty()); + EXPECT_FALSE(lexer.pushSource("no-such-file", &error_txt)); + EXPECT_FALSE(error_txt.empty()); + + // It's safe to pass NULL error_txt (either explicitly or implicitly as + // the default) + EXPECT_FALSE(lexer.pushSource("no-such-file", NULL)); + EXPECT_FALSE(lexer.pushSource("no-such-file")); +} + +TEST_F(MasterLexerTest, nestedPush) { + const string test_txt = "test"; + ss << test_txt; + lexer.pushSource(ss); + + EXPECT_EQ(test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(0, lexer.getPosition()); + + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + + // Read the string; getPosition() should reflect that. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + EXPECT_EQ(test_txt.size(), lexer.getPosition()); + + // We can push another source without popping the previous one. + lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt"); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", lexer.getSourceName()); + EXPECT_EQ(143 + test_txt.size(), + lexer.getTotalSourceSize()); // see above for magic nums + + // the next token should be the EOL (skipping a comment line), its + // position in the file is 35 (hardcoded). + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); + + // popSource() works on the "topmost" (last-pushed) source + lexer.popSource(); + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + + // pop shouldn't change the total size and the current position + EXPECT_EQ(143 + test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); + + lexer.popSource(); + EXPECT_TRUE(lexer.getSourceName().empty()); + + // size and position still shouldn't change + EXPECT_EQ(143 + test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); +} + +TEST_F(MasterLexerTest, unknownSourceSize) { + // Similar to the previous case, but the size of the second source + // will be considered "unknown" (by emulating an error). + ss << "test"; + lexer.pushSource(ss); + EXPECT_EQ(4, lexer.getTotalSourceSize()); + + stringstream ss2; + ss2.setstate(std::ios_base::failbit); // this will make the size unknown + lexer.pushSource(ss2); + // Then the total size is also unknown. + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, lexer.getTotalSourceSize()); + + // Even if we pop that source, the size is still unknown. + lexer.popSource(); + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, lexer.getTotalSourceSize()); +} + +TEST_F(MasterLexerTest, invalidPop) { + // popSource() cannot be called if the sources stack is empty. + EXPECT_THROW(lexer.popSource(), isc::InvalidOperation); +} + +// Test it is not possible to get token when no source is available. +TEST_F(MasterLexerTest, noSource) { + EXPECT_THROW(lexer.getNextToken(), isc::InvalidOperation); +} + +// Test getting some tokens. It also check basic behavior of getPosition(). +TEST_F(MasterLexerTest, getNextToken) { + ss << "\n \n\"STRING\"\n"; + lexer.pushSource(ss); + + // First, the newline should get out. + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Then the whitespace, if we specify the option. + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(2, lexer.getPosition()); + // The newline + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(5, lexer.getPosition()); // 1st \n + 3 spaces, then 2nd \n + // The (quoted) string + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(5 + 8, lexer.getPosition()); // 8 = len("STRING') + quotes + + // And the end of line and file + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(5 + 8 + 1, lexer.getPosition()); // previous + 3rd \n + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + EXPECT_EQ(5 + 8 + 1, lexer.getPosition()); // position doesn't change +} + +// Test we correctly find end of file. +TEST_F(MasterLexerTest, eof) { + // Let the ss empty. + lexer.pushSource(ss); + + // The first one is found to be EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + // And it stays on EOF for any following attempts + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + // And we can step back one token, but that is the EOF too. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Check we properly return error when there's an opened parentheses and no +// closing one +TEST_F(MasterLexerTest, getUnbalancedParen) { + ss << "(string"; + lexer.pushSource(ss); + + // The string gets out first + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + // Then an unbalanced parenthesis + EXPECT_EQ(MasterToken::UNBALANCED_PAREN, + lexer.getNextToken().getErrorCode()); + // And then EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Check we properly return error when there's an opened quoted string and no +// closing one +TEST_F(MasterLexerTest, getUnbalancedString) { + ss << "\"string"; + lexer.pushSource(ss); + + // Then an unbalanced qstring (reported as an unexpected end) + EXPECT_EQ(MasterToken::UNEXPECTED_END, + lexer.getNextToken(MasterLexer::QSTRING).getErrorCode()); + // And then EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Test ungetting tokens works. Also check getPosition() is adjusted +TEST_F(MasterLexerTest, ungetToken) { + ss << "\n (\"string\"\n) more"; + lexer.pushSource(ss); + + // Try getting the newline + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Return it and get again + lexer.ungetToken(); + EXPECT_EQ(0, lexer.getPosition()); + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Get the string and return it back + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(string("\n (\"string\"").size(), lexer.getPosition()); + lexer.ungetToken(); + EXPECT_EQ(1, lexer.getPosition()); // back to just after 1st \n + // But if we change the options, it honors them + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::QSTRING | + MasterLexer::INITIAL_WS).getType()); + // Get to the "more" string + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(MasterToken::STRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + // Return it back. It should get inside the parentheses. + // Upon next attempt to get it again, the newline inside the parentheses + // should be still ignored. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::STRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); +} + +// Check ungetting token without overriding the start method. We also +// check it works well with changing options between the calls. +TEST_F(MasterLexerTest, ungetRealOptions) { + ss << " \n"; + lexer.pushSource(ss); + + // If we call it the usual way, it skips up to the newline and returns + // it + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + + // Now we return it. If we call it again, but with different options, + // we get the initial whitespace. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); +} + +// Check the initial whitespace is found even in the first line of included +// file. It also confirms getPosition() works for multiple sources, each +// of which is partially parsed. +TEST_F(MasterLexerTest, includeAndInitialWS) { + ss << " \n"; + lexer.pushSource(ss); + + stringstream ss2; + ss2 << " \n"; + + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(1, lexer.getPosition()); + lexer.pushSource(ss2); + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(2, lexer.getPosition()); // should be sum of pushed positions. +} + +// Test only one token can be ungotten +TEST_F(MasterLexerTest, ungetTwice) { + ss << "\n"; + lexer.pushSource(ss); + + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // Unget the token. It can be done once + lexer.ungetToken(); + // But not twice + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Test we can't unget a token before we get one +TEST_F(MasterLexerTest, ungetBeforeGet) { + lexer.pushSource(ss); // Just to eliminate the missing source problem + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Test we can't unget a token after a source switch, even when we got +// something before. +TEST_F(MasterLexerTest, ungetAfterSwitch) { + ss << "\n\n"; + lexer.pushSource(ss); + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // Switch the source + std::stringstream ss2; + ss2 << "\n\n"; + lexer.pushSource(ss2); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); + // We can get from the new source + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // And when we drop the current source, we can't unget again + lexer.popSource(); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Common checks for the case when getNextToken() should result in LexerError +void +lexerErrorCheck(MasterLexer& lexer, MasterToken::Type expect, + MasterToken::ErrorCode expected_error) +{ + bool thrown = false; + try { + lexer.getNextToken(expect); + } catch (const MasterLexer::LexerError& error) { + EXPECT_EQ(expected_error, error.token_.getErrorCode()); + thrown = true; + } + EXPECT_TRUE(thrown); +} + +// Common checks regarding expected/unexpected end-of-line +// +// The 'lexer' should be at a position before two consecutive '\n's. +// The first one will be recognized, and the second one will be considered an +// unexpected token. Then this helper consumes the second '\n', so the caller +// can continue the test after these '\n's. +void +eolCheck(MasterLexer& lexer, MasterToken::Type expect) { + // If EOL is found and eol_ok is true, we get it. + EXPECT_EQ(MasterToken::END_OF_LINE, + lexer.getNextToken(expect, true).getType()); + // We'll see the second '\n'; by default it will fail. + EXPECT_THROW(lexer.getNextToken(expect), MasterLexer::LexerError); + // Same if eol_ok is explicitly set to false. This also checks the + // offending '\n' was "ungotten". + EXPECT_THROW(lexer.getNextToken(expect, false), MasterLexer::LexerError); + + // And also check the error token set in the exception object. + lexerErrorCheck(lexer, expect, MasterToken::UNEXPECTED_END); + + // Then skip the 2nd '\n' + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); +} + +// Common checks regarding expected/unexpected end-of-file +// +// The 'lexer' should be at a position just before an end-of-file. +void +eofCheck(MasterLexer& lexer, MasterToken::Type expect) { + EXPECT_EQ(MasterToken::END_OF_FILE, + lexer.getNextToken(expect, true).getType()); + EXPECT_THROW(lexer.getNextToken(expect), MasterLexer::LexerError); + EXPECT_THROW(lexer.getNextToken(expect, false), MasterLexer::LexerError); +} + +TEST_F(MasterLexerTest, getNextTokenString) { + ss << "normal-string\n"; + ss << "\n"; + ss << "another-string"; + lexer.pushSource(ss); + + // Normal successful case: Expecting a string and get one. + EXPECT_EQ("normal-string", + lexer.getNextToken(MasterToken::STRING).getString()); + eolCheck(lexer, MasterToken::STRING); + + // Same set of tests but for end-of-file + EXPECT_EQ("another-string", + lexer.getNextToken(MasterToken::STRING, true).getString()); + eofCheck(lexer, MasterToken::STRING); +} + +TEST_F(MasterLexerTest, getNextTokenQString) { + ss << "\"quoted-string\"\n"; + ss << "\n"; + ss << "normal-string"; + lexer.pushSource(ss); + + // Expecting a quoted string and get one. + EXPECT_EQ("quoted-string", + lexer.getNextToken(MasterToken::QSTRING).getString()); + eolCheck(lexer, MasterToken::QSTRING); + + // Expecting a quoted string but see a normal string. It's okay. + EXPECT_EQ("normal-string", + lexer.getNextToken(MasterToken::QSTRING).getString()); + eofCheck(lexer, MasterToken::QSTRING); +} + +TEST_F(MasterLexerTest, getNextTokenNumber) { + ss << "3600\n"; + ss << "\n"; + ss << "4294967296 "; // =2^32, out of range + ss << "not-a-number "; + ss << "123abc "; // starting with digits, but resulting in a string + ss << "86400"; + lexer.pushSource(ss); + + // Expecting a number string and get one. + EXPECT_EQ(3600, + lexer.getNextToken(MasterToken::NUMBER).getNumber()); + eolCheck(lexer, MasterToken::NUMBER); + + // Expecting a number, but it's too big for uint32. + lexerErrorCheck(lexer, MasterToken::NUMBER, + MasterToken::NUMBER_OUT_OF_RANGE); + // The token should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Expecting a number, but see a string. + lexerErrorCheck(lexer, MasterToken::NUMBER, MasterToken::BAD_NUMBER); + // The unexpected string should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Expecting a number, but see a string. + lexerErrorCheck(lexer, MasterToken::NUMBER, MasterToken::BAD_NUMBER); + // The unexpected string should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Unless we specify NUMBER, decimal number string should be recognized + // as a string. + EXPECT_EQ("86400", + lexer.getNextToken(MasterToken::STRING).getString()); + eofCheck(lexer, MasterToken::NUMBER); +} + +TEST_F(MasterLexerTest, getNextTokenErrors) { + // Check miscellaneous error cases + + ss << ") "; // unbalanced parenthesis + ss << "string-after-error "; + lexer.pushSource(ss); + + // Only string/qstring/number can be "expected". + EXPECT_THROW(lexer.getNextToken(MasterToken::END_OF_LINE), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::END_OF_FILE), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::INITIAL_WS), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::ERROR), + isc::InvalidParameter); + + // If it encounters a syntax error, it results in LexerError exception. + lexerErrorCheck(lexer, MasterToken::STRING, MasterToken::UNBALANCED_PAREN); + + // Unlike the NUMBER_OUT_OF_RANGE case, the error part has been skipped + // within getNextToken(). We should be able to get the next token. + EXPECT_EQ("string-after-error", + lexer.getNextToken(MasterToken::STRING).getString()); +} + +} diff --git a/src/lib/dns/tests/master_loader_callbacks_test.cc b/src/lib/dns/tests/master_loader_callbacks_test.cc new file mode 100644 index 0000000..9d23802 --- /dev/null +++ b/src/lib/dns/tests/master_loader_callbacks_test.cc @@ -0,0 +1,79 @@ +// 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 <config.h> + +#include <dns/master_loader_callbacks.h> +#include <dns/rrset.h> +#include <dns/name.h> +#include <dns/rrttl.h> +#include <dns/rrclass.h> + +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> +#include <functional> + +namespace { + +using std::string; +using namespace isc::dns; +namespace ph = std::placeholders; + +class MasterLoaderCallbacksTest : public ::testing::Test { +protected: + MasterLoaderCallbacksTest() : + last_was_error_(false), // Not needed, but then cppcheck complains + issue_called_(false), + rrset_(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), + RRTTL(3600))), + error_(std::bind(&MasterLoaderCallbacksTest::checkCallback, this, + true, ph::_1, ph::_2, ph::_3)), + warning_(std::bind(&MasterLoaderCallbacksTest::checkCallback, this, + false, ph::_1, ph::_2, ph::_3)), + callbacks_(error_, warning_) + {} + + void checkCallback(bool error, const string& source, size_t line, + const string& reason) + { + issue_called_ = true; + last_was_error_ = error; + EXPECT_EQ("source", source); + EXPECT_EQ(1, line); + EXPECT_EQ("reason", reason); + } + bool last_was_error_; + bool issue_called_; + const RRsetPtr rrset_; + const MasterLoaderCallbacks::IssueCallback error_, warning_; + MasterLoaderCallbacks callbacks_; +}; + +// Check the constructor rejects empty callbacks, but accepts non-empty ones +TEST_F(MasterLoaderCallbacksTest, constructor) { + EXPECT_THROW(MasterLoaderCallbacks(MasterLoaderCallbacks::IssueCallback(), + warning_), isc::InvalidParameter); + EXPECT_THROW(MasterLoaderCallbacks(error_, + MasterLoaderCallbacks::IssueCallback()), + isc::InvalidParameter); + EXPECT_NO_THROW(MasterLoaderCallbacks(error_, warning_)); +} + +// Call the issue callbacks +TEST_F(MasterLoaderCallbacksTest, issueCall) { + callbacks_.error("source", 1, "reason"); + EXPECT_TRUE(last_was_error_); + EXPECT_TRUE(issue_called_); + + issue_called_ = false; + + callbacks_.warning("source", 1, "reason"); + EXPECT_FALSE(last_was_error_); + EXPECT_TRUE(issue_called_); +} + +} diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc new file mode 100644 index 0000000..74300d3 --- /dev/null +++ b/src/lib/dns/tests/master_loader_unittest.cc @@ -0,0 +1,1445 @@ +// Copyright (C) 2012-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 <config.h> + +#include <dns/master_loader_callbacks.h> +#include <dns/master_loader.h> +#include <dns/rrtype.h> +#include <dns/rrset.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/name.h> +#include <dns/rdata.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> +#include <boost/scoped_ptr.hpp> + +#include <functional> +#include <string> +#include <vector> +#include <list> +#include <sstream> + +using namespace isc::dns; +using std::vector; +using std::string; +using std::list; +using std::stringstream; +using std::endl; +using boost::lexical_cast; +namespace ph = std::placeholders; + +namespace { +class MasterLoaderTest : public ::testing::Test { +public: + MasterLoaderTest() : + callbacks_(std::bind(&MasterLoaderTest::callback, this, + &errors_, ph::_1, ph::_2, ph::_3), + std::bind(&MasterLoaderTest::callback, this, + &warnings_, ph::_1, ph::_2, ph::_3)) + {} + + void TearDown() { + // Check there are no more RRs we didn't expect + EXPECT_TRUE(rrsets_.empty()); + } + + /// Concatenate file, line, and reason, and add it to either errors + /// or warnings + void callback(vector<string>* target, const std::string& file, size_t line, + const std::string& reason) + { + std::stringstream ss; + ss << reason << " [" << file << ":" << line << "]"; + target->push_back(ss.str()); + } + + void addRRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& rrttl, + const rdata::RdataPtr& data) { + const RRsetPtr rrset(new BasicRRset(name, rrclass, rrtype, rrttl)); + rrset->addRdata(data); + rrsets_.push_back(rrset); + } + + void setLoader(const char* file, const Name& origin, + const RRClass& rrclass, const MasterLoader::Options options) + { + loader_.reset(new MasterLoader(file, origin, rrclass, callbacks_, + std::bind(&MasterLoaderTest::addRRset, + this, ph::_1, ph::_2, ph::_3, + ph::_4, ph::_5), + options)); + } + + void setLoader(std::istream& stream, const Name& origin, + const RRClass& rrclass, const MasterLoader::Options options) + { + loader_.reset(new MasterLoader(stream, origin, rrclass, callbacks_, + std::bind(&MasterLoaderTest::addRRset, + this, ph::_1, ph::_2, ph::_3, + ph::_4, ph::_5), + options)); + } + + static string prepareZone(const string& line, bool include_last) { + string result; + result += "example.org. 3600 IN SOA ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200\n"; + result += line; + if (include_last) { + result += "\n"; + result += "correct 3600 IN A 192.0.2.2\n"; + } + return (result); + } + + void clear() { + warnings_.clear(); + errors_.clear(); + rrsets_.clear(); + } + + // Check the next RR in the ones produced by the loader + // Other than passed arguments are checked to be the default for the tests + void checkRR(const string& name, const RRType& type, const string& data, + const RRTTL& rrttl = RRTTL(3600)) { + ASSERT_FALSE(rrsets_.empty()); + RRsetPtr current = rrsets_.front(); + rrsets_.pop_front(); + + EXPECT_EQ(Name(name), current->getName()); + EXPECT_EQ(type, current->getType()); + EXPECT_EQ(RRClass::IN(), current->getClass()); + EXPECT_EQ(rrttl, current->getTTL()); + ASSERT_EQ(1, current->getRdataCount()); + EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)-> + compare(current->getRdataIterator()->getCurrent())) + << data << " vs. " + << current->getRdataIterator()->getCurrent().toText(); + } + + void checkBasicRRs() { + checkRR("example.org", RRType::SOA(), + "ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200"); + checkRR("example.org", RRType::NS(), "ns1.example.org."); + checkRR("www.example.org", RRType::A(), "192.0.2.1"); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); + } + + void checkARR(const string& name) { + checkRR(name, RRType::A(), "192.0.2.1"); + } + + MasterLoaderCallbacks callbacks_; + boost::scoped_ptr<MasterLoader> loader_; + vector<string> errors_; + vector<string> warnings_; + list<RRsetPtr> rrsets_; +}; + +// Test simple loading. The zone file contains no tricky things, and nothing is +// omitted. No RRset contains more than one RR Also no errors or warnings. +TEST_F(MasterLoaderTest, basicLoad) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + + // The following three should be set to 0 initially in case the loader + // is constructed from a file name. + EXPECT_EQ(0, loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + // Hardcode expected values taken from the test data file, assuming it + // won't change too often. + EXPECT_EQ(550, loader_->getSize()); + EXPECT_EQ(550, loader_->getPosition()); + + checkBasicRRs(); +} + +// Test the $INCLUDE directive +TEST_F(MasterLoaderTest, include) { + // Test various cases of include + const char* includes[] = { + "$include", + "$INCLUDE", + "$Include", + "$InCluDe", + "\"$INCLUDE\"", + NULL + }; + for (const char** include = includes; *include != NULL; ++include) { + SCOPED_TRACE(*include); + + clear(); + // Prepare input source that has the include and some more data + // below (to see it returns back to the original source). + const string include_str = string(*include) + " " + + TEST_DATA_SRCDIR + "/example.org\nwww 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(include_str); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkBasicRRs(); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); + } +} + +TEST_F(MasterLoaderTest, includeAndIncremental) { + // Check getSize() and getPosition() are adjusted before and after + // $INCLUDE. + const string first_rr = "before.example.org. 0 A 192.0.2.1\n"; + const string include_str = "$INCLUDE " TEST_DATA_SRCDIR "/example.org"; + const string zone_data = first_rr + include_str + "\n" + + "www 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(zone_data); + setLoader(ss, Name("example.org."), RRClass::IN(), MasterLoader::DEFAULT); + + // On construction, getSize() returns the size of the data (exclude the + // the file to be included); position is set to 0. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + // Read the first RR. getSize() doesn't change; position should be + // at the end of the first line. + loader_->loadIncremental(1); + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(first_rr.size(), loader_->getPosition()); + + // Read next 4. It includes $INCLUDE processing. Magic number of 550 + // is the size of the test zone file (see above); 507 is the position in + // the file at the end of 4th RR (due to extra comments it's smaller than + // the file size). + loader_->loadIncremental(4); + EXPECT_EQ(zone_data.size() + 550, loader_->getSize()); + EXPECT_EQ(first_rr.size() + include_str.size() + 507, + loader_->getPosition()); + + // Read the last one. At this point getSize and getPosition return + // the same value, indicating progress of 100%. + loader_->loadIncremental(1); + EXPECT_EQ(zone_data.size() + 550, loader_->getSize()); + EXPECT_EQ(zone_data.size() + 550, loader_->getPosition()); + + // we were not interested in checking RRs in this test. clear them to + // not confuse TearDown(). + rrsets_.clear(); +} + +// A commonly used helper to check callback message. +void +checkCallbackMessage(const string& actual_msg, const string& expected_msg, + size_t expected_line) { + // The actual message should begin with the expected message. + EXPECT_EQ(0, actual_msg.find(expected_msg)) << "actual message: " << + actual_msg << " expected: " << + expected_msg; + + // and it should end with "...:<line_num>]" + const string line_desc = ":" + lexical_cast<string>(expected_line) + "]"; + EXPECT_EQ(actual_msg.size() - line_desc.size(), + actual_msg.find(line_desc)) << "Expected on line " << + expected_line; +} + +TEST_F(MasterLoaderTest, origin) { + // Various forms of the directive + const char* origins[] = { + "$origin", + "$ORIGIN", + "$Origin", + "$OrigiN", + "\"$ORIGIN\"", + NULL + }; + for (const char** origin = origins; *origin != NULL; ++origin) { + SCOPED_TRACE(*origin); + + clear(); + const string directive = *origin; + const string input = + "@ 1H IN A 192.0.2.1\n" + + directive + " sub.example.org.\n" + "\"www\" 1H IN A 192.0.2.1\n" + + // Relative name in the origin + directive + " relative\n" + "@ 1H IN A 192.0.2.1\n" + // Origin is _not_ used here (absolute name) + "noorigin.example.org. 60M IN A 192.0.2.1\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There's a relative origin in it, we warn about that. + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "The new origin is relative, did you really mean " + "relative.sub.example.org.?", 4); + + checkARR("example.org"); + checkARR("www.sub.example.org"); + checkARR("relative.sub.example.org"); + checkARR("noorigin.example.org"); + } +} + +TEST_F(MasterLoaderTest, generate) { + // Various forms of the directive + const char* generates[] = { + "$generate", + "$GENERATE", + "$Generate", + "$GeneratE", + "\"$GENERATE\"", + NULL + }; + for (const char** generate = generates; *generate != NULL; ++generate) { + SCOPED_TRACE(*generate); + + clear(); + const string directive = *generate; + const string input = + "$ORIGIN example.org.\n" + "before.example.org. 3600 IN A 192.0.2.0\n" + + directive + " 3-5 host$ A 192.0.2.$\n" + + "after.example.org. 3600 IN A 192.0.2.255\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + // The "before" and "after" scaffolding below checks that no + // extra records are added by $GENERATE outside the requested + // range. + checkRR("before.example.org", RRType::A(), "192.0.2.0"); + checkRR("host3.example.org", RRType::A(), "192.0.2.3"); + checkRR("host4.example.org", RRType::A(), "192.0.2.4"); + checkRR("host5.example.org", RRType::A(), "192.0.2.5"); + checkRR("after.example.org", RRType::A(), "192.0.2.255"); + } +} + +TEST_F(MasterLoaderTest, generateRelativeLHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 @ 3600 NS ns$.example.org.\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("example.org", RRType::NS(), "ns1.example.org."); + checkRR("example.org", RRType::NS(), "ns2.example.org."); +} + +TEST_F(MasterLoaderTest, generateInFront) { + // $ is in the front + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 $host 3600 TXT \"$ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("9host.example.org", RRType::TXT(), "9 pomegranate"); + checkRR("10host.example.org", RRType::TXT(), "10 pomegranate"); +} + +TEST_F(MasterLoaderTest, generateInMiddle) { + // $ is in the middle + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 num$-host 3600 TXT \"This is $ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("num9-host.example.org", RRType::TXT(), "This is 9 pomegranate"); + checkRR("num10-host.example.org", RRType::TXT(), "This is 10 pomegranate"); +} + +TEST_F(MasterLoaderTest, generateAtEnd) { + // $ is at the end + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 num$-host 3600 TXT Pomegranate$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("num9-host.example.org", RRType::TXT(), "Pomegranate9"); + checkRR("num10-host.example.org", RRType::TXT(), "Pomegranate10"); +} + +TEST_F(MasterLoaderTest, generateStripsQuotes) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 @ 3600 MX \"$ mx$.example.org.\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("example.org", RRType::MX(), "1 mx1.example.org."); + checkRR("example.org", RRType::MX(), "2 mx2.example.org."); +} + +TEST_F(MasterLoaderTest, generateWithDoublePlaceholder) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 host$ 3600 TXT \"This is $$ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host9.example.org", RRType::TXT(), "This is $ pomegranate"); + checkRR("host10.example.org", RRType::TXT(), "This is $ pomegranate"); +} + +TEST_F(MasterLoaderTest, generateWithEscape) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 host$ 3600 TXT \"This is \\$\\pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host9.example.org", RRType::TXT(), "This is \\$\\pomegranate"); + checkRR("host10.example.org", RRType::TXT(), "This is \\$\\pomegranate"); +} + +TEST_F(MasterLoaderTest, generateWithParams) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 2-3 host$ A 192.0.2.$\n" + "$GENERATE 5-6 host$ 3600 A 192.0.2.$\n" + "$GENERATE 8-9 host$ IN A 192.0.2.$\n" + "$GENERATE 11-12 host$ IN 3600 A 192.0.2.$\n" + "$GENERATE 14-15 host$ 3600 IN A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host2.example.org", RRType::A(), "192.0.2.2"); + checkRR("host3.example.org", RRType::A(), "192.0.2.3"); + + checkRR("host5.example.org", RRType::A(), "192.0.2.5"); + checkRR("host6.example.org", RRType::A(), "192.0.2.6"); + + checkRR("host8.example.org", RRType::A(), "192.0.2.8"); + checkRR("host9.example.org", RRType::A(), "192.0.2.9"); + + checkRR("host11.example.org", RRType::A(), "192.0.2.11"); + checkRR("host12.example.org", RRType::A(), "192.0.2.12"); + + checkRR("host14.example.org", RRType::A(), "192.0.2.14"); + checkRR("host15.example.org", RRType::A(), "192.0.2.15"); +} + +TEST_F(MasterLoaderTest, generateWithStep) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-9/2 host$ 3600 A 192.0.2.$\n" + "$GENERATE 12-21/3 host$ 3600 A 192.0.2.$\n" + "$GENERATE 30-31/1 host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host2.example.org", RRType::A(), "192.0.2.2"); + checkRR("host4.example.org", RRType::A(), "192.0.2.4"); + checkRR("host6.example.org", RRType::A(), "192.0.2.6"); + checkRR("host8.example.org", RRType::A(), "192.0.2.8"); + + checkRR("host12.example.org", RRType::A(), "192.0.2.12"); + checkRR("host15.example.org", RRType::A(), "192.0.2.15"); + checkRR("host18.example.org", RRType::A(), "192.0.2.18"); + checkRR("host21.example.org", RRType::A(), "192.0.2.21"); + + checkRR("host30.example.org", RRType::A(), "192.0.2.30"); + checkRR("host31.example.org", RRType::A(), "192.0.2.31"); +} + +TEST_F(MasterLoaderTest, generateWithModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + + // Use a positive delta of 1 in the LHS and a negative delta of + // -1 in the RHS + "$GENERATE 2-9/2 host${1} A 192.0.2.${-1}\n" + + "$GENERATE 10-12 host${0,4} A 192.0.2.$\n" + "$GENERATE 14-15 host${0,4,d} A 192.0.2.$\n" + + // Names are case-insensitive, so we use TXT's RDATA to check + // case with hex representation. + "$GENERATE 30-31 host$ TXT \"Value ${0,4,x}\"\n" + "$GENERATE 42-43 host$ TXT \"Value ${0,4,X}\"\n" + + // Octal does not use any alphabets + "$GENERATE 45-46 host${0,4,o} A 192.0.2.$\n" + + // Here, the LHS has a trailing dot (which would result in an + // out-of-zone name), but that should be handled as a relative + // name. + "$GENERATE 90-92 ${0,8,n} A 192.0.2.$\n" + + // Here, the LHS has no trailing dot, and results in the same + // number of labels as width=8 above. + "$GENERATE 94-96 ${0,7,n} A 192.0.2.$\n" + + // Names are case-insensitive, so we use TXT's RDATA to check + // case with nibble representation. + "$GENERATE 106-107 host$ TXT \"Value ${0,9,n}\"\n" + "$GENERATE 109-110 host$ TXT \"Value ${0,9,N}\"\n" + + // Junk type will not parse and 'd' is assumed. No error is + // generated (this is to match BIND 9 behavior). + "$GENERATE 200-201 host${0,4,j} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host3.example.org", RRType::A(), "192.0.2.1"); + checkRR("host5.example.org", RRType::A(), "192.0.2.3"); + checkRR("host7.example.org", RRType::A(), "192.0.2.5"); + checkRR("host9.example.org", RRType::A(), "192.0.2.7"); + + checkRR("host0010.example.org", RRType::A(), "192.0.2.10"); + checkRR("host0011.example.org", RRType::A(), "192.0.2.11"); + checkRR("host0012.example.org", RRType::A(), "192.0.2.12"); + + checkRR("host0014.example.org", RRType::A(), "192.0.2.14"); + checkRR("host0015.example.org", RRType::A(), "192.0.2.15"); + + checkRR("host30.example.org", RRType::TXT(), "Value 001e"); + checkRR("host31.example.org", RRType::TXT(), "Value 001f"); + + checkRR("host42.example.org", RRType::TXT(), "Value 002A"); + checkRR("host43.example.org", RRType::TXT(), "Value 002B"); + + checkRR("host0055.example.org", RRType::A(), "192.0.2.45"); + checkRR("host0056.example.org", RRType::A(), "192.0.2.46"); + + checkRR("a.5.0.0.example.org", RRType::A(), "192.0.2.90"); + checkRR("b.5.0.0.example.org", RRType::A(), "192.0.2.91"); + checkRR("c.5.0.0.example.org", RRType::A(), "192.0.2.92"); + + checkRR("e.5.0.0.example.org", RRType::A(), "192.0.2.94"); + checkRR("f.5.0.0.example.org", RRType::A(), "192.0.2.95"); + checkRR("0.6.0.0.example.org", RRType::A(), "192.0.2.96"); + + checkRR("host106.example.org", RRType::TXT(), "Value a.6.0.0.0"); + checkRR("host107.example.org", RRType::TXT(), "Value b.6.0.0.0"); + checkRR("host109.example.org", RRType::TXT(), "Value D.6.0.0.0"); + checkRR("host110.example.org", RRType::TXT(), "Value E.6.0.0.0"); + + checkRR("host0200.example.org", RRType::A(), "192.0.2.200"); + checkRR("host0201.example.org", RRType::A(), "192.0.2.201"); +} + +TEST_F(MasterLoaderTest, generateWithNoModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 10-12 host${} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(2, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Invalid $GENERATE format modifiers", 3); + checkCallbackMessage(errors_.at(1), + "$GENERATE error", 3); +} + +TEST_F(MasterLoaderTest, generateWithBadModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 10-12 host${GARBAGE} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(2, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Invalid $GENERATE format modifiers", 3); + checkCallbackMessage(errors_.at(1), + "$GENERATE error", 3); +} + +TEST_F(MasterLoaderTest, generateMissingRange) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingLHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingType) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4 host$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingRHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4 host$ A\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateWithBadRangeSyntax) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE ABCD host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "$GENERATE: invalid range: ABCD", 2); +} + +TEST_F(MasterLoaderTest, generateWithInvalidRange) { + // start > stop + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-1 host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "$GENERATE: invalid range: 2-1", 2); +} + +TEST_F(MasterLoaderTest, generateWithInvalidClass) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 host$ 3600 CH A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Class mismatch: CH vs. IN", 2); +} + +TEST_F(MasterLoaderTest, generateWithNoAvailableTTL) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 host$ A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "no TTL specified; load rejected", 2); +} + +// Test the source is correctly popped even after error +TEST_F(MasterLoaderTest, popAfterError) { + const string include_str = "$include " TEST_DATA_SRCDIR + "/broken.zone\nwww 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(include_str); + // We perform the test with MANY_ERRORS, we want to see what happens + // after the error. + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken RR + EXPECT_EQ(1, warnings_.size()); // For missing EOLN + + // The included file doesn't contain anything usable, but the + // line after the include should be there. + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Check it works the same when created based on a stream, not filename +TEST_F(MasterLoaderTest, streamConstructor) { + const string zone_data(prepareZone("", true)); + stringstream zone_stream(zone_data); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + + // Unlike the basicLoad test, if we construct the loader from a stream + // getSize() returns the data size in the stream immediately after the + // construction. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + checkRR("correct.example.org", RRType::A(), "192.0.2.2"); + + // On completion of the load, both getSize() and getPosition() return the + // size of the data. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(zone_data.size(), loader_->getPosition()); +} + +// Try loading data incrementally. +TEST_F(MasterLoaderTest, incrementalLoad) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_FALSE(loader_->loadIncremental(2)); + EXPECT_FALSE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("example.org", RRType::SOA(), + "ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200"); + checkRR("example.org", RRType::NS(), "ns1.example.org."); + + // The third one is not loaded yet + EXPECT_TRUE(rrsets_.empty()); + + // Load the rest. + EXPECT_TRUE(loader_->loadIncremental(20)); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("www.example.org", RRType::A(), "192.0.2.1"); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Try loading from file that doesn't exist. There should be single error +// saying so. +TEST_F(MasterLoaderTest, invalidFile) { + setLoader("This file doesn't exist at all", + Name("example.org."), RRClass::IN(), MasterLoader::MANY_ERRORS); + + // Nothing yet. The loader is dormant until invoked. + // Is it really what we want? + EXPECT_TRUE(errors_.empty()); + + loader_->load(); + + EXPECT_TRUE(warnings_.empty()); + EXPECT_TRUE(rrsets_.empty()); + ASSERT_EQ(1, errors_.size()); + EXPECT_EQ(0, errors_[0].find("Error opening the input source file: ")) << + "Different error: " << errors_[0]; +} + +struct ErrorCase { + const char* const line; // The broken line in master file + const char* const reason; // If non NULL, the reason string + const char* const problem; // Description of the problem for SCOPED_TRACE +} const error_cases[] = { + { "www... 3600 IN A 192.0.2.1", NULL, "Invalid name" }, + { "www FORTNIGHT IN A 192.0.2.1", NULL, "Invalid TTL" }, + { "www 3600 XX A 192.0.2.1", NULL, "Invalid class" }, + { "www 3600 IN A bad_ip", NULL, "Invalid Rdata" }, + + // Parameter ordering errors + { "www IN A 3600 192.168.2.7", + "createRdata from text failed: Bad IN/A RDATA text: '3600'", + "Incorrect order of class, TTL and type" }, + { "www A IN 3600 192.168.2.8", + "createRdata from text failed: Bad IN/A RDATA text: 'IN'", + "Incorrect order of class, TTL and type" }, + { "www 3600 A IN 192.168.2.7", + "createRdata from text failed: Bad IN/A RDATA text: 'IN'", + "Incorrect order of class, TTL and type" }, + { "www A 3600 IN 192.168.2.8", + "createRdata from text failed: Bad IN/A RDATA text: '3600'", + "Incorrect order of class, TTL and type" }, + + // Missing type and Rdata + { "www", "unexpected end of input", "Missing type and Rdata" }, + { "www 3600", "unexpected end of input", "Missing type and Rdata" }, + { "www IN", "unexpected end of input", "Missing type and Rdata" }, + { "www 3600 IN", "unexpected end of input", "Missing type and Rdata" }, + { "www IN 3600", "unexpected end of input", "Missing type and Rdata" }, + + // Missing Rdata + { "www A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www 3600 A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www IN A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www 3600 IN A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www IN 3600 A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + + { "www 3600 IN", NULL, "Unexpected EOLN" }, + { "www 3600 CH TXT nothing", "Class mismatch: CH vs. IN", + "Class mismatch" }, + { "www \"3600\" IN A 192.0.2.1", NULL, "Quoted TTL" }, + { "www 3600 \"IN\" A 192.0.2.1", NULL, "Quoted class" }, + { "www 3600 IN \"A\" 192.0.2.1", NULL, "Quoted type" }, + { "unbalanced)paren 3600 IN A 192.0.2.1", NULL, "Token error 1" }, + { "www 3600 unbalanced)paren A 192.0.2.1", NULL, + "Token error 2" }, + // Check the unknown directive. The rest looks like ordinary RR, + // so we see the $ is actually special. + { "$UNKNOWN 3600 IN A 192.0.2.1", NULL, "Unknown $ directive" }, + { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Unknown directive 'INCLUD'", + "Include too short" }, + { "$INCLUDES " TEST_DATA_SRCDIR "/example.org", + "Unknown directive 'INCLUDES'", "Include too long" }, + { "$INCLUDE", "unexpected end of input", "Missing include path" }, + // The following two error messages are system dependent, omitting + { "$INCLUDE /file/not/found", NULL, "Include file not found" }, + { "$INCLUDE /file/not/found example.org. and here goes bunch of garbage", + NULL, "Include file not found and garbage at the end of line" }, + { "$ORIGIN", "unexpected end of input", "Missing origin name" }, + { "$ORIGIN invalid...name", "duplicate period in invalid...name", + "Invalid name for origin" }, + { "$ORIGIN )brokentoken", "unbalanced parentheses", + "Broken token in origin" }, + { "$ORIGIN example.org. garbage", "Extra tokens at the end of line", + "Garbage after origin" }, + { "$ORIGI name.", "Unknown directive 'ORIGI'", "$ORIGIN too short" }, + { "$ORIGINAL name.", "Unknown directive 'ORIGINAL'", "$ORIGIN too long" }, + { "$TTL 100 extra-garbage", "Extra tokens at the end of line", + "$TTL with extra token" }, + { "$TTL", "unexpected end of input", "missing TTL" }, + { "$TTL No-ttl", "Unknown unit used: N in: No-ttl", "bad TTL" }, + { "$TTL \"100\"", "unexpected quotes", "bad TTL, quoted" }, + { "$TT 100", "Unknown directive 'TT'", "bad directive, too short" }, + { "$TTLLIKE 100", "Unknown directive 'TTLLIKE'", "bad directive, extra" }, + { NULL, NULL, NULL } +}; + +// Test a broken zone is handled properly. We test several problems, +// both in strict and lenient mode. +TEST_F(MasterLoaderTest, brokenZone) { + for (const ErrorCase* ec = error_cases; ec->line != NULL; ++ec) { + SCOPED_TRACE(ec->problem); + const string zone(prepareZone(ec->line, true)); + + { + SCOPED_TRACE("Strict mode"); + clear(); + stringstream zone_stream(zone); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_THROW(loader_->load(), MasterLoaderError); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + if (ec->reason != NULL) { + checkCallbackMessage(errors_.at(0), ec->reason, 2); + } + EXPECT_TRUE(warnings_.empty()); + + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + // In the strict mode, it is aborted. The last RR is not + // even attempted. + EXPECT_TRUE(rrsets_.empty()); + } + + { + SCOPED_TRACE("Lenient mode"); + clear(); + stringstream zone_stream(zone); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + EXPECT_TRUE(warnings_.empty()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + // This one is below the error one. + checkRR("correct.example.org", RRType::A(), "192.0.2.2"); + EXPECT_TRUE(rrsets_.empty()); + } + + { + SCOPED_TRACE("Error at EOF"); + // This case is interesting only in the lenient mode. + clear(); + const string zoneEOF(prepareZone(ec->line, false)); + stringstream zone_stream(zoneEOF); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()) << errors_[0] << "\n" << errors_[1]; + // The unexpected EOF warning + EXPECT_EQ(1, warnings_.size()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + EXPECT_TRUE(rrsets_.empty()); + } + } +} + +// Check that a garbage after the include generates an error, but not fatal +// one (in lenient mode) and we can recover. +TEST_F(MasterLoaderTest, includeWithGarbage) { + // Include an origin (example.org) because we expect it to be handled + // soon and we don't want it to break here. + const string include_str("$INCLUDE " TEST_DATA_SRCDIR + "/example.org example.org. bunch of other stuff\n" + "www 3600 IN AAAA 2001:db8::1\n"); + stringstream zone_stream(include_str); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1); + // It says something about extra tokens at the end + EXPECT_NE(string::npos, errors_[0].find("Extra")); + EXPECT_TRUE(warnings_.empty()); + checkBasicRRs(); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Check we error about garbage at the end of $ORIGIN line (but the line +// works). +TEST_F(MasterLoaderTest, originWithGarbage) { + const string origin_str = "$ORIGIN www.example.org. More garbage here\n" + "@ 1H IN A 192.0.2.1\n"; + stringstream ss(origin_str); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1); + EXPECT_TRUE(warnings_.empty()); + checkARR("www.example.org"); +} + +// Test we can pass both file to include and the origin to switch +TEST_F(MasterLoaderTest, includeAndOrigin) { + // First, switch origin to something else, so we can check it is + // switched back. + const string include_string = "$ORIGIN www.example.org.\n" + "@ 1H IN A 192.0.2.1\n" + // Then include the file with data and switch origin back + "$INCLUDE " TEST_DATA_SRCDIR "/example.org example.org.\n" + // Another RR to see we fall back to the previous origin. + "www 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); + checkBasicRRs(); + checkARR("www.www.example.org"); +} + +// Like above, but the origin after include is bogus. The whole line should +// be rejected. +TEST_F(MasterLoaderTest, includeAndBadOrigin) { + const string include_string = + "$INCLUDE " TEST_DATA_SRCDIR "/example.org example..org.\n" + // Another RR to see the switch survives after we exit include + "www 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "duplicate period in example..org.", + 1); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); +} + +// Check the origin doesn't get outside of the included file. +TEST_F(MasterLoaderTest, includeOriginRestore) { + const string include_string = + "$INCLUDE " TEST_DATA_SRCDIR "/origincheck.txt\n" + "@ 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); + checkARR("example.org"); +} + +// Check we restore the last name for initial whitespace when returning from +// include. But we do produce a warning if there's one just ofter the include. +TEST_F(MasterLoaderTest, includeAndInitialWS) { + const string include_string = "xyz 1H IN A 192.0.2.1\n" + "$INCLUDE " TEST_DATA_SRCDIR "/example.org\n" + " 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "Owner name omitted around $INCLUDE, the result might " + "not be as expected", 3); + checkARR("xyz.example.org"); + checkBasicRRs(); + checkARR("xyz.example.org"); +} + +// Test for "$TTL" +TEST_F(MasterLoaderTest, ttlDirective) { + stringstream zone_stream; + + // Set the default TTL with $TTL followed by an RR omitting the TTL + zone_stream << "$TTL 1800\nexample.org. IN A 192.0.2.1\n"; + // $TTL can be quoted. Also testing the case of $TTL being changed. + zone_stream << "\"$TTL\" 100\na.example.org. IN A 192.0.2.2\n"; + // Extended TTL form is accepted. + zone_stream << "$TTL 1H\nb.example.org. IN A 192.0.2.3\n"; + // Matching is case insensitive. + zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.4\n"; + // Maximum allowable TTL + zone_stream << "$TTL 2147483647\nd.example.org. IN A 192.0.2.5\n"; + + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("a.example.org", RRType::A(), "192.0.2.2", RRTTL(100)); + checkRR("b.example.org", RRType::A(), "192.0.2.3", RRTTL(3600)); + checkRR("c.example.org", RRType::A(), "192.0.2.4", RRTTL(360)); + checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647)); +} + +TEST_F(MasterLoaderTest, ttlFromSOA) { + // No $TTL, and the SOA doesn't have an explicit TTL field. Its minimum + // TTL field will be used as the RR's TTL, and it'll be used as the + // default TTL for others. + stringstream zone_stream("example.org. IN SOA . . 0 0 0 0 1800\n" + "a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(1800)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + + // The use of SOA minimum TTL should have caused a warning. + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "no TTL specified; using SOA MINTTL instead", 1); +} + +TEST_F(MasterLoaderTest, ttlFromPrevious) { + // No available default TTL. 2nd and 3rd RR will use the TTL of the + // 1st RR. This will result in a warning, but only for the first time. + stringstream zone_stream("a.example.org. 1800 IN A 192.0.2.1\n" + "b.example.org. IN A 192.0.2.2\n" + "c.example.org. IN A 192.0.2.3\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(1800)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, RRParamsOrdering) { + // We test the order and existence of TTL, class and type. See + // MasterLoader::MasterLoaderImpl::parseRRParams() for ordering. + + stringstream zone_stream; + // <TTL> <class> <type> <RDATA> + zone_stream << "a.example.org. 1800 IN A 192.0.2.1\n"; + // <type> <RDATA> + zone_stream << "b.example.org. A 192.0.2.2\n"; + // <class> <TTL> <type> <RDATA> + zone_stream << "c.example.org. IN 3600 A 192.0.2.3\n"; + // <TTL> <type> <RDATA> + zone_stream << "d.example.org. 7200 A 192.0.2.4\n"; + // <class> <type> <RDATA> + zone_stream << "e.example.org. IN A 192.0.2.5\n"; + + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(3600)); + checkRR("d.example.org", RRType::A(), "192.0.2.4", RRTTL(7200)); + checkRR("e.example.org", RRType::A(), "192.0.2.5", RRTTL(7200)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, ttlFromPreviousSOA) { + // Mixture of the previous two cases: SOA has explicit TTL, followed by + // an RR without an explicit TTL. In this case the minimum TTL won't be + // recognized as the "default TTL". + stringstream zone_stream("example.org. 100 IN SOA . . 0 0 0 0 1800\n" + "a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(100)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(100)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, ttlUnknown) { + // No available TTL is known for the first RR. + stringstream zone_stream("a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + EXPECT_THROW(loader_->load(), MasterLoaderError); +} + +TEST_F(MasterLoaderTest, ttlUnknownAndContinue) { + stringstream zone_stream("a.example.org. IN A 192.0.2.1\n" + "b.example.org. 1800 IN A 192.0.2.2\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + + EXPECT_TRUE(warnings_.empty()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1); +} + +TEST_F(MasterLoaderTest, ttlUnknownAndEOF) { + // Similar to the previous case, but the input will be abruptly terminated + // after the offending RR. This will cause an additional warning. + stringstream zone_stream("a.example.org. IN A 192.0.2.1"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_TRUE(rrsets_.empty()); + + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1); + + // RDATA implementation can complain about it, too. To be independent of + // its details, we focus on the very last warning. + EXPECT_FALSE(warnings_.empty()); + checkCallbackMessage(*warnings_.rbegin(), "File does not end with newline", + 1); +} + +TEST_F(MasterLoaderTest, ttlOverflow) { + stringstream zone_stream; + zone_stream << "example.org. IN SOA . . 0 0 0 0 2147483648\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "$TTL 2147483649\n"; + zone_stream << "a.example.org. IN A 192.0.2.1\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "b.example.org. 2147483650 IN A 192.0.2.2\n"; + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_EQ(3, rrsets_.size()); + + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 2147483648", RRTTL(0)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(0)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(0)); + + EXPECT_EQ(4, warnings_.size()); + checkCallbackMessage(warnings_.at(1), + "TTL 2147483648 > MAXTTL, setting to 0 per RFC2181", + 1); + checkCallbackMessage(warnings_.at(2), + "TTL 2147483649 > MAXTTL, setting to 0 per RFC2181", + 3); + checkCallbackMessage(warnings_.at(3), + "TTL 2147483650 > MAXTTL, setting to 0 per RFC2181", + 6); +} + +// Test the constructor rejects empty add callback. +TEST_F(MasterLoaderTest, emptyCallback) { + EXPECT_THROW(MasterLoader(TEST_DATA_SRCDIR "/example.org", + Name("example.org"), RRClass::IN(), callbacks_, + AddRRCallback()), isc::InvalidParameter); + // And the same with the second constructor + stringstream ss(""); + EXPECT_THROW(MasterLoader(ss, Name("example.org"), RRClass::IN(), + callbacks_, AddRRCallback()), + isc::InvalidParameter); +} + +// Check it throws when we try to load after loading was complete. +TEST_F(MasterLoaderTest, loadTwice) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_THROW(loader_->load(), isc::InvalidOperation); + // Don't check them, they are not interesting, so suppress the error + // at TearDown + rrsets_.clear(); +} + +// Load 0 items should be rejected +TEST_F(MasterLoaderTest, loadZero) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + EXPECT_THROW(loader_->loadIncremental(0), isc::InvalidParameter); +} + +// Test there's a warning when the file terminates without end of +// line. +TEST_F(MasterLoaderTest, noEOLN) { + // No \n at the end + const string input("example.org. 3600 IN SOA ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There should be one warning about the EOLN + EXPECT_EQ(1, warnings_.size()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); +} + +// Test it rejects when we don't have the previous name to use in place of +// initial whitespace +TEST_F(MasterLoaderTest, noPreviousName) { + const string input(" 1H IN A 192.0.2.1\n"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "No previous name to use in place of " + "initial whitespace", 1); + EXPECT_TRUE(warnings_.empty()); +} + +// Check we warn if the first RR in an included file has omitted name +TEST_F(MasterLoaderTest, previousInInclude) { + const string input("www 1H IN A 192.0.2.1\n" + "$INCLUDE " TEST_DATA_SRCDIR "/omitcheck.txt\n"); + stringstream ss(input); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There should be one warning about the EOLN + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "Owner name omitted around " + "$INCLUDE, the result might not be as expected", 1); + checkARR("www.example.org"); + checkARR("www.example.org"); +} + +TEST_F(MasterLoaderTest, numericOwnerName) { + const string input("$ORIGIN example.org.\n" + "1 3600 IN A 192.0.2.1\n"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("1.example.org", RRType::A(), "192.0.2.1"); +} + +} diff --git a/src/lib/dns/tests/masterload_unittest.cc b/src/lib/dns/tests/masterload_unittest.cc new file mode 100644 index 0000000..e412de0 --- /dev/null +++ b/src/lib/dns/tests/masterload_unittest.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2010-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 <config.h> + +#include <functional> +#include <ios> +#include <fstream> +#include <sstream> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include <dns/masterload.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rrclass.h> +#include <dns/rrset.h> + +using namespace std; +using namespace isc::dns; +namespace ph = std::placeholders; + +namespace { +// A callback functor for masterLoad() commonly used for the following tests. +class TestCallback { +public: + TestCallback(vector<ConstRRsetPtr>& rrsets) : rrsets_(rrsets) {} + void operator()(ConstRRsetPtr rrset) { + rrsets_.push_back(rrset); + } +private: + vector<ConstRRsetPtr>& rrsets_; +}; + +// A function version of TestCallback. +void +testCallback(ConstRRsetPtr rrset, vector<ConstRRsetPtr>* rrsets) { + rrsets->push_back(rrset); +} + +class MasterLoadTest : public ::testing::Test { +protected: + MasterLoadTest() : origin("example.com"), zclass(RRClass::IN()), + callback(results) {} +public: + void rrsetCallback(ConstRRsetPtr rrset) { + results.push_back(rrset); + } +protected: + Name origin; + RRClass zclass; + stringstream rr_stream; + vector<ConstRRsetPtr> results; + TestCallback callback; +}; + +// Commonly used test RRs +const char* const txt_rr = "example.com. 3600 IN TXT \"test data\"\n"; +const char* const a_rr1 = "www.example.com. 60 IN A 192.0.2.1\n"; +const char* const a_rr2 = "www.example.com. 60 IN A 192.0.2.2\n"; +const char* const a_rr3 = "ftp.example.com. 60 IN A 192.0.2.3\n"; +// multi-field RR case +const char* const soa_rr = "example.com. 7200 IN SOA . . 0 0 0 0 0\n"; +// A couple of RRSIGs, different type covered +const char* const rrsig_rr1 = + "www.example.com. 60 IN RRSIG A 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE\n"; +const char* const rrsig_rr2 = + "www.example.com. 60 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE\n"; + +// Commonly used for some tests to check the constructed RR content. +const char* const dnskey_rdata = + "256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH " + "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=\n"; + +TEST_F(MasterLoadTest, loadRRs) { + // a simple case: loading 3 RRs, each consists of a single RRset. + rr_stream << txt_rr << a_rr1 << soa_rr; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(3, results.size()); + EXPECT_EQ(txt_rr, results[0]->toText()); + EXPECT_EQ(a_rr1, results[1]->toText()); + EXPECT_EQ(soa_rr, results[2]->toText()); +} + +TEST_F(MasterLoadTest, loadWithFunctionCallback) { + // The same test as loadRRs but using a normal function (not a functor + // object) + rr_stream << txt_rr << a_rr1 << soa_rr; + masterLoad(rr_stream, origin, zclass, + std::bind(&testCallback, ph::_1, &results)); + ASSERT_EQ(3, results.size()); + EXPECT_EQ(txt_rr, results[0]->toText()); + EXPECT_EQ(a_rr1, results[1]->toText()); + EXPECT_EQ(soa_rr, results[2]->toText()); +} + +TEST_F(MasterLoadTest, loadWithMemFunctionCallback) { + // The same test as loadRRs but using a class member function (with a + // help of std.bind) + rr_stream << txt_rr << a_rr1 << soa_rr; + masterLoad(rr_stream, origin, zclass, + std::bind(&MasterLoadTest::rrsetCallback, this, ph::_1)); + ASSERT_EQ(3, results.size()); + EXPECT_EQ(txt_rr, results[0]->toText()); + EXPECT_EQ(a_rr1, results[1]->toText()); + EXPECT_EQ(soa_rr, results[2]->toText()); +} + +TEST_F(MasterLoadTest, loadComments) { + rr_stream << ";; comment line, should be skipped\n" + << "\n" // blank line (should be skipped) + << txt_rr; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(txt_rr, results[0]->toText()); +} + +TEST_F(MasterLoadTest, loadRRset) { + // load an RRset containing two RRs + rr_stream << a_rr1 << a_rr2; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(string(a_rr1) + string(a_rr2), results[0]->toText()); +} + +TEST_F(MasterLoadTest, loadRRsetsOfSameType) { + // load two RRsets with the same RR type and different owner names. + // the loader must distinguish them as separate RRsets. + rr_stream << a_rr1 << a_rr3; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(2, results.size()); + EXPECT_EQ(a_rr1, results[0]->toText()); + EXPECT_EQ(a_rr3, results[1]->toText()); +} + +TEST_F(MasterLoadTest, loadRRsetsInterleaved) { + // two RRs that belongs to the same RRset (rr1 and rr2) are interleaved + // by another. This is an unexpected case for this loader, but it's + // not considered an error. The loader will simply treat them separate + // RRsets. + rr_stream << a_rr1 << a_rr3 << a_rr2; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(3, results.size()); + EXPECT_EQ(a_rr1, results[0]->toText()); + EXPECT_EQ(a_rr3, results[1]->toText()); + EXPECT_EQ(a_rr2, results[2]->toText()); +} + +TEST_F(MasterLoadTest, loadRRsigs) { + // RRSIGs of different types covered should be separated + rr_stream << rrsig_rr1 << rrsig_rr2; + masterLoad(rr_stream, origin, zclass, callback); + EXPECT_EQ(2, results.size()); +} + +// This test was disabled by #2387, because the test data has trailing +// comments and it (eventually) uses the string RDATA constructor which +// doesn't support them. This test should be fixed and re-enabled by +// #2381, or deleted. +TEST_F(MasterLoadTest, DISABLED_loadRRWithComment) { + // Comment at the end of line should be ignored and the RR should be + // accepted. + rr_stream << "example.com. 3600 IN DNSKEY\t256 3 7 " + "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH " + "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ; key id = 40430\n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::DNSKEY(), zclass, + dnskey_rdata))); +} + +// This test was disabled by #2387, because the test data has trailing +// comments and it (eventually) uses the string RDATA constructor which +// doesn't support them. This test should be fixed and re-enabled by +// #2381, or deleted. +TEST_F(MasterLoadTest, DISABLED_loadRRWithCommentNoSpace) { + // Similar to the previous one, but there's no space before comments. + // It should still work. + rr_stream << "example.com. 3600 IN DNSKEY\t256 3 7 " + "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH " + "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=; key id = 40430\n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::DNSKEY(), zclass, + dnskey_rdata))); +} + +// This test was disabled by #2387, because the test data has trailing +// comments and it (eventually) uses the string RDATA constructor which +// doesn't support them. This test should be fixed and re-enabled by +// #2381, or deleted. +TEST_F(MasterLoadTest, DISABLED_loadRRWithCommentEmptyComment) { + // Similar to the previous one, but there's no data after the ; + // It should still work. + rr_stream << "example.com. 3600 IN DNSKEY\t256 3 7 " + "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH " + "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ;\n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::DNSKEY(), zclass, + dnskey_rdata))); +} + +// This test was disabled by #2387, because the test data has trailing +// comments and it (eventually) uses the string RDATA constructor which +// doesn't support them. This test should be fixed and re-enabled by +// #2381, or deleted. +TEST_F(MasterLoadTest, DISABLED_loadRRWithCommentEmptyCommentNoSpace) { + // Similar to the previous one, but there's no space before or after ; + // It should still work. + rr_stream << "example.com. 3600 IN DNSKEY\t256 3 7 " + "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH " + "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=;\n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::DNSKEY(), zclass, + dnskey_rdata))); +} + +TEST_F(MasterLoadTest, loadRRWithEOLWhitespace) { + // Test with whitespace after rdata + // It should still work. + rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef \n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::NSEC3PARAM(), zclass, + "1 0 1 beef"))); +} + +TEST_F(MasterLoadTest, loadRRWithEOLWhitespaceTab) { + // Similar to the previous one, tab instead of space. + // It should still work. + rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef\t\n"; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::NSEC3PARAM(), zclass, + "1 0 1 beef"))); +} + +TEST_F(MasterLoadTest, loadRRNoComment) { + // A semicolon in a character-string shouldn't confuse the parser. + rr_stream << "example.com. 3600 IN TXT \"aaa;bbb\"\n"; + masterLoad(rr_stream, origin, zclass, callback); + EXPECT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::TXT(), zclass, + "\"aaa;bbb\""))); +} + +TEST_F(MasterLoadTest, nonAbsoluteOwner) { + // If the owner name is not absolute, the zone origin is assumed to be + // its origin. + rr_stream << "example.com 3600 IN A 192.0.2.1"; + masterLoad(rr_stream, origin, zclass, callback); + EXPECT_EQ(1, results.size()); + EXPECT_EQ(results[0]->getName(), Name("example.com.example.com")); +} + +TEST_F(MasterLoadTest, loadRREmptyAndComment) { + // There's no RDATA (invalid in this case) but a comment. This position + // shouldn't cause any disruption and should be treated as a normal error. + rr_stream << "example.com. 3600 IN A ;\n"; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadWithNoEOF) { + // the input stream doesn't end with a new line (and the following blank + // line). It should be accepted. + string rr_string(a_rr1); + rr_string.erase(rr_string.end() - 1); + rr_stream << rr_string; + masterLoad(rr_stream, origin, zclass, callback); + ASSERT_EQ(1, results.size()); + EXPECT_EQ(a_rr1, results[0]->toText()); +} + +TEST_F(MasterLoadTest, loadEmpty) { + // an unusual case: empty input. load must succeed with an empty result. + masterLoad(rr_stream, origin, zclass, callback); + EXPECT_EQ(0, results.size()); +} + +TEST_F(MasterLoadTest, loadWithBeginningSpace) { + rr_stream << " " << a_rr1; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadWithBeginningTab) { + rr_stream << "\t" << a_rr1; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadInvalidRRClass) { + rr_stream << "example.com. 3600 CH TXT \"test text\""; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadOutOfZoneData) { + rr_stream << "example.org. 3600 IN A 192.0.2.255"; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadNonAtopSOA) { + // SOA's owner name must be zone's origin. + rr_stream << "soa.example.com. 3600 IN SOA . . 0 0 0 0 0"; + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); +} + +// Load TTL with units +TEST_F(MasterLoadTest, loadUnitTTL) { + stringstream rr_stream2("example.com. 1D IN A 192.0.2.1"); + masterLoad(rr_stream2, origin, zclass, callback); + EXPECT_EQ(1, results.size()); + EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare( + *rdata::createRdata(RRType::A(), zclass, "192.0.2.1"))); +} + +TEST_F(MasterLoadTest, loadBadRRText) { + rr_stream << "example..com. 3600 IN A 192.0.2.1"; // bad owner name + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback), + MasterLoadError); + + // bad RR class text + stringstream rr_stream3("example.com. 3600 BAD A 192.0.2.1"); + EXPECT_THROW(masterLoad(rr_stream3, origin, zclass, callback), + MasterLoadError); + + // bad RR type text + stringstream rr_stream4("example.com. 3600 IN BAD 192.0.2.1"); + EXPECT_THROW(masterLoad(rr_stream4, origin, zclass, callback), + MasterLoadError); + + // bad RDATA text + stringstream rr_stream5("example.com. 3600 IN A 2001:db8::1"); + EXPECT_THROW(masterLoad(rr_stream5, origin, zclass, callback), + MasterLoadError); + + // incomplete RR text + stringstream rr_stream6("example.com. 3600 IN A"); + EXPECT_THROW(masterLoad(rr_stream6, origin, zclass, callback), + MasterLoadError); +} + +// This is a helper callback to test the case the input stream becomes bad +// in the middle of processing. +class StreamInvalidator { +public: + StreamInvalidator(stringstream& ss) : ss_(ss) {} + void operator()(ConstRRsetPtr) { + ss_.setstate(ios::badbit); + } +private: + stringstream& ss_; +}; + +TEST_F(MasterLoadTest, loadBadStream) { + rr_stream << txt_rr << a_rr1; + StreamInvalidator invalidator(rr_stream); + EXPECT_THROW(masterLoad(rr_stream, origin, zclass, invalidator), + MasterLoadError); +} + +TEST_F(MasterLoadTest, loadFromFile) { + // The main parser is shared with the stream version, so we simply test + // file I/O specific parts. + masterLoad(TEST_DATA_SRCDIR "/masterload.txt", origin, zclass, callback); + ASSERT_EQ(2, results.size()); + EXPECT_EQ(txt_rr, results[0]->toText()); + EXPECT_EQ(string(a_rr1) + string(a_rr2), results[1]->toText()); + + // NULL file name. Should result in exception. + EXPECT_THROW(masterLoad(NULL, origin, zclass, callback), MasterLoadError); + + // Non existent file name. Ditto. + EXPECT_THROW(masterLoad(TEST_DATA_BUILDDIR "/nonexistent.txt", origin, + zclass, callback), MasterLoadError); +} +} // end namespace diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc new file mode 100644 index 0000000..8b6832b --- /dev/null +++ b/src/lib/dns/tests/message_unittest.cc @@ -0,0 +1,1162 @@ +// 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 <config.h> + +#include <fstream> + +#include <boost/scoped_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> + +#include <util/unittests/testdata.h> +#include <util/unittests/textdata.h> + +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/question.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +// +// Note: we need more tests, including: +// parsing malformed headers +// more complete tests about parsing/rendering header flags, opcode, rcode, etc. +// tests for adding RRsets +// tests for RRset/Question iterators +// But, we'll ship with the current set of tests for now, partly because many +// of the above are covered as part of other tests, and partly due to time +// limitation. We also expect to revisit the fundamental design of the Message +// class, at which point we'll also revise the tests including more cases. +// + +const uint16_t Message::DEFAULT_MAX_UDPSIZE; + +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +// XXX: this is defined as class static constants, but some compilers +// seemingly cannot find the symbol when used in the EXPECT_xxx macros. +const uint16_t TSIGContext::DEFAULT_FUDGE; + +namespace { +class MessageTest : public ::testing::Test { +protected: + MessageTest() : test_name("test.example.com"), obuffer(0), + message_parse(Message::PARSE), + message_render(Message::RENDER), + bogus_section(static_cast<Message::Section>( + Message::SECTION_ADDITIONAL + 1)), + tsig_ctx(TSIGKey("www.example.com:" + "SFuWd/q99SzF8Yzd1QbB9g==")) + { + rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::A(), RRTTL(3600))); + rrset_a->addRdata(in::A("192.0.2.1")); + rrset_a->addRdata(in::A("192.0.2.2")); + + rrset_aaaa = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::AAAA(), RRTTL(3600))); + rrset_aaaa->addRdata(in::AAAA("2001:db8::1234")); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + rrset_rrsig->addRdata(generic::RRSIG("AAAA 5 3 7200 20100322084538 " + "20100220084538 1 example.com. " + "FAKEFAKEFAKEFAKE")); + rrset_aaaa->addRRsig(rrset_rrsig); + } + + static Question factoryFromFile(const char* datafile); + const Name test_name; + OutputBuffer obuffer; + MessageRenderer renderer; + Message message_parse; + Message message_render; + const Message::Section bogus_section; + RRsetPtr rrset_a; // A RRset with two RDATAs + RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG + RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset + TSIGContext tsig_ctx; + vector<unsigned char> received_data; + vector<unsigned char> expected_data; + + void factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options = + Message::PARSE_DEFAULT); +}; + +void +MessageTest::factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options) +{ + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); + + InputBuffer buffer(&received_data[0], received_data.size()); + message.fromWire(buffer, options); +} + +TEST_F(MessageTest, headerFlag) { + // by default no flag is set + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_TC)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RA)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AD)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_CD)); + + // set operation: by default it will be on + message_render.setHeaderFlag(Message::HEADERFLAG_QR); + EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_QR)); + + // it can be set to on explicitly, too + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + + // the bit can also be cleared + message_render.setHeaderFlag(Message::HEADERFLAG_AA, false); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + + // Invalid flag values + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0)), InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x7000)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x0800)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x0040)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x10000)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x80000000)), + InvalidParameter); + + // set operation isn't allowed in the parse mode. + EXPECT_THROW(message_parse.setHeaderFlag(Message::HEADERFLAG_QR), + InvalidMessageOperation); +} +TEST_F(MessageTest, getEDNS) { + EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set + + factoryFromFile(message_parse, "message_fromWire10.wire"); + EXPECT_TRUE(message_parse.getEDNS()); + EXPECT_EQ(0, message_parse.getEDNS()->getVersion()); + EXPECT_EQ(4096, message_parse.getEDNS()->getUDPSize()); + EXPECT_TRUE(message_parse.getEDNS()->getDNSSECAwareness()); +} + +TEST_F(MessageTest, setEDNS) { + // setEDNS() isn't allowed in the parse mode + EXPECT_THROW(message_parse.setEDNS(EDNSPtr(new EDNS())), + InvalidMessageOperation); + + EDNSPtr edns = EDNSPtr(new EDNS()); + message_render.setEDNS(edns); + EXPECT_EQ(edns, message_render.getEDNS()); +} + +TEST_F(MessageTest, fromWireWithTSIG) { + // Initially there should be no TSIG + EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord()); + + // getTSIGRecord() is only valid in the parse mode. + EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation); + + factoryFromFile(message_parse, "message_toWire2.wire"); + const uint8_t expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 + }; + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast<void*>(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength + EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm()); + EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned()); + EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge()); + matchWireData(expected_mac, sizeof(expected_mac), + tsig_rr->getRdata().getMAC(), + tsig_rr->getRdata().getMACSize()); + EXPECT_EQ(0, tsig_rr->getRdata().getError()); + EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen()); + EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData()); + + // If we clear the message for reuse, the recorded TSIG will be cleared. + message_parse.clear(Message::PARSE); + EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord()); +} + +TEST_F(MessageTest, fromWireWithTSIGCompressed) { + // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed. + factoryFromFile(message_parse, "message_fromWire12.wire"); + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast<void*>(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + // len(www.example.com) = 17, but when fully compressed, the length is + // 2 bytes. So the length of the record should be 15 bytes shorter. + EXPECT_EQ(70, tsig_rr->getLength()); +} + +TEST_F(MessageTest, fromWireWithBadTSIG) { + // Multiple TSIG RRs + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG in the answer section (must be in additional) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG is not the last record. + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // Unexpected RR Class (this will fail in constructing TSIGRecord) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"), + DNSMessageFORMERR); +} + +TEST_F(MessageTest, getRRCount) { + // by default all counters should be 0 + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.addQuestion(Question(Name("test.example.com"), + RRClass::IN(), RRType::A())); + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + + // rrset_a contains two RRs + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + + // parse a message containing a Question and EDNS OPT RR. + // OPT shouldn't be counted as normal RR, so result of getRRCount + // shouldn't change. + factoryFromFile(message_parse, "message_fromWire11.wire"); + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + // out-of-band section ID + EXPECT_THROW(message_parse.getRRCount(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, addRRset) { + // initially, we have 0 + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); + + // add two A RRs (unsigned) + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_EQ(rrset_a, + *message_render.beginSection(Message::SECTION_ANSWER)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + + message_render.clear(Message::RENDER); + + // add one AAAA RR (signed) + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + EXPECT_EQ(rrset_aaaa, + *message_render.beginSection(Message::SECTION_ANSWER)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, badAddRRset) { + // addRRset() isn't allowed in the parse mode. + EXPECT_THROW(message_parse.addRRset(Message::SECTION_ANSWER, + rrset_a), InvalidMessageOperation); + // out-of-band section ID + EXPECT_THROW(message_render.addRRset(bogus_section, rrset_a), OutOfRange); + + // NULL RRset + EXPECT_THROW(message_render.addRRset(Message::SECTION_ANSWER, RRsetPtr()), + InvalidParameter); +} + +TEST_F(MessageTest, hasRRset) { + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + // section doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + // name doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, + Name("nomatch.example"), + RRClass::IN(), RRType::A())); + // RR class doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::CH(), RRType::A())); + // RR type doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + + // out-of-band section ID + EXPECT_THROW(message_render.hasRRset(bogus_section, test_name, + RRClass::IN(), RRType::A()), + OutOfRange); + + // Repeat the checks having created an RRset of the appropriate type. + + RRsetPtr rrs1(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(60))); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, rrs1)); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, rrs1)); + + RRsetPtr rrs2(new RRset(Name("nomatch.example"), RRClass::IN(), RRType::A(), + RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs2)); + + RRsetPtr rrs3(new RRset(test_name, RRClass::CH(), RRType::A(), RRTTL(60))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs3)); + + RRsetPtr rrs4(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4)); + + RRsetPtr rrs5(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4)); + + EXPECT_THROW(message_render.hasRRset(bogus_section, rrs1), OutOfRange); +} + +TEST_F(MessageTest, removeRRset) { + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + + // Locate the AAAA RRset and remove it and any associated RRSIGs + RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER); + if ((*i)->getType() == RRType::A()) { + ++i; + } + EXPECT_EQ(RRType::AAAA(), (*i)->getType()); + message_render.removeRRset(Message::SECTION_ANSWER, i); + + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, clearQuestionSection) { + QuestionPtr q(new Question(Name("www.example.com"), RRClass::IN(), + RRType::A())); + message_render.addQuestion(q); + ASSERT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + + message_render.clearSection(Message::SECTION_QUESTION); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_TRUE(message_render.beginQuestion() == + message_render.endQuestion()); +} + + +TEST_F(MessageTest, clearAnswerSection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + + message_render.clearSection(Message::SECTION_ANSWER); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, clearAuthoritySection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a); + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_AUTHORITY)); + + message_render.clearSection(Message::SECTION_AUTHORITY); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); +} + +TEST_F(MessageTest, clearAdditionalSection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.clearSection(Message::SECTION_ADDITIONAL); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); +} + +TEST_F(MessageTest, badClearSection) { + // attempt of clearing a message in the parse mode. + EXPECT_THROW(message_parse.clearSection(Message::SECTION_QUESTION), + InvalidMessageOperation); + // attempt of clearing out-of-range section + EXPECT_THROW(message_render.clearSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, badBeginSection) { + // valid cases are tested via other tests + EXPECT_THROW(message_render.beginSection(Message::SECTION_QUESTION), + InvalidMessageSection); + EXPECT_THROW(message_render.beginSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, badEndSection) { + // valid cases are tested via other tests + EXPECT_THROW(message_render.endSection(Message::SECTION_QUESTION), + InvalidMessageSection); + EXPECT_THROW(message_render.endSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, appendSection) { + Message target(Message::RENDER); + + // Section check + EXPECT_THROW(target.appendSection(bogus_section, message_render), + OutOfRange); + + // Make sure nothing is copied if there is nothing to copy + target.appendSection(Message::SECTION_QUESTION, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_QUESTION)); + target.appendSection(Message::SECTION_ANSWER, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_ANSWER)); + target.appendSection(Message::SECTION_AUTHORITY, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_AUTHORITY)); + target.appendSection(Message::SECTION_ADDITIONAL, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_ADDITIONAL)); + + // Now add some data, copy again, and see if it got added + message_render.addQuestion(Question(Name("test.example.com"), + RRClass::IN(), RRType::A())); + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa); + + target.appendSection(Message::SECTION_QUESTION, message_render); + EXPECT_EQ(1, target.getRRCount(Message::SECTION_QUESTION)); + + target.appendSection(Message::SECTION_ANSWER, message_render); + EXPECT_EQ(2, target.getRRCount(Message::SECTION_ANSWER)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + + target.appendSection(Message::SECTION_AUTHORITY, message_render); + EXPECT_EQ(2, target.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + + target.appendSection(Message::SECTION_ADDITIONAL, message_render); + EXPECT_EQ(4, target.getRRCount(Message::SECTION_ADDITIONAL)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + + // One more test, test to see if the section gets added, not replaced + Message source2(Message::RENDER); + source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + target.appendSection(Message::SECTION_ANSWER, source2); + EXPECT_EQ(4, target.getRRCount(Message::SECTION_ANSWER)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + +} + +TEST_F(MessageTest, parseHeader) { + received_data.clear(); + UnitTestUtil::readWireData("message_fromWire1", received_data); + + // parseHeader() isn't allowed in the render mode. + InputBuffer buffer(&received_data[0], received_data.size()); + EXPECT_THROW(message_render.parseHeader(buffer), InvalidMessageOperation); + + message_parse.parseHeader(buffer); + EXPECT_EQ(0x1035, message_parse.getQid()); + EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); + EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode()); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_RA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_AD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_CD)); + EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL)); + + // Only the header part should have been examined. + EXPECT_EQ(12, buffer.getPosition()); // 12 = size of the header section + EXPECT_TRUE(message_parse.beginQuestion() == message_parse.endQuestion()); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ANSWER) == + message_parse.endSection(Message::SECTION_ANSWER)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_AUTHORITY) == + message_parse.endSection(Message::SECTION_AUTHORITY)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ADDITIONAL) == + message_parse.endSection(Message::SECTION_ADDITIONAL)); +} + +void +checkMessageFromWire(const Message& message_parse, + const Name& test_name) +{ + EXPECT_EQ(0x1035, message_parse.getQid()); + EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); + EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode()); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA)); + + QuestionPtr q = *message_parse.beginQuestion(); + EXPECT_EQ(test_name, q->getName()); + EXPECT_EQ(RRType::A(), q->getType()); + EXPECT_EQ(RRClass::IN(), q->getClass()); + EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL)); + + RRsetPtr rrset = *message_parse.beginSection(Message::SECTION_ANSWER); + EXPECT_EQ(test_name, rrset->getName()); + EXPECT_EQ(RRType::A(), rrset->getType()); + EXPECT_EQ(RRClass::IN(), rrset->getClass()); + // TTL should be 3600, even though that of the 2nd RR is 7200 + EXPECT_EQ(RRTTL(3600), rrset->getTTL()); + RdataIteratorPtr it = rrset->getRdataIterator(); + EXPECT_EQ("192.0.2.1", it->getCurrent().toText()); + it->next(); + EXPECT_EQ("192.0.2.2", it->getCurrent().toText()); + it->next(); + EXPECT_TRUE(it->isLast()); +} + + +TEST_F(MessageTest, fromWire) { + // fromWire() isn't allowed in the render mode. + EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"), + InvalidMessageOperation); + + factoryFromFile(message_parse, "message_fromWire1"); + checkMessageFromWire(message_parse, test_name); +} + +TEST_F(MessageTest, fromWireMultiple) { + // Parse from wire multiple times. + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + checkMessageFromWire(message_parse, test_name); + + // Calling parseHeader() directly before fromWire() should not cause + // any problems. + received_data.clear(); + UnitTestUtil::readWireData("message_fromWire1", received_data); + + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.parseHeader(buffer); + message_parse.fromWire(buffer); + message_parse.parseHeader(buffer); + message_parse.fromWire(buffer); + checkMessageFromWire(message_parse, test_name); +} + +TEST_F(MessageTest, fromWireShortBuffer) { + // We trim a valid message (ending with an SOA RR) for one byte. + // fromWire() should throw an exception while parsing the trimmed RR. + UnitTestUtil::readWireData("message_fromWire22.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size() - 1); + EXPECT_THROW(message_parse.fromWire(buffer), InvalidBufferPosition); +} + +TEST_F(MessageTest, fromWireCombineRRs) { + // This message contains 3 RRs in the answer section in the order of + // A, AAAA, A types. fromWire() should combine the two A RRs into a + // single RRset by default. + factoryFromFile(message_parse, "message_fromWire19.wire"); + + RRsetIterator it = message_parse.beginSection(Message::SECTION_ANSWER); + RRsetIterator it_end = message_parse.endSection(Message::SECTION_ANSWER); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(2, (*it)->getRdataCount()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); +} + +// A helper function for a test pattern commonly used in several tests below. +void +preserveRRCheck(const Message& message, Message::Section section) { + RRsetIterator it = message.beginSection(section); + RRsetIterator it_end = message.endSection(section); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("2001:db8::1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.2", (*it)->getRdataIterator()->getCurrent().toText()); +} + +TEST_F(MessageTest, fromWirePreserveAnswer) { + // Using the same data as the previous test, but specify the PRESERVE_ORDER + // option. The received order of RRs should be preserved, and each RR + // should be stored in a single RRset. + factoryFromFile(message_parse, "message_fromWire19.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve answer RRs"); + preserveRRCheck(message_parse, Message::SECTION_ANSWER); + } +} + +TEST_F(MessageTest, fromWirePreserveAuthority) { + // Same for the previous test, but for the authority section. + factoryFromFile(message_parse, "message_fromWire20.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve authority RRs"); + preserveRRCheck(message_parse, Message::SECTION_AUTHORITY); + } +} + +TEST_F(MessageTest, fromWirePreserveAdditional) { + // Same for the previous test, but for the additional section. + factoryFromFile(message_parse, "message_fromWire21.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve additional RRs"); + preserveRRCheck(message_parse, Message::SECTION_ADDITIONAL); + } +} + +TEST_F(MessageTest, EDNS0ExtRcode) { + // Extended Rcode = BADVERS + factoryFromFile(message_parse, "message_fromWire10.wire"); + EXPECT_EQ(Rcode::BADVERS(), message_parse.getRcode()); + + // Maximum extended Rcode + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_fromWire11.wire"); + EXPECT_EQ(0xfff, message_parse.getRcode().getCode()); +} + +TEST_F(MessageTest, BadEDNS0) { + // OPT RR in the answer section + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire4"), + DNSMessageFORMERR); + // multiple OPT RRs (in the additional section) + message_parse.clear(Message::PARSE); + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire5"), + DNSMessageFORMERR); +} + +TEST_F(MessageTest, toWire) { + message_render.setQid(0x1035); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::A())); + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire1", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireSigned) { + message_render.setQid(0x75c1); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::A())); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(2, rrset_a->getRRsigDataCount()); + + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire6", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireSignedAndTruncated) { + message_render.setQid(0x75c1); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::TXT())); + + RRsetPtr rrset_txt = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::TXT(), RRTTL(3600))); + rrset_txt->addRdata(generic::TXT(string(255, 'a'))); + rrset_txt->addRdata(generic::TXT(string(255, 'b'))); + rrset_txt->addRdata(generic::TXT(string(255, 'c'))); + rrset_txt->addRdata(generic::TXT(string(255, 'd'))); + rrset_txt->addRdata(generic::TXT(string(255, 'e'))); + rrset_txt->addRdata(generic::TXT(string(255, 'f'))); + rrset_txt->addRdata(generic::TXT(string(255, 'g'))); + rrset_txt->addRdata(generic::TXT(string(255, 'h'))); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("TXT 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_txt->addRRsig(rrset_rrsig); + EXPECT_EQ(1, rrset_txt->getRRsigDataCount()); + + message_render.addRRset(Message::SECTION_ANSWER, rrset_txt); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(9, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire7", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireInParseMode) { + // toWire() isn't allowed in the parse mode. + EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation); +} + +// See dnssectime_unittest.cc +template <int64_t NOW> +int64_t +testGetTime() { + return (NOW); +} + +// bit-wise constant flags to configure DNS header flags for test +// messages. +const unsigned int QR_FLAG = 0x1; +const unsigned int AA_FLAG = 0x2; +const unsigned int RD_FLAG = 0x4; + +void +commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, + TSIGContext& tsig_ctx, const char* const expected_file, + unsigned int message_flags = RD_FLAG, + RRType qtype = RRType::A(), + const vector<const char*>* answer_data = NULL) +{ + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), + qtype)); + + if (answer_data != NULL) { + RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(), + qtype, RRTTL(86400))); + for (vector<const char*>::const_iterator it = answer_data->begin(); + it != answer_data->end(); + ++it) { + ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), *it)); + } + message.addRRset(Message::SECTION_ANSWER, ans_rrset); + } + + message.toWire(renderer, &tsig_ctx); + vector<unsigned char> expected_data; + UnitTestUtil::readWireData(expected_file, expected_data); + matchWireData(&expected_data[0], expected_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireWithTSIG) { + // Rendering a message with TSIG. Various special cases specific to + // TSIG are tested in the tsig tests. We only check the message contains + // a TSIG at the end and the ARCOUNT of the header is updated. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + message_render.setQid(0x2d65); + + { + SCOPED_TRACE("Message sign with TSIG"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"); + } +} + +TEST_F(MessageTest, toWireWithEDNSAndTSIG) { + // Similar to the previous test, but with an EDNS before TSIG. + // The wire data check will confirm the ordering. + isc::util::detail::gettimeFunction = testGetTime<0x4db60d1f>; + + message_render.setQid(0x6cd); + + EDNSPtr edns(new EDNS()); + edns->setUDPSize(4096); + message_render.setEDNS(edns); + + { + SCOPED_TRACE("Message sign with TSIG and EDNS"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire3.wire"); + } +} + +// Some of the following tests involve truncation. We use the query name +// "www.example.com" and some TXT question/answers. The length of the +// header and question will be 33 bytes. If we also try to include a +// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR +// will be 85 bytes. + +// A long TXT RDATA. With a fully compressed owner name, the corresponding +// RR will be 268 bytes. +const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"; + +// With a fully compressed owner name, the corresponding RR will be 212 bytes. +// It should result in truncation even without TSIG (33 + 268 + 212 = 513) +const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; + +// With a fully compressed owner name, the corresponding RR will be 127 bytes. +// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but +// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513) +const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"; + +// This is 1 byte shorter than txt3, which will result in a possible longest +// message containing answer RRs and TSIG. +const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"; + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt +// QID: 0x22c2 +// Time Signed: 0x00004e179212 +TEST_F(MessageTest, toWireTSIGTruncation) { + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + + // Verify a validly signed query so that we can use the TSIG context + + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0x22c2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt2); + { + SCOPED_TRACE("Message sign with TSIG and TC bit on"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGTruncation2) { + // Similar to the previous test, but without TSIG it wouldn't cause + // truncation. + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0x22c2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt3); + { + SCOPED_TRACE("Message sign with TSIG and TC bit on (2)"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGTruncation3) { + // Similar to previous ones, but truncation occurs due to too many + // Questions (very unusual, but not necessarily illegal). + + // We are going to create a message starting with a standard + // header (12 bytes) and multiple questions in the Question + // section of the same owner name (changing the RRType, just so + // that it would be the form that would be accepted by the BIND 9 + // parser). The first Question is 21 bytes in length, and the subsequent + // ones are 6 bytes. We'll also use a TSIG whose size is 85 bytes. + // Up to 66 questions can fit in the standard 512-byte buffer + // (12 + 21 + 6 * 65 + 85 = 508). If we try to add one more it would + // result in truncation. + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + for (int i = 1; i <= 67; ++i) { + message_render.addQuestion(Question(Name("www.example.com"), + RRClass::IN(), RRType(i))); + } + message_render.toWire(renderer, &tsig_ctx); + + // Check the rendered data by parsing it. We only check it has the + // TC bit on, has the correct number of questions, and has a TSIG RR. + // Checking the signature wouldn't be necessary for this rare case + // scenario. + InputBuffer buffer(renderer.getData(), renderer.getLength()); + message_parse.fromWire(buffer); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + // Note that the number of questions are 66, not 67 as we tried to add. + EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_TRUE(message_parse.getTSIGRecord() != NULL); +} + +TEST_F(MessageTest, toWireTSIGNoTruncation) { + // A boundary case that shouldn't cause truncation: the resulting + // response message with a TSIG will be 512 bytes long. + isc::util::detail::gettimeFunction = testGetTime<0x4e17b38d>; + factoryFromFile(message_parse, "message_fromWire18.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0xd6e2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt4); + { + SCOPED_TRACE("Message sign with TSIG, no truncation"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire5.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +// This is a buggy renderer for testing. It behaves like the straightforward +// MessageRenderer, but once it has some data, its setLengthLimit() ignores +// the given parameter and resets the limit to the current length, making +// subsequent insertion result in truncation, which would make TSIG RR +// rendering fail unexpectedly in the test that follows. +class BadRenderer : public MessageRenderer { +public: + virtual void setLengthLimit(size_t len) { + if (getLength() > 0) { + MessageRenderer::setLengthLimit(getLength()); + } else { + MessageRenderer::setLengthLimit(len); + } + } +}; + +TEST_F(MessageTest, toWireTSIGLengthErrors) { + // specify an unusual short limit that wouldn't be able to hold + // the TSIG. + renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1); + // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid + // conditions. The checks inside it don't matter because we expect an + // exception before any of the checks. + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // This one is large enough for TSIG, but the remaining limit isn't + // even enough for the Header section. + renderer.clear(); + message_render.clear(Message::RENDER); + renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1); + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // Trying to render a message with TSIG using a buggy renderer. + BadRenderer bad_renderer; + bad_renderer.setLengthLimit(512); + message_render.clear(Message::RENDER); + EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx, + "message_toWire2.wire"), + Unexpected); +} + +TEST_F(MessageTest, toWireWithoutOpcode) { + message_render.setRcode(Rcode::NOERROR()); + EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); +} + +TEST_F(MessageTest, toWireWithoutRcode) { + message_render.setOpcode(Opcode::QUERY()); + EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); +} + +TEST_F(MessageTest, toText) { + // Check toText() output for a typical DNS response with records in + // all sections + + factoryFromFile(message_parse, "message_toText1.wire"); + { + SCOPED_TRACE("Message toText test (basic case)"); + ifstream ifs; + unittests::openTestData("message_toText1.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with EDNS. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): adding + // a newline after the "OPT PSEUDOSECTION". This is an intentional change + // in our version for better readability. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText2.wire"); + { + SCOPED_TRACE("Message toText test with EDNS"); + ifstream ifs; + unittests::openTestData("message_toText2.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with TSIG. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): removing + // a redundant white space at the end of TSIG RDATA. We'd rather consider + // it a dig's defect than a feature. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText3.wire"); + { + SCOPED_TRACE("Message toText test with TSIG"); + ifstream ifs; + unittests::openTestData("message_toText3.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } +} + +TEST_F(MessageTest, toTextWithoutOpcode) { + message_render.setRcode(Rcode::NOERROR()); + EXPECT_THROW(message_render.toText(), InvalidMessageOperation); +} + +TEST_F(MessageTest, toTextWithoutRcode) { + message_render.setOpcode(Opcode::QUERY()); + EXPECT_THROW(message_render.toText(), InvalidMessageOperation); +} +} diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc new file mode 100644 index 0000000..c3a53eb --- /dev/null +++ b/src/lib/dns/tests/messagerenderer_unittest.cc @@ -0,0 +1,292 @@ +// 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/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/labelsequence.h> +#include <dns/messagerenderer.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> + +#include <string> +#include <vector> + +using isc::UnitTestUtil; +using isc::dns::Name; +using isc::dns::LabelSequence; +using isc::dns::MessageRenderer; +using isc::util::OutputBuffer; +using boost::lexical_cast; +using isc::util::unittests::matchWireData; + +namespace { +class MessageRendererTest : public ::testing::Test { +protected: + MessageRendererTest() : expected_size(0) { + data16 = (2 << 8) | 3; + data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7; + } + size_t expected_size; + uint16_t data16; + uint32_t data32; + MessageRenderer renderer; + std::vector<unsigned char> data; + static const uint8_t testdata[5]; +}; + +const uint8_t MessageRendererTest::testdata[5] = {1, 2, 3, 4, 5}; + +// The test cases are borrowed from those for the OutputBuffer class. +TEST_F(MessageRendererTest, writeInteger) { + renderer.writeUint16(data16); + expected_size += sizeof(data16); + + matchWireData(&testdata[1], sizeof(data16), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeName) { + UnitTestUtil::readWireData("name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + renderer.writeName(Name("a.example.org.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameInLargeBuffer) { + size_t offset = 0x3fff; + renderer.skip(offset); + + UnitTestUtil::readWireData("name_toWire2", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + static_cast<const uint8_t*>(renderer.getData()) + offset, + renderer.getLength() - offset); +} + +TEST_F(MessageRendererTest, writeNameWithUncompressed) { + UnitTestUtil::readWireData("name_toWire3", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com."), false); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNamePointerChain) { + UnitTestUtil::readWireData("name_toWire4", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, compressMode) { + // By default the render performs case insensitive compression. + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); + + // The mode can be explicitly changed. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + EXPECT_EQ(MessageRenderer::CASE_SENSITIVE, renderer.getCompressMode()); + renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE); + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); + + // The clear() method resets the mode to the default. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + renderer.clear(); + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); +} + +TEST_F(MessageRendererTest, writeNameCaseCompress) { + // By default MessageRenderer performs case insensitive compression. + + UnitTestUtil::readWireData("name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + // this should match the first name in terms of compression: + renderer.writeName(Name("b.exAmple.CoM.")); + renderer.writeName(Name("a.example.org.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) { + // name compression in case sensitive manner. See the data file + // description for details. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + UnitTestUtil::readWireData("name_toWire5.wire", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.eXample.com.")); + renderer.writeName(Name("c.eXample.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameMixedCaseCompress) { + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + UnitTestUtil::readWireData("name_toWire6.wire", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.eXample.com.")); + + // Change the compression mode in the middle of rendering. This is not + // allowed in this implementation. + EXPECT_THROW(renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE), + isc::InvalidParameter); + + // Once the renderer is cleared, it's okay again. + renderer.clear(); + EXPECT_NO_THROW(renderer.setCompressMode( + MessageRenderer::CASE_INSENSITIVE)); +} + +TEST_F(MessageRendererTest, writeRootName) { + // root name is special: it never causes compression or can (reasonably) + // be a compression pointer. So it makes sense to check this case + // explicitly. + Name example_name = Name("www.example.com"); + + OutputBuffer expected(0); + expected.writeUint8(0); // root name + example_name.toWire(expected); + + renderer.writeName(Name(".")); + renderer.writeName(example_name); + matchWireData(static_cast<const uint8_t*>(expected.getData()), + expected.getLength(), + static_cast<const uint8_t*>(renderer.getData()), + renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence1) { + UnitTestUtil::readWireData("name_toWire7", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + // a.example.com. + renderer.writeName(ls1); + + ls1.stripLeft(1); + + // example.com. + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence2) { + UnitTestUtil::readWireData("name_toWire8", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + ls1.stripRight(1); + + // a.example.com (without root .) + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence3) { + UnitTestUtil::readWireData("name_toWire9", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + // a.example.com. + renderer.writeName(ls1); + + ls1.stripRight(1); + + // a.example.com (without root .) + renderer.writeName(ls1); + + ls1.stripRight(1); + + // a.example + renderer.writeName(ls1); + + ls1.stripLeft(1); + + // example + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, setBuffer) { + OutputBuffer new_buffer(0); + renderer.setBuffer(&new_buffer); + EXPECT_EQ(0, new_buffer.getLength()); // the buffer should be still empty + renderer.writeUint32(42); + EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength()); + EXPECT_EQ(sizeof(uint32_t), renderer.getLength()); + + // Change some other internal state for the reset test below. + EXPECT_EQ(512, renderer.getLengthLimit()); + renderer.setLengthLimit(4096); + EXPECT_EQ(4096, renderer.getLengthLimit()); + + // Reset the buffer to the default again. Other internal states and + // resources should be cleared. The used buffer should be intact. + renderer.setBuffer(NULL); + EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength()); + EXPECT_EQ(0, renderer.getLength()); + EXPECT_EQ(512, renderer.getLengthLimit()); +} + +TEST_F(MessageRendererTest, setBufferErrors) { + OutputBuffer new_buffer(0); + + // Buffer cannot be reset when the renderer is in use. + renderer.writeUint32(10); + EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter); + + renderer.clear(); + renderer.setBuffer(&new_buffer); + renderer.writeUint32(10); + EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter); + + // Resetting the buffer isn't allowed for the default buffer. + renderer.setBuffer(NULL); + EXPECT_THROW(renderer.setBuffer(NULL), isc::InvalidParameter); + + // It's okay to reset a temporary buffer without using it. + renderer.setBuffer(&new_buffer); + EXPECT_NO_THROW(renderer.setBuffer(NULL)); +} + +TEST_F(MessageRendererTest, manyRRs) { + // Render a large number of names, and the confirm the resulting wire + // data store the expected names in the correct order (1000 is an + // arbitrary choice). + for (size_t i = 0; i < 1000; ++i) { + renderer.writeName(Name(lexical_cast<std::string>(i) + ".example")); + } + isc::util::InputBuffer b(renderer.getData(), renderer.getLength()); + for (size_t i = 0; i < 1000; ++i) { + EXPECT_EQ(Name(lexical_cast<std::string>(i) + ".example"), Name(b)); + } + // This will trigger trimming excessive hash items. It shouldn't cause + // any disruption. + EXPECT_NO_THROW(renderer.clear()); +} +} diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc new file mode 100644 index 0000000..caf1f12 --- /dev/null +++ b/src/lib/dns/tests/name_unittest.cc @@ -0,0 +1,797 @@ +// 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 <config.h> + +#include <vector> +#include <string> +#include <sstream> +#include <iomanip> +#include <limits> +#include <stdexcept> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using isc::util::unittests::matchWireData; + +// +// XXX: these are defined as class static constants, but some compilers +// seemingly cannot find the symbols when used in the EXPECT_xxx macros. +// +const size_t Name::MAX_WIRE; +const size_t Name::MAX_LABELS; + +// This is a name of maximum allowed number of labels +const char* max_labels_str = "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240 + "0.1.2.3.4.5.6"; +// This is a name of maximum allowed length +const char* max_len_str = "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123"; + +namespace { +class NameTest : public ::testing::Test { +protected: + NameTest() : example_name("www.example.com"), + example_name_upper("WWW.EXAMPLE.COM"), + small_name("aaa.example.com"), + large_name("zzz.example.com"), + origin_name("example.com."), + origin_name_upper("EXAMPLE.COM"), + buffer_actual(0), buffer_expected(0) + {} + + const Name example_name; + Name example_name_upper; // this will be modified and cannot be const + const Name small_name; + const Name large_name; + const Name origin_name; + const Name origin_name_upper; + OutputBuffer buffer_actual, buffer_expected; + + // + // helper methods + // + static Name nameFactoryFromWire(const char* datafile, size_t position, + bool downcase = false); + // construct a name including all non-upper-case-alphabet characters. + static Name nameFactoryLowerCase(); + void compareInWireFormat(const Name& name_actual, + const Name& name_expected); +}; + +const Name downcased_global("\\255.EXAMPLE.COM", true); + +Name +NameTest::nameFactoryFromWire(const char* datafile, size_t position, + bool downcase) +{ + vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + return (Name(buffer, downcase)); +} + +Name +NameTest::nameFactoryLowerCase() { + string lowercase_namestr; + lowercase_namestr.reserve(Name::MAX_WIRE); + + unsigned int ch = 0; + unsigned int labelcount = 0; + do { + if (ch < 'A' || ch > 'Z') { + ostringstream ss; + ss.setf(ios_base::right, ios_base::adjustfield); + ss.width(3); + ss << setfill('0') << ch; + lowercase_namestr += '\\' + ss.str(); + + if (++labelcount == Name::MAX_LABELLEN) { + lowercase_namestr.push_back('.'); + labelcount = 0; + } + } + } while (++ch <= Name::MAX_WIRE); + + return (Name(lowercase_namestr)); +} + +void +NameTest::compareInWireFormat(const Name& name_actual, + const Name& name_expected) +{ + buffer_actual.clear(); + buffer_expected.clear(); + + name_actual.toWire(buffer_actual); + name_expected.toWire(buffer_expected); + + matchWireData(buffer_expected.getData(), buffer_expected.getLength(), + buffer_actual.getData(), buffer_actual.getLength()); +} + +TEST_F(NameTest, nonlocalObject) { + // A previous version of code relied on a non local static object for + // name construction, so a non local static Name object defined outside + // the name module might not be initialized correctly. This test detects + // that kind of bug. + EXPECT_EQ("\\255.example.com.", downcased_global.toText()); +} + +template <typename ExceptionType> +void +checkBadTextName(const string& txt) { + // Check it results in the specified type of exception as well as + // NameParserException. + EXPECT_THROW(Name(txt, false), ExceptionType); + EXPECT_THROW(Name(txt, false), NameParserException); + // The same is thrown when constructing by the master-file constructor + EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()), + ExceptionType); + EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()), + NameParserException); +} + +TEST_F(NameTest, checkExceptionsHierarchy) { + EXPECT_NO_THROW({ + const isc::dns::EmptyLabel exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::TooLongName exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::TooLongLabel exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::BadLabelType exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::BadEscape exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::IncompleteName exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::MissingNameOrigin exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); +} + +TEST_F(NameTest, fromText) { + vector<string> strnames; + strnames.push_back("www.example.com"); + strnames.push_back("www.example.com."); // with a trailing dot + strnames.push_back("wWw.exAmpLe.com"); // mixed cases + strnames.push_back("\\wWw.exAmpLe.com"); // escape with a backslash + // decimal representation for "WWW" + strnames.push_back("\\087\\087\\087.example.com"); + + vector<string>::const_iterator it; + for (it = strnames.begin(); it != strnames.end(); ++it) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name, Name(*it)); + } + + // root names + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, Name("@"), Name(".")); + + // downcase + EXPECT_EQ(Name("Www.eXample.coM", true).toText(), example_name.toText()); + + // + // Tests for bogus names. These should trigger exceptions. + // + // empty label cannot be followed by another label + checkBadTextName<EmptyLabel>(".a"); + // duplicate period + checkBadTextName<EmptyLabel>("a.."); + // label length must be < 64 + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "0123"); + // now-unsupported bitstring labels + checkBadTextName<BadLabelType>("\\[b11010000011101]"); + // label length must be < 64 + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\x"); + // but okay as long as resulting len < 64 even if the original string is + // "too long" + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\x")); + // incomplete \DDD pattern (exactly 3 D's must appear) + checkBadTextName<BadEscape>("\\12abc"); + // \DDD must not exceed 255 + checkBadTextName<BadEscape>("\\256"); + // Same tests for \111 as for \\x above + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\111"); + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\111")); + // A domain name must be 255 octets or less + checkBadTextName<TooLongName>("123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.1234"); + // This is a possible longest name and should be accepted + EXPECT_NO_THROW(Name(string(max_len_str))); + // \DDD must consist of 3 digits. + checkBadTextName<IncompleteName>("\\12"); + + // a name with the max number of labels. should be constructed without + // an error, and its length should be the max value. + Name maxlabels = Name(string(max_labels_str)); + EXPECT_EQ(Name::MAX_LABELS, maxlabels.getLabelCount()); +} + +// The following test uses a name data that was produced by +// fuzz testing and causes an unexpected condition in stringParser. +// Formerly this condition was trapped by an assert, but for +// robustness it has been replaced by a throw. +TEST_F(NameTest, unexpectedParseError) { + std::vector<uint8_t> badname { + 0xff,0xff,0x7f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x04,0x63,0x82,0x53,0x63,0x35,0x01,0x01,0x3d,0x07,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x19,0x0c,0x4e,0x01,0x00,0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x04,0x00,0x00,0x07,0x08,0x3b,0x04,0x00, + 0x00,0x2e,0x3b,0x04,0x00,0x19,0x2e,0x00,0x00,0x00,0x0a,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x04,0x00,0x00,0x07,0x08,0x3b,0x04, + 0x00,0x00,0x2e,0x3b,0x04,0x00,0x19,0x2e,0x56,0x00,0x00,0x0a,0x00,0x12,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x19,0x0c, + 0x4e,0x01,0x05,0x3a,0x04,0xde,0x00,0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x07,0x08,0x3b,0x04,0x00,0x00,0x2e,0x3b,0x04, + 0x00,0x19,0x2e,0x56,0x40,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x19,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xfc,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x35,0x01,0x05,0xff,0xff,0x05,0x00,0x07,0x08,0x3b,0x04, + 0x00,0x00,0x2e,0x3b + }; + + std::string badnamestr(badname.begin(), badname.end()); + EXPECT_THROW(Name(badnamestr, false), Unexpected); +} + +// on the rest while we prepare it. +// Check the @ syntax is accepted and it just copies the origin. +TEST_F(NameTest, copyOrigin) { + EXPECT_EQ(origin_name, Name("@", 1, &origin_name)); + // The downcase works on the origin too. But only when we provide it. + EXPECT_EQ(origin_name, Name("@", 1, &origin_name_upper, true)); + EXPECT_EQ(origin_name_upper, Name("@", 1, &origin_name_upper, true)); + // If we don't provide the origin, it throws + EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin); +} + +// Test the master-file constructor does not append the origin when the +// provided name is absolute +TEST_F(NameTest, dontAppendOrigin) { + EXPECT_EQ(example_name, Name("www.example.com.", 16, &origin_name)); + // The downcase works (only if provided, though) + EXPECT_EQ(example_name, Name("WWW.EXAMPLE.COM.", 16, &origin_name, true)); + EXPECT_EQ(example_name_upper, Name("WWW.EXAMPLE.COM.", 16, &origin_name)); + // And it does not require the origin to be provided + EXPECT_NO_THROW(Name("www.example.com.", 16, NULL)); +} + +// Test the master-file constructor properly appends the origin when +// the provided name is relative. +TEST_F(NameTest, appendOrigin) { + EXPECT_EQ(example_name, Name("www", 3, &origin_name)); + // Check the downcase works (if provided) + EXPECT_EQ(example_name, Name("WWW", 3, &origin_name, true)); + EXPECT_EQ(example_name, Name("WWW", 3, &origin_name_upper, true)); + EXPECT_EQ(example_name_upper, Name("WWW", 3, &origin_name_upper)); + // Check we can prepend more than one label + EXPECT_EQ(Name("a.b.c.d.example.com."), Name("a.b.c.d", 7, &origin_name)); + // When the name is relative, we throw. + EXPECT_THROW(Name("www", 3, NULL), MissingNameOrigin); +} + +// When we don't provide the data, it throws +TEST_F(NameTest, noDataProvided) { + EXPECT_THROW(Name(NULL, 10, NULL), isc::InvalidParameter); + EXPECT_THROW(Name(NULL, 10, &origin_name), isc::InvalidParameter); + EXPECT_THROW(Name("www", 0, NULL), isc::InvalidParameter); + EXPECT_THROW(Name("www", 0, &origin_name), isc::InvalidParameter); +} + +// When we combine the first part and the origin together, the resulting name +// is too long. It should throw. Other test checks this is valid when alone +// (without the origin appended). +TEST_F(NameTest, combinedTooLong) { + EXPECT_THROW(Name(max_len_str, strlen(max_len_str), &origin_name), + TooLongName); + EXPECT_THROW(Name(max_labels_str, strlen(max_labels_str), &origin_name), + TooLongName); + // Appending the root should be OK + EXPECT_NO_THROW(Name(max_len_str, strlen(max_len_str), + &Name::ROOT_NAME())); + EXPECT_NO_THROW(Name(max_labels_str, strlen(max_labels_str), + &Name::ROOT_NAME())); +} + +// Test the handling of @ in the name. If it is alone, it is the origin (when +// it exists) or the root. If it is somewhere else, it has no special meaning. +TEST_F(NameTest, atSign) { + // If it is alone, it is the origin + EXPECT_EQ(origin_name, Name("@", 1, &origin_name)); + EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin); + EXPECT_EQ(Name::ROOT_NAME(), Name("@")); + + // It is not alone. It is taken verbatim. We check the name converted + // back to the textual form, since checking it against other name object + // may be wrong -- if we create it wrong the same way as the tested + // object. + EXPECT_EQ("\\@.", Name("@.").toText()); + EXPECT_EQ("\\@.", Name("@.", 2, NULL).toText()); + EXPECT_EQ("\\@something.", Name("@something").toText()); + EXPECT_EQ("something\\@.", Name("something@").toText()); + EXPECT_EQ("\\@x.example.com.", Name("@x", 2, &origin_name).toText()); + EXPECT_EQ("x\\@.example.com.", Name("x@", 2, &origin_name).toText()); + + // An escaped at-sign isn't active + EXPECT_EQ("\\@.", Name("\\@").toText()); + EXPECT_EQ("\\@.example.com.", Name("\\@", 2, &origin_name).toText()); +} + +TEST_F(NameTest, fromWire) { + // + // test cases derived from BIND9 tests. + // + // normal case with a compression pointer + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("name_fromWire1", 25), + Name("vix.com")); + // bogus label character (looks like a local compression pointer) + EXPECT_THROW(nameFactoryFromWire("name_fromWire2", 25), DNSMessageFORMERR); + // a bad compression pointer (too big) + EXPECT_THROW(nameFactoryFromWire("name_fromWire3_1", 25), + DNSMessageFORMERR); + // forward reference + EXPECT_THROW(nameFactoryFromWire("name_fromWire3_2", 25), + DNSMessageFORMERR); + // invalid name length + EXPECT_THROW(nameFactoryFromWire("name_fromWire4", 550), DNSMessageFORMERR); + + // skip test for from Wire5. It's for disabling decompression, but our + // implementation always allows it. + + // bad pointer (too big) + EXPECT_THROW(nameFactoryFromWire("name_fromWire6", 25), DNSMessageFORMERR); + // input ends unexpectedly + EXPECT_THROW(nameFactoryFromWire("name_fromWire7", 25), DNSMessageFORMERR); + // many hops of compression but valid. should succeed. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("name_fromWire8", 383), + Name("vix.com")); + + // + // Additional test cases + // + + // large names, a long but valid one, and invalid (too long) one. + EXPECT_EQ(Name::MAX_WIRE, + nameFactoryFromWire("name_fromWire9", 0).getLength()); + EXPECT_THROW(nameFactoryFromWire("name_fromWire10", 0).getLength(), + DNSMessageFORMERR); + + // A name with possible maximum number of labels; awkward but valid + EXPECT_EQ(nameFactoryFromWire("name_fromWire11", 0).getLabelCount(), + Name::MAX_LABELS); + + // Wire format including an invalid label length + EXPECT_THROW(nameFactoryFromWire("name_fromWire12", 0), DNSMessageFORMERR); + + // converting upper-case letters to down-case + EXPECT_EQ("vix.com.", + nameFactoryFromWire("name_fromWire1", 25, true).toText()); + EXPECT_EQ(3, nameFactoryFromWire("name_fromWire1", 25).getLabelCount()); +} + +TEST_F(NameTest, copyConstruct) { + Name copy(example_name); + EXPECT_EQ(copy, example_name); + + // Check the copied data is valid even after the original is deleted + Name* copy2 = new Name(example_name); + Name copy3(*copy2); + delete copy2; + EXPECT_EQ(copy3, example_name); +} + +TEST_F(NameTest, assignment) { + Name copy("."); + copy = example_name; + EXPECT_EQ(copy, example_name); + + // Check if the copied data is valid even after the original is deleted + Name* copy2 = new Name(example_name); + Name copy3("."); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(copy3, example_name); + + // Self assignment + copy = *© + EXPECT_EQ(example_name, copy); +} + +TEST_F(NameTest, toText) { + // tests derived from BIND9 + EXPECT_EQ("a.b.c.d", Name("a.b.c.d").toText(true)); + EXPECT_EQ("a.\\\\[[.c.d", Name("a.\\\\[\\[.c.d").toText(true)); + EXPECT_EQ("a.b.C.d.", Name("a.b.C.d").toText(false)); + EXPECT_EQ("a.b.", Name("a.b.").toText(false)); + + // test omit_final_dot. It's false by default. + EXPECT_EQ("a.b.c.d", Name("a.b.c.d.").toText(true)); + EXPECT_EQ(Name("a.b.").toText(false), Name("a.b.").toText()); + + // the root name is a special case: omit_final_dot will be ignored. + EXPECT_EQ(".", Name(".").toText(true)); + + // test all printable characters to see whether special characters are + // escaped while the others are intact. note that the conversion is + // implementation specific; for example, it's not invalid to escape a + // "normal" character such as 'a' with regard to the standard. + string all_printable("!\\\"#\\$%&'\\(\\)*+,-\\./0123456789:\\;<=>?\\@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "[\\\\]^_.`abcdefghijklmnopqrstuvwxyz{|}~."); + EXPECT_EQ(all_printable, + nameFactoryFromWire("name_fromWire13", 0).toText()); + + string all_nonprintable( + "\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009" + "\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019" + "\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029" + "\\030\\031\\032\\127\\128\\129" + "\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139" + "\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149" + "\\150\\151\\152\\153\\154\\155\\156." + "\\157\\158\\159" + "\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169" + "\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179" + "\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189" + "\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199" + "\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209" + "\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219." + "\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229" + "\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239" + "\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249" + "\\250\\251\\252\\253\\254\\255."); + EXPECT_EQ(all_nonprintable, + nameFactoryFromWire("name_fromWire14", 0).toText()); +} + +TEST_F(NameTest, toWireBuffer) { + vector<unsigned char> data; + OutputBuffer buffer(0); + + UnitTestUtil::readWireData(string("01610376697803636f6d00"), data); + Name("a.vix.com.").toWire(buffer); + matchWireData(&data[0], data.size(), + buffer.getData(), buffer.getLength()); +} + +// +// We test various corner cases in Renderer tests, but add this test case +// to fill the code coverage gap. +// +TEST_F(NameTest, toWireRenderer) { + vector<unsigned char> data; + MessageRenderer renderer; + + UnitTestUtil::readWireData(string("01610376697803636f6d00"), data); + Name("a.vix.com.").toWire(renderer); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +// +// Helper class to hold comparison test parameters. +// +struct CompareParameters { + CompareParameters(const Name& n1, const Name& n2, + NameComparisonResult::NameRelation r, int o, + unsigned int l) : + name1(n1), name2(n2), reln(r), order(o), labels(l) {} + static int normalizeOrder(int o) + { + if (o > 0) { + return (1); + } else if (o < 0) { + return (-1); + } + return (0); + } + Name name1; + Name name2; + NameComparisonResult::NameRelation reln; + int order; + unsigned int labels; +}; + +TEST_F(NameTest, compare) { + vector<CompareParameters> params; + params.push_back(CompareParameters(Name("c.d"), Name("a.b.c.d"), + NameComparisonResult::SUPERDOMAIN, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d"), + NameComparisonResult::SUBDOMAIN, 1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d.e.f"), + NameComparisonResult::COMMONANCESTOR, + -1, 1)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("f.g.c.d"), + NameComparisonResult::COMMONANCESTOR, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("A.b.C.d."), + NameComparisonResult::EQUAL, + 0, 5)); + + vector<CompareParameters>::const_iterator it; + for (it = params.begin(); it != params.end(); ++it) { + NameComparisonResult result = (*it).name1.compare((*it).name2); + EXPECT_EQ((*it).reln, result.getRelation()); + EXPECT_EQ((*it).order, + CompareParameters::normalizeOrder(result.getOrder())); + EXPECT_EQ((*it).labels, result.getCommonLabels()); + } +} + +TEST_F(NameTest, equal) { + EXPECT_TRUE(example_name == Name("WWW.EXAMPLE.COM.")); + EXPECT_TRUE(example_name.equals(Name("WWW.EXAMPLE.COM."))); + EXPECT_TRUE(example_name != Name("www.example.org.")); + EXPECT_TRUE(example_name.nequals(Name("www.example.org."))); + // lengths don't match + EXPECT_TRUE(example_name != Name("www2.example.com.")); + EXPECT_TRUE(example_name.nequals(Name("www2.example.com."))); + // lengths are equal, but # of labels don't match (first test checks the + // prerequisite). + EXPECT_EQ(example_name.getLength(), Name("www\\.example.com.").getLength()); + EXPECT_TRUE(example_name != Name("www\\.example.com.")); + EXPECT_TRUE(example_name.nequals(Name("www\\.example.com."))); +} + +TEST_F(NameTest, isWildcard) { + EXPECT_FALSE(example_name.isWildcard()); + EXPECT_TRUE(Name("*.a.example.com").isWildcard()); + EXPECT_FALSE(Name("a.*.example.com").isWildcard()); +} + +TEST_F(NameTest, concatenate) { + NameComparisonResult result = + Name("aaa.www.example.com.").compare(Name("aaa").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(Name(".").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(example_name.concatenate(Name("."))); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + // concatenating two valid names would result in too long a name. + Name n1("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789."); + Name n2("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "1234."); + EXPECT_THROW(n1.concatenate(n2), TooLongName); +} + +TEST_F(NameTest, reverse) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.reverse(), + Name("com.example.www.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, Name(".").reverse(), + Name(".")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + Name("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s").reverse(), + Name("s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a")); +} + +TEST_F(NameTest, split) { + // normal cases with or without explicitly specifying the trailing dot. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 2), + Name("example.com.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 3), + Name("example.com.")); + // edge cases: only the first or last label. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(0, 1), + Name("www.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(3, 1), + Name(".")); + // invalid range: an exception should be thrown. + EXPECT_THROW(example_name.split(1, 0), OutOfRange); + EXPECT_THROW(example_name.split(2, 3), OutOfRange); + + // invalid range: the following parameters would cause overflow, + // bypassing naive validation. + EXPECT_THROW(example_name.split(1, numeric_limits<unsigned int>::max()), + OutOfRange); +} + +TEST_F(NameTest, split_for_suffix) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1), + Name("example.com")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(0), + example_name); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(3), + Name(".")); + + // Invalid case: the level must be less than the original label count. + EXPECT_THROW(example_name.split(4), OutOfRange); +} + +TEST_F(NameTest, downcase) { + // usual case: all-upper case name to all-lower case + compareInWireFormat(example_name_upper.downcase(), example_name); + // confirm that non upper-case characters are intact + compareInWireFormat(nameFactoryLowerCase().downcase(), + nameFactoryLowerCase()); + // confirm the calling object is actually modified + example_name_upper.downcase(); + compareInWireFormat(example_name_upper, example_name); +} + +TEST_F(NameTest, at) { + // Confirm at() produces the exact sequence of wire-format name data + vector<uint8_t> data; + + for (size_t i = 0; i < example_name.getLength(); i++) { + data.push_back(example_name.at(i)); + } + + example_name.toWire(buffer_expected); + matchWireData(&data[0], data.size(), + buffer_expected.getData(), buffer_expected.getLength()); + + // Out-of-range access: should trigger an exception. + EXPECT_THROW(example_name.at(example_name.getLength()), OutOfRange); +} + +// +// The following set of tests confirm the result of <=, <, >=, > +// The test logic is simple, and all tests are just straightforward variations +// of the first one. +// +TEST_F(NameTest, leq) { + // small <= large is true + EXPECT_TRUE(small_name.leq(large_name)); + EXPECT_TRUE(small_name <= large_name); + + // small <= small is true + EXPECT_TRUE(small_name.leq(small_name)); + EXPECT_LE(small_name, small_name); + + // large <= small is false + EXPECT_FALSE(large_name.leq(small_name)); + EXPECT_FALSE(large_name <= small_name); +} + +TEST_F(NameTest, geq) { + EXPECT_TRUE(large_name.geq(small_name)); + EXPECT_TRUE(large_name >= small_name); + + EXPECT_TRUE(large_name.geq(large_name)); + EXPECT_GE(large_name, large_name); + + EXPECT_FALSE(small_name.geq(large_name)); + EXPECT_FALSE(small_name >= large_name); +} + +TEST_F(NameTest, lthan) { + EXPECT_TRUE(small_name.lthan(large_name)); + EXPECT_TRUE(small_name < large_name); + + EXPECT_FALSE(small_name.lthan(small_name)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(small_name < small_name); + + EXPECT_FALSE(large_name.lthan(small_name)); + EXPECT_FALSE(large_name < small_name); +} + +TEST_F(NameTest, gthan) { + EXPECT_TRUE(large_name.gthan(small_name)); + EXPECT_TRUE(large_name > small_name); + + EXPECT_FALSE(large_name.gthan(large_name)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(large_name > large_name); + + EXPECT_FALSE(small_name.gthan(large_name)); + EXPECT_FALSE(small_name > large_name); +} + +TEST_F(NameTest, constants) { + EXPECT_EQ(Name("."), Name::ROOT_NAME()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(NameTest, LeftShiftOperator) { + ostringstream oss; + oss << example_name; + EXPECT_EQ(example_name.toText(), oss.str()); +} + +// The following verifies that toRawText() returns a string +// actual characters in place of escape sequences. We do not +// bother with an exhaustive set of tests here as this is +// not a primary use case. +TEST_F(NameTest, toRawText) { + Name n("a bc.$exa(m)ple.@org"); + EXPECT_EQ("a bc.$exa(m)ple.@org", n.toRawText(true)); + EXPECT_EQ("a bc.$exa(m)ple.@org.", n.toRawText(false)); + // Verify default value of omit parameter is false. + EXPECT_EQ("a bc.$exa(m)ple.@org.", n.toRawText()); +} + +} diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc new file mode 100644 index 0000000..b47bb49 --- /dev/null +++ b/src/lib/dns/tests/nsec3hash_unittest.cc @@ -0,0 +1,269 @@ +// 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 <config.h> + +#include <string> + +#include <gtest/gtest.h> + +#include <boost/scoped_ptr.hpp> + +#include <dns/nsec3hash.h> +#include <dns/labelsequence.h> +#include <dns/rdataclass.h> +#include <util/encode/hex.h> + +using boost::scoped_ptr; +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using namespace isc::util::encode; + +namespace { +typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr; + +// Commonly used NSEC3 suffix, defined to reduce the amount of typing +const char* const nsec3_common = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"; + +class NSEC3HashTest : public ::testing::Test { +protected: + NSEC3HashTest() : + test_hash(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))), + test_hash_nsec3(NSEC3Hash::create(generic::NSEC3 + ("1 0 12 aabbccdd " + + string(nsec3_common)))) + { + const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd}; + test_hash_args.reset(NSEC3Hash::create(1, 12, salt, sizeof(salt))); + } + + ~NSEC3HashTest() { + // Make sure we reset the hash creator to the default + setNSEC3HashCreator(NULL); + } + + // An NSEC3Hash object commonly used in tests. Parameters are borrowed + // from the RFC5155 example. Construction of this object implicitly + // checks a successful case of the creation. + NSEC3HashPtr test_hash; + + // Similar to test_hash, but created from NSEC3 RR. + NSEC3HashPtr test_hash_nsec3; + + // Similar to test_hash, but created from passed args. + NSEC3HashPtr test_hash_args; +}; + +TEST_F(NSEC3HashTest, unknownAlgorithm) { + EXPECT_THROW(NSEC3HashPtr( + NSEC3Hash::create( + generic::NSEC3PARAM("2 0 12 aabbccdd"))), + UnknownNSEC3HashAlgorithm); + EXPECT_THROW(NSEC3HashPtr( + NSEC3Hash::create( + generic::NSEC3("2 0 12 aabbccdd " + + string(nsec3_common)))), + UnknownNSEC3HashAlgorithm); + + const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd}; + EXPECT_THROW(NSEC3HashPtr(NSEC3Hash::create(2, 12, salt, sizeof(salt))), + UnknownNSEC3HashAlgorithm); +} + +// Common checks for NSEC3 hash calculation +void +calculateCheck(NSEC3Hash& hash) { + // A couple of normal cases from the RFC5155 example. + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(Name("example"))); + EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL", + hash.calculate(Name("a.example"))); + + // Check case-insensitiveness + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(Name("EXAMPLE"))); + + // Repeat for the LabelSequence variant. + + // A couple of normal cases from the RFC5155 example. + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(LabelSequence(Name("example")))); + EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL", + hash.calculate(LabelSequence(Name("a.example")))); + + // Check case-insensitiveness + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + hash.calculate(LabelSequence(Name("EXAMPLE")))); +} + +TEST_F(NSEC3HashTest, calculate) { + { + SCOPED_TRACE("calculate check with NSEC3PARAM based hash"); + calculateCheck(*test_hash); + } + { + SCOPED_TRACE("calculate check with NSEC3 based hash"); + calculateCheck(*test_hash_nsec3); + } + { + SCOPED_TRACE("calculate check with args based hash"); + calculateCheck(*test_hash_args); + } + + // Some boundary cases: 0-iteration and empty salt. Borrowed from the + // .com zone data. + EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM", + NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -"))) + ->calculate(Name("com"))); + EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM", + NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -"))) + ->calculate(LabelSequence(Name("com")))); + + // Using unusually large iterations, something larger than the 8-bit range. + // (expected hash value generated by BIND 9's dnssec-signzone) + EXPECT_EQ("COG6A52MJ96MNMV3QUCAGGCO0RHCC2Q3", + NSEC3HashPtr(NSEC3Hash::create( + generic::NSEC3PARAM("1 0 256 AABBCCDD"))) + ->calculate(LabelSequence(Name("example.org")))); +} + +// Common checks for match cases +template <typename RDATAType> +void +matchCheck(NSEC3Hash& hash, const string& postfix) { + // If all parameters match, it's considered to be matched. + EXPECT_TRUE(hash.match(RDATAType("1 0 12 aabbccdd" + postfix))); + + // Algorithm doesn't match + EXPECT_FALSE(hash.match(RDATAType("2 0 12 aabbccdd" + postfix))); + // Iterations doesn't match + EXPECT_FALSE(hash.match(RDATAType("1 0 1 aabbccdd" + postfix))); + // Salt doesn't match + EXPECT_FALSE(hash.match(RDATAType("1 0 12 aabbccde" + postfix))); + // Salt doesn't match: the other has an empty salt + EXPECT_FALSE(hash.match(RDATAType("1 0 12 -" + postfix))); + // Flags don't matter + EXPECT_TRUE(hash.match(RDATAType("1 1 12 aabbccdd" + postfix))); +} + +TEST_F(NSEC3HashTest, matchWithNSEC3) { + { + SCOPED_TRACE("match NSEC3PARAM based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3>(*test_hash, " " + string(nsec3_common)); + } + { + SCOPED_TRACE("match NSEC3 based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3>(*test_hash_nsec3, + " " + string(nsec3_common)); + } +} + +TEST_F(NSEC3HashTest, matchWithNSEC3PARAM) { + { + SCOPED_TRACE("match NSEC3PARAM based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3PARAM>(*test_hash, ""); + } + { + SCOPED_TRACE("match NSEC3 based hash against NSEC3 parameters"); + matchCheck<generic::NSEC3PARAM>(*test_hash_nsec3, ""); + } +} + +// A simple faked hash calculator and a dedicated creator for it. +class TestNSEC3Hash : public NSEC3Hash { + virtual string calculate(const Name&) const { + return ("00000000000000000000000000000000"); + } + virtual string calculate(const LabelSequence&) const { + return ("00000000000000000000000000000000"); + } + virtual bool match(const generic::NSEC3PARAM&) const { + return (true); + } + virtual bool match(const generic::NSEC3&) const { + return (true); + } +}; + +// This faked creator basically creates the faked calculator regardless of +// the passed NSEC3PARAM or NSEC3. But if the most significant bit of flags +// is set, it will behave like the default creator. +class TestNSEC3HashCreator : public NSEC3HashCreator { +public: + virtual NSEC3Hash* create(const generic::NSEC3PARAM& param) const { + if ((param.getFlags() & 0x80) != 0) { + return (default_creator_.create(param)); + } + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(const generic::NSEC3& nsec3) const { + if ((nsec3.getFlags() & 0x80) != 0) { + return (default_creator_.create(nsec3)); + } + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(uint8_t, uint16_t, + const uint8_t*, size_t) const { + isc_throw(isc::Unexpected, + "This method is not implemented here."); + } +private: + DefaultNSEC3HashCreator default_creator_; +}; + +TEST_F(NSEC3HashTest, setCreator) { + // Re-check an existing case using the default creator/hash implementation + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + + // Replace the creator, and confirm the hash values are faked + TestNSEC3HashCreator test_creator; + setNSEC3HashCreator(&test_creator); + // Re-create the hash object with the new creator + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(Name("example"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(LabelSequence(Name("example")))); + // Same for hash from NSEC3 RDATA + test_hash.reset(NSEC3Hash::create(generic::NSEC3 + ("1 0 12 aabbccdd " + + string(nsec3_common)))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(Name("example"))); + EXPECT_EQ("00000000000000000000000000000000", + test_hash->calculate(LabelSequence(Name("example")))); + + // If we set a special flag big (0x80) on creation, it will act like the + // default creator. + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM( + "1 128 12 aabbccdd"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + test_hash.reset(NSEC3Hash::create(generic::NSEC3 + ("1 128 12 aabbccdd " + + string(nsec3_common)))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); + + // Reset the creator to default, and confirm that + setNSEC3HashCreator(NULL); + test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(Name("example"))); + EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", + test_hash->calculate(LabelSequence(Name("example")))); +} + +} // end namespace diff --git a/src/lib/dns/tests/opcode_unittest.cc b/src/lib/dns/tests/opcode_unittest.cc new file mode 100644 index 0000000..9bc60b5 --- /dev/null +++ b/src/lib/dns/tests/opcode_unittest.cc @@ -0,0 +1,100 @@ +// 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 <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <dns/opcode.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::dns; + +namespace { +TEST(OpcodeTest, construct) { + // This test also tests getCode() + EXPECT_EQ(0, Opcode(0).getCode()); + EXPECT_EQ(15, Opcode(Opcode::RESERVED15_CODE).getCode()); + + EXPECT_THROW(Opcode(16), isc::OutOfRange); +} + +TEST(OpcodeTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(Opcode::QUERY_CODE, Opcode(0).getCode()); + EXPECT_EQ(Opcode::IQUERY_CODE, Opcode(1).getCode()); + EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode(4).getCode()); + EXPECT_EQ(Opcode::UPDATE_CODE, Opcode(5).getCode()); + EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode(15).getCode()); + + EXPECT_EQ(Opcode::QUERY_CODE, Opcode::QUERY().getCode()); + EXPECT_EQ(Opcode::IQUERY_CODE, Opcode::IQUERY().getCode()); + EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode::NOTIFY().getCode()); + EXPECT_EQ(Opcode::UPDATE_CODE, Opcode::UPDATE().getCode()); + EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode::RESERVED15().getCode()); +} + +TEST(OpcodeTest, equal) { + EXPECT_TRUE(Opcode::QUERY() == Opcode(Opcode::QUERY_CODE)); + EXPECT_TRUE(Opcode::QUERY().equals(Opcode(Opcode::QUERY_CODE))); + EXPECT_TRUE(Opcode::IQUERY() == Opcode(Opcode::IQUERY_CODE)); + EXPECT_TRUE(Opcode::IQUERY().equals(Opcode(Opcode::IQUERY_CODE))); + EXPECT_TRUE(Opcode::NOTIFY() == Opcode(Opcode::NOTIFY_CODE)); + EXPECT_TRUE(Opcode::NOTIFY().equals(Opcode(Opcode::NOTIFY_CODE))); + EXPECT_TRUE(Opcode::UPDATE() == Opcode(Opcode::UPDATE_CODE)); + EXPECT_TRUE(Opcode::UPDATE().equals(Opcode(Opcode::UPDATE_CODE))); + EXPECT_TRUE(Opcode::RESERVED15() == Opcode(Opcode::RESERVED15())); + EXPECT_TRUE(Opcode::RESERVED15().equals(Opcode(Opcode::RESERVED15()))); +} + +TEST(OpcodeTest, nequal) { + EXPECT_TRUE(Opcode::QUERY() != Opcode::IQUERY()); + EXPECT_TRUE(Opcode::QUERY().nequals(Opcode::IQUERY())); + EXPECT_TRUE(Opcode::NOTIFY() != Opcode(1)); + EXPECT_TRUE(Opcode::NOTIFY().nequals(Opcode(1))); + EXPECT_TRUE(Opcode(10) != Opcode(11)); + EXPECT_TRUE(Opcode(10).nequals(Opcode(11))); +} + +TEST(OpcodeTest, toText) { + vector<const char*> expects; + expects.resize(Opcode::RESERVED15_CODE + 1); + expects[Opcode::QUERY_CODE] = "QUERY"; + expects[Opcode::IQUERY_CODE] = "IQUERY"; + expects[Opcode::STATUS_CODE] = "STATUS"; + expects[Opcode::RESERVED3_CODE] = "RESERVED3"; + expects[Opcode::NOTIFY_CODE] = "NOTIFY"; + expects[Opcode::UPDATE_CODE] = "UPDATE"; + expects[Opcode::RESERVED6_CODE] = "RESERVED6"; + expects[Opcode::RESERVED7_CODE] = "RESERVED7"; + expects[Opcode::RESERVED8_CODE] = "RESERVED8"; + expects[Opcode::RESERVED9_CODE] = "RESERVED9"; + expects[Opcode::RESERVED10_CODE] = "RESERVED10"; + expects[Opcode::RESERVED11_CODE] = "RESERVED11"; + expects[Opcode::RESERVED12_CODE] = "RESERVED12"; + expects[Opcode::RESERVED13_CODE] = "RESERVED13"; + expects[Opcode::RESERVED14_CODE] = "RESERVED14"; + expects[Opcode::RESERVED15_CODE] = "RESERVED15"; + + for (unsigned int i = 0; i <= Opcode::RESERVED15_CODE; ++i) { + EXPECT_EQ(expects.at(i), Opcode(i).toText()); + } +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(OpcodeTest, LeftShiftOperator) { + ostringstream oss; + oss << Opcode::NOTIFY(); + EXPECT_EQ(Opcode::NOTIFY().toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/qid_gen_unittest.cc b/src/lib/dns/tests/qid_gen_unittest.cc new file mode 100644 index 0000000..20a2dfa --- /dev/null +++ b/src/lib/dns/tests/qid_gen_unittest.cc @@ -0,0 +1,39 @@ +// 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/. + +/// \brief Test of QidGenerator +/// + +#include <config.h> + +#include <gtest/gtest.h> + +#include <dns/qid_gen.h> + +using namespace isc::dns; + +// Tests the operation of the Qid generator + +// Check that getInstance returns a singleton +TEST(QidGenerator, singleton) { + QidGenerator& g1 = QidGenerator::getInstance(); + QidGenerator& g2 = QidGenerator::getInstance(); + + EXPECT_TRUE(&g1 == &g2); +} + +TEST(QidGenerator, generate) { + // We'll assume that cryptolink's generator is 'good enough', and won't + // do full statistical checking here. Let's just call it the xkcd + // test (http://xkcd.com/221/), and check if three consecutive + // generates are not all the same. + uint16_t one, two, three; + QidGenerator& gen = QidGenerator::getInstance(); + one = gen.generateQid(); + two = gen.generateQid(); + three = gen.generateQid(); + ASSERT_FALSE((one == two) && (one == three)); +} diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc new file mode 100644 index 0000000..03d31b1 --- /dev/null +++ b/src/lib/dns/tests/question_unittest.cc @@ -0,0 +1,196 @@ +// 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 <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/question.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class QuestionTest : public ::testing::Test { +protected: + QuestionTest() : obuffer(0), + example_name1(Name("foo.example.com")), + example_name2(Name("bar.example.com")), + test_question1(example_name1, RRClass::IN(), + RRType::NS()), + test_question2(example_name2, RRClass::CH(), + RRType::A()) + {} + OutputBuffer obuffer; + MessageRenderer renderer; + Name example_name1; + Name example_name2; + Question test_question1; + Question test_question2; + vector<unsigned char> wiredata; +}; + +Question +questionFromWire(const char* datafile, size_t position = 0) { + vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + return (Question(buffer)); +} + +TEST_F(QuestionTest, fromWire) { + Question q = questionFromWire("question_fromWire"); + + EXPECT_EQ(example_name1, q.getName()); + EXPECT_EQ(RRClass::IN(), q.getClass()); + EXPECT_EQ(RRType::NS(), q.getType()); + + // owner name of the second Question is compressed. It's uncommon + // (to have multiple questions), but isn't prohibited by the protocol. + q = questionFromWire("question_fromWire", 21); + EXPECT_EQ(example_name2, q.getName()); + EXPECT_EQ(RRClass::CH(), q.getClass()); + EXPECT_EQ(RRType::A(), q.getType()); + + // Pathological cases: Corresponding exceptions will be thrown from + // the underlying parser. + EXPECT_THROW(questionFromWire("question_fromWire", 31), DNSMessageFORMERR); + EXPECT_THROW(questionFromWire("question_fromWire", 36), IncompleteRRClass); +} + +TEST_F(QuestionTest, toText) { + EXPECT_EQ("foo.example.com. IN NS", test_question1.toText()); + EXPECT_EQ("bar.example.com. CH A", test_question2.toText()); + + EXPECT_EQ("foo.example.com. IN NS", test_question1.toText(false)); + EXPECT_EQ("bar.example.com. CH A", test_question2.toText(false)); + + EXPECT_EQ("foo.example.com. IN NS\n", test_question1.toText(true)); + EXPECT_EQ("bar.example.com. CH A\n", test_question2.toText(true)); +} + +TEST_F(QuestionTest, toWireBuffer) { + test_question1.toWire(obuffer); + test_question2.toWire(obuffer); + UnitTestUtil::readWireData("question_toWire1", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(QuestionTest, toWireRenderer) { + test_question1.toWire(renderer); + test_question2.toWire(renderer); + UnitTestUtil::readWireData("question_toWire2", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(QuestionTest, toWireTruncated) { + // If the available length in the renderer is too small, it would require + // truncation. This won't happen in normal cases, but protocol wise it + // could still happen if and when we support some (possibly future) opcode + // that allows multiple questions. + + // Set the length limit to the qname length so that the whole question + // would request truncated + renderer.setLengthLimit(example_name1.getLength()); + + EXPECT_FALSE(renderer.isTruncated()); // check pre-render condition + EXPECT_EQ(0, test_question1.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); + EXPECT_EQ(0, renderer.getLength()); // renderer shouldn't have any data +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(QuestionTest, LeftShiftOperator) { + ostringstream oss; + oss << test_question1; + EXPECT_EQ(test_question1.toText(), oss.str()); +} + +TEST_F(QuestionTest, comparison) { + const Name a("a"), b("b"); + const RRClass in(RRClass::IN()), ch(RRClass::CH()); + const RRType ns(RRType::NS()), aaaa(RRType::AAAA()); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, in, aaaa)); + EXPECT_FALSE(Question(a, in, aaaa) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, ns)); + EXPECT_FALSE(Question(a, ch, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, aaaa)); + EXPECT_FALSE(Question(a, ch, aaaa) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, in, ns)); + EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, ns)); + EXPECT_FALSE(Question(b, ch, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, aaaa)); + EXPECT_FALSE(Question(b, ch, aaaa) < Question(a, in, ns)); + + EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns)); + EXPECT_FALSE(Question(a, ch, ns) < Question(a, ch, ns)); + EXPECT_FALSE(Question(b, in, ns) < Question(b, in, ns)); + EXPECT_FALSE(Question(b, in, aaaa) < Question(b, in, aaaa)); + + // Identical questions are equal + + EXPECT_TRUE(Question(a, in, ns) == Question(a, in, ns)); + EXPECT_FALSE(Question(a, in, ns) != Question(a, in, ns)); + + // Components differing by one component are unequal... + + EXPECT_FALSE(Question(b, in, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, in, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, ch, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, ch, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, in, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, in, aaaa) != Question(a, in, ns)); + + // ... as are those differing by two components + + EXPECT_FALSE(Question(b, ch, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, ch, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(b, in, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, in, aaaa) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, ch, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, ch, aaaa) != Question(a, in, ns)); + + // ... and question differing by all three + + EXPECT_FALSE(Question(b, ch, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, ch, aaaa) != Question(a, in, ns)); + +} + +} diff --git a/src/lib/dns/tests/rcode_unittest.cc b/src/lib/dns/tests/rcode_unittest.cc new file mode 100644 index 0000000..220248f --- /dev/null +++ b/src/lib/dns/tests/rcode_unittest.cc @@ -0,0 +1,126 @@ +// 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 <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::dns; + +namespace { +TEST(RcodeTest, constructFromCode) { + // Normal cases. This test also tests getCode() + EXPECT_EQ(0, Rcode(0).getCode()); + EXPECT_EQ(0xfff, Rcode(0xfff).getCode()); // possible max code + + // should fail on attempt of construction with an out of range code + EXPECT_THROW(Rcode(0x1000), isc::OutOfRange); + EXPECT_THROW(Rcode(0xffff), isc::OutOfRange); +} + +TEST(RcodeTest, constructFromCodePair) { + EXPECT_EQ(3, Rcode(Rcode::NXDOMAIN_CODE, 0).getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(0, 1).getCode()); + EXPECT_EQ(0xfff, Rcode(0xf, 0xff).getCode()); + EXPECT_THROW(Rcode(0x10, 0xff), isc::OutOfRange); +} + +TEST(RcodeTest, getExtendedCode) { + EXPECT_EQ(0, Rcode::NOERROR().getExtendedCode()); + EXPECT_EQ(0, Rcode::YXRRSET().getExtendedCode()); + EXPECT_EQ(1, Rcode::BADVERS().getExtendedCode()); + EXPECT_EQ(0xab, Rcode(0xabf).getExtendedCode()); + EXPECT_EQ(0xff, Rcode(0xfff).getExtendedCode()); +} + +TEST(RcodeTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(Rcode::NOERROR_CODE, Rcode(0).getCode()); + EXPECT_EQ(Rcode::FORMERR_CODE, Rcode(1).getCode()); + EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode(4).getCode()); + EXPECT_EQ(Rcode::REFUSED_CODE, Rcode(5).getCode()); + EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode(15).getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(16).getCode()); + + EXPECT_EQ(Rcode::NOERROR_CODE, Rcode::NOERROR().getCode()); + EXPECT_EQ(Rcode::FORMERR_CODE, Rcode::FORMERR().getCode()); + EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode::NOTIMP().getCode()); + EXPECT_EQ(Rcode::REFUSED_CODE, Rcode::REFUSED().getCode()); + EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode::RESERVED15().getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode::BADVERS().getCode()); +} + +TEST(RcodeTest, equal) { + EXPECT_TRUE(Rcode::NOERROR() == Rcode(Rcode::NOERROR_CODE)); + EXPECT_TRUE(Rcode::NOERROR().equals(Rcode(Rcode::NOERROR_CODE))); + EXPECT_TRUE(Rcode::FORMERR() == Rcode(Rcode::FORMERR_CODE)); + EXPECT_TRUE(Rcode::FORMERR().equals(Rcode(Rcode::FORMERR_CODE))); + EXPECT_TRUE(Rcode::NOTIMP() == Rcode(Rcode::NOTIMP_CODE)); + EXPECT_TRUE(Rcode::NOTIMP().equals(Rcode(Rcode::NOTIMP_CODE))); + EXPECT_TRUE(Rcode::REFUSED() == Rcode(Rcode::REFUSED_CODE)); + EXPECT_TRUE(Rcode::REFUSED().equals(Rcode(Rcode::REFUSED_CODE))); + EXPECT_TRUE(Rcode::RESERVED15() == Rcode(Rcode::RESERVED15())); + EXPECT_TRUE(Rcode::RESERVED15().equals(Rcode(Rcode::RESERVED15()))); + EXPECT_TRUE(Rcode::BADVERS() == Rcode(Rcode::BADVERS_CODE)); + EXPECT_TRUE(Rcode::BADVERS().equals(Rcode(Rcode::BADVERS_CODE))); +} + +TEST(RcodeTest, nequal) { + EXPECT_TRUE(Rcode::NOERROR() != Rcode::FORMERR()); + EXPECT_TRUE(Rcode::NOERROR().nequals(Rcode::FORMERR())); + EXPECT_TRUE(Rcode::NOTIMP() != Rcode(1)); + EXPECT_TRUE(Rcode::NOTIMP().nequals(Rcode(1))); + EXPECT_TRUE(Rcode(10) != Rcode(11)); + EXPECT_TRUE(Rcode(10).nequals(Rcode(11))); +} + +TEST(RcodeTest, toText) { + vector<const char*> expects; + expects.resize(Rcode::BADVERS_CODE + 1); + expects[Rcode::NOERROR_CODE] = "NOERROR"; + expects[Rcode::FORMERR_CODE] = "FORMERR"; + expects[Rcode::SERVFAIL_CODE] = "SERVFAIL"; + expects[Rcode::NXDOMAIN_CODE] = "NXDOMAIN"; + expects[Rcode::NOTIMP_CODE] = "NOTIMP"; + expects[Rcode::REFUSED_CODE] = "REFUSED"; + expects[Rcode::YXDOMAIN_CODE] = "YXDOMAIN"; + expects[Rcode::YXRRSET_CODE] = "YXRRSET"; + expects[Rcode::NXRRSET_CODE] = "NXRRSET"; + expects[Rcode::NOTAUTH_CODE] = "NOTAUTH"; + expects[Rcode::NOTZONE_CODE] = "NOTZONE"; + expects[Rcode::RESERVED11_CODE] = "RESERVED11"; + expects[Rcode::RESERVED12_CODE] = "RESERVED12"; + expects[Rcode::RESERVED13_CODE] = "RESERVED13"; + expects[Rcode::RESERVED14_CODE] = "RESERVED14"; + expects[Rcode::RESERVED15_CODE] = "RESERVED15"; + expects[Rcode::BADVERS_CODE] = "BADVERS"; + + for (unsigned int i = 0; i <= Rcode::BADVERS_CODE; ++i) { + EXPECT_EQ(expects.at(i), Rcode(i).toText()); + } + + // Non well-known Rcodes + EXPECT_EQ("17", Rcode(Rcode::BADVERS().getCode() + 1).toText()); + EXPECT_EQ("4095", Rcode(Rcode(0xfff)).toText()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(RcodeTest, LeftShiftOperator) { + ostringstream oss; + oss << Rcode::SERVFAIL(); + EXPECT_EQ(Rcode::SERVFAIL().toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc new file mode 100644 index 0000000..26c3135 --- /dev/null +++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc @@ -0,0 +1,235 @@ +// Copyright (C) 2011-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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +const char* const afsdb_text = "1 afsdb.example.com."; +const char* const afsdb_text2 = "0 root.example.com."; +const char* const too_long_label("012345678901234567890123456789" + "0123456789012345678901234567890123."); + +namespace { +class Rdata_AFSDB_Test : public RdataTest { +protected: + Rdata_AFSDB_Test() : + rdata_afsdb(string(afsdb_text)), rdata_afsdb2(string(afsdb_text2)) + {} + + const generic::AFSDB rdata_afsdb; + const generic::AFSDB rdata_afsdb2; + vector<uint8_t> expected_wire; +}; + + +TEST_F(Rdata_AFSDB_Test, createFromText) { + EXPECT_EQ(1, rdata_afsdb.getSubtype()); + EXPECT_EQ(Name("afsdb.example.com."), rdata_afsdb.getServer()); + + EXPECT_EQ(0, rdata_afsdb2.getSubtype()); + EXPECT_EQ(Name("root.example.com."), rdata_afsdb2.getServer()); +} + +TEST_F(Rdata_AFSDB_Test, badText) { + // subtype is too large + EXPECT_THROW(const generic::AFSDB rdata_afsdb("99999999 afsdb.example.com."), + InvalidRdataText); + // incomplete text + EXPECT_THROW(const generic::AFSDB rdata_afsdb("10"), InvalidRdataText); + EXPECT_THROW(const generic::AFSDB rdata_afsdb("SPOON"), InvalidRdataText); + EXPECT_THROW(const generic::AFSDB rdata_afsdb("1root.example.com."), InvalidRdataText); + // number of fields (must be 2) is incorrect + EXPECT_THROW(const generic::AFSDB rdata_afsdb("10 afsdb. example.com."), + InvalidRdataText); + // No origin and relative + EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com"), + MissingNameOrigin); + // bad name + EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com." + + string(too_long_label)), TooLongLabel); +} + +TEST_F(Rdata_AFSDB_Test, copy) { + const generic::AFSDB rdata_afsdb2(rdata_afsdb); + EXPECT_EQ(0, rdata_afsdb.compare(rdata_afsdb2)); +} + +TEST_F(Rdata_AFSDB_Test, assignment) { + generic::AFSDB copy((string(afsdb_text2))); + copy = rdata_afsdb; + EXPECT_EQ(0, copy.compare(rdata_afsdb)); + + // Check if the copied data is valid even after the original is deleted + generic::AFSDB* copy2 = new generic::AFSDB(rdata_afsdb); + generic::AFSDB copy3((string(afsdb_text2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_afsdb)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_afsdb)); +} + +TEST_F(Rdata_AFSDB_Test, createFromWire) { + // uncompressed names + EXPECT_EQ(0, rdata_afsdb.compare( + *rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire1.wire"))); + // compressed name + EXPECT_EQ(0, rdata_afsdb.compare( + *rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire2.wire", 13))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire3.wire"), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire4.wire"), + InvalidRdataLength); + // bogus server name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::AFSDB(), RRClass::IN(), + "rdata_afsdb_fromWire5.wire"), + DNSMessageFORMERR); +} + +TEST_F(Rdata_AFSDB_Test, createFromLexer) { + EXPECT_EQ(0, rdata_afsdb.compare( + *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(), + afsdb_text))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + generic::AFSDB tmp = generic::AFSDB("1 afsdb2.example.org."); + EXPECT_EQ(0, tmp.compare( + *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(), + "1 afsdb2"))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(), + "1root.example.com.")); + + // 65536 is larger than maximum possible subtype + EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(), + "65536 afsdb.example.com.")); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(), + "1 afsdb.example.com. extra.")); +} + +TEST_F(Rdata_AFSDB_Test, toWireBuffer) { + // construct actual data + rdata_afsdb.toWire(obuffer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire); + + // then compare them + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); + + // clear buffer for the next test + obuffer.clear(); + + // construct actual data + Name("example.com.").toWire(obuffer); + rdata_afsdb2.toWire(obuffer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire); + + // then compare them + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_AFSDB_Test, toWireRenderer) { + // similar to toWireBuffer, but names in RDATA could be compressed due to + // preceding names. Actually they must not be compressed according to + // RFC3597, and this test checks that. + + // construct actual data + rdata_afsdb.toWire(renderer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire); + + // then compare them + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); + + // clear renderer for the next test + renderer.clear(); + + // construct actual data + renderer.writeName(Name("example.com.")); + rdata_afsdb2.toWire(renderer); + + // construct expected data + UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire); + + // then compare them + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_AFSDB_Test, toText) { + EXPECT_EQ(afsdb_text, rdata_afsdb.toText()); + EXPECT_EQ(afsdb_text2, rdata_afsdb2.toText()); +} + +TEST_F(Rdata_AFSDB_Test, compare) { + // check reflexivity + EXPECT_EQ(0, rdata_afsdb.compare(rdata_afsdb)); + + // name must be compared in case-insensitive manner + EXPECT_EQ(0, rdata_afsdb.compare(generic::AFSDB("1 " + "AFSDB.example.com."))); + + const generic::AFSDB small1("10 afsdb.example.com."); + const generic::AFSDB large1("65535 afsdb.example.com."); + const generic::AFSDB large2("256 afsdb.example.com."); + + // confirm these are compared as unsigned values + EXPECT_GT(0, rdata_afsdb.compare(large1)); + EXPECT_LT(0, large1.compare(rdata_afsdb)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small1.compare(large2)); + EXPECT_LT(0, large2.compare(small1)); + + // another AFSDB whose server name is larger than that of rdata_afsdb. + const generic::AFSDB large3("256 zzzzz.example.com."); + EXPECT_GT(0, large2.compare(large3)); + EXPECT_LT(0, large3.compare(large2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_afsdb.compare(*rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_caa_unittest.cc b/src/lib/dns/tests/rdata_caa_unittest.cc new file mode 100644 index 0000000..48b9076 --- /dev/null +++ b/src/lib/dns/tests/rdata_caa_unittest.cc @@ -0,0 +1,322 @@ +// Copyright (C) 2014-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 <config.h> + +#include <algorithm> +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <boost/algorithm/string.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_CAA_Test : public RdataTest { +protected: + Rdata_CAA_Test() : + caa_txt("0 issue \"ca.example.net\""), + rdata_caa(caa_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::CAA, isc::Exception, isc::Exception>( + rdata_str, rdata_caa, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::CAA, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_caa, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::CAA, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_caa, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::CAA, InvalidRdataText, isc::Exception>( + rdata_str, rdata_caa, true, false); + } + + const string caa_txt; + const generic::CAA rdata_caa; +}; + +const uint8_t rdata_caa_wiredata[] = { + // flags + 0x00, + // tag length + 0x5, + // tag + 'i', 's', 's', 'u', 'e', + // value + 'c', 'a', '.', 'e', 'x', 'a', 'm', 'p', 'l', 'e', + '.', 'n', 'e', 't' +}; + +TEST_F(Rdata_CAA_Test, createFromText) { + // Basic test + checkFromText_None(caa_txt); + + // With different spacing + checkFromText_None("0 issue \"ca.example.net\""); + + // Combination of lowercase and uppercase + checkFromText_None("0 IssUE \"ca.example.net\""); + + // string constructor throws if there's extra text, + // but lexer constructor doesn't + checkFromText_BadString(caa_txt + "\n" + caa_txt); + + // Missing value field + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 issue")); +} + +TEST_F(Rdata_CAA_Test, fields) { + // Some of these may not be RFC conformant, but we relax the check + // in our code to work with other field values that may show up in + // the future. + EXPECT_NO_THROW(const generic::CAA rdata_caa2("1 issue \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("2 issue \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("3 issue \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("128 issue \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("255 issue \"ca.example.net\"")); + + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 foo \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 bar \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 12345 \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 w0x1y2z3 \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 relaxed-too \"ca.example.net\"")); + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 RELAXED.too \"ca.example.net\"")); + + // No value (this is redundant to the last test case in the + // .createFromText test + EXPECT_NO_THROW(const generic::CAA rdata_caa2("0 issue")); + + // > 255 would be broken + EXPECT_THROW(const generic::CAA rdata_caa2("256 issue \"ca.example.net\""), + InvalidRdataText); + + // Missing tag causes the value to be parsed as the tag field. As + // the tag field does not allow quoted strings, this throws. + EXPECT_THROW(const generic::CAA rdata_caa2("0 \"ca.example.net\""), + InvalidRdataText); + + // Tag is too long + const std::string tag(256, 'a'); + const std::string rdata_txt("0 " + tag + " \"ca.example.net\""); + EXPECT_THROW(const generic::CAA rdata_caa2(rdata_txt), InvalidRdataText); +} + +TEST_F(Rdata_CAA_Test, characterStringValue) { + const generic::CAA rdata_caa_unquoted("0 issue ca.example.net"); + EXPECT_EQ(0, rdata_caa_unquoted.compare(rdata_caa)); + + const generic::CAA rdata_caa_escape_X("0 issue ca.e\\xample.net"); + EXPECT_EQ(0, rdata_caa_escape_X.compare(rdata_caa)); + + const generic::CAA rdata_caa_escape_DDD("0 issue ca.e\\120ample.net"); + EXPECT_EQ(0, rdata_caa_escape_DDD.compare(rdata_caa)); + + const generic::CAA rdata_caa_multiline("0 issue (\nca.example.net)"); + EXPECT_EQ(0, rdata_caa_multiline.compare(rdata_caa)); +} + +TEST_F(Rdata_CAA_Test, badText) { + checkFromText_LexerError("0"); + checkFromText_LexerError("ZERO issue \"ca.example.net\""); + EXPECT_THROW(const generic::CAA rdata_caa2(caa_txt + " extra text"), + InvalidRdataText); + + // Yes, this is redundant to the last test cases in the .fields test + checkFromText_InvalidText("2345 issue \"ca.example.net\""); + + // negative values are trapped in the lexer rather than the + // constructor + checkFromText_LexerError("-2 issue \"ca.example.net\""); +} + +TEST_F(Rdata_CAA_Test, copyAndAssign) { + // Copy construct + generic::CAA rdata_caa2(rdata_caa); + EXPECT_EQ(0, rdata_caa.compare(rdata_caa2)); + + // Assignment, mainly to confirm it doesn't cause disruption. + rdata_caa2 = rdata_caa; + EXPECT_EQ(0, rdata_caa.compare(rdata_caa2)); +} + +TEST_F(Rdata_CAA_Test, createFromWire) { + // Basic test + EXPECT_EQ(0, rdata_caa.compare( + *rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire1.wire"))); + + // Combination of lowercase and uppercase + EXPECT_EQ(0, rdata_caa.compare( + *rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire2.wire"))); + + // Value field is empty + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire3.wire")); + + // Tag field is empty + EXPECT_THROW(rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire4.wire"), + InvalidRdataText); + + // Value field is shorter than rdata len + EXPECT_THROW(rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire5"), + InvalidBufferPosition); + + // all RDATA is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire6"), + InvalidBufferPosition); +} + +TEST_F(Rdata_CAA_Test, createFromParams) { + const generic::CAA rdata_caa2(0, "issue", "ca.example.net"); + EXPECT_EQ(0, rdata_caa2.compare(rdata_caa)); + + const generic::CAA rdata_caa4(0, "issue", "ca.e\\xample.net"); + EXPECT_EQ(0, rdata_caa4.compare(rdata_caa)); + + const generic::CAA rdata_caa5(0, "issue", "ca.e\\120ample.net"); + EXPECT_EQ(0, rdata_caa5.compare(rdata_caa)); + + // Tag is empty + EXPECT_THROW(const generic::CAA rdata_caa3(0, "", "ca.example.net"), + isc::InvalidParameter); + + // Tag is too long + const std::string tag(256, 'a'); + EXPECT_THROW(const generic::CAA rdata_caa3(0, tag, "ca.example.net"), + isc::InvalidParameter); + + // Value is too long + const std::string value(65536, 'a'); + EXPECT_THROW(const generic::CAA rdata_caa3(0, "issue", value), + InvalidRdataLength); +} + +TEST_F(Rdata_CAA_Test, toText) { + EXPECT_TRUE(boost::iequals(caa_txt, rdata_caa.toText())); + + const string caa_txt2("1 issue \"\""); + const generic::CAA rdata_caa2(caa_txt2); + EXPECT_TRUE(boost::iequals(caa_txt2, rdata_caa2.toText())); +} + +TEST_F(Rdata_CAA_Test, toWire) { + obuffer.clear(); + rdata_caa.toWire(obuffer); + + matchWireData(rdata_caa_wiredata, sizeof(rdata_caa_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_CAA_Test, compare) { + // Equality test is repeated from createFromWire tests above. + EXPECT_EQ(0, rdata_caa.compare( + *rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire1.wire"))); + + const generic::CAA rdata_caa2("1 issue \"ca.example.net\""); + + EXPECT_EQ(1, rdata_caa2.compare(rdata_caa)); + EXPECT_EQ(-1, rdata_caa.compare(rdata_caa2)); +} + +TEST_F(Rdata_CAA_Test, getFlags) { + EXPECT_EQ(0, rdata_caa.getFlags()); +} + +TEST_F(Rdata_CAA_Test, getTag) { + EXPECT_EQ("issue", rdata_caa.getTag()); +} + +TEST_F(Rdata_CAA_Test, getValue) { + const uint8_t value_data[] = { + 'c', 'a', '.', + 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', + 'n', 'e', 't' + }; + + const std::vector<uint8_t>& value = rdata_caa.getValue(); + matchWireData(value_data, sizeof(value_data), + &value[0], value.size()); +} + +TEST_F(Rdata_CAA_Test, emptyValueFromWire) { + const uint8_t rdf_wiredata[] = { + // flags + 0x00, + // tag length + 0x5, + // tag + 'i', 's', 's', 'u', 'e' + }; + + const generic::CAA rdf = + dynamic_cast<const generic::CAA&> + (*rdataFactoryFromFile(RRType("CAA"), RRClass("IN"), + "rdata_caa_fromWire3.wire")); + + EXPECT_EQ(0, rdf.getFlags()); + EXPECT_EQ("issue", rdf.getTag()); + + obuffer.clear(); + rdf.toWire(obuffer); + + matchWireData(rdf_wiredata, sizeof(rdf_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_CAA_Test, emptyValueFromString) { + const generic::CAA rdata_caa2("0 issue"); + const uint8_t rdata_caa2_wiredata[] = { + // flags + 0x00, + // tag length + 0x5, + // tag + 'i', 's', 's', 'u', 'e' + }; + + EXPECT_EQ(0, rdata_caa2.getFlags()); + EXPECT_EQ("issue", rdata_caa2.getTag()); + + obuffer.clear(); + rdata_caa2.toWire(obuffer); + + matchWireData(rdata_caa2_wiredata, sizeof(rdata_caa2_wiredata), + obuffer.getData(), obuffer.getLength()); +} +} diff --git a/src/lib/dns/tests/rdata_char_string_data_unittest.cc b/src/lib/dns/tests/rdata_char_string_data_unittest.cc new file mode 100644 index 0000000..4ffeadb --- /dev/null +++ b/src/lib/dns/tests/rdata_char_string_data_unittest.cc @@ -0,0 +1,181 @@ +// Copyright (C) 2014-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 <config.h> + +#include <util/unittests/wiredata.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::dns::rdata::generic::detail::CharStringData; +using isc::dns::rdata::generic::detail::stringToCharStringData; +using isc::dns::rdata::generic::detail::charStringDataToString; +using isc::dns::rdata::generic::detail::compareCharStringDatas; +using isc::util::unittests::matchWireData; + +namespace { +const uint8_t test_charstr[] = { + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' +}; + +class CharStringDataTest : public ::testing::Test { +protected: + CharStringDataTest() : + // char-string representation for test data using two types of escape + // ('r' = 114) + test_str("Test\\ St\\114ing") + { + str_region.beg = &test_str[0]; + str_region.len = test_str.size(); + } + CharStringData chstr; // place holder + const std::string test_str; + MasterToken::StringRegion str_region; +}; + +MasterToken::StringRegion +createStringRegion(const std::string& str) { + MasterToken::StringRegion region; + region.beg = &str[0]; // note std ensures this works even if str is empty + region.len = str.size(); + return (region); +} + +TEST_F(CharStringDataTest, normalConversion) { + uint8_t tmp[3]; // placeholder for expected sequence + + stringToCharStringData(str_region, chstr); + matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size()); + + // Empty string + chstr.clear(); + stringToCharStringData(createStringRegion(""), chstr); + EXPECT_TRUE(chstr.empty()); + + // Possible largest char string + chstr.clear(); + std::string long_str(255, 'x'); + stringToCharStringData(createStringRegion(long_str), chstr); + std::vector<uint8_t> expected; + expected.insert(expected.end(), long_str.begin(), long_str.end()); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Escaped '\' + chstr.clear(); + tmp[0] = '\\'; + stringToCharStringData(createStringRegion("\\\\"), chstr); + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Boundary values for \DDD + chstr.clear(); + tmp[0] = 0; + stringToCharStringData(createStringRegion("\\000"), chstr); + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + chstr.clear(); + stringToCharStringData(createStringRegion("\\255"), chstr); + tmp[0] = 255; + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Another digit follows DDD; it shouldn't cause confusion + chstr.clear(); + stringToCharStringData(createStringRegion("\\2550"), chstr); + tmp[1] = '0'; + matchWireData(tmp, 2, &chstr[0], chstr.size()); +} + +TEST_F(CharStringDataTest, badConversion) { + // input string ending with (non escaped) '\' + chstr.clear(); + EXPECT_THROW(stringToCharStringData(createStringRegion("foo\\"), chstr), + InvalidRdataText); +} + +TEST_F(CharStringDataTest, badDDD) { + // Check various type of bad form of \DDD + + // Not a number + EXPECT_THROW(stringToCharStringData(createStringRegion("\\1a2"), chstr), + InvalidRdataText); + EXPECT_THROW(stringToCharStringData(createStringRegion("\\12a"), chstr), + InvalidRdataText); + + // Not in the range of uint8_t + EXPECT_THROW(stringToCharStringData(createStringRegion("\\256"), chstr), + InvalidRdataText); + + // Short buffer + EXPECT_THROW(stringToCharStringData(createStringRegion("\\42"), chstr), + InvalidRdataText); +} + +const struct TestData { + const char *data; + const char *expected; +} conversion_data[] = { + {"Test\"Test", "Test\\\"Test"}, + {"Test;Test", "Test\\;Test"}, + {"Test\\Test", "Test\\\\Test"}, + {"Test\x1fTest", "Test\\031Test"}, + {"Test ~ Test", "Test ~ Test"}, + {"Test\x7fTest", "Test\\127Test"}, + {NULL, NULL} +}; + +TEST_F(CharStringDataTest, charStringDataToString) { + for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) { + uint8_t idata[32]; + size_t length = std::strlen(cur->data); + ASSERT_LT(length, sizeof(idata)); + std::memcpy(idata, cur->data, length); + const CharStringData test_data(idata, idata + length); + EXPECT_EQ(cur->expected, charStringDataToString(test_data)); + } +} + +TEST_F(CharStringDataTest, compareCharStringData) { + CharStringData charstr; + CharStringData charstr2; + CharStringData charstr_small1; + CharStringData charstr_small2; + CharStringData charstr_large1; + CharStringData charstr_large2; + CharStringData charstr_empty; + + stringToCharStringData(createStringRegion("test string"), charstr); + stringToCharStringData(createStringRegion("test string"), charstr2); + stringToCharStringData(createStringRegion("test strin"), charstr_small1); + stringToCharStringData(createStringRegion("test strina"), charstr_small2); + stringToCharStringData(createStringRegion("test stringa"), charstr_large1); + stringToCharStringData(createStringRegion("test strinz"), charstr_large2); + + EXPECT_EQ(0, compareCharStringDatas(charstr, charstr2)); + EXPECT_EQ(0, compareCharStringDatas(charstr2, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small1)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small2)); + EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large1)); + EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large2)); + EXPECT_EQ(-1, compareCharStringDatas(charstr_small1, charstr)); + EXPECT_EQ(-1, compareCharStringDatas(charstr_small2, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr_large1, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr_large2, charstr)); + + EXPECT_EQ(-1, compareCharStringDatas(charstr_empty, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_empty)); + EXPECT_EQ(0, compareCharStringDatas(charstr_empty, charstr_empty)); +} + +} // unnamed namespace diff --git a/src/lib/dns/tests/rdata_char_string_unittest.cc b/src/lib/dns/tests/rdata_char_string_unittest.cc new file mode 100644 index 0000000..f1618b5 --- /dev/null +++ b/src/lib/dns/tests/rdata_char_string_unittest.cc @@ -0,0 +1,246 @@ +// 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 <config.h> + +#include <util/unittests/wiredata.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdata/generic/detail/char_string.h> +#include <util/buffer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::dns::rdata::generic::detail::CharString; +using isc::dns::rdata::generic::detail::bufferToCharString; +using isc::dns::rdata::generic::detail::stringToCharString; +using isc::dns::rdata::generic::detail::charStringToString; +using isc::dns::rdata::generic::detail::compareCharStrings; +using isc::util::unittests::matchWireData; + +namespace { +const uint8_t test_charstr[] = { + sizeof("Test String") - 1, + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' +}; + +class CharStringTest : public ::testing::Test { +protected: + CharStringTest() : + // char-string representation for test data using two types of escape + // ('r' = 114) + test_str("Test\\ St\\114ing") + { + str_region.beg = &test_str[0]; + str_region.len = test_str.size(); + } + CharString chstr; // place holder + const std::string test_str; + MasterToken::StringRegion str_region; +}; + +MasterToken::StringRegion +createStringRegion(const std::string& str) { + MasterToken::StringRegion region; + region.beg = &str[0]; // note std ensures this works even if str is empty + region.len = str.size(); + return (region); +} + +TEST_F(CharStringTest, normalConversion) { + uint8_t tmp[3]; // placeholder for expected sequence + + stringToCharString(str_region, chstr); + matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size()); + + // Empty string + chstr.clear(); + stringToCharString(createStringRegion(""), chstr); + tmp[0] = 0; + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Possible largest char string + chstr.clear(); + std::string long_str(255, 'x'); + stringToCharString(createStringRegion(long_str), chstr); + std::vector<uint8_t> expected; + expected.push_back(255); // len of char string + expected.insert(expected.end(), long_str.begin(), long_str.end()); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Same data as the previous case, but the original string is longer than + // the max; this shouldn't be rejected + chstr.clear(); + long_str.at(254) = '\\'; // replace the last 'x' with '\' + long_str.append("120"); // 'x' = 120 + stringToCharString(createStringRegion(long_str), chstr); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Escaped '\' + chstr.clear(); + tmp[0] = 1; + tmp[1] = '\\'; + stringToCharString(createStringRegion("\\\\"), chstr); + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + // Boundary values for \DDD + chstr.clear(); + tmp[0] = 1; + tmp[1] = 0; + stringToCharString(createStringRegion("\\000"), chstr); + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + chstr.clear(); + stringToCharString(createStringRegion("\\255"), chstr); + tmp[0] = 1; + tmp[1] = 255; + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + // Another digit follows DDD; it shouldn't cause confusion + chstr.clear(); + stringToCharString(createStringRegion("\\2550"), chstr); + tmp[0] = 2; // string len is now 2 + tmp[2] = '0'; + matchWireData(tmp, 3, &chstr[0], chstr.size()); +} + +TEST_F(CharStringTest, badConversion) { + // string cannot exceed 255 bytes + EXPECT_THROW(stringToCharString(createStringRegion(std::string(256, 'a')), + chstr), + CharStringTooLong); + + // input string ending with (non escaped) '\' + chstr.clear(); + EXPECT_THROW(stringToCharString(createStringRegion("foo\\"), chstr), + InvalidRdataText); +} + +TEST_F(CharStringTest, badDDD) { + // Check various type of bad form of \DDD + + // Not a number + EXPECT_THROW(stringToCharString(createStringRegion("\\1a2"), chstr), + InvalidRdataText); + EXPECT_THROW(stringToCharString(createStringRegion("\\12a"), chstr), + InvalidRdataText); + + // Not in the range of uint8_t + EXPECT_THROW(stringToCharString(createStringRegion("\\256"), chstr), + InvalidRdataText); + + // Short buffer + EXPECT_THROW(stringToCharString(createStringRegion("\\42"), chstr), + InvalidRdataText); +} + +const struct TestData { + const char *data; + const char *expected; +} conversion_data[] = { + {"Test\"Test", "Test\\\"Test"}, + {"Test;Test", "Test\\;Test"}, + {"Test\\Test", "Test\\\\Test"}, + {"Test\x1fTest", "Test\\031Test"}, + {"Test ~ Test", "Test ~ Test"}, + {"Test\x7fTest", "Test\\127Test"}, + {NULL, NULL} +}; + +TEST_F(CharStringTest, charStringToString) { + for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) { + uint8_t idata[32]; + size_t length = std::strlen(cur->data); + // length (1 byte) + string (length bytes) + assert(sizeof(idata) > length); + idata[0] = static_cast<uint8_t>(length); + std::memcpy(idata + 1, cur->data, length); + const CharString test_data(idata, idata + length + 1); + EXPECT_EQ(cur->expected, charStringToString(test_data)); + } +} + +TEST_F(CharStringTest, bufferToCharString) { + const size_t chstr_size = sizeof(test_charstr); + isc::util::InputBuffer buf(test_charstr, chstr_size); + size_t read = bufferToCharString(buf, chstr_size, chstr); + + EXPECT_EQ(chstr_size, read); + EXPECT_EQ("Test String", charStringToString(chstr)); +} + +TEST_F(CharStringTest, bufferToCharString_bad) { + const size_t chstr_size = sizeof(test_charstr); + isc::util::InputBuffer buf(test_charstr, chstr_size); + // Set valid data in both so we can make sure the charstr is not + // modified + bufferToCharString(buf, chstr_size, chstr); + ASSERT_EQ("Test String", charStringToString(chstr)); + + // Should be at end of buffer now, so it should fail + EXPECT_THROW(bufferToCharString(buf, chstr_size - 1, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + + // reset and try to read with too low rdata_len + buf.setPosition(0); + EXPECT_THROW(bufferToCharString(buf, chstr_size - 1, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + + // set internal charstring len too high + const uint8_t test_charstr_err[] = { + sizeof("Test String") + 1, + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' + }; + buf = isc::util::InputBuffer(test_charstr_err, sizeof(test_charstr_err)); + EXPECT_THROW(bufferToCharString(buf, chstr_size, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + +} + + + +TEST_F(CharStringTest, compareCharString) { + CharString charstr; + CharString charstr2; + CharString charstr_small1; + CharString charstr_small2; + CharString charstr_large1; + CharString charstr_large2; + CharString charstr_empty; + + stringToCharString(createStringRegion("test string"), charstr); + stringToCharString(createStringRegion("test string"), charstr2); + stringToCharString(createStringRegion("test strin"), charstr_small1); + stringToCharString(createStringRegion("test strina"), charstr_small2); + stringToCharString(createStringRegion("test stringa"), charstr_large1); + stringToCharString(createStringRegion("test strinz"), charstr_large2); + + EXPECT_EQ(0, compareCharStrings(charstr, charstr2)); + EXPECT_EQ(0, compareCharStrings(charstr2, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_small1)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_small2)); + EXPECT_EQ(-1, compareCharStrings(charstr, charstr_large1)); + EXPECT_EQ(-1, compareCharStrings(charstr, charstr_large2)); + EXPECT_EQ(-1, compareCharStrings(charstr_small1, charstr)); + EXPECT_EQ(-1, compareCharStrings(charstr_small2, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr_large1, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr_large2, charstr)); + + EXPECT_EQ(-1, compareCharStrings(charstr_empty, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_empty)); + EXPECT_EQ(0, compareCharStrings(charstr_empty, charstr_empty)); +} + +} // unnamed namespace diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc new file mode 100644 index 0000000..e666355 --- /dev/null +++ b/src/lib/dns/tests/rdata_cname_unittest.cc @@ -0,0 +1,135 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_CNAME_Test : public RdataTest { +public: + Rdata_CNAME_Test() : + rdata_cname("cn.example.com."), + rdata_cname2("cn2.example.com.") + {} + + const generic::CNAME rdata_cname; + const generic::CNAME rdata_cname2; +}; + +const uint8_t wiredata_cname[] = { + 0x02, 0x63, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_cname2[] = { + // first name: cn.example.com. + 0x02, 0x63, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: cn2.example.com. all labels except the first should be + // compressed. + 0x03, 0x63, 0x6e, 0x32, 0xc0, 0x03 }; + +TEST_F(Rdata_CNAME_Test, createFromText) { + EXPECT_EQ(0, rdata_cname.compare(generic::CNAME("cn.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_cname.compare(generic::CNAME("cn.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_cname.compare(generic::CNAME("CN.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_cname.compare(*createRdata(RRType("CNAME"), + RRClass(65000), + "cn.example.com."))); +} + +TEST_F(Rdata_CNAME_Test, badText) { + // Extra text at end of line + EXPECT_THROW(generic::CNAME("cname.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_CNAME_Test, createFromWire) { + EXPECT_EQ(0, rdata_cname.compare( + *rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::CNAME("cn2.example.com.").compare( + *rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("CNAME"), RRClass("IN"), + "rdata_cname_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_CNAME_Test, createFromLexer) { + EXPECT_EQ(0, rdata_cname.compare( + *test::createRdataUsingLexer(RRType::CNAME(), RRClass::IN(), + "cn.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::CNAME("cname10.example.org.").compare( + *test::createRdataUsingLexer(RRType::CNAME(), RRClass::IN(), + "cname10"))); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::CNAME(), RRClass::IN(), + "cname.example.com. extra.")); +} + +TEST_F(Rdata_CNAME_Test, toWireBuffer) { + rdata_cname.toWire(obuffer); + matchWireData(wiredata_cname, sizeof(wiredata_cname), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_CNAME_Test, toWireRenderer) { + rdata_cname.toWire(renderer); + matchWireData(wiredata_cname, sizeof(wiredata_cname), + renderer.getData(), renderer.getLength()); + + rdata_cname2.toWire(renderer); + matchWireData(wiredata_cname2, sizeof(wiredata_cname2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_CNAME_Test, toText) { + EXPECT_EQ("cn.example.com.", rdata_cname.toText()); +} + +TEST_F(Rdata_CNAME_Test, getCname) { + EXPECT_EQ(Name("cn.example.com."), rdata_cname.getCname()); +} +} diff --git a/src/lib/dns/tests/rdata_dhcid_unittest.cc b/src/lib/dns/tests/rdata_dhcid_unittest.cc new file mode 100644 index 0000000..a96c3e4 --- /dev/null +++ b/src/lib/dns/tests/rdata_dhcid_unittest.cc @@ -0,0 +1,165 @@ +// Copyright (C) 2011-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 <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/rdataclass.h> +#include <util/encode/base64.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_DHCID_Test : public RdataTest { +protected: + Rdata_DHCID_Test() : + dhcid_txt("0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA="), + rdata_dhcid(dhcid_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<in::DHCID, isc::Exception, isc::Exception>( + rdata_str, rdata_dhcid, false, false); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<in::DHCID, BadValue, BadValue>( + rdata_str, rdata_dhcid, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <in::DHCID, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_dhcid, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <in::DHCID, InvalidRdataText, isc::Exception>( + rdata_str, rdata_dhcid, true, false); + } + + const string dhcid_txt; + const in::DHCID rdata_dhcid; +}; + +TEST_F(Rdata_DHCID_Test, fromText) { + EXPECT_EQ(dhcid_txt, rdata_dhcid.toText()); + + // Space in digest data is OK + checkFromText_None( + "0LIg0LvQtdGB0YMg 0YDQvtC00LjQu9Cw 0YHRjCDRkdC70L7R h9C60LA="); + + // Multi-line digest data is OK, if enclosed in parentheses + checkFromText_None( + "( 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw\n0YHRjCDRkdC70L7R h9C60LA= )"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString( + "0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=" + " ; comment\n" + "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA="); +} + +TEST_F(Rdata_DHCID_Test, badText) { + // missing digest data + checkFromText_LexerError(""); + + // invalid base64 + checkFromText_BadValue("EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="); + + // unterminated multi-line base64 + checkFromText_LexerError( + "( 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw\n0YHRjCDRkdC70L7R h9C60LA="); +} + +TEST_F(Rdata_DHCID_Test, copy) { + const in::DHCID rdata_dhcid2(rdata_dhcid); + EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid2)); +} + +TEST_F(Rdata_DHCID_Test, createFromWire) { + EXPECT_EQ(0, rdata_dhcid.compare( + *rdataFactoryFromFile(RRType("DHCID"), RRClass("IN"), + "rdata_dhcid_fromWire"))); + + InputBuffer buffer(NULL, 0); + EXPECT_THROW(in::DHCID(buffer, 0), InvalidRdataLength); + + // TBD: more tests +} + +TEST_F(Rdata_DHCID_Test, createFromLexer) { + EXPECT_EQ(0, rdata_dhcid.compare( + *test::createRdataUsingLexer(RRType::DHCID(), RRClass::IN(), + dhcid_txt))); +} + +TEST_F(Rdata_DHCID_Test, toWireRenderer) { + rdata_dhcid.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dhcid_toWire", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_DHCID_Test, toWireBuffer) { + rdata_dhcid.toWire(obuffer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dhcid_toWire", data); + matchWireData(&data[0], data.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_DHCID_Test, toText) { + EXPECT_EQ(dhcid_txt, rdata_dhcid.toText()); +} + +TEST_F(Rdata_DHCID_Test, getDHCIDDigest) { + const string dhcid_txt1(encodeBase64(rdata_dhcid.getDigest())); + + EXPECT_EQ(dhcid_txt, dhcid_txt1); +} + +TEST_F(Rdata_DHCID_Test, compare) { + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid)); + + in::DHCID rdata_dhcid1("0YLQvtC/0L7Qu9GPINC00LLQsCDRgNGD0LHQu9GP"); + in::DHCID rdata_dhcid2("0YLQvtC/0L7Qu9GPINGC0YDQuCDRgNGD0LHQu9GP"); + in::DHCID rdata_dhcid3("0YLQvtC/0L7Qu9GPINGH0LXRgtGL0YDQtSDRgNGD0LHQu9GP"); + + EXPECT_LT(rdata_dhcid1.compare(rdata_dhcid2), 0); + EXPECT_GT(rdata_dhcid2.compare(rdata_dhcid1), 0); + + EXPECT_LT(rdata_dhcid2.compare(rdata_dhcid3), 0); + EXPECT_GT(rdata_dhcid3.compare(rdata_dhcid2), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_dhcid.compare(*rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc new file mode 100644 index 0000000..3bdfb23 --- /dev/null +++ b/src/lib/dns/tests/rdata_dname_unittest.cc @@ -0,0 +1,137 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_DNAME_Test : public RdataTest { +public: + Rdata_DNAME_Test() : + rdata_dname("dn.example.com."), + rdata_dname2("dn2.example.com.") + {} + + const generic::DNAME rdata_dname; + const generic::DNAME rdata_dname2; +}; + +const uint8_t wiredata_dname[] = { + 0x02, 0x64, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_dname2[] = { + // first name: dn.example.com. + 0x02, 0x64, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: dn2.example.com. The "example.com" shouldn't be compressed + // because DNAME is not a well know type per RFC3597. + 0x03, 0x64, 0x6e, 0x32, + 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; + +TEST_F(Rdata_DNAME_Test, createFromText) { + EXPECT_EQ(0, rdata_dname.compare(generic::DNAME("dn.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_dname.compare(generic::DNAME("dn.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_dname.compare(generic::DNAME("DN.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_dname.compare(*createRdata(RRType("DNAME"), + RRClass(65000), + "dn.example.com."))); +} + +TEST_F(Rdata_DNAME_Test, badText) { + // Extra text at end of line + EXPECT_THROW(generic::DNAME("dname.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_DNAME_Test, createFromWire) { + EXPECT_EQ(0, rdata_dname.compare( + *rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::DNAME("dn2.example.com.").compare( + *rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("DNAME"), RRClass("IN"), + "rdata_dname_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_DNAME_Test, createFromLexer) { + EXPECT_EQ(0, rdata_dname.compare( + *test::createRdataUsingLexer(RRType::DNAME(), RRClass::IN(), + "dn.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::DNAME("dname8.example.org.").compare( + *test::createRdataUsingLexer(RRType::DNAME(), RRClass::IN(), + "dname8"))); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::DNAME(), RRClass::IN(), + "dname.example.com. extra.")); +} + +TEST_F(Rdata_DNAME_Test, toWireBuffer) { + rdata_dname.toWire(obuffer); + matchWireData(wiredata_dname, sizeof(wiredata_dname), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_DNAME_Test, toWireRenderer) { + rdata_dname.toWire(renderer); + matchWireData(wiredata_dname, sizeof(wiredata_dname), + renderer.getData(), renderer.getLength()); + + rdata_dname2.toWire(renderer); + matchWireData(wiredata_dname2, sizeof(wiredata_dname2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_DNAME_Test, toText) { + EXPECT_EQ("dn.example.com.", rdata_dname.toText()); +} + +TEST_F(Rdata_DNAME_Test, getDname) { + EXPECT_EQ(Name("dn.example.com."), rdata_dname.getDname()); +} +} diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc new file mode 100644 index 0000000..6f0866d --- /dev/null +++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc @@ -0,0 +1,200 @@ +// 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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_DNSKEY_Test : public RdataTest { +protected: + Rdata_DNSKEY_Test() : + dnskey_txt("257 3 5 BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMV" + "Fu2hWLDMvoOMRXjGrhhCeFvAZih7yJHf8ZGfW6hd38hXG/x" + "ylYCO6Krpbdojwx8YMXLA5/kA+u50WIL8ZR1R6KTbsYVMf/" + "Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy347cBB1zMnnz/" + "4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQzBkj" + "0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ" + "7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA" + "8lVUgEf/rzeC/bByBNsO70aEFTd"), + dnskey_txt2("257 3 5 YmluZDEwLmlzYy5vcmc="), + rdata_dnskey(dnskey_txt), + rdata_dnskey2(dnskey_txt2) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::DNSKEY, isc::Exception, isc::Exception>( + rdata_str, rdata_dnskey2, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::DNSKEY, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_dnskey2, true, true); + } + + void checkFromText_InvalidLength(const string& rdata_str) { + checkFromText<generic::DNSKEY, InvalidRdataLength, InvalidRdataLength>( + rdata_str, rdata_dnskey2, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::DNSKEY, BadValue, BadValue>( + rdata_str, rdata_dnskey2, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::DNSKEY, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_dnskey2, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::DNSKEY, InvalidRdataText, isc::Exception>( + rdata_str, rdata_dnskey2, true, false); + } + + const string dnskey_txt; + const string dnskey_txt2; + const generic::DNSKEY rdata_dnskey; + const generic::DNSKEY rdata_dnskey2; +}; + +TEST_F(Rdata_DNSKEY_Test, fromText) { + EXPECT_EQ(dnskey_txt, rdata_dnskey.toText()); + + // Space in key data is OK + checkFromText_None("257 3 5 YmluZDEw LmlzYy5vcmc="); + + // Delimited number in key data is OK + checkFromText_None("257 3 5 YmluZDEwLmlzYy 5 vcmc="); + + // Missing keydata is OK + EXPECT_NO_THROW(const generic::DNSKEY rdata_dnskey3("257 3 5")); + + // Key data too short for RSA/MD5 algorithm is OK when + // constructing. But getTag() on this object would throw (see + // .getTag tests). + EXPECT_NO_THROW(const generic::DNSKEY rdata_dnskey4("1 1 1 YQ==")); + + // Flags field out of range + checkFromText_InvalidText("65536 3 5 YmluZDEwLmlzYy5vcmc="); + + // Protocol field out of range + checkFromText_InvalidText("257 256 5 YmluZDEwLmlzYy5vcmc="); + + // Algorithm field out of range + checkFromText_InvalidText("257 3 256 YmluZDEwLmlzYy5vcmc="); + + // Missing algorithm field + checkFromText_LexerError("257 3 YmluZDEwLmlzYy5vcmc="); + + // Invalid key data field (not Base64) + checkFromText_BadValue("257 3 5 BAAAAAAAAAAAD"); + + // String instead of number + checkFromText_LexerError("foo 3 5 YmluZDEwLmlzYy5vcmc="); + checkFromText_LexerError("257 foo 5 YmluZDEwLmlzYy5vcmc="); + checkFromText_LexerError("257 3 foo YmluZDEwLmlzYy5vcmc="); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString("257 3 5 YmluZDEwLmlzYy5vcmc= ; comment\n" + "257 3 4 YmluZDEwLmlzYy5vcmc="); + + // Unmatched parenthesis should cause a lexer error + checkFromText_LexerError("257 3 5 )YmluZDEwLmlzYy5vcmc="); +} + +TEST_F(Rdata_DNSKEY_Test, assign) { + generic::DNSKEY rdata_dnskey2("257 3 5 YQ=="); + rdata_dnskey2 = rdata_dnskey; + EXPECT_EQ(0, rdata_dnskey.compare(rdata_dnskey2)); +} + +TEST_F(Rdata_DNSKEY_Test, createFromLexer) { + EXPECT_EQ(0, rdata_dnskey.compare( + *test::createRdataUsingLexer(RRType::DNSKEY(), RRClass::IN(), + dnskey_txt))); +} + +TEST_F(Rdata_DNSKEY_Test, toWireRenderer) { + renderer.skip(2); + rdata_dnskey.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dnskey_fromWire.wire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(renderer.getData()) + 2, + renderer.getLength() - 2); +} + +TEST_F(Rdata_DNSKEY_Test, toWireBuffer) { + rdata_dnskey.toWire(obuffer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dnskey_fromWire.wire", data); + matchWireData(&data[2], data.size() - 2, + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_DNSKEY_Test, createFromWire) { + EXPECT_EQ(0, rdata_dnskey.compare( + *rdataFactoryFromFile(RRType("DNSKEY"), RRClass("IN"), + "rdata_dnskey_fromWire.wire"))); + + // Missing keydata is OK + const generic::DNSKEY rdata_dnskey_missing_keydata("257 3 5"); + EXPECT_EQ(0, rdata_dnskey_missing_keydata.compare( + *rdataFactoryFromFile(RRType("DNSKEY"), RRClass("IN"), + "rdata_dnskey_empty_keydata_fromWire.wire"))); +} + +TEST_F(Rdata_DNSKEY_Test, getTag) { + EXPECT_EQ(12892, rdata_dnskey.getTag()); + + // Short keydata with algorithm RSA/MD5 must throw. + const generic::DNSKEY rdata_dnskey_short_keydata1("1 1 1 YQ=="); + EXPECT_THROW(rdata_dnskey_short_keydata1.getTag(), isc::OutOfRange); + + // Short keydata with algorithm not RSA/MD5 must not throw. + const generic::DNSKEY rdata_dnskey_short_keydata2("257 3 5 YQ=="); + EXPECT_NO_THROW(rdata_dnskey_short_keydata2.getTag()); +} + +TEST_F(Rdata_DNSKEY_Test, getAlgorithm) { + EXPECT_EQ(5, rdata_dnskey.getAlgorithm()); +} + +TEST_F(Rdata_DNSKEY_Test, getFlags) { + EXPECT_EQ(257, rdata_dnskey.getFlags()); +} + +} diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc new file mode 100644 index 0000000..d4ea924 --- /dev/null +++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc @@ -0,0 +1,229 @@ +// Copyright (C) 2011-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 <config.h> + +#include <algorithm> +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +// hacks to make templates work +template <class T> +class RRTYPE : public RRType { +public: + RRTYPE(); +}; + +template<> RRTYPE<generic::DS>::RRTYPE() : RRType(RRType::DS()) {} +template<> RRTYPE<generic::DLV>::RRTYPE() : RRType(RRType::DLV()) {} + +template <class DS_LIKE> +class Rdata_DS_LIKE_Test : public RdataTest { +protected: + Rdata_DS_LIKE_Test() : + ds_like_txt("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"), + rdata_ds_like(ds_like_txt) + {} + const string ds_like_txt; + const DS_LIKE rdata_ds_like; +}; + +// The list of types we want to test. +typedef testing::Types<generic::DS, generic::DLV> Implementations; + +#ifdef TYPED_TEST_SUITE +TYPED_TEST_SUITE(Rdata_DS_LIKE_Test, Implementations); +#else +TYPED_TEST_CASE(Rdata_DS_LIKE_Test, Implementations); +#endif + +TYPED_TEST(Rdata_DS_LIKE_Test, createFromText) { + // It's valid for the digest's presentation format to contain + // spaces. See RFC4034 section 5.3. + EXPECT_EQ(0, this->rdata_ds_like.compare( + TypeParam("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D5F0EB5" + "C777 58 6DE18 \t DA6B5"))); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, toText_DS_LIKE) { + EXPECT_EQ(this->ds_like_txt, this->rdata_ds_like.toText()); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, badText_DS_LIKE) { + EXPECT_THROW(const TypeParam ds_like2("99999 5 2 BEEF"), InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 555 2 BEEF"), + InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 5 22222 BEEF"), + InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("11111 5 2"), InvalidRdataText); + EXPECT_THROW(const TypeParam ds_like2("GARBAGE IN"), InvalidRdataText); + // no space between the digest type and the digest. + EXPECT_THROW(const TypeParam ds_like2( + "12892 5 2F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"), InvalidRdataText); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, createFromWire_DS_LIKE) { + EXPECT_EQ(0, this->rdata_ds_like.compare( + *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass::IN(), + "rdata_ds_fromWire"))); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, createFromLexer_DS_LIKE) { + EXPECT_EQ(0, this->rdata_ds_like.compare( + *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + this->ds_like_txt))); + + // Whitespace is okay + EXPECT_EQ(0, this->rdata_ds_like.compare( + *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "12892 5 2 F1E184C0E1D615D20EB3C223ACED3B0" + "3C773DD952D5F0EB5C777 58 6DE18 \t DA6B5" + ))); + + // Exceptions cause NULL to be returned. + + // Bad tag + EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "65536 5 2 BEEF")); + + // Bad algorithm + EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "1024 256 2 BEEF")); + + // Bad digest type + EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "2048 2 256 BEEF")); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, assignment_DS_LIKE) { + TypeParam copy(this->ds_like_txt); + copy = this->rdata_ds_like; + EXPECT_EQ(0, copy.compare(this->rdata_ds_like)); + + // Check if the copied data is valid even after the original is deleted + TypeParam* copy2 = new TypeParam(this->rdata_ds_like); + TypeParam copy3(this->ds_like_txt); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(this->rdata_ds_like)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(this->rdata_ds_like)); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, getTag_DS_LIKE) { + EXPECT_EQ(12892, this->rdata_ds_like.getTag()); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) { + Rdata_DS_LIKE_Test<TypeParam>::renderer.skip(2); + TypeParam rdata_ds_like(this->ds_like_txt); + rdata_ds_like.toWire(this->renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_ds_fromWire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t*>(this->renderer.getData()) + 2, + this->renderer.getLength() - 2); +} + +TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) { + TypeParam rdata_ds_like(this->ds_like_txt); + rdata_ds_like.toWire(this->obuffer); +} + +string ds_like_txt1("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different tag +string ds_like_txt2("12893 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different algorithm +string ds_like_txt3("12892 6 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest type +string ds_like_txt4("12892 5 3 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest +string ds_like_txt5("12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); +// different digest length +string ds_like_txt6("12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B555"); + +TYPED_TEST(Rdata_DS_LIKE_Test, compare) { + const string ds_like_txt1( + "12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + // different tag + const string ds_like_txt2( + "12893 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + // different algorithm + const string ds_like_txt3( + "12892 6 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + // different digest type + const string ds_like_txt4( + "12892 5 3 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + // different digest + const string ds_like_txt5( + "12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B5"); + // different digest length + const string ds_like_txt6( + "12892 5 2 F2E184C0E1D615D20EB3C223ACED3B03C773DD952D" + "5F0EB5C777586DE18DA6B555"); + + // trivial case: self equivalence + EXPECT_EQ(0, TypeParam(this->ds_like_txt). + compare(TypeParam(this->ds_like_txt))); + + // non-equivalence tests + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt2)), 0); + EXPECT_GT(TypeParam(ds_like_txt2).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt3)), 0); + EXPECT_GT(TypeParam(ds_like_txt3).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt4)), 0); + EXPECT_GT(TypeParam(ds_like_txt4).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt5)), 0); + EXPECT_GT(TypeParam(ds_like_txt5).compare(TypeParam(ds_like_txt1)), 0); + + EXPECT_LT(TypeParam(ds_like_txt1).compare(TypeParam(ds_like_txt6)), 0); + EXPECT_GT(TypeParam(ds_like_txt6).compare(TypeParam(ds_like_txt1)), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(this->rdata_ds_like.compare(*this->rdata_nomatch), + bad_cast); +} + +} diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc new file mode 100644 index 0000000..1830f05 --- /dev/null +++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc @@ -0,0 +1,154 @@ +// Copyright (C) 2011-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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::generic; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_HINFO_Test : public RdataTest { +}; + +static uint8_t hinfo_rdata[] = {0x07,0x50,0x65,0x6e,0x74,0x69,0x75,0x6d,0x05, + 0x4c,0x69,0x6e,0x75,0x78}; +static const char *hinfo_str = "\"Pentium\" \"Linux\""; +static const char *hinfo_str1 = "\"Pen\\\"tium\" \"Linux\""; + +static const char *hinfo_str_equal = "\"Pentium\"\"Linux\""; +static const char *hinfo_str_small1 = "\"Lentium\" \"Linux\""; +static const char *hinfo_str_small2 = "\"Pentium\" \"Kinux\""; +static const char *hinfo_str_large1 = "\"Qentium\" \"Linux\""; +static const char *hinfo_str_large2 = "\"Pentium\" \"UNIX\""; + +TEST_F(Rdata_HINFO_Test, createFromText) { + HINFO hinfo(hinfo_str); + EXPECT_EQ(string("Pentium"), hinfo.getCPU()); + EXPECT_EQ(string("Linux"), hinfo.getOS()); + // Test the text with double quotes in the middle of string + HINFO hinfo1(hinfo_str1); + EXPECT_EQ(string("Pen\\\"tium"), hinfo1.getCPU()); +} + +TEST_F(Rdata_HINFO_Test, badText) { + // Only 2 fields must exist + EXPECT_THROW(const HINFO hinfo("\"Pentium\"\"Linux\"\"Computer\""), + InvalidRdataText); + EXPECT_THROW(const HINFO hinfo("\"Pentium\" \"Linux\" \"Computer\""), + InvalidRdataText); + // Field cannot be missing + EXPECT_THROW(const HINFO hinfo("Pentium"), InvalidRdataText); + // The <character-string> cannot exceed 255 characters + string hinfo_str; + for (int i = 0; i < 257; ++i) { + hinfo_str += 'A'; + } + hinfo_str += " Linux"; + EXPECT_THROW(const HINFO hinfo(hinfo_str), CharStringTooLong); +} + +TEST_F(Rdata_HINFO_Test, createFromWire) { + InputBuffer input_buffer(hinfo_rdata, sizeof(hinfo_rdata)); + HINFO hinfo(input_buffer, sizeof(hinfo_rdata)); + EXPECT_EQ(string("Pentium"), hinfo.getCPU()); + EXPECT_EQ(string("Linux"), hinfo.getOS()); +} + +TEST_F(Rdata_HINFO_Test, createFromLexer) { + HINFO rdata_hinfo(hinfo_str); + EXPECT_EQ(0, rdata_hinfo.compare( + *test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(), + hinfo_str))); + EXPECT_EQ(0, rdata_hinfo.compare( + *test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(), + "\"Pentium\"\"Linux\""))); + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(), + "\"Pentium\"\"Linux\"" + "\"Computer\"")); + EXPECT_FALSE(test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(), + "\"Pentium\" \"Linux\" " + "\"Computer\"")); + EXPECT_FALSE(test::createRdataUsingLexer(RRType::HINFO(), RRClass::IN(), + "\"Pentium\"")); +} + +TEST_F(Rdata_HINFO_Test, toText) { + HINFO hinfo(hinfo_str); + EXPECT_EQ(hinfo_str, hinfo.toText()); + + // will add quotes even if they were not in the original input + EXPECT_EQ("\"a\" \"b\"", HINFO("a b").toText()); + // will not add additional quotes + EXPECT_EQ("\"a\" \"b\"", HINFO("\"a\" \"b\"").toText()); + // And make sure escaped quotes and spaces are left intact + EXPECT_EQ("\"a\\\"\" \"b c\"", HINFO("\"a\\\"\" \"b c\"").toText()); +} + +TEST_F(Rdata_HINFO_Test, toWire) { + HINFO hinfo(hinfo_str); + + hinfo.toWire(obuffer); + matchWireData(hinfo_rdata, sizeof (hinfo_rdata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_HINFO_Test, toWireRenderer) { + HINFO hinfo(hinfo_str); + + hinfo.toWire(renderer); + matchWireData(hinfo_rdata, sizeof (hinfo_rdata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_HINFO_Test, compare) { + HINFO hinfo(hinfo_str); + HINFO hinfo_small1(hinfo_str_small1); + HINFO hinfo_small2(hinfo_str_small2); + HINFO hinfo_large1(hinfo_str_large1); + HINFO hinfo_large2(hinfo_str_large2); + + EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str))); + EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str_equal))); + EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small1))); + EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small2))); + EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large1))); + EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large2))); +} + +// Copy/assign test +TEST_F(Rdata_HINFO_Test, copy) { + HINFO hinfo(hinfo_str); + HINFO hinfo2(hinfo); + HINFO hinfo3 = hinfo; + + EXPECT_EQ(0, hinfo.compare(hinfo2)); + EXPECT_EQ(0, hinfo.compare(hinfo3)); + + hinfo3 = hinfo; + EXPECT_EQ(0, hinfo.compare(hinfo3)); +} + +} diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc new file mode 100644 index 0000000..809cfb5 --- /dev/null +++ b/src/lib/dns/tests/rdata_in_a_unittest.cc @@ -0,0 +1,159 @@ +// 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 <config.h> + +#include <dns/rdataclass.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/rdata.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <sstream> + +#include <arpa/inet.h> +#include <sys/socket.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_IN_A_Test : public RdataTest { +protected: + Rdata_IN_A_Test() : rdata_in_a("192.0.2.1") {} + + void checkFromTextIN_A(const std::string& rdata_txt, + bool throw_str_version = true, + bool throw_lexer_version = true) { + checkFromText<in::A, InvalidRdataText, InvalidRdataText>( + rdata_txt, rdata_in_a, throw_str_version, throw_lexer_version); + } + + const in::A rdata_in_a; +}; + +const uint8_t wiredata_in_a[] = { 192, 0, 2, 1 }; + +TEST_F(Rdata_IN_A_Test, createFromText) { + // Normal case: no exception for either case, so the exception type + // doesn't matter. + checkFromText<in::A, isc::Exception, isc::Exception>("192.0.2.1", + rdata_in_a, false, + false); + + // should reject an abbreviated form of IPv4 address + checkFromTextIN_A("10.1"); + // or an IPv6 address + checkFromTextIN_A("2001:db8::1234"); + // or any meaningless text as an IP address + checkFromTextIN_A("xxx"); + + // NetBSD's inet_pton accepts trailing space after an IPv4 address, which + // would confuse some of the tests below. We check the case differently + // in these cases depending on the strictness of inet_pton (most + // implementations seem to be stricter). + uint8_t v4addr_buf[4]; + const bool reject_extra_space = + inet_pton(AF_INET, "192.0.2.1 ", v4addr_buf) == 0; + + // trailing white space: only string version throws + checkFromTextIN_A("192.0.2.1 ", reject_extra_space, false); + // same for beginning white space. + checkFromTextIN_A(" 192.0.2.1", true, false); + // same for trailing non-space garbage (note that lexer version still + // ignore it; it's expected to be detected at a higher layer). + checkFromTextIN_A("192.0.2.1 xxx", reject_extra_space, false); + + // nul character after a valid textual representation. + string nul_after_addr = "192.0.2.1"; + nul_after_addr.push_back(0); + checkFromTextIN_A(nul_after_addr, true, true); + + // a valid address surrounded by parentheses; only okay with lexer + checkFromTextIN_A("(192.0.2.1)", true, false); + + // input that would cause lexer-specific error; it's bad text as an + // address so should result in the string version, too. + checkFromText<in::A, InvalidRdataText, MasterLexer::LexerError>( + ")192.0.2.1", rdata_in_a); +} + +TEST_F(Rdata_IN_A_Test, createFromWire) { + // Valid data + EXPECT_EQ(0, rdata_in_a.compare( + *rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 6), + DNSMessageFORMERR); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 12), + DNSMessageFORMERR); + // buffer too short. + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 19), + DNSMessageFORMERR); +} + +TEST_F(Rdata_IN_A_Test, toWireBuffer) { + rdata_in_a.toWire(obuffer); + matchWireData(wiredata_in_a, sizeof (wiredata_in_a), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_IN_A_Test, toWireRenderer) { + rdata_in_a.toWire(renderer); + matchWireData(wiredata_in_a, sizeof (wiredata_in_a), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_IN_A_Test, toText) { + EXPECT_EQ("192.0.2.1", rdata_in_a.toText()); + + // this shouldn't make the code crash + const string longaddr("255.255.255.255"); + EXPECT_EQ(longaddr, in::A(longaddr).toText()); +} + +TEST_F(Rdata_IN_A_Test, compare) { + const in::A small1("1.1.1.1"); + const in::A small2("1.2.3.4"); + const in::A large1("255.255.255.255"); + const in::A large2("4.3.2.1"); + + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, small1.compare(small1)); + + // confirm these are compared as unsigned values + EXPECT_GT(0, small1.compare(large1)); + EXPECT_LT(0, large1.compare(small1)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small2.compare(large2)); + EXPECT_LT(0, large2.compare(small2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_in_a.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc new file mode 100644 index 0000000..cc7b07d --- /dev/null +++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc @@ -0,0 +1,151 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_IN_AAAA_Test : public RdataTest { +protected: + Rdata_IN_AAAA_Test() : rdata_in_aaaa("2001:db8::1234") {} + + // Common check to see the result of in::A Rdata construction either from + // std::string or with MasterLexer object. If it's expected to succeed + // the result should be identical to the commonly used test data + // (rdata_in_a); otherwise it should result in the exception specified as + // the template parameter. + void checkFromTextIN_AAAA(const string& in_aaaa_txt, + bool throw_str_version = true, + bool throw_lexer_version = true) + { + checkFromText<in::AAAA, InvalidRdataText, InvalidRdataText>( + in_aaaa_txt, rdata_in_aaaa, throw_str_version, + throw_lexer_version); + } + + const in::AAAA rdata_in_aaaa; +}; + +const uint8_t wiredata_in_aaaa[] = { + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x34 }; + +TEST_F(Rdata_IN_AAAA_Test, createFromText) { + // Normal case: no exception for either case, so the exception type + // doesn't matter. + checkFromText<in::AAAA, isc::Exception, isc::Exception>( + "2001:db8::1234", rdata_in_aaaa, false, false); + + // should reject an IP4 address. + checkFromTextIN_AAAA("192.0.2.1"); + // or any meaningless text as an IPv6 address + checkFromTextIN_AAAA("xxx"); + + // trailing white space: only string version throws + checkFromTextIN_AAAA("2001:db8::1234 ", true, false); + // same for beginning white space. + checkFromTextIN_AAAA(" 2001:db8::1234", true, false); + // same for trailing non-space garbage (note that lexer version still + // ignore it; it's expected to be detected at a higher layer). + checkFromTextIN_AAAA("2001:db8::1234 xxx", true, false); + + // nul character after a valid textual representation. + string nul_after_addr = "2001:db8::1234"; + nul_after_addr.push_back(0); + checkFromTextIN_AAAA(nul_after_addr, true, true); + + // a valid address surrounded by parentheses; only okay with lexer + checkFromTextIN_AAAA("(2001:db8::1234)", true, false); + + // input that would cause lexer-specific error; it's bad text as an + // address so should result in the string version, too. + checkFromText<in::AAAA, InvalidRdataText, MasterLexer::LexerError>( + ")2001:db8::1234", rdata_in_aaaa); +} + +TEST_F(Rdata_IN_AAAA_Test, createFromWire) { + // Valid data + EXPECT_EQ(0, rdata_in_aaaa.compare( + *rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 18), + DNSMessageFORMERR); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 36), + DNSMessageFORMERR); + // buffer too short. + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 55), + DNSMessageFORMERR); +} + +TEST_F(Rdata_IN_AAAA_Test, createFromLexer) { + EXPECT_EQ(0, rdata_in_aaaa.compare( + *test::createRdataUsingLexer(RRType::AAAA(), RRClass::IN(), + "2001:db8::1234"))); +} + +TEST_F(Rdata_IN_AAAA_Test, toWireBuffer) { + rdata_in_aaaa.toWire(obuffer); + matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_IN_AAAA_Test, toWireRenderer) { + rdata_in_aaaa.toWire(renderer); + matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_IN_AAAA_Test, toText) { + EXPECT_EQ("2001:db8::1234", rdata_in_aaaa.toText()); +} + +TEST_F(Rdata_IN_AAAA_Test, compare) { + in::AAAA small1("::1"); + in::AAAA small2("1:2:3:4:5:6:7:8"); + in::AAAA large1("ffff::"); + in::AAAA large2("8:7:6:5:4:3:2:1"); + + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, small1.compare(small1)); + + // confirm these are compared as unsigned values + EXPECT_GT(0, small1.compare(large1)); + EXPECT_LT(0, large1.compare(small1)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small2.compare(large2)); + EXPECT_LT(0, large2.compare(small2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_in_aaaa.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc new file mode 100644 index 0000000..35ff55c --- /dev/null +++ b/src/lib/dns/tests/rdata_minfo_unittest.cc @@ -0,0 +1,230 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_MINFO_Test : public RdataTest { +protected: + Rdata_MINFO_Test(): + minfo_txt("rmailbox.example.com. emailbox.example.com."), + minfo_txt2("root.example.com. emailbox.example.com."), + too_long_label("01234567890123456789012345678901234567" + "89012345678901234567890123."), + rdata_minfo(minfo_txt), + rdata_minfo2(minfo_txt2) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::MINFO, isc::Exception, isc::Exception>( + rdata_str, rdata_minfo, false, false); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::MINFO, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_minfo, true, true); + } + + void checkFromText_TooLongLabel(const string& rdata_str) { + checkFromText<generic::MINFO, TooLongLabel, TooLongLabel>( + rdata_str, rdata_minfo, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText<generic::MINFO, InvalidRdataText, isc::Exception>( + rdata_str, rdata_minfo, true, false); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<generic::MINFO, EmptyLabel, EmptyLabel>( + rdata_str, rdata_minfo, true, true); + } + + void checkFromText_MissingOrigin(const string& rdata_str) { + checkFromText + <generic::MINFO, MissingNameOrigin, MissingNameOrigin>( + rdata_str, rdata_minfo, true, true); + } + + void checkFromText_Origin(const string& rdata_str, const Name* origin) { + checkFromText<generic::MINFO, MissingNameOrigin, isc::Exception>( + rdata_str, rdata_minfo, true, false, origin); + } + + const string minfo_txt; + const string minfo_txt2; + const string too_long_label; + const generic::MINFO rdata_minfo; + const generic::MINFO rdata_minfo2; +}; + + +TEST_F(Rdata_MINFO_Test, createFromText) { + EXPECT_EQ(Name("rmailbox.example.com."), rdata_minfo.getRmailbox()); + EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo.getEmailbox()); + + EXPECT_EQ(Name("root.example.com."), rdata_minfo2.getRmailbox()); + EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo2.getEmailbox()); + + checkFromText_None(minfo_txt); + + // origin defined for lexer constructor, but not string constructor + const Name origin("example.com"); + checkFromText_Origin("rmailbox emailbox", &origin); + + // lexer constructor accepts extra text, but string constructor doesn't + checkFromText_BadString("rmailbox.example.com. emailbox.example.com. " + "extra.example.com."); +} + +TEST_F(Rdata_MINFO_Test, badText) { + // too long names + checkFromText_TooLongLabel("root.example.com." + too_long_label + + " emailbox.example.com."); + checkFromText_TooLongLabel("root.example.com. emailbox.example.com." + + too_long_label); + + // invalid names + checkFromText_EmptyLabel("root..example.com. emailbox.example.com."); + checkFromText_EmptyLabel("root.example.com. emailbox..example.com."); + + // missing name + checkFromText_LexerError("root.example.com."); + + // missing origin + checkFromText_MissingOrigin("root.example.com emailbox.example.com."); + checkFromText_MissingOrigin("root.example.com. emailbox.example.com"); +} + +TEST_F(Rdata_MINFO_Test, createFromWire) { + // uncompressed names + EXPECT_EQ(0, rdata_minfo.compare( + *rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire1.wire"))); + // compressed names + EXPECT_EQ(0, rdata_minfo.compare( + *rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire2.wire", 15))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire3.wire"), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire4.wire"), + InvalidRdataLength); + // bogus rmailbox name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire5.wire"), + DNSMessageFORMERR); + // bogus emailbox name, the error should be detected in the name + // constructor + EXPECT_THROW(rdataFactoryFromFile(RRType::MINFO(), RRClass::IN(), + "rdata_minfo_fromWire6.wire"), + DNSMessageFORMERR); +} + +TEST_F(Rdata_MINFO_Test, assignment) { + generic::MINFO copy((string(minfo_txt2))); + copy = rdata_minfo; + EXPECT_EQ(0, copy.compare(rdata_minfo)); + + // Check if the copied data is valid even after the original is deleted + generic::MINFO* copy2 = new generic::MINFO(rdata_minfo); + generic::MINFO copy3((string(minfo_txt2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_minfo)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_minfo)); +} + +TEST_F(Rdata_MINFO_Test, toWireBuffer) { + rdata_minfo.toWire(obuffer); + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed1.wire", data); + matchWireData(&data[0], data.size(), + obuffer.getData(), obuffer.getLength()); + + obuffer.clear(); + rdata_minfo2.toWire(obuffer); + vector<unsigned char> data2; + UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed2.wire", data2); + matchWireData(&data2[0], data2.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_MINFO_Test, toWireRenderer) { + rdata_minfo.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_minfo_toWire1.wire", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); + + renderer.clear(); + rdata_minfo2.toWire(renderer); + vector<unsigned char> data2; + UnitTestUtil::readWireData("rdata_minfo_toWire2.wire", data2); + matchWireData(&data2[0], data2.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_MINFO_Test, toText) { + EXPECT_EQ(minfo_txt, rdata_minfo.toText()); + EXPECT_EQ(minfo_txt2, rdata_minfo2.toText()); +} + +TEST_F(Rdata_MINFO_Test, compare) { + // check reflexivity + EXPECT_EQ(0, rdata_minfo.compare(rdata_minfo)); + + // names must be compared in case-insensitive manner + EXPECT_EQ(0, rdata_minfo.compare(generic::MINFO("RMAILBOX.example.com. " + "emailbox.EXAMPLE.com."))); + + // another MINFO whose rmailbox name is larger than that of rdata_minfo. + const generic::MINFO large1_minfo("zzzzzzzz.example.com. " + "emailbox.example.com."); + EXPECT_GT(0, rdata_minfo.compare(large1_minfo)); + EXPECT_LT(0, large1_minfo.compare(rdata_minfo)); + + // another MINFO whose emailbox name is larger than that of rdata_minfo. + const generic::MINFO large2_minfo("rmailbox.example.com. " + "zzzzzzzzzzz.example.com."); + EXPECT_GT(0, rdata_minfo.compare(large2_minfo)); + EXPECT_LT(0, large2_minfo.compare(rdata_minfo)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_minfo.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc new file mode 100644 index 0000000..b11411f --- /dev/null +++ b/src/lib/dns/tests/rdata_mx_unittest.cc @@ -0,0 +1,147 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_MX_Test : public RdataTest { +public: + Rdata_MX_Test() : + rdata_mx(10, Name("mx.example.com")) + {} + + const generic::MX rdata_mx; +}; + +TEST_F(Rdata_MX_Test, createFromText) { + const generic::MX rdata_mx2("10 mx.example.com."); + EXPECT_EQ(0, rdata_mx2.compare(rdata_mx)); +} + +TEST_F(Rdata_MX_Test, badText) { + EXPECT_THROW(const generic::MX rdata_mx("99999999 mx."), InvalidRdataText); + EXPECT_THROW(const generic::MX rdata_mx("10"), InvalidRdataText); + EXPECT_THROW(const generic::MX rdata_mx("SPOON"), InvalidRdataText); + EXPECT_THROW(const generic::MX rdata_mx("10 mx. example.com."), + InvalidRdataText); + // No origin and relative + EXPECT_THROW(const generic::MX rdata_mx("10 mx.example.com"), + MissingNameOrigin); + // Extra text at end of line + EXPECT_THROW(const generic::MX rdata_mx("10 mx.example.com. extra."), + InvalidRdataText); +} + +TEST_F(Rdata_MX_Test, copy) { + const generic::MX rdata_mx2(rdata_mx); + EXPECT_EQ(0, rdata_mx.compare(rdata_mx2)); +} + +TEST_F(Rdata_MX_Test, createFromWire) { + EXPECT_EQ(0, rdata_mx.compare( + *rdataFactoryFromFile(RRType("MX"), RRClass("IN"), + "rdata_mx_fromWire"))); + // TBD: more tests +} + +TEST_F(Rdata_MX_Test, createFromLexer) { + EXPECT_EQ(0, rdata_mx.compare( + *test::createRdataUsingLexer(RRType::MX(), RRClass::IN(), + "10 mx.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::MX("10 mx2.example.org.").compare( + *test::createRdataUsingLexer(RRType::MX(), RRClass::IN(), + "10 mx2"))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::MX(), RRClass::IN(), + "10 mx. example.com.")); + + // 65536 is larger than maximum possible preference + EXPECT_FALSE(test::createRdataUsingLexer(RRType::MX(), RRClass::IN(), + "65536 mx.example.com.")); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::MX(), RRClass::IN(), + "10 mx.example.com. extra.")); +} + +TEST_F(Rdata_MX_Test, toWireRenderer) { + renderer.writeName(Name("example.com")); + rdata_mx.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_mx_toWire1", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_MX_Test, toWireBuffer) { + Name("example.com").toWire(obuffer); + rdata_mx.toWire(obuffer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_mx_toWire2", data); + matchWireData(&data[0], data.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_MX_Test, toText) { + EXPECT_EQ("10 mx.example.com.", rdata_mx.toText()); +} + +TEST_F(Rdata_MX_Test, getMXName) { + EXPECT_EQ(Name("mx.example.com."), rdata_mx.getMXName()); +} + +TEST_F(Rdata_MX_Test, getMXPref) { + EXPECT_EQ(10, rdata_mx.getMXPref()); +} + +TEST_F(Rdata_MX_Test, compare) { + generic::MX small1(1, Name("mx.example.com")); + generic::MX small2(10, Name("mx.example.com")); + generic::MX large1(65535, Name("mx.example.com")); + generic::MX large2(256, Name("mx.example.com")); + + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, small1.compare(small1)); + + // confirm these are compared as unsigned values + EXPECT_GT(0, small1.compare(large1)); + EXPECT_LT(0, large1.compare(small1)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small2.compare(large2)); + EXPECT_LT(0, large2.compare(small2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_mx.compare(*rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc new file mode 100644 index 0000000..4ce008e --- /dev/null +++ b/src/lib/dns/tests/rdata_naptr_unittest.cc @@ -0,0 +1,239 @@ +// Copyright (C) 2011-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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::generic; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_NAPTR_Test : public RdataTest { +}; + +// 10 100 "S" "SIP+D2U" "" _sip._udp.example.com. +static uint8_t naptr_rdata[] = {0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49, + 0x50,0x2b,0x44,0x32,0x55,0x00,0x04,0x5f,0x73,0x69,0x70,0x04,0x5f,0x75,0x64, + 0x70,0x07,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00}; + +static const char *naptr_str = + "10 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str2 = + "10 100 S SIP+D2U \"\" _sip._udp.example.com."; + +static const char *naptr_str_small1 = + "9 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small2 = + "10 90 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small3 = + "10 100 \"R\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small4 = + "10 100 \"S\" \"SIP+C2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_small5 = + "10 100 \"S\" \"SIP+D2U\" \"\" _rip._udp.example.com."; + +static const char *naptr_str_large1 = + "11 100 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large2 = + "10 110 \"S\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large3 = + "10 100 \"T\" \"SIP+D2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large4 = + "10 100 \"S\" \"SIP+E2U\" \"\" _sip._udp.example.com."; +static const char *naptr_str_large5 = + "10 100 \"S\" \"SIP+D2U\" \"\" _tip._udp.example.com."; + +TEST_F(Rdata_NAPTR_Test, createFromText) { + NAPTR naptr(naptr_str); + EXPECT_EQ(10, naptr.getOrder()); + EXPECT_EQ(100, naptr.getPreference()); + EXPECT_EQ(string("S"), naptr.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); + EXPECT_EQ(string(""), naptr.getRegexp()); + EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); + + // Test <char-string> that separated by space + NAPTR naptr2(naptr_str2); + EXPECT_EQ(string("S"), naptr2.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr2.getServices()); +} + +TEST_F(Rdata_NAPTR_Test, badText) { + // Order number cannot exceed 65535 + EXPECT_THROW(const NAPTR naptr("65536 10 S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // Preference number cannot exceed 65535 + EXPECT_THROW(const NAPTR naptr("100 65536 S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // No regexp given + EXPECT_THROW(const NAPTR naptr("100 10 S SIP _sip._udp.example.com."), + InvalidRdataText); + // The double quotes seperator must match + EXPECT_THROW(const NAPTR naptr("100 10 \"S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // Order or preference cannot be missed + EXPECT_THROW(const NAPTR naptr("10 \"S\" SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // Unquoted fields must be separated by spaces + EXPECT_THROW(const NAPTR naptr("100 10S SIP \"\" _sip._udp.example.com."), + InvalidRdataText); + EXPECT_THROW(const NAPTR naptr("10010 \"S\" \"SIP\" \"\" _sip._udp.example.com."), + InvalidRdataText); + EXPECT_THROW(const NAPTR naptr("100 10 SSIP \"\" _sip._udp.example.com."), + InvalidRdataText); + // Field cannot be missing + EXPECT_THROW(const NAPTR naptr("100 10 \"S\""), InvalidRdataText); + + // The <character-string> cannot exceed 255 characters + string naptr_str; + naptr_str += "100 10 "; + for (int i = 0; i < 257; ++i) { + naptr_str += 'A'; + } + naptr_str += " SIP \"\" _sip._udp.example.com."; + EXPECT_THROW(const NAPTR naptr(naptr_str), CharStringTooLong); +} + +TEST_F(Rdata_NAPTR_Test, createFromWire) { + InputBuffer input_buffer(naptr_rdata, sizeof(naptr_rdata)); + NAPTR naptr(input_buffer, sizeof(naptr_rdata)); + EXPECT_EQ(10, naptr.getOrder()); + EXPECT_EQ(100, naptr.getPreference()); + EXPECT_EQ(string("S"), naptr.getFlags()); + EXPECT_EQ(string("SIP+D2U"), naptr.getServices()); + EXPECT_EQ(string(""), naptr.getRegexp()); + EXPECT_EQ(Name("_sip._udp.example.com."), naptr.getReplacement()); +} + +TEST_F(Rdata_NAPTR_Test, createFromWireTooLongDataLen) { + static uint8_t naptr_rdata_long[] = { + 0x00,0x0a,0x00,0x64,0x01,0x53,0x07,0x53,0x49,0x50,0x2b,0x44,0x32,0x55, + 0x00,0x04,0x5f,0x73,0x69,0x70,0x04,0x5f,0x75,0x64,0x70,0x07,0x65,0x78, + 0x61,0x6d,0x70,0x6c,0x65,0x03,0x63,0x6f,0x6d,0x00,0xff,0xff,0xff,0xff}; + InputBuffer input_buffer(naptr_rdata_long, sizeof(naptr_rdata_long)); + EXPECT_THROW(NAPTR naptr(input_buffer, sizeof(naptr_rdata_long)), + isc::dns::DNSMessageFORMERR); +} + +TEST_F(Rdata_NAPTR_Test, createFromWireTooShortDataLen) { + // missing data (just set rdata_len too low) + for (size_t i = 0; i < sizeof(naptr_rdata); ++i) { + // Just use existing correct buffer but set rdata_len too low + InputBuffer input_buffer(naptr_rdata, sizeof(naptr_rdata)); + EXPECT_THROW(NAPTR naptr(input_buffer, i), + isc::dns::DNSMessageFORMERR); + } +} + +TEST_F(Rdata_NAPTR_Test, createFromLexer) { + const NAPTR rdata_naptr(naptr_str); + + EXPECT_EQ(0, rdata_naptr.compare( + *test::createRdataUsingLexer(RRType::NAPTR(), RRClass::IN(), + naptr_str))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NAPTR(), RRClass::IN(), + "65536 10 S SIP \"\" " + "_sip._udp.example.com.")); +} + +TEST_F(Rdata_NAPTR_Test, toWire) { + NAPTR naptr(naptr_str); + + naptr.toWire(obuffer); + matchWireData(naptr_rdata, sizeof(naptr_rdata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_NAPTR_Test, toWireRenderer) { + NAPTR naptr(naptr_str); + + naptr.toWire(renderer); + matchWireData(naptr_rdata, sizeof(naptr_rdata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_NAPTR_Test, toText) { + NAPTR naptr(naptr_str); + EXPECT_EQ(naptr_str, naptr.toText()); + + // will add quotes even if they were not in the original input + EXPECT_EQ("10 100 \"S\" \"SIP+D2U\" \".*\" _sip._udp.example.com.", + NAPTR("10 100 S SIP+D2U .* _sip._udp.example.com.").toText()); + // will not add additional quotes + EXPECT_EQ("10 100 \"S\" \"SIP+D2U\" \".*\" _sip._udp.example.com.", + NAPTR("10 100 \"S\" \"SIP+D2U\" \".*\" _sip._udp.example.com.") + .toText()); +} + +TEST_F(Rdata_NAPTR_Test, compare) { + NAPTR naptr(naptr_str); + NAPTR naptr_small1(naptr_str_small1); + NAPTR naptr_small2(naptr_str_small2); + NAPTR naptr_small3(naptr_str_small3); + NAPTR naptr_small4(naptr_str_small4); + NAPTR naptr_small5(naptr_str_small5); + NAPTR naptr_large1(naptr_str_large1); + NAPTR naptr_large2(naptr_str_large2); + NAPTR naptr_large3(naptr_str_large3); + NAPTR naptr_large4(naptr_str_large4); + NAPTR naptr_large5(naptr_str_large5); + + EXPECT_EQ(0, naptr.compare(NAPTR(naptr_str))); + EXPECT_EQ(1, naptr.compare(naptr_small1)); + EXPECT_EQ(1, naptr.compare(naptr_small2)); + EXPECT_EQ(1, naptr.compare(naptr_small3)); + EXPECT_EQ(1, naptr.compare(naptr_small4)); + EXPECT_EQ(1, naptr.compare(naptr_small5)); + EXPECT_EQ(-1, naptr.compare(naptr_large1)); + EXPECT_EQ(-1, naptr.compare(naptr_large2)); + EXPECT_EQ(-1, naptr.compare(naptr_large3)); + EXPECT_EQ(-1, naptr.compare(naptr_large4)); + EXPECT_EQ(-1, naptr.compare(naptr_large5)); + EXPECT_EQ(-1, naptr_small1.compare(naptr)); + EXPECT_EQ(-1, naptr_small2.compare(naptr)); + EXPECT_EQ(-1, naptr_small3.compare(naptr)); + EXPECT_EQ(-1, naptr_small4.compare(naptr)); + EXPECT_EQ(-1, naptr_small5.compare(naptr)); + EXPECT_EQ(1, naptr_large1.compare(naptr)); + EXPECT_EQ(1, naptr_large2.compare(naptr)); + EXPECT_EQ(1, naptr_large3.compare(naptr)); + EXPECT_EQ(1, naptr_large4.compare(naptr)); + EXPECT_EQ(1, naptr_large5.compare(naptr)); +} + +TEST_F(Rdata_NAPTR_Test, copy) { + NAPTR naptr(naptr_str); + NAPTR naptr2(naptr); + NAPTR naptr3 = naptr; + + EXPECT_EQ(0, naptr.compare(naptr2)); + EXPECT_EQ(0, naptr.compare(naptr3)); + + naptr3 = naptr; + EXPECT_EQ(0, naptr.compare(naptr3)); +} + +} diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc new file mode 100644 index 0000000..31bb508 --- /dev/null +++ b/src/lib/dns/tests/rdata_ns_unittest.cc @@ -0,0 +1,145 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_NS_Test : public RdataTest { +public: + Rdata_NS_Test() : + rdata_ns("ns.example.com."), + rdata_ns2("ns2.example.com.") + {} + + const generic::NS rdata_ns; + const generic::NS rdata_ns2; +}; + +const uint8_t wiredata_ns[] = { + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_ns2[] = { + // first name: ns.example.com. + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: ns2.example.com. all labels except the first should be + // compressed. + 0x03, 0x6e, 0x73, 0x32, 0xc0, 0x03 }; + +TEST_F(Rdata_NS_Test, createFromText) { + EXPECT_EQ(0, rdata_ns.compare(generic::NS("ns.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_ns.compare(generic::NS("ns.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_ns.compare(generic::NS("NS.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_ns.compare(*createRdata(RRType("NS"), RRClass(65000), + "ns.example.com."))); +} + +TEST_F(Rdata_NS_Test, badText) { + // Extra input at end of line + EXPECT_THROW(generic::NS("ns.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_NS_Test, createFromWire) { + EXPECT_EQ(0, rdata_ns.compare( + *rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::NS("ns2.example.com.").compare( + *rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_NS_Test, createFromLexer) { + EXPECT_EQ(0, rdata_ns.compare( + *test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::NS("ns8.example.org.").compare( + *test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns8"))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "")); + + // Extra input at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns.example.com. extra.")); +} + +TEST_F(Rdata_NS_Test, toWireBuffer) { + rdata_ns.toWire(obuffer); + matchWireData(wiredata_ns, sizeof(wiredata_ns), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_NS_Test, toWireRenderer) { + rdata_ns.toWire(renderer); + matchWireData(wiredata_ns, sizeof(wiredata_ns), + renderer.getData(), renderer.getLength()); + + rdata_ns2.toWire(renderer); + matchWireData(wiredata_ns2, sizeof(wiredata_ns2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_NS_Test, toText) { + EXPECT_EQ("ns.example.com.", rdata_ns.toText()); +} + +TEST_F(Rdata_NS_Test, compare) { + generic::NS small("a.example."); + generic::NS large("example."); + EXPECT_TRUE(Name("a.example") > Name("example")); + EXPECT_GT(0, small.compare(large)); +} + +TEST_F(Rdata_NS_Test, getNSName) { + EXPECT_EQ(Name("ns.example.com."), rdata_ns.getNSName()); +} +} diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc new file mode 100644 index 0000000..c6d3a07 --- /dev/null +++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc @@ -0,0 +1,226 @@ +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> + +using isc::UnitTestUtil; +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { + +// Note: some tests can be shared with NSEC3PARAM. They are unified as +// typed tests defined in nsec3param_like_unittest. +class Rdata_NSEC3_Test : public RdataTest { +protected: + Rdata_NSEC3_Test() : + nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 " + "A NS SOA"), + nsec3_nosalt_txt("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"), + nsec3_notype_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6"), + rdata_nsec3(nsec3_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::NSEC3, isc::Exception, isc::Exception>( + rdata_str, rdata_nsec3, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::NSEC3, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_nsec3, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::NSEC3, BadValue, BadValue>( + rdata_str, rdata_nsec3, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::NSEC3, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_nsec3, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::NSEC3, InvalidRdataText, isc::Exception>( + rdata_str, rdata_nsec3, true, false); + } + + const string nsec3_txt; + const string nsec3_nosalt_txt; + const string nsec3_notype_txt; + const generic::NSEC3 rdata_nsec3; +}; + +TEST_F(Rdata_NSEC3_Test, fromText) { + // Hash that has the possible max length + EXPECT_EQ(255, generic::NSEC3("1 1 1 D399EAAB " + + string((255 * 8) / 5, '0') + + " NS").getNext().size()); + + // Hash is too long. Max = 255 bytes, base32-hex converts each 5 bytes + // of the original to 8 characters, so 260 * 8 / 5 is the smallest length + // of the encoded string that exceeds the max and doesn't require padding. + checkFromText_InvalidText("1 1 1 D399EAAB " + string((260 * 8) / 5, '0') + + " A NS SOA"); + + // Type bitmap is empty. it's possible and allowed for NSEC3. + EXPECT_NO_THROW(const generic::NSEC3 rdata_notype_nsec3(nsec3_notype_txt)); + + // Empty salt is also okay. + EXPECT_NO_THROW(const generic::NSEC3 rdata_nosalt_nsec3(nsec3_nosalt_txt)); + + // Bad type mnemonics + checkFromText_InvalidText("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6" + " BIFF POW SPOON"); + + // Bad base32hex + checkFromText_BadValue("1 1 1 D399EAAB " + "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"); + + // Hash algorithm out of range + checkFromText_InvalidText("256 1 1 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + + // Flags out of range + checkFromText_InvalidText("1 256 1 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + + // Iterations out of range + checkFromText_InvalidText("1 1 65536 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + + // Space is not allowed in salt or the next hash. This actually + // causes the Base32 decoder that parses the next hash that comes + // afterwards, to throw. + checkFromText_BadValue("1 1 1 D399 EAAB H9RSFB7FPF2L8" + "HG35CMPC765TDK23RP6 A NS SOA"); + + // Next hash must not contain padding (trailing '=' characters) + checkFromText_InvalidText("1 1 1 D399EAAB " + "AAECAwQFBgcICQoLDA0ODw== A NS SOA"); + + // String instead of number + checkFromText_LexerError("foo 1 1 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + checkFromText_LexerError("1 foo 1 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + checkFromText_LexerError("1 1 foo D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString( + "1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA ;comment\n" + "1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A NS SOA"); + + // Unmatched parenthesis should cause a lexer error + checkFromText_LexerError("1 1 1 D399EAAB " + "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A ) NS SOA"); +} + +TEST_F(Rdata_NSEC3_Test, createFromWire) { + // A valid NSEC3 RR with empty type bitmap. + EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(), + "rdata_nsec3_fromWire15.wire")); + + // Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test. + + // hash length is too large + EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(), + "rdata_nsec3_fromWire12.wire"), + DNSMessageFORMERR); + + // empty hash. invalid. + EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(), + "rdata_nsec3_fromWire14.wire"), + DNSMessageFORMERR); + + // RDLEN is too short to hold the hash length field + EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(), + "rdata_nsec3_fromWire17.wire"), + DNSMessageFORMERR); + + // Short buffer cases. The data is valid NSEC3 RDATA, but the buffer + // is trimmed at the end. All cases should result in an exception from + // the buffer class. + vector<uint8_t> data; + UnitTestUtil::readWireData("rdata_nsec3_fromWire1", data); + const uint16_t rdlen = (data.at(0) << 8) + data.at(1); + for (int i = 0; i < rdlen; ++i) { + // intentionally construct a short buffer + InputBuffer b(&data[0] + 2, i); + EXPECT_THROW(createRdata(RRType::NSEC3(), RRClass::IN(), b, 39), + InvalidBufferPosition); + } +} + +TEST_F(Rdata_NSEC3_Test, createFromLexer) { + EXPECT_EQ(0, rdata_nsec3.compare( + *test::createRdataUsingLexer(RRType::NSEC3(), RRClass::IN(), + nsec3_txt))); + + // empty salt is also okay. + const generic::NSEC3 rdata_nosalt_nsec3(nsec3_nosalt_txt); + EXPECT_EQ(0, rdata_nosalt_nsec3.compare( + *test::createRdataUsingLexer(RRType::NSEC3(), RRClass::IN(), + nsec3_nosalt_txt))); +} + +TEST_F(Rdata_NSEC3_Test, assign) { + generic::NSEC3 other_nsec3 = rdata_nsec3; + EXPECT_EQ(0, rdata_nsec3.compare(other_nsec3)); +} + +TEST_F(Rdata_NSEC3_Test, compare) { + // trivial case: self equivalence + EXPECT_EQ(0, generic::NSEC3(nsec3_txt).compare(generic::NSEC3(nsec3_txt))); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(generic::NSEC3(nsec3_txt).compare(*rdata_nomatch), + bad_cast); + + // test RDATAs, sorted in the ascending order. We only check comparison + // on NSEC3-specific fields. Bitmap comparison is tested in the bitmap + // tests. Common cases for NSEC3 and NSECPARAM3 are in their shared tests. + vector<generic::NSEC3> compare_set; + compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ38")); + compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ0000000000")); + compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ00UUUUUUUU")); + + vector<generic::NSEC3>::const_iterator it; + const vector<generic::NSEC3>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + SCOPED_TRACE("compare " + it->toText() + " to " + (it + 1)->toText()); + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } +} + +} diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc new file mode 100644 index 0000000..914a6f4 --- /dev/null +++ b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc @@ -0,0 +1,272 @@ +// 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 <config.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <string> +#include <vector> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +// Template for shared tests for NSEC3 and NSEC3PARAM +template <typename RDATA_TYPE> +class NSEC3PARAMLikeTest : public RdataTest { +protected: + NSEC3PARAMLikeTest() : + salt_txt("1 1 1 D399EAAB" + getCommonText()), + nosalt_txt("1 1 1 -" + getCommonText()), + obuffer(0) + {} + + RDATA_TYPE fromText(const string& rdata_text) { + return (RDATA_TYPE(rdata_text)); + } + + void compareCheck() const { + typename vector<RDATA_TYPE>::const_iterator it; + typename vector<RDATA_TYPE>::const_iterator const it_end = + compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + SCOPED_TRACE("compare " + it->toText() + " to " + + (it + 1)->toText()); + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + } + + const string salt_txt; // RDATA text with salt + const string nosalt_txt; // RDATA text without salt + OutputBuffer obuffer; // used in toWire() tests + MessageRenderer renderer; // ditto + vector<RDATA_TYPE> compare_set; // used in compare() tests + + // Convert generic Rdata to the corresponding derived Rdata class object. + // Defined here because it depends on the template parameter. + static const RDATA_TYPE& convert(const Rdata& rdata) { + return (dynamic_cast<const RDATA_TYPE&>(rdata)); + } + + // These depend on the specific RR type. We use specialized methods + // for them. + static RRType getType(); // return either RRType::NSEC3() or NSEC3PARAM() + static string getWireFilePrefix(); + static string getCommonText(); // commonly used part of textual form +}; + +// Instantiate specific typed tests +typedef ::testing::Types<generic::NSEC3, generic::NSEC3PARAM> TestRdataTypes; +#ifdef TYPED_TEST_SUITE +TYPED_TEST_SUITE(NSEC3PARAMLikeTest, TestRdataTypes); +#else +TYPED_TEST_CASE(NSEC3PARAMLikeTest, TestRdataTypes); +#endif + +template <> +RRType +NSEC3PARAMLikeTest<generic::NSEC3>::getType() { + return (RRType::NSEC3()); +} + +template <> +RRType +NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getType() { + return (RRType::NSEC3PARAM()); +} + +template <> +string +NSEC3PARAMLikeTest<generic::NSEC3>::getWireFilePrefix() { + return ("rdata_nsec3_"); +} + +template <> +string +NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getWireFilePrefix() { + return ("rdata_nsec3param_"); +} + +template <> +string +NSEC3PARAMLikeTest<generic::NSEC3>::getCommonText() { + // next hash + RR type bitmap + return (" H9RSFB7FPF2L8HG35CMPC765TDK23RP6 " + "NS SOA RRSIG DNSKEY NSEC3PARAM"); +} + +template <> +string +NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getCommonText() { + // there's no more text for NSEC3PARAM + return (""); +} + +TYPED_TEST(NSEC3PARAMLikeTest, fromText) { + // Numeric parameters have possible maximum values. Unusual, but must + // be accepted. + EXPECT_NO_THROW(this->fromText("255 255 65535 D399EAAB" + + this->getCommonText())); + + // 0-length salt + EXPECT_EQ(0, this->fromText(this->nosalt_txt).getSalt().size()); + + // salt that has the possible max length + EXPECT_EQ(255, this->fromText("1 1 1 " + string(255 * 2, '0') + + this->getCommonText()).getSalt().size()); +} + +TYPED_TEST(NSEC3PARAMLikeTest, badText) { + // Bad salt hex + EXPECT_THROW(this->fromText("1 1 1 SPORK0" + this->getCommonText()), + isc::BadValue); + EXPECT_THROW(this->fromText("1 1 1 ADDAFEE" + this->getCommonText()), + isc::BadValue); + + // Space within salt + EXPECT_THROW(this->fromText("1 1 1 ADDAFE ADDAFEEE" + + this->getCommonText()), + InvalidRdataText); + + // Similar to empty salt, but not really. This shouldn't cause confusion. + EXPECT_THROW(this->fromText("1 1 1 --" + this->getCommonText()), + isc::BadValue); + + // Too large algorithm + EXPECT_THROW(this->fromText("1000000 1 1 ADDAFEEE" + this->getCommonText()), + InvalidRdataText); + + // Too large flags + EXPECT_THROW(this->fromText("1 1000000 1 ADDAFEEE" + this->getCommonText()), + InvalidRdataText); + + // Too large iterations + EXPECT_THROW(this->fromText("1 1 65536 ADDAFEEE" + this->getCommonText()), + InvalidRdataText); + + // There should be a space between "1" and "D399EAAB" (salt) + EXPECT_THROW(this->fromText("1 1 1D399EAAB" + this->getCommonText()), + InvalidRdataText); + + // Salt is too long (possible max + 1 bytes) + EXPECT_THROW(this->fromText("1 1 1 " + string(256 * 2, '0') + + this->getCommonText()), + InvalidRdataText); +} + +TYPED_TEST(NSEC3PARAMLikeTest, toText) { + // normal case + EXPECT_EQ(this->salt_txt, this->fromText(this->salt_txt).toText()); + + // empty salt case + EXPECT_EQ(this->nosalt_txt, this->fromText(this->nosalt_txt).toText()); +} + +TYPED_TEST(NSEC3PARAMLikeTest, createFromWire) { + // Normal case + EXPECT_EQ(0, this->fromText(this->salt_txt).compare( + *this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire1").c_str()))); + + // Too short RDLENGTH: it doesn't even contain the first 5 octets. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire2.wire").c_str()), + DNSMessageFORMERR); + + // salt length is too large + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire11.wire").c_str()), + DNSMessageFORMERR); + + // empty salt. not so usual, but valid. + ConstRdataPtr rdata = + this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire13.wire").c_str()); + EXPECT_EQ(0, this->convert(*rdata).getSalt().size()); +} + +TYPED_TEST(NSEC3PARAMLikeTest, createFromLexer) { + EXPECT_EQ(0, this->fromText(this->salt_txt).compare( + *test::createRdataUsingLexer(this->getType(), RRClass::IN(), + this->salt_txt))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(this->getType(), RRClass::IN(), + "1000000 1 1 ADDAFEEE" + + this->getCommonText())); +} + +template <typename OUTPUT_TYPE> +void +toWireCheck(RRType rrtype, OUTPUT_TYPE& output, const string& data_file) { + vector<uint8_t> data; + UnitTestUtil::readWireData(data_file.c_str(), data); + InputBuffer buffer(&data[0], data.size()); + const uint16_t rdlen = buffer.readUint16(); + + output.clear(); + output.writeUint16(rdlen); + createRdata(rrtype, RRClass::IN(), buffer, rdlen)->toWire(output); + matchWireData(&data[0], data.size(), + output.getData(), output.getLength()); +} + +TYPED_TEST(NSEC3PARAMLikeTest, toWire) { + // normal case + toWireCheck(this->getType(), this->renderer, + this->getWireFilePrefix() + "fromWire1"); + toWireCheck(this->getType(), this->obuffer, + this->getWireFilePrefix() + "fromWire1"); + + // empty salt + toWireCheck(this->getType(), this->renderer, + this->getWireFilePrefix() + "fromWire13.wire"); + toWireCheck(this->getType(), this->obuffer, + this->getWireFilePrefix() + "fromWire13.wire"); +} + +TYPED_TEST(NSEC3PARAMLikeTest, compare) { + // test RDATAs, sorted in the ascending order. + this->compare_set.push_back(this->fromText("0 0 0 D399EAAB" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 0 0 D399EAAB" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 1 0 D399EAAB" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 1 1 -" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 1 1 D399EAAB" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 1 1 FF99EAAB" + + this->getCommonText())); + this->compare_set.push_back(this->fromText("1 1 1 FF99EA0000" + + this->getCommonText())); + + this->compareCheck(); +} + +} diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc new file mode 100644 index 0000000..9a677be --- /dev/null +++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc @@ -0,0 +1,209 @@ +// 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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_NSEC3PARAM_Test : public RdataTest { +protected: + Rdata_NSEC3PARAM_Test() : + nsec3param_txt("1 1 1 D399EAAB"), + nsec3param_nosalt_txt("1 1 1 -"), + rdata_nsec3param(nsec3param_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::NSEC3PARAM, isc::Exception, isc::Exception>( + rdata_str, rdata_nsec3param, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::NSEC3PARAM, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_nsec3param, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::NSEC3PARAM, BadValue, BadValue>( + rdata_str, rdata_nsec3param, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::NSEC3PARAM, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_nsec3param, true, true); + } + + void checkFromText_BadString(const string& rdata_str, + const generic::NSEC3PARAM& rdata) + { + checkFromText + <generic::NSEC3PARAM, InvalidRdataText, isc::Exception>( + rdata_str, rdata, true, false); + } + + const string nsec3param_txt; + const string nsec3param_nosalt_txt; + const generic::NSEC3PARAM rdata_nsec3param; +}; + +TEST_F(Rdata_NSEC3PARAM_Test, fromText) { + // Empty salt is okay. + EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_nosalt_txt).getSalt().size()); + + // Salt is missing. + checkFromText_LexerError("1 1 1"); + + // Salt has whitespace within. This only fails in the string + // constructor, as the lexer constructor stops reading at the end of + // its RDATA. + const generic::NSEC3PARAM rdata_nsec3param2("1 1 1 D399"); + checkFromText_BadString("1 1 1 D399 EAAB", rdata_nsec3param2); + + // Hash algorithm out of range. + checkFromText_InvalidText("256 1 1 D399EAAB"); + + // Flags out of range. + checkFromText_InvalidText("1 256 1 D399EAAB"); + + // Iterations out of range. + checkFromText_InvalidText("1 1 65536 D399EAAB"); + + // Bad hex sequence + checkFromText_BadValue("1 1 256 D399EAABZOO"); + + // String instead of number + checkFromText_LexerError("foo 1 256 D399EAAB"); + checkFromText_LexerError("1 foo 256 D399EAAB"); + checkFromText_LexerError("1 1 foo D399EAAB"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString("1 1 1 D399EAAB ; comment\n" + "1 1 1 D399EAAB", rdata_nsec3param); +} + +TEST_F(Rdata_NSEC3PARAM_Test, toText) { + EXPECT_EQ(nsec3param_txt, rdata_nsec3param.toText()); + + // Garbage space at the end should be ok. RFC5155 only forbids + // whitespace within the salt field, but any whitespace afterwards + // should be fine. + EXPECT_NO_THROW(generic::NSEC3PARAM("1 1 1 D399EAAB ")); + + // Hash algorithm in range. + EXPECT_NO_THROW(generic::NSEC3PARAM("255 1 1 D399EAAB")); + + // Flags in range. + EXPECT_NO_THROW(generic::NSEC3PARAM("1 255 1 D399EAAB")); + + // Iterations in range. + EXPECT_NO_THROW(generic::NSEC3PARAM("1 1 65535 D399EAAB")); +} + +TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) { + EXPECT_EQ(0, rdata_nsec3param.compare( + *rdataFactoryFromFile(RRType::NSEC3PARAM(), RRClass::IN(), + "rdata_nsec3param_fromWire1"))); + + // Short buffer cases. The data is valid NSEC3PARAM RDATA, but the buffer + // is trimmed at the end. All cases should result in an exception from + // the buffer class. + vector<uint8_t> data; + UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data); + const uint16_t rdlen = (data.at(0) << 8) + data.at(1); + for (int i = 0; i < rdlen; ++i) { + // intentionally construct a short buffer + InputBuffer b(&data[0] + 2, i); + EXPECT_THROW(createRdata(RRType::NSEC3PARAM(), RRClass::IN(), b, 9), + InvalidBufferPosition); + } +} + +TEST_F(Rdata_NSEC3PARAM_Test, createFromLexer) { + EXPECT_EQ(0, rdata_nsec3param.compare( + *test::createRdataUsingLexer(RRType::NSEC3PARAM(), RRClass::IN(), + nsec3param_txt))); + + // empty salt is also okay. + const generic::NSEC3PARAM rdata_nosalt_nsec3param(nsec3param_nosalt_txt); + EXPECT_EQ(0, rdata_nosalt_nsec3param.compare( + *test::createRdataUsingLexer(RRType::NSEC3PARAM(), RRClass::IN(), + nsec3param_nosalt_txt))); +} + +TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) { + renderer.skip(2); + rdata_nsec3param.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(renderer.getData()) + 2, + renderer.getLength() - 2); +} + +TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) { + rdata_nsec3param.toWire(obuffer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data); + matchWireData(&data[2], data.size() - 2, + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_NSEC3PARAM_Test, getHashAlg) { + EXPECT_EQ(1, rdata_nsec3param.getHashalg()); +} + +TEST_F(Rdata_NSEC3PARAM_Test, getFlags) { + EXPECT_EQ(1, rdata_nsec3param.getFlags()); +} + +TEST_F(Rdata_NSEC3PARAM_Test, assign) { + generic::NSEC3PARAM other_nsec3param("1 1 1 -"); + other_nsec3param = rdata_nsec3param; + EXPECT_EQ(0, rdata_nsec3param.compare(other_nsec3param)); +} + +TEST_F(Rdata_NSEC3PARAM_Test, compare) { + // trivial case: self equivalence + EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_txt). + compare(generic::NSEC3PARAM(nsec3param_txt))); + EXPECT_EQ(0, generic::NSEC3PARAM("1 1 1 -"). + compare(generic::NSEC3PARAM("1 1 1 -"))); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(generic::NSEC3PARAM(nsec3param_txt).compare(*rdata_nomatch), + bad_cast); +} + +} diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc new file mode 100644 index 0000000..570a2ab --- /dev/null +++ b/src/lib/dns/tests/rdata_nsec_unittest.cc @@ -0,0 +1,138 @@ +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_NSEC_Test : public RdataTest { + // there's nothing to specialize +}; + +const char* const nsec_txt = "www2.isc.org. CNAME RRSIG NSEC"; + +TEST_F(Rdata_NSEC_Test, toText_NSEC) { + const generic::NSEC rdata_nsec(nsec_txt); + EXPECT_EQ(nsec_txt, rdata_nsec.toText()); +} + +TEST_F(Rdata_NSEC_Test, badText_NSEC) { + EXPECT_THROW(generic::NSEC rdata_nsec("www.isc.org. BIFF POW SPOON"), + InvalidRdataText); + EXPECT_THROW(generic::NSEC rdata_nsec("www.isc.org."), + InvalidRdataText); +} + +TEST_F(Rdata_NSEC_Test, createFromWire_NSEC) { + const generic::NSEC rdata_nsec(nsec_txt); + EXPECT_EQ(0, rdata_nsec.compare( + *rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(), + "rdata_nsec_fromWire1"))); + + // Too short RDLENGTH + EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(), + "rdata_nsec_fromWire2"), + DNSMessageFORMERR); + + // Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test. +} + +TEST_F(Rdata_NSEC_Test, createFromLexer_NSEC) { + const generic::NSEC rdata_nsec(nsec_txt); + EXPECT_EQ(0, rdata_nsec.compare( + *test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(), + nsec_txt))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::NSEC("www2.example.org. CNAME RRSIG NSEC").compare( + *test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(), + "www2 CNAME RRSIG NSEC"))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(), + "www.isc.org.")); +} + +TEST_F(Rdata_NSEC_Test, toWireRenderer_NSEC) { + renderer.skip(2); + const generic::NSEC rdata_nsec(nsec_txt); + rdata_nsec.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_nsec_fromWire1", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(renderer.getData()) + 2, + renderer.getLength() - 2); +} + +TEST_F(Rdata_NSEC_Test, toWireBuffer_NSEC) { + const generic::NSEC rdata_nsec(nsec_txt); + rdata_nsec.toWire(obuffer); +} + +TEST_F(Rdata_NSEC_Test, assign) { + generic::NSEC rdata_nsec(nsec_txt); + generic::NSEC rdata_nsec2 = rdata_nsec; + EXPECT_EQ(0, rdata_nsec.compare(rdata_nsec2)); +} + +TEST_F(Rdata_NSEC_Test, getNextName) { + // The implementation is quite trivial, so we simply check it's actually + // defined and does work as intended in a simple case. + EXPECT_EQ(Name("www2.isc.org"), generic::NSEC((nsec_txt)).getNextName()); +} + +TEST_F(Rdata_NSEC_Test, compare) { + // trivial case: self equivalence + EXPECT_EQ(0, generic::NSEC("example. A"). + compare(generic::NSEC("example. A"))); + EXPECT_EQ(0, generic::NSEC("EXAMPLE. A"). // should be case insensitive + compare(generic::NSEC("example. A"))); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(generic::NSEC(nsec_txt).compare(*rdata_nomatch), + bad_cast); + + // test RDATAs, sorted in the ascending order. We only compare the + // next name here. Bitmap comparison is tested in the bitmap tests. + // Note that names are compared as wire-format data, not based on the + // domain name comparison. + vector<generic::NSEC> compare_set; + compare_set.push_back(generic::NSEC("a.example. A")); + compare_set.push_back(generic::NSEC("example. A")); + vector<generic::NSEC>::const_iterator it; + const vector<generic::NSEC>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + SCOPED_TRACE("compare " + it->toText() + " to " + (it + 1)->toText()); + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } +} + +} diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc new file mode 100644 index 0000000..f18e9df --- /dev/null +++ b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc @@ -0,0 +1,269 @@ +// Copyright (C) 2011-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 <config.h> + +#include <dns/tests/unittest_util.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <boost/lexical_cast.hpp> + +#include <string> +#include <vector> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; +using boost::lexical_cast; + +namespace { + +// Template for shared tests for NSEC and NSEC3 bitmaps +template <typename RDATA_TYPE> +class NSECLikeBitmapTest : public RdataTest { +protected: + RDATA_TYPE fromText(const string& rdata_text) { + return (RDATA_TYPE(rdata_text)); + } + + vector<RDATA_TYPE> compare_set; // used in compare() tests + + void compareCheck() const { + typename vector<RDATA_TYPE>::const_iterator it; + typename vector<RDATA_TYPE>::const_iterator const it_end = + compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + SCOPED_TRACE("compare " + it->toText() + " to " + + (it + 1)->toText()); + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + } + + // These depend on the specific RR type. We use specialized methods + // for them. + static RRType getType(); // return either RRType::NSEC() or NSEC3() + static string getWireFilePrefix(); + static string getCommonText(); // commonly used part of textual form +}; + +// Instantiate specific typed tests +typedef ::testing::Types<generic::NSEC, generic::NSEC3> TestRdataTypes; +#ifdef TYPED_TEST_SUITE +TYPED_TEST_SUITE(NSECLikeBitmapTest, TestRdataTypes); +#else +TYPED_TEST_CASE(NSECLikeBitmapTest, TestRdataTypes); +#endif + +// NSEC and NSEC3 bitmaps have some subtle differences, in which case we +// need to test them separately. Using these typedef type names with TEST_F +// will do the trick. +typedef NSECLikeBitmapTest<generic::NSEC3> NSEC3BitmapTest; +typedef NSECLikeBitmapTest<generic::NSEC> NSECBitmapTest; + +template <> +string +NSECLikeBitmapTest<generic::NSEC>::getWireFilePrefix() { + return ("rdata_nsec_"); +} + +template <> +RRType +NSECLikeBitmapTest<generic::NSEC>::getType() { + return (RRType::NSEC()); +} + +template <> +string +NSECLikeBitmapTest<generic::NSEC3>::getWireFilePrefix() { + return ("rdata_nsec3_"); +} + +template <> +RRType +NSECLikeBitmapTest<generic::NSEC3>::getType() { + return (RRType::NSEC3()); +} + +template <> +string +NSECLikeBitmapTest<generic::NSEC>::getCommonText() { + return ("next. "); +} + +template <> +string +NSECLikeBitmapTest<generic::NSEC3>::getCommonText() { + return ("1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR "); +} + +// Tests against various types of bogus NSEC/NSEC3 type bitmaps. +// The syntax and semantics are common for both RR types, and our +// implementation of that part is shared, so in theory it should be sufficient +// to test for only one RR type. But we check for both just in case. +TYPED_TEST(NSECLikeBitmapTest, createFromWire) { + // A malformed NSEC bitmap length field that could cause overflow. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire4.wire").c_str()), + DNSMessageFORMERR); + + // The bitmap field is incomplete (only the first byte is included) + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire5.wire").c_str()), + DNSMessageFORMERR); + + // Bitmap length is 0, which is invalid. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire6.wire").c_str()), + DNSMessageFORMERR); + + // Too large bitmap length with a short buffer. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire3").c_str()), + DNSMessageFORMERR); + + // A boundary case: longest possible bitmaps (32 maps). This should be + // accepted. + EXPECT_NO_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire7.wire").c_str())); + + // Another boundary condition: 33 bitmaps, which should be rejected. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire8.wire").c_str()), + DNSMessageFORMERR); + + // Disordered bitmap window blocks. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire9.wire").c_str()), + DNSMessageFORMERR); + + // Bitmap ending with all-zero bytes. Not necessarily harmful except + // the additional overhead of parsing, but invalid according to the + // spec anyway. + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire10.wire").c_str()), + DNSMessageFORMERR); +} + +// This tests the result of toText() with various kinds of NSEC/NSEC3 bitmaps. +// It also tests the "from text" constructor as a result. +TYPED_TEST(NSECLikeBitmapTest, toText) { + // A simple case (some commonly seen RR types in NSEC(3) bitmaps) + string rdata_text = this->getCommonText() + "NS SOA RRSIG DNSKEY"; + EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); + + // Similar to above, but involves more than one bitmap window blocks. + rdata_text = this->getCommonText() + "NS DLV"; + EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); + + // Make sure all possible bits in a one-octet bitmap field are handled + // correctly. + // We use the range around 1024 (reasonably higher number) so it's + // unlikely that they have predefined mnemonic and can be safely converted + // to TYPEnnnn by toText(). + for (unsigned int i = 1024; i < 1032; ++i) { + rdata_text = this->getCommonText() + "TYPE" + lexical_cast<string>(i); + EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); + } + + // Make sure all possible 32 octets in a longest possible block are + // handled correctly. + for (unsigned int i = 1024; i < 1024 + 256; i += 8) { + rdata_text = this->getCommonText() + "TYPE" + lexical_cast<string>(i); + EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); + } + + // Check for the highest window block. + rdata_text = this->getCommonText() + "TYPE65535"; + EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText()); +} + +TYPED_TEST(NSECLikeBitmapTest, compare) { + // Bit map: [win=0][len=1] 00000010 + this->compare_set.push_back(this->fromText(this->getCommonText() + "SOA")); + // Bit map: [win=0][len=1] 00000010, [win=4][len=1] 10000000 + this->compare_set.push_back(this->fromText(this->getCommonText() + + "SOA TYPE1024")); + // Bit map: [win=0][len=1] 00100000 + this->compare_set.push_back(this->fromText(this->getCommonText() + "NS")); + // Bit map: [win=0][len=1] 00100010 + this->compare_set.push_back(this->fromText(this->getCommonText() + + "NS SOA")); + // Bit map: [win=0][len=2] 00100000, 00000001 + this->compare_set.push_back(this->fromText(this->getCommonText() + + "NS MX")); + // Bit map: [win=4][len=1] 10000000 + this->compare_set.push_back(this->fromText(this->getCommonText() + + "TYPE1024")); + + this->compareCheck(); +} + +// NSEC bitmaps must not be empty +TEST_F(NSECBitmapTest, emptyMap) { + EXPECT_THROW(this->fromText("next.example.").toText(), InvalidRdataText); + + EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(), + (this->getWireFilePrefix() + + "fromWire16.wire").c_str()), + DNSMessageFORMERR); +} + +// NSEC3 bitmaps can be empty +TEST_F(NSEC3BitmapTest, emptyMap) { + // Read wire data wit an empty NSEC3 bitmap. This should succeed. + vector<uint8_t> data; + UnitTestUtil::readWireData((this->getWireFilePrefix() + + "fromWire16.wire").c_str(), data); + InputBuffer buffer(&data[0], data.size()); + const uint16_t rdlen = buffer.readUint16(); + const generic::NSEC3 empty_nsec3 = + dynamic_cast<const generic::NSEC3&>(*createRdata( + RRType::NSEC3(), RRClass::IN(), + buffer, rdlen)); + + // Check the toText() result. + EXPECT_EQ("1 0 1 7373737373 D1K6GQ38D1K6GQ38D1K6GQ38D1K6GQ38", + empty_nsec3.toText()); + + // Check the toWire() result. + OutputBuffer obuffer(0); + obuffer.writeUint16(rdlen); + empty_nsec3.toWire(obuffer); + matchWireData(&data[0], data.size(), + obuffer.getData(), obuffer.getLength()); + + // Same for MessageRenderer. + obuffer.clear(); + MessageRenderer renderer; + renderer.writeUint16(rdlen); + empty_nsec3.toWire(renderer); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +} diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc new file mode 100644 index 0000000..a235183 --- /dev/null +++ b/src/lib/dns/tests/rdata_opt_unittest.cc @@ -0,0 +1,198 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_OPT_Test : public RdataTest { + // there's nothing to specialize +}; + +const uint8_t rdata_opt_wiredata[] = { + // Option code + 0x00, 0x2a, + // Option length + 0x00, 0x03, + // Option data + 0x00, 0x01, 0x02 +}; + +TEST_F(Rdata_OPT_Test, createFromText) { + // OPT RR cannot be created from text. + EXPECT_THROW(generic::OPT("this does not matter"), InvalidRdataText); +} + +TEST_F(Rdata_OPT_Test, createFromWire) { + // Valid cases: in the simple implementation with no supported options, + // we can only check these don't throw. + EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass("CLASS4096"), + "rdata_opt_fromWire1")); + EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::CH(), + "rdata_opt_fromWire1", 2)); + + // Short RDLEN. This throws InvalidRdataLength even if subsequent + // pseudo RRs cause RDLEN size to be exhausted. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire2"), + InvalidRdataLength); + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire3"), + InvalidRdataLength); + // Option lengths can add up and overflow RDLEN. Unlikely when + // parsed from wire data, but we'll check for it anyway. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire4"), + InvalidRdataText); + + // short buffer case. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire1", 11), + InvalidBufferPosition); +} + +TEST_F(Rdata_OPT_Test, createFromLexer) { + // OPT RR cannot be created from text. Exceptions cause NULL to be + // returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::OPT(), RRClass::IN(), + "this does not matter")); +} + +TEST_F(Rdata_OPT_Test, toWireBuffer) { + const generic::OPT rdata_opt = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + obuffer.clear(); + rdata_opt.toWire(obuffer); + + matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_OPT_Test, toWireRenderer) { + const generic::OPT rdata_opt = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + renderer.clear(); + rdata_opt.toWire(renderer); + + matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_OPT_Test, toText) { + // empty OPT + const generic::OPT rdata_opt; + + EXPECT_THROW(rdata_opt.toText(), + isc::InvalidOperation); +} + +TEST_F(Rdata_OPT_Test, compare) { + // empty OPT + const generic::OPT rdata_opt; + + EXPECT_THROW(rdata_opt.compare( + *rdataFactoryFromFile(RRType::OPT(), RRClass::CH(), + "rdata_opt_fromWire1", 2)), + isc::InvalidOperation); + + // comparison attempt between incompatible RR types also results in + // isc::InvalidOperation. + EXPECT_THROW(rdata_opt.compare(*RdataTest::rdata_nomatch), + isc::InvalidOperation); +} + +TEST_F(Rdata_OPT_Test, appendPseudoRR) { + generic::OPT rdata_opt; + + // Append empty option data + rdata_opt.appendPseudoRR(0x0042, NULL, 0); + + // Append simple option data + const uint8_t option_data[] = {'H', 'e', 'l', 'l', 'o'}; + rdata_opt.appendPseudoRR(0x0043, option_data, sizeof(option_data)); + + // Duplicate option codes are okay. + rdata_opt.appendPseudoRR(0x0042, option_data, sizeof(option_data)); + + // When option length may overflow RDLEN, append should throw. + const std::vector<uint8_t> buffer((1 << 16) - 1); + EXPECT_THROW(rdata_opt.appendPseudoRR(0x0044, &buffer[0], buffer.size()), + isc::InvalidParameter); + + const uint8_t rdata_opt_wiredata2[] = { + // OPTION #1 + // ` Option code + 0x00, 0x42, + // ` Option length + 0x00, 0x00, + + // OPTION #2 + // ` Option code + 0x00, 0x43, + // ` Option length + 0x00, 0x05, + // ` Option data + 'H', 'e', 'l', 'l', 'o', + + // OPTION #3 + // ` Option code + 0x00, 0x42, + // ` Option length + 0x00, 0x05, + // ` Option data + 'H', 'e', 'l', 'l', 'o' + }; + + obuffer.clear(); + rdata_opt.toWire(obuffer); + + matchWireData(rdata_opt_wiredata2, sizeof(rdata_opt_wiredata2), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_OPT_Test, getPseudoRRs) { + const generic::OPT rdf = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + const std::vector<generic::OPT::PseudoRR>& rrs = rdf.getPseudoRRs(); + ASSERT_FALSE(rrs.empty()); + EXPECT_EQ(1, rrs.size()); + EXPECT_EQ(0x2a, rrs.at(0).getCode()); + EXPECT_EQ(3, rrs.at(0).getLength()); + + const uint8_t expected_data[] = {0x00, 0x01, 0x02}; + const uint8_t* actual_data = rrs.at(0).getData(); + EXPECT_EQ(0, std::memcmp(expected_data, actual_data, + sizeof(expected_data))); +} +} diff --git a/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc b/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc new file mode 100644 index 0000000..d639541 --- /dev/null +++ b/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc @@ -0,0 +1,56 @@ +// Copyright (C) 2014-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/. + +#include <config.h> + +#include <dns/rdata_pimpl_holder.h> + +#include <gtest/gtest.h> + +using namespace isc::dns::rdata; + +namespace { + +TEST(RdataPimplHolderTest, all) { + // Let's check with an integer + int* i1 = new int(42); + RdataPimplHolder<int> holder1(i1); + // The same pointer must be returned. + EXPECT_EQ(i1, holder1.get()); + // Obviously the value should match too. + EXPECT_EQ(42, *holder1.get()); + // We don't explicitly delete i or holder1, so it should not leak + // anything when the test is done (checked by Valgrind). + + // The following cases are similar: + + // Test no-argument reset() + int* i2 = new int(43); + RdataPimplHolder<int> holder2(i2); + holder2.reset(); + EXPECT_EQ(NULL, holder2.get()); + + // Test reset() with argument + int* i3 = new int(44); + int* i4 = new int(45); + RdataPimplHolder<int> holder3(i3); + EXPECT_EQ(i3, holder3.get()); + holder3.reset(i4); + EXPECT_EQ(i4, holder3.get()); + EXPECT_EQ(45, *holder3.get()); + + // Test release() + RdataPimplHolder<int> holder4(new int(46)); + EXPECT_NE(static_cast<void*>(NULL), holder4.get()); + EXPECT_EQ(46, *holder4.get()); + int* i5 = holder4.release(); + EXPECT_EQ(NULL, holder4.get()); + EXPECT_NE(static_cast<void*>(NULL), i5); + EXPECT_EQ(46, *i5); + delete i5; +} + +} diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc new file mode 100644 index 0000000..5ed3bce --- /dev/null +++ b/src/lib/dns/tests/rdata_ptr_unittest.cc @@ -0,0 +1,145 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +// +// This test currently simply copies the NS RDATA tests. +// + +namespace { +class Rdata_PTR_Test : public RdataTest { +public: + Rdata_PTR_Test() : + rdata_ptr("ns.example.com."), + rdata_ptr2("ns2.example.com.") + {} + + const generic::PTR rdata_ptr; + const generic::PTR rdata_ptr2; +}; + +const uint8_t wiredata_ptr[] = { + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_ptr2[] = { + // first name: ns.example.com. + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: ns2.example.com. all labels except the first should be + // compressed. + 0x03, 0x6e, 0x73, 0x32, 0xc0, 0x03 }; + +TEST_F(Rdata_PTR_Test, createFromText) { + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("ns.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("ns.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("NS.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_ptr.compare(*createRdata(RRType("PTR"), RRClass(65000), + "ns.example.com."))); +} + +TEST_F(Rdata_PTR_Test, badText) { + // Extra text at end of line + EXPECT_THROW(generic::PTR("foo.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_PTR_Test, createFromWire) { + EXPECT_EQ(0, rdata_ptr.compare( + *rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::PTR("ns2.example.com.").compare( + *rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_PTR_Test, createFromLexer) { + EXPECT_EQ(0, rdata_ptr.compare( + *test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "ns.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::PTR("foo0.example.org.").compare( + *test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "foo0"))); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "foo.example.com. extra.")); +} + +TEST_F(Rdata_PTR_Test, toWireBuffer) { + rdata_ptr.toWire(obuffer); + matchWireData(wiredata_ptr, sizeof(wiredata_ptr), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_PTR_Test, toWireRenderer) { + rdata_ptr.toWire(renderer); + matchWireData(wiredata_ptr, sizeof(wiredata_ptr), + renderer.getData(), renderer.getLength()); + + rdata_ptr2.toWire(renderer); + matchWireData(wiredata_ptr2, sizeof(wiredata_ptr2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_PTR_Test, toText) { + EXPECT_EQ("ns.example.com.", rdata_ptr.toText()); +} + +TEST_F(Rdata_PTR_Test, compare) { + generic::PTR small("a.example."); + generic::PTR large("example."); + EXPECT_TRUE(Name("a.example") > Name("example")); + EXPECT_GT(0, small.compare(large)); +} + +TEST_F(Rdata_PTR_Test, getPTRName) { + EXPECT_EQ(Name("ns.example.com"), rdata_ptr.getPTRName()); +} +} diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc new file mode 100644 index 0000000..e752f13 --- /dev/null +++ b/src/lib/dns/tests/rdata_rp_unittest.cc @@ -0,0 +1,200 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/rdataclass.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_RP_Test : public RdataTest { +protected: + Rdata_RP_Test() : + mailbox_name("root.example.com."), + text_name("rp-text.example.com."), + // this also serves as a test for "from text" constructor in a normal + // case. + rdata_rp("root.example.com. rp-text.example.com."), + obuffer(0) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::RP, isc::Exception, isc::Exception>( + rdata_str, rdata_rp, false, false); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::RP, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_rp, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText<generic::RP, InvalidRdataText, isc::Exception>( + rdata_str, rdata_rp, true, false); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<generic::RP, EmptyLabel, EmptyLabel>( + rdata_str, rdata_rp, true, true); + } + + void checkFromText_MissingOrigin(const string& rdata_str) { + checkFromText + <generic::RP, MissingNameOrigin, MissingNameOrigin>( + rdata_str, rdata_rp, true, true); + } + + void checkFromText_Origin(const string& rdata_str, const Name* origin) { + checkFromText<generic::RP, MissingNameOrigin, isc::Exception>( + rdata_str, rdata_rp, true, false, origin); + } + + const Name mailbox_name, text_name; + const generic::RP rdata_rp; // commonly used test RDATA + OutputBuffer obuffer; + MessageRenderer renderer; + vector<uint8_t> expected_wire; +}; + +TEST_F(Rdata_RP_Test, createFromText) { + EXPECT_EQ(mailbox_name, rdata_rp.getMailbox()); + EXPECT_EQ(text_name, rdata_rp.getText()); + + checkFromText_None("root.example.com. rp-text.example.com."); + + // origin defined for lexer constructor, but not string constructor + const Name origin("example.com"); + checkFromText_Origin("root rp-text", &origin); + + // lexer constructor accepts extra text, but string constructor doesn't + checkFromText_BadString("root.example.com. rp-text.example.com. " + "extra.example.com."); +} + +TEST_F(Rdata_RP_Test, badText) { + // invalid names + checkFromText_EmptyLabel("root..example.com. rp-text.example.com."); + checkFromText_EmptyLabel("root.example.com. rp-text..example.com."); + + // missing field + checkFromText_LexerError("root.example.com."); + + // missing origin + checkFromText_MissingOrigin("root.example.com rp-text.example.com."); + checkFromText_MissingOrigin("root.example.com. rp-text.example.com"); +} + +TEST_F(Rdata_RP_Test, createFromWire) { + RdataPtr rdata(rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire1.wire")); + EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox()); + EXPECT_EQ(text_name, dynamic_cast<generic::RP&>(*rdata).getText()); + + // a similar test with names being compressed + rdata = rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire2.wire", 30); + EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox()); + EXPECT_EQ(Name("rp-text.example.net"), + dynamic_cast<generic::RP&>(*rdata).getText()); +} + +TEST_F(Rdata_RP_Test, badFromWire) { + // RDLEN is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire3.wire"), + InvalidRdataLength); + + // RDLEN is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire4.wire"), + InvalidRdataLength); + + // bogus mailbox name + EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire5.wire"), + DNSMessageFORMERR); + + // bogus text name + EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(), + "rdata_rp_fromWire6.wire"), + DNSMessageFORMERR); +} + +TEST_F(Rdata_RP_Test, createFromParams) { + EXPECT_EQ(mailbox_name, generic::RP(mailbox_name, text_name).getMailbox()); + EXPECT_EQ(text_name, generic::RP(mailbox_name, text_name).getText()); +} + +TEST_F(Rdata_RP_Test, toWireBuffer) { + // construct expected data + UnitTestUtil::readWireData("rdata_rp_toWire1.wire", expected_wire); + + // construct actual data + rdata_rp.toWire(obuffer); + + // then compare them + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_RP_Test, toWireRenderer) { + // similar to toWireBuffer, but names in RDATA could be compressed due to + // preceding names. Actually they must not be compressed according to + // RFC3597, and this test checks that. + + UnitTestUtil::readWireData("rdata_rp_toWire2.wire", expected_wire); + + renderer.writeName(Name("a.example.com")); + renderer.writeName(Name("b.example.net")); + generic::RP(mailbox_name, Name("rp-text.example.net")).toWire(renderer); + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_RP_Test, toText) { + // there's not much to test for this method. Only checking a simple case. + EXPECT_EQ("root.example.com. rp-text.example.com.", rdata_rp.toText()); +} + +TEST_F(Rdata_RP_Test, compare) { + // check reflexivity + EXPECT_EQ(0, rdata_rp.compare(rdata_rp)); + + // names must be compared in case-insensitive manner + EXPECT_EQ(0, rdata_rp.compare(generic::RP("ROOT.example.com. " + "rp-text.EXAMPLE.com."))); + + // another RP whose mailbox name is larger than that of rdata_rp. + const generic::RP large1_rp("zzzz.example.com. rp-text.example.com."); + EXPECT_GT(0, rdata_rp.compare(large1_rp)); + EXPECT_LT(0, large1_rp.compare(rdata_rp)); + + // yet another RP whose text name is larger than that of rdata_rp. + const generic::RP large2_rp("root.example.com. zzzzzzz.example.com."); + EXPECT_GT(0, rdata_rp.compare(large2_rp)); + EXPECT_LT(0, large2_rp.compare(rdata_rp)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_rp.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc new file mode 100644 index 0000000..b198d15 --- /dev/null +++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc @@ -0,0 +1,369 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> +#include <dns/tests/rdata_unittest.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +const uint8_t wiredata_rrsig[] = { + // type covered = A + 0x00, 0x01, + // algorithm = 5 + 0x05, + // labels = 4 + 0x04, + // original TTL = 43200 (0x0000a8c0) + 0x00, 0x00, 0xa8, 0xc0, + // signature expiration = 1266961577 (0x4b844ca9) + 0x4b, 0x84, 0x4c, 0xa9, + // signature inception = 1266875177 (0x4b82fb29) + 0x4b, 0x82, 0xfb, 0x29, + // key tag = 8496 (0x2130) + 0x21, 0x30, + // signer's name (isc.org.) + // 3 i s c 3 o r g 0 + 0x03, 0x69, 0x73, 0x63, 0x03, 0x6f, 0x72, 0x67, 0x00, + // signature data follows + 0x7a, 0xfc, 0x61, 0x94, 0x6c, + 0x75, 0xde, 0x6a, 0x4a, 0x2d, 0x59, 0x0a, 0xb2, + 0x3a, 0x46, 0xcf, 0x27, 0x12, 0xe6, 0xdc, 0x2d, + 0x22, 0x8c, 0x4e, 0x9a, 0x53, 0x75, 0xe3, 0x0f, + 0x6d, 0xe4, 0x08, 0x33, 0x18, 0x19, 0xb3, 0x76, + 0x21, 0x9d, 0x2c, 0x8a, 0xc5, 0x69, 0xba, 0xab, + 0xef, 0x66, 0x9f, 0xda, 0xb5, 0x2a, 0xf9, 0x40, + 0xc1, 0x28, 0xc5, 0x97, 0xba, 0x3c, 0x19, 0x4d, + 0x95, 0x13, 0xc2, 0xcd, 0xf6, 0xb1, 0x59, 0x5d, + 0x0c, 0xf9, 0x3f, 0x35, 0xbb, 0x9a, 0x70, 0x93, + 0x36, 0xe5, 0xf4, 0x17, 0x7e, 0xfe, 0x66, 0x3b, + 0x70, 0x1f, 0xed, 0x33, 0xa8, 0xa3, 0x0d, 0xc0, + 0x8c, 0xc6, 0x95, 0x1b, 0xd8, 0x9c, 0x8c, 0x25, + 0xb4, 0x57, 0x9e, 0x56, 0x71, 0x64, 0x14, 0x7f, + 0x8f, 0x6d, 0xfa, 0xc5, 0xca, 0x3f, 0x36, 0xe2, + 0xa4, 0xdf, 0x60, 0xfa, 0xcd, 0x59, 0x3e, 0x22, + 0x32, 0xa1, 0xf7 +}; + +class Rdata_RRSIG_Test : public RdataTest { +protected: + Rdata_RRSIG_Test() : + rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="), + rdata_rrsig(rrsig_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::RRSIG, isc::Exception, isc::Exception>( + rdata_str, rdata_rrsig, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_InvalidType(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidRRType, InvalidRRType>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_InvalidTime(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidTime, InvalidTime>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::RRSIG, BadValue, BadValue>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::RRSIG, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_MissingOrigin(const string& rdata_str) { + checkFromText + <generic::RRSIG, MissingNameOrigin, MissingNameOrigin>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::RRSIG, InvalidRdataText, isc::Exception>( + rdata_str, rdata_rrsig, true, false); + } + + const string rrsig_txt; + const generic::RRSIG rdata_rrsig; +}; + +TEST_F(Rdata_RRSIG_Test, fromText) { + EXPECT_EQ(rrsig_txt, rdata_rrsig.toText()); + EXPECT_EQ(isc::dns::RRType::A(), rdata_rrsig.typeCovered()); + + // Missing signature is OK + EXPECT_NO_THROW(const generic::RRSIG sig( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org.")); + + // Space in signature data is OK + checkFromText_None( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz " + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/ " + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU " + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + + // Multi-line signature data is OK, if enclosed in parentheses + checkFromText_None( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "( evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz\n" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/\n" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU\n" + "f49t+sXKPzbipN9g+s1ZPiIyofc= )"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc= ; comment\n" + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_missingFields) { + checkFromText_LexerError("A"); + checkFromText_LexerError("A 5"); + checkFromText_LexerError("A 5 4"); + checkFromText_LexerError("A 5 4 43200"); + checkFromText_LexerError("A 5 4 43200 20100223214617"); + checkFromText_LexerError("A 5 4 43200 20100223214617 20100222214617"); + checkFromText_LexerError("A 5 4 43200 20100223214617 20100222214617 " + "8496"); +} + +TEST_F(Rdata_RRSIG_Test, badText_coveredType) { + checkFromText_InvalidType("SPORK"); +} + +TEST_F(Rdata_RRSIG_Test, badText_algorithm) { + checkFromText_InvalidText( + "A 555 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A FIVE 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_labels) { + checkFromText_InvalidText( + "A 5 4444 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 FOUR 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_ttl) { + checkFromText_LexerError( + "A 5 4 999999999999 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 4 TTL " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + + // alternate form of TTL is not okay + checkFromText_LexerError( + "A 5 4 12H 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz " + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/ " + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU " + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_expiration) { + checkFromText_InvalidTime( + "A 5 4 43200 " + "201002232 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_InvalidTime( + "A 5 4 43200 " + "EXPIRATION 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_inception) { + checkFromText_InvalidTime( + "A 5 4 43200 " + "20100223214617 20100227 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_InvalidTime( + "A 5 4 43200 " + "20100223214617 INCEPTION 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_keytag) { + checkFromText_InvalidText( + "A 5 4 43200 " + "20100223214617 20100222214617 999999 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 4 43200 " + "20100223214617 20100222214617 TAG isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_signer) { + checkFromText_MissingOrigin( + "A 5 4 43200 " + "20100223214617 20100222214617 8496 isc.org " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_signature) { + checkFromText_BadValue( + "A 5 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="); + + // no space between the tag and signer + checkFromText_LexerError( + "A 5 4 43200 20100223214617 20100222214617 " + "8496isc.org. ofc="); + + // unterminated multi-line base64 + checkFromText_LexerError( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "( evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz\n" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/\n" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU\n" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, createFromLexer) { + EXPECT_EQ(0, rdata_rrsig.compare( + *test::createRdataUsingLexer(RRType::RRSIG(), RRClass::IN(), + rrsig_txt))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::RRSIG(), RRClass::IN(), + "INVALIDINPUT")); +} + +TEST_F(Rdata_RRSIG_Test, toWireRenderer) { + rdata_rrsig.toWire(renderer); + + matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_RRSIG_Test, toWireBuffer) { + rdata_rrsig.toWire(obuffer); + + matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_RRSIG_Test, createFromWire) { + const string rrsig_txt2( + "A 5 2 43200 20100327070149 20100225070149 2658 isc.org. " + "HkJk/xZTvzePU8NENl/ley8bbUumhk1hXciyqhLnz1VQFzkDooej6neX" + "ZgWZzQKeTKPOYWrnYtdZW4PnPQFeUl3orgLev7F8J6FZlDn0y/J/ThR5" + "m36Mo2/Gdxjj8lJ/IjPVkdpKyBpcnYND8KEIma5MyNCNeyO1UkfPQZGHNSQ="); + EXPECT_EQ(rrsig_txt2, + rdataFactoryFromFile(RRType("RRSIG"), RRClass("IN"), + "rdata_rrsig_fromWire1")->toText()); + const generic::RRSIG rdata_rrsig2(rrsig_txt2); + EXPECT_EQ(0, rdata_rrsig2.compare( + *rdataFactoryFromFile(RRType("RRSIG"), RRClass("IN"), + "rdata_rrsig_fromWire1"))); + + // RDLEN is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::RRSIG(), RRClass::IN(), + "rdata_rrsig_fromWire2.wire"), + InvalidRdataLength); +} +} diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc new file mode 100644 index 0000000..21f4dc4 --- /dev/null +++ b/src/lib/dns/tests/rdata_soa_unittest.cc @@ -0,0 +1,249 @@ +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_SOA_Test : public RdataTest { +protected: + Rdata_SOA_Test() : + rdata_soa(Name("ns.example.com"), + Name("root.example.com"), + 2010012601, 3600, 300, 3600000, 1200) + {} + + template <typename ExForString, typename ExForLexer> + void checkFromTextSOA(const string& soa_txt, const Name* origin = NULL, + bool throw_str_version = true, + bool throw_lexer_version = true) + { + checkFromText<generic::SOA, ExForString, ExForLexer>( + soa_txt, rdata_soa, throw_str_version, throw_lexer_version, + origin); + } + + const generic::SOA rdata_soa; +}; + +TEST_F(Rdata_SOA_Test, createFromText) { + // Below we specify isc::Exception as a dummy value for the exception type + // in case it's not expected to throw an exception; the type isn't used + // in the check code. + + // A simple case. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 3600 300 3600000 1200", + NULL, false, false); + + // Beginning and trailing space are ignored. + checkFromTextSOA<isc::Exception, isc::Exception>( + " ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200 ", NULL, false, false); + + // using extended TTL-like form for some parameters. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M", + NULL, false, false); + + // multi-line. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. (root.example.com.\n" + "2010012601 1H 5M 1000H) 20M", NULL, false, false); + + // relative names for MNAME and RNAME with a separate origin (lexer + // version only) + const Name origin("example.com"); + checkFromTextSOA<MissingNameOrigin, isc::Exception>( + "ns root 2010012601 1H 5M 1000H 20M", &origin, true, false); + + // with the '@' notation with a separate origin (lexer version only; + // string version would throw) + const Name full_mname("ns.example.com"); + checkFromTextSOA<MissingNameOrigin, isc::Exception>( + "@ root.example.com. 2010012601 1H 5M 1000H 20M", &full_mname, true, + false); + + // bad MNAME/RNAMEs + checkFromTextSOA<EmptyLabel, EmptyLabel>( + "bad..example. . 2010012601 1H 5M 1000H 20M"); + checkFromTextSOA<EmptyLabel, EmptyLabel>( + ". bad..example. 2010012601 1H 5M 1000H 20M"); + + // Names shouldn't be quoted. + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + "\".\" . 0 0 0 0 0"); + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". \".\" 0 0 0 0 0"); + + // Missing MAME or RNAME: for the string version, the serial would be + // tried as RNAME and result in "not absolute". For the lexer version, + // it reaches the end-of-line, missing min TTL. + checkFromTextSOA<MissingNameOrigin, MasterLexer::LexerError>( + ". 2010012601 0 0 0 0", &Name::ROOT_NAME()); + + // bad serial. the string version converts lexer error to + // InvalidRdataText. + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". . bad 0 0 0 0"); + + // bad serial; exceeding the uint32_t range (4294967296 = 2^32) + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". . 4294967296 0 0 0 0"); + + // Bad format for other numeric parameters. These will be tried as a TTL, + // and result in an exception there. + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 bad 0 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 4294967296 0 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 bad 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 4294967296 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 bad 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 4294967296 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 0 bad"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 0 4294967296"); + + // No space between RNAME and serial. This case is the same as missing + // M/RNAME. + checkFromTextSOA<MissingNameOrigin, MasterLexer::LexerError>( + ". example.0 0 0 0 0", &Name::ROOT_NAME()); + + // Extra parameter. string version immediately detects the error. + // lexer version defers the check to the upper layer (we pass origin + // to skip the check with the string version). + checkFromTextSOA<InvalidRdataText, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M " + "extra", &origin, true, false); + + // Likewise. Redundant newline is also considered an error. The lexer + // version accepts trailing newline, but not the beginning one (where + // the lexer expects a string excluding newline and EOF). + checkFromTextSOA<InvalidRdataText, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M\n", + NULL, true, false); + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + "\nns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M", + NULL, true, true); +} + +TEST_F(Rdata_SOA_Test, createFromWire) { + EXPECT_EQ(0, rdata_soa.compare( + *rdataFactoryFromFile(RRType("SOA"), RRClass("IN"), + "rdata_soa_fromWire"))); + // TBD: more tests +} + +TEST_F(Rdata_SOA_Test, createFromLexer) { + EXPECT_EQ(0, rdata_soa.compare( + *test::createRdataUsingLexer(RRType::SOA(), RRClass::IN(), + "ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200"))); +} + +TEST_F(Rdata_SOA_Test, toWireRenderer) { + renderer.skip(2); + rdata_soa.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_soa_fromWire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(renderer.getData()) + 2, + renderer.getLength() - 2); +} + +TEST_F(Rdata_SOA_Test, toWireBuffer) { + obuffer.skip(2); + rdata_soa.toWire(obuffer); + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_soa_toWireUncompressed.wire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(obuffer.getData()) + 2, + obuffer.getLength() - 2); +} + +TEST_F(Rdata_SOA_Test, toText) { + EXPECT_EQ("ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200", rdata_soa.toText()); +} + +TEST_F(Rdata_SOA_Test, getSerial) { + EXPECT_EQ(2010012601, rdata_soa.getSerial().getValue()); +} + +TEST_F(Rdata_SOA_Test, getMinimum) { + EXPECT_EQ(1200, rdata_soa.getMinimum()); + + // Also check with a very large number (with the MSB being 1). + EXPECT_EQ(2154848336u, generic::SOA(Name("ns.example.com"), + Name("root.example.com"), + 0, 0, 0, 0, 0x80706050).getMinimum()); +} + +void +compareCheck(const generic::SOA& small, const generic::SOA& large) { + EXPECT_GT(0, small.compare(large)); + EXPECT_LT(0, large.compare(small)); +} + +TEST_F(Rdata_SOA_Test, compare) { + // Check simple equivalence + EXPECT_EQ(0, rdata_soa.compare(generic::SOA( + "ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200"))); + // Check name comparison is case insensitive + EXPECT_EQ(0, rdata_soa.compare(generic::SOA( + "NS.example.com. root.EXAMPLE.com. " + "2010012601 3600 300 3600000 1200"))); + + // Check names are compared in the RDATA comparison semantics (different + // from DNSSEC ordering for owner names) + compareCheck(generic::SOA("a.example. . 0 0 0 0 0"), + generic::SOA("example. . 0 0 0 0 0")); + compareCheck(generic::SOA(". a.example. 0 0 0 0 0"), + generic::SOA(". example. 0 0 0 0 0")); + + // Compare other numeric fields: 1076895760 = 0x40302010, + // 270544960 = 0x10203040. These are chosen to make sure that machine + // endianness doesn't confuse the comparison results. + compareCheck(generic::SOA(". . 270544960 0 0 0 0"), + generic::SOA(". . 1076895760 0 0 0 0")); + compareCheck(generic::SOA(". . 0 270544960 0 0 0"), + generic::SOA(". . 0 1076895760 0 0 0")); + compareCheck(generic::SOA(". . 0 0 270544960 0 0"), + generic::SOA(". . 0 0 1076895760 0 0")); + compareCheck(generic::SOA(". . 0 0 0 270544960 0"), + generic::SOA(". . 0 0 0 1076895760 0")); + compareCheck(generic::SOA(". . 0 0 0 0 270544960"), + generic::SOA(". . 0 0 0 0 1076895760")); +} + +} diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc new file mode 100644 index 0000000..a3296f2 --- /dev/null +++ b/src/lib/dns/tests/rdata_srv_unittest.cc @@ -0,0 +1,205 @@ +// Copyright (C) 2011-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 <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_SRV_Test : public RdataTest { +public: + Rdata_SRV_Test() : + srv_txt("1 5 1500 a.example.com."), + srv_txt2("1 5 1400 example.com."), + too_long_label("012345678901234567890123456789" + "0123456789012345678901234567890123."), + rdata_srv(srv_txt), + rdata_srv2(srv_txt2) + {} + + const string srv_txt; + const string srv_txt2; + const string too_long_label; + const in::SRV rdata_srv; + const in::SRV rdata_srv2; +}; + +// 1 5 1500 a.example.com. +const uint8_t wiredata_srv[] = { + 0x00, 0x01, 0x00, 0x05, 0x05, 0xdc, 0x01, 0x61, 0x07, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; +// 1 5 1400 example.com. +const uint8_t wiredata_srv2[] = { + 0x00, 0x01, 0x00, 0x05, 0x05, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00}; + +TEST_F(Rdata_SRV_Test, createFromText) { + EXPECT_EQ(1, rdata_srv.getPriority()); + EXPECT_EQ(5, rdata_srv.getWeight()); + EXPECT_EQ(1500, rdata_srv.getPort()); + EXPECT_EQ(Name("a.example.com."), rdata_srv.getTarget()); +} + +TEST_F(Rdata_SRV_Test, badText) { + // priority is too large (2814...6 is 2^48) + EXPECT_THROW(in::SRV("281474976710656 5 1500 a.example.com."), + InvalidRdataText); + // weight is too large + EXPECT_THROW(in::SRV("1 281474976710656 1500 a.example.com."), + InvalidRdataText); + // port is too large + EXPECT_THROW(in::SRV("1 5 281474976710656 a.example.com."), + InvalidRdataText); + // incomplete text + EXPECT_THROW(in::SRV("1 5 a.example.com."), + InvalidRdataText); + EXPECT_THROW(in::SRV("1 5 1500a.example.com."), + InvalidRdataText); + // bad name + EXPECT_THROW(in::SRV("1 5 1500 a.example.com." + too_long_label), + TooLongLabel); + // Extra text at end of line + EXPECT_THROW(in::SRV("1 5 1500 a.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_SRV_Test, assignment) { + in::SRV copy((string(srv_txt2))); + copy = rdata_srv; + EXPECT_EQ(0, copy.compare(rdata_srv)); + + // Check if the copied data is valid even after the original is deleted + in::SRV* copy2 = new in::SRV(rdata_srv); + in::SRV copy3((string(srv_txt2))); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_srv)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_srv)); +} + +TEST_F(Rdata_SRV_Test, createFromWire) { + EXPECT_EQ(0, rdata_srv.compare( + *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 23), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 46), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_cname_fromWire", 69), + DNSMessageFORMERR); + // parse compressed target name + EXPECT_EQ(0, rdata_srv.compare( + *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"), + "rdata_srv_fromWire", 89))); +} + +TEST_F(Rdata_SRV_Test, createFromLexer) { + EXPECT_EQ(0, rdata_srv.compare( + *test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "1 5 1500 a.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, in::SRV("1 5 1500 server16.example.org.").compare( + *test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "1 5 1500 server16"))); + + // Exceptions cause NULL to be returned. + + // Bad priority + EXPECT_FALSE(test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "65536 5 1500 " + "a.example.com.")); + // Bad weight + EXPECT_FALSE(test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "1 65536 1500 " + "a.example.com.")); + // Bad port + EXPECT_FALSE(test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "1 5 281474976710656 " + "a.example.com.")); + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::SRV(), RRClass::IN(), + "1 5 1500 a.example.com. extra.")); +} + +TEST_F(Rdata_SRV_Test, toWireBuffer) { + rdata_srv.toWire(obuffer); + matchWireData(wiredata_srv, sizeof(wiredata_srv), + obuffer.getData(), obuffer.getLength()); + + obuffer.clear(); + rdata_srv2.toWire(obuffer); + matchWireData(wiredata_srv2, sizeof(wiredata_srv2), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_SRV_Test, toWireRenderer) { + rdata_srv.toWire(renderer); + matchWireData(wiredata_srv, sizeof(wiredata_srv), + renderer.getData(), renderer.getLength()); + + renderer.clear(); + rdata_srv2.toWire(renderer); + matchWireData(wiredata_srv2, sizeof(wiredata_srv2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_SRV_Test, toText) { + EXPECT_EQ(srv_txt, rdata_srv.toText()); + EXPECT_EQ(srv_txt2, rdata_srv2.toText()); +} + +TEST_F(Rdata_SRV_Test, compare) { + // test RDATAs, sorted in the ascending order. + vector<in::SRV> compare_set; + compare_set.push_back(in::SRV("1 5 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 5 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1500 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1600 a.example.com.")); + compare_set.push_back(in::SRV("2 6 1600 example.com.")); + + EXPECT_EQ(0, compare_set[0].compare( + in::SRV("1 5 1500 a.example.com."))); + + vector<in::SRV>::const_iterator it; + vector<in::SRV>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_srv.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc new file mode 100644 index 0000000..2bf88f3 --- /dev/null +++ b/src/lib/dns/tests/rdata_sshfp_unittest.cc @@ -0,0 +1,311 @@ +// 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 <config.h> + +#include <algorithm> +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <boost/algorithm/string.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_SSHFP_Test : public RdataTest { +protected: + Rdata_SSHFP_Test() : + sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890"), + rdata_sshfp(sshfp_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::SSHFP, isc::Exception, isc::Exception>( + rdata_str, rdata_sshfp, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::SSHFP, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_sshfp, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::SSHFP, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_sshfp, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::SSHFP, InvalidRdataText, isc::Exception>( + rdata_str, rdata_sshfp, true, false); + } + + const string sshfp_txt; + const generic::SSHFP rdata_sshfp; +}; + +const uint8_t rdata_sshfp_wiredata[] = { + // algorithm + 0x02, + // fingerprint type + 0x01, + // fingerprint + 0x12, 0x34, 0x56, 0x78, + 0x9a, 0xbc, 0xde, 0xf6, + 0x78, 0x90, 0x12, 0x34, + 0x56, 0x78, 0x9a, 0xbc, + 0xde, 0xf6, 0x78, 0x90 +}; + +TEST_F(Rdata_SSHFP_Test, createFromText) { + // Basic test + checkFromText_None(sshfp_txt); + + // With different spacing + checkFromText_None("2 1 123456789abcdef67890123456789abcdef67890"); + + // Combination of lowercase and uppercase + checkFromText_None("2 1 123456789ABCDEF67890123456789abcdef67890"); + + // spacing in the fingerprint field + checkFromText_None("2 1 123456789abcdef67890 123456789abcdef67890"); + + // multi-line fingerprint field + checkFromText_None("2 1 ( 123456789abcdef67890\n 123456789abcdef67890 )"); + + // string constructor throws if there's extra text, + // but lexer constructor doesn't + checkFromText_BadString(sshfp_txt + "\n" + sshfp_txt); +} + +TEST_F(Rdata_SSHFP_Test, algorithmTypes) { + // Some of these may not be RFC conformant, but we relax the check + // in our code to work with algorithm and fingerprint types that may + // show up in the future. + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("2 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("3 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("128 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("255 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 2 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 3 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 128 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 255 12ab")); + + // 0 is reserved, but we allow that too + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("0 1 12ab")); + EXPECT_NO_THROW(const generic::SSHFP rdata_sshfp("1 0 12ab")); + + // > 255 would be broken + EXPECT_THROW(const generic::SSHFP rdata_sshfp("256 1 12ab"), + InvalidRdataText); + EXPECT_THROW(const generic::SSHFP rdata_sshfp("2 256 12ab"), + InvalidRdataText); +} + +TEST_F(Rdata_SSHFP_Test, badText) { + checkFromText_LexerError("1"); + checkFromText_LexerError("ONE 2 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("1 TWO 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText("1 2 BUCKLEMYSHOE"); + checkFromText_InvalidText(sshfp_txt + " extra text"); + + // yes, these are redundant to the last test cases in algorithmTypes + checkFromText_InvalidText( + "2345 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText( + "2 1234 123456789abcdef67890123456789abcdef67890"); + + // negative values are trapped in the lexer rather than the constructor + checkFromText_LexerError("-2 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("2 -1 123456789abcdef67890123456789abcdef67890"); +} + +TEST_F(Rdata_SSHFP_Test, copyAndAssign) { + // Copy construct + generic::SSHFP rdata_sshfp2(rdata_sshfp); + EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2)); + + // Assignment, mainly to confirm it doesn't cause disruption. + rdata_sshfp2 = rdata_sshfp; + EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2)); +} + +TEST_F(Rdata_SSHFP_Test, createFromWire) { + // Basic test + EXPECT_EQ(0, rdata_sshfp.compare( + *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire"))); + // Combination of lowercase and uppercase + EXPECT_EQ(0, rdata_sshfp.compare( + *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire2"))); + // algorithm=1, fingerprint=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire3.wire")); + + // algorithm=255, fingerprint=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire4.wire")); + + // algorithm=0, fingerprint=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire5.wire")); + + // algorithm=5, fingerprint=0 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire6.wire")); + + // algorithm=255, fingerprint=255 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire7.wire")); + + // short fingerprint data + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire8.wire")); + + // fingerprint is shorter than rdata len + EXPECT_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire9"), + InvalidBufferPosition); + + // fingerprint is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire10"), + InvalidBufferPosition); + + // all rdata is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire11"), + InvalidBufferPosition); +} + +TEST_F(Rdata_SSHFP_Test, createFromParams) { + const generic::SSHFP rdata_sshfp2( + 2, 1, "123456789abcdef67890123456789abcdef67890"); + EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp)); +} + +TEST_F(Rdata_SSHFP_Test, toText) { + EXPECT_TRUE(boost::iequals(sshfp_txt, rdata_sshfp.toText())); + + const string sshfp_txt2("2 1"); + const generic::SSHFP rdata_sshfp2(sshfp_txt2); + EXPECT_TRUE(boost::iequals(sshfp_txt2, rdata_sshfp2.toText())); + + const generic::SSHFP rdata_sshfp3("2 1 "); + EXPECT_TRUE(boost::iequals(sshfp_txt2, rdata_sshfp3.toText())); +} + +TEST_F(Rdata_SSHFP_Test, toWire) { + this->obuffer.clear(); + rdata_sshfp.toWire(this->obuffer); + + EXPECT_EQ(sizeof (rdata_sshfp_wiredata), + this->obuffer.getLength()); + + matchWireData(rdata_sshfp_wiredata, sizeof(rdata_sshfp_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_SSHFP_Test, compare) { + const generic::SSHFP rdata_sshfp2("2 1"); + EXPECT_EQ(-1, rdata_sshfp2.compare(rdata_sshfp)); + EXPECT_EQ(1, rdata_sshfp.compare(rdata_sshfp2)); +} + +TEST_F(Rdata_SSHFP_Test, getAlgorithmNumber) { + EXPECT_EQ(2, rdata_sshfp.getAlgorithmNumber()); +} + +TEST_F(Rdata_SSHFP_Test, getFingerprintType) { + EXPECT_EQ(1, rdata_sshfp.getFingerprintType()); +} + +TEST_F(Rdata_SSHFP_Test, getFingerprint) { + const std::vector<uint8_t>& fingerprint = + rdata_sshfp.getFingerprint(); + + EXPECT_EQ(rdata_sshfp.getFingerprintLength(), + fingerprint.size()); + for (size_t i = 0; i < fingerprint.size(); ++i) { + EXPECT_EQ(rdata_sshfp_wiredata[i + 2], + fingerprint.at(i)); + } +} + +TEST_F(Rdata_SSHFP_Test, getFingerprintLength) { + EXPECT_EQ(20, rdata_sshfp.getFingerprintLength()); +} + +TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromWire) { + const uint8_t rdf_wiredata[] = { + // algorithm + 0x04, + // fingerprint type + 0x09 + }; + + const generic::SSHFP rdf = + dynamic_cast<const generic::SSHFP&> + (*rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire12")); + + EXPECT_EQ(4, rdf.getAlgorithmNumber()); + EXPECT_EQ(9, rdf.getFingerprintType()); + EXPECT_EQ(0, rdf.getFingerprintLength()); + + this->obuffer.clear(); + rdf.toWire(this->obuffer); + + EXPECT_EQ(2, this->obuffer.getLength()); + + matchWireData(rdf_wiredata, sizeof(rdf_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromString) { + const generic::SSHFP rdata_sshfp2("5 6"); + const uint8_t rdata_sshfp2_wiredata[] = { + // algorithm + 0x05, + // fingerprint type + 0x06 + }; + + EXPECT_EQ(5, rdata_sshfp2.getAlgorithmNumber()); + EXPECT_EQ(6, rdata_sshfp2.getFingerprintType()); + EXPECT_EQ(0, rdata_sshfp2.getFingerprintLength()); + + this->obuffer.clear(); + rdata_sshfp2.toWire(this->obuffer); + + EXPECT_EQ(2, this->obuffer.getLength()); + + matchWireData(rdata_sshfp2_wiredata, sizeof(rdata_sshfp2_wiredata), + obuffer.getData(), obuffer.getLength()); +} +} diff --git a/src/lib/dns/tests/rdata_tkey_unittest.cc b/src/lib/dns/tests/rdata_tkey_unittest.cc new file mode 100644 index 0000000..8592a27 --- /dev/null +++ b/src/lib/dns/tests/rdata_tkey_unittest.cc @@ -0,0 +1,450 @@ +// Copyright (C) 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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsigerror.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_TKEY_Test : public RdataTest { +protected: + Rdata_TKEY_Test() : + // no Key or Other Data + valid_text1("gss-tsig. 20210501120000 20210501130000 GSS-API NOERROR 0 0"), + // Key but no Other Data + valid_text2("GSS-TSIG. 20210501120000 20210501130000 GSS-API BADSIG " + "12 FAKEFAKEFAKEFAKE 0"), + // Key and Other Data + valid_text3("gss.tsig. 20210501120000 20210501130000 GSS-API BADSIG " + "12 FAKEFAKEFAKEFAKE 6 FAKEFAKE"), + // Key and Other Data (with Error that doesn't expect Other Data) + valid_text4("gss.tsig. 20210501120000 20210501130000 3 BADSIG 12 " + "FAKEFAKEFAKEFAKE 6 FAKEFAKE"), + // numeric error code + valid_text5("GSS-TSIG. 20210501120000 20210501130000 GSS-API 2845 12 " + "FAKEFAKEFAKEFAKE 0"), + // GSS-API mode + valid_text6("gss-tsig. 20210501120000 20210501130000 GSS-API 0 12 " + "FAKEFAKEFAKEFAKE 0"), + rdata_tkey(valid_text1) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::TKEY, isc::Exception, isc::Exception>( + rdata_str, rdata_tkey, false, false); + } + + void checkFromText_InvalidTime(const string& rdata_str) { + checkFromText<generic::TKEY, InvalidTime, InvalidTime>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::TKEY, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::TKEY, BadValue, BadValue>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::TKEY, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_TooLongLabel(const string& rdata_str) { + checkFromText<generic::TKEY, TooLongLabel, TooLongLabel>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<generic::TKEY, EmptyLabel, EmptyLabel>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::TKEY, InvalidRdataText, isc::Exception>( + rdata_str, rdata_tkey, true, false); + } + + template <typename Output> + void toWireCommonChecks(Output& output) const; + + const string valid_text1; + const string valid_text2; + const string valid_text3; + const string valid_text4; + const string valid_text5; + const string valid_text6; + vector<uint8_t> expect_data; + const generic::TKEY rdata_tkey; // commonly used test RDATA +}; + +TEST_F(Rdata_TKEY_Test, fromText) { + // normal case. it also tests getter methods. + EXPECT_EQ(Name("gss-tsig"), rdata_tkey.getAlgorithm()); + EXPECT_EQ(1619870400, rdata_tkey.getInception()); + EXPECT_EQ("20210501120000", rdata_tkey.getInceptionDate()); + EXPECT_EQ(1619874000, rdata_tkey.getExpire()); + EXPECT_EQ("20210501130000", rdata_tkey.getExpireDate()); + EXPECT_EQ(3, rdata_tkey.getMode()); + EXPECT_EQ(0, rdata_tkey.getError()); + EXPECT_EQ(0, rdata_tkey.getKeyLen()); + EXPECT_EQ(static_cast<void*>(0), rdata_tkey.getKey()); + EXPECT_EQ(0, rdata_tkey.getOtherLen()); + EXPECT_EQ(static_cast<void*>(0), rdata_tkey.getOtherData()); + + generic::TKEY tkey2(valid_text2); + EXPECT_EQ(12, tkey2.getKeyLen()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, tkey2.getError()); + + generic::TKEY tkey3(valid_text3); + EXPECT_EQ(6, tkey3.getOtherLen()); + + // The other data is unusual, but we don't reject it. + EXPECT_NO_THROW(generic::TKEY tkey4(valid_text4)); + + // numeric representation of TKEY error + generic::TKEY tkey5(valid_text5); + EXPECT_EQ(2845, tkey5.getError()); + + // symbolic representation of TKEY mode + generic::TKEY tkey6(valid_text6); + EXPECT_EQ(generic::TKEY::GSS_API_MODE, tkey6.getMode()); + + // not fully qualified algorithm name + generic::TKEY tkey1("gss-tsig 20210501120000 20210501130000 3 0 0 0"); + EXPECT_EQ(0, tkey1.compare(rdata_tkey)); + + // multi-line rdata + checkFromText_None("gss-tsig. ( 20210501120000 20210501130000 GSS-API \n" + "NOERROR 0 0 )"); +}; + +TEST_F(Rdata_TKEY_Test, badText) { + // too many fields + checkFromText_BadString(valid_text1 + " 0 0"); + // not enough fields + checkFromText_LexerError("foo 20210501120000 20210501130000 0 BADKEY"); + // bad domain name + checkFromText_TooLongLabel( + "0123456789012345678901234567890123456789012345678901234567890123" + " 20210501120000 20210501130000 0 0 0 0"); + checkFromText_EmptyLabel("foo..bar 20210501120000 20210501130000 0 0 0 0"); + // invalid inception (no digit) + checkFromText_InvalidTime("foo TIME 20210501130000 0 0 0 0"); + // invalid inception (bad format) + checkFromText_InvalidTime("foo 0 20210501130000 0 0 0 0"); + // invalid expire (no digit) + checkFromText_InvalidTime("foo 20210501120000 TIME 0 0 0 0"); + // invalid expire (bad format) + checkFromText_InvalidTime("foo 20210501120000 0 0 0 0 0"); + // Unknown mode + checkFromText_InvalidText("foo 20210501120000 20210501130000 TEST 0 0 0"); + // Numeric mode is is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 65536 0 0 0"); + // Numeric mode is negative + checkFromText_InvalidText("foo 20210501120000 20210501130000 -1 0 0 0 0"); + // Unknown error code + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 TEST 0 0"); + // Numeric error code is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 65536 0 0"); + // Numeric error code is negative + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 -1 0 0"); + // Key len is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 65536 0"); + // invalid Key len (negative) + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 -1 0"); + // invalid Key len (not a number) + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 MACSIZE 0"); + // Key len and Key mismatch + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 9 FAKE 0"); + // Key is bad base64 + checkFromText_BadValue("foo 20210501120000 20210501130000 0 0 3 FAK= 0"); + // Other len is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 0 65536 FAKE"); + // Other len is negative + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 0 -1 FAKE"); + // invalid Other len + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 0 LEN FAKE"); + // Other len and data mismatch + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 0 9 FAKE"); +} + +void +fromWireCommonChecks(const generic::TKEY& tkey) { + EXPECT_EQ(Name("gss-tsig"), tkey.getAlgorithm()); + EXPECT_EQ(1619870400, tkey.getInception()); + EXPECT_EQ("20210501120000", tkey.getInceptionDate()); + EXPECT_EQ(1619874000, tkey.getExpire()); + EXPECT_EQ("20210501130000", tkey.getExpireDate()); + EXPECT_EQ(3, tkey.getMode()); + EXPECT_EQ(0, tkey.getError()); + + vector<uint8_t> expect_key(32, 'x'); + matchWireData(&expect_key[0], expect_key.size(), + tkey.getKey(), tkey.getKeyLen()); + + EXPECT_EQ(0, tkey.getOtherLen()); + EXPECT_EQ(static_cast<const void*>(0), tkey.getOtherData()); +} + +TEST_F(Rdata_TKEY_Test, createFromWire) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire1.wire")); + fromWireCommonChecks(dynamic_cast<generic::TKEY&>(*rdata)); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithOtherData) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire2.wire")); + const generic::TKEY& tkey(dynamic_cast<generic::TKEY&>(*rdata)); + + vector<uint8_t> expect_key(32, 'x'); + matchWireData(&expect_key[0], expect_key.size(), + tkey.getKey(), tkey.getKeyLen()); + + vector<uint8_t> expect_data = { 'a', 'b', 'c', 'd', '0', '1', '2', '3' }; + matchWireData(&expect_data[0], expect_data.size(), + tkey.getOtherData(), tkey.getOtherLen()); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithoutKey) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire3.wire")); + const generic::TKEY& tkey(dynamic_cast<generic::TKEY&>(*rdata)); + EXPECT_EQ(0, tkey.getKeyLen()); + EXPECT_EQ(static_cast<const void*>(0), tkey.getKey()); + + vector<uint8_t> expect_data = { 'a', 'b', 'c', 'd', '0', '1', '2', '3' }; + matchWireData(&expect_data[0], expect_data.size(), + tkey.getOtherData(), tkey.getOtherLen()); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithCompression) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire4.wire", + // we need to skip the dummy name: + Name("gss-tsig").getLength())); + fromWireCommonChecks(dynamic_cast<generic::TKEY&>(*rdata)); +} + +TEST_F(Rdata_TKEY_Test, badFromWire) { + // RDLENGTH is too short: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire5.wire"), + InvalidRdataLength); + // RDLENGTH is too long: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire6.wire"), + InvalidRdataLength); + // Algorithm name is broken: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire7.wire"), + DNSMessageFORMERR); + // Key length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire8.wire"), + InvalidBufferPosition); + // Other-data length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire9.wire"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TKEY_Test, copyConstruct) { + const generic::TKEY copy(rdata_tkey); + EXPECT_EQ(0, copy.compare(rdata_tkey)); + + // Check the copied data is valid even after the original is deleted + generic::TKEY* copy2 = new generic::TKEY(rdata_tkey); + generic::TKEY copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tkey)); +} + +TEST_F(Rdata_TKEY_Test, createFromParams) { + EXPECT_EQ(0, rdata_tkey.compare(generic::TKEY(Name("gss-tsig"), + 1619870400, + 1619874000, + 3, 0, 0, 0, 0, 0))); + + const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84, + 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, generic::TKEY(valid_text2).compare( + generic::TKEY(Name("GSS-TSIG"), 1619870400, 1619874000, + 3, 16, 12, fake_data, 0, 0))); + + const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, generic::TKEY(valid_text3).compare( + generic::TKEY(Name("gss.tsig"), 1619870400, 1619874000, + 3, 16, 12, fake_data, 6, fake_data2))); + + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 0, fake_data, 0, 0), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 12, 0, 0, 0), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 0, 0, 0, fake_data), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("fake_data"), 0, 0, 0, 0, 0, 0, 6, 0), + isc::InvalidParameter); +} + +TEST_F(Rdata_TKEY_Test, assignment) { + generic::TKEY copy(valid_text2); + copy = rdata_tkey; + EXPECT_EQ(0, copy.compare(rdata_tkey)); + + // Check if the copied data is valid even after the original is deleted + generic::TKEY* copy2 = new generic::TKEY(rdata_tkey); + generic::TKEY copy3(valid_text2); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tkey)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_tkey)); +} + +template <typename Output> +void +Rdata_TKEY_Test::toWireCommonChecks(Output& output) const { + vector<uint8_t> expect_data; + + output.clear(); + expect_data.clear(); + rdata_tkey.toWire(output); + // read the expected wire format data and trim the RDLEN part. + UnitTestUtil::readWireData("rdata_tkey_toWire1.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + generic::TKEY(valid_text2).toWire(output); + UnitTestUtil::readWireData("rdata_tkey_toWire2.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + generic::TKEY(valid_text3).toWire(output); + UnitTestUtil::readWireData("rdata_tkey_toWire3.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); +} + +TEST_F(Rdata_TKEY_Test, toWireBuffer) { + toWireCommonChecks<OutputBuffer>(obuffer); +} + +TEST_F(Rdata_TKEY_Test, toWireRenderer) { + toWireCommonChecks<MessageRenderer>(renderer); + + // check algorithm name won't compressed when it would otherwise. + expect_data.clear(); + renderer.clear(); + renderer.writeName(Name("gss-tsig")); + renderer.writeUint16(26); // RDLEN + rdata_tkey.toWire(renderer); + UnitTestUtil::readWireData("rdata_tkey_toWire4.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); + + // check algorithm can be used as a compression target. + expect_data.clear(); + renderer.clear(); + renderer.writeUint16(26); + rdata_tkey.toWire(renderer); + renderer.writeName(Name("gss-tsig")); + UnitTestUtil::readWireData("rdata_tkey_toWire5.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_TKEY_Test, toText) { + EXPECT_EQ(valid_text1, rdata_tkey.toText()); + EXPECT_EQ(valid_text2, generic::TKEY(valid_text2).toText()); + EXPECT_EQ(valid_text3, generic::TKEY(valid_text3).toText()); + EXPECT_EQ(valid_text5, generic::TKEY(valid_text5).toText()); +} + +TEST_F(Rdata_TKEY_Test, compare) { + // test RDATAs, sorted in the ascending order. + // "AAAA" encoded in BASE64 corresponds to 0x000000, so it should be the + // smallest data of the same length. + vector<generic::TKEY> compare_set; + compare_set.push_back(generic::TKEY("a.example 20210501120000 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120000 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 AAAA 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 3 AAAA")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 3 FAKE")); + + EXPECT_EQ(0, + compare_set[0].compare(generic::TKEY("A.EXAMPLE 20210501120000 " + "20210501130000 3 0 0 0"))); + + vector<generic::TKEY>::const_iterator it; + vector<generic::TKEY>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_tkey.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_tlsa_unittest.cc b/src/lib/dns/tests/rdata_tlsa_unittest.cc new file mode 100644 index 0000000..92970cb --- /dev/null +++ b/src/lib/dns/tests/rdata_tlsa_unittest.cc @@ -0,0 +1,276 @@ +// Copyright (C) 2014-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 <config.h> + +#include <algorithm> +#include <string> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <boost/algorithm/string.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_TLSA_Test : public RdataTest { +protected: + Rdata_TLSA_Test() : + tlsa_txt("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"), + rdata_tlsa(tlsa_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::TLSA, isc::Exception, isc::Exception>( + rdata_str, rdata_tlsa, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::TLSA, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_tlsa, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::TLSA, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_tlsa, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::TLSA, InvalidRdataText, isc::Exception>( + rdata_str, rdata_tlsa, true, false); + } + + const string tlsa_txt; + const generic::TLSA rdata_tlsa; +}; + +const uint8_t rdata_tlsa_wiredata[] = { + // certificate usage + 0x00, + // selector + 0x00, + // matching type + 0x01, + // certificate association data + 0xd2, 0xab, 0xde, 0x24, 0x0d, 0x7c, 0xd3, 0xee, + 0x6b, 0x4b, 0x28, 0xc5, 0x4d, 0xf0, 0x34, 0xb9, + 0x79, 0x83, 0xa1, 0xd1, 0x6e, 0x8a, 0x41, 0x0e, + 0x45, 0x61, 0xcb, 0x10, 0x66, 0x18, 0xe9, 0x71 +}; + +TEST_F(Rdata_TLSA_Test, createFromText) { + // Basic test + checkFromText_None(tlsa_txt); + + // With different spacing + checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"); + + // Combination of lowercase and uppercase + checkFromText_None("0 0 1 D2ABDE240D7CD3EE6B4B28C54DF034B9" + "7983a1d16e8a410e4561cb106618e971"); + + // spacing in the certificate association data field + checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + " 7983a1d16e8a410e4561cb106618e971"); + + // multi-line certificate association data field + checkFromText_None("0 0 1 ( d2abde240d7cd3ee6b4b28c54df034b9\n" + " 7983a1d16e8a410e4561cb106618e971 )"); + + // string constructor throws if there's extra text, + // but lexer constructor doesn't + checkFromText_BadString(tlsa_txt + "\n" + tlsa_txt); +} + +TEST_F(Rdata_TLSA_Test, fields) { + // Some of these may not be RFC conformant, but we relax the check + // in our code to work with other field values that may show up in + // the future. + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("1 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("2 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("3 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("128 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("255 0 1 12ab")); + + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 1 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 2 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 3 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 128 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 255 1 12ab")); + + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 2 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 3 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 128 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 255 12ab")); + + // > 255 would be broken + EXPECT_THROW(const generic::TLSA rdata_tlsa("256 0 1 12ab"), + InvalidRdataText); + EXPECT_THROW(const generic::TLSA rdata_tlsa("0 256 1 12ab"), + InvalidRdataText); + EXPECT_THROW(const generic::TLSA rdata_tlsa("0 0 256 12ab"), + InvalidRdataText); +} + +TEST_F(Rdata_TLSA_Test, badText) { + checkFromText_LexerError("1"); + checkFromText_LexerError("ONE 2 3 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("1 TWO 3 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("1 2 THREE 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText("1 2 3 BAABAABLACKSHEEP"); + checkFromText_InvalidText(tlsa_txt + " extra text"); + + // yes, these are redundant to the last test cases in the .fields + // test + checkFromText_InvalidText( + "2345 1 2 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText( + "3 1234 4 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText( + "5 6 1234 123456789abcdef67890123456789abcdef67890"); + + // negative values are trapped in the lexer rather than the + // constructor + checkFromText_LexerError("-2 0 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("0 -2 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("0 0 -2 123456789abcdef67890123456789abcdef67890"); +} + +TEST_F(Rdata_TLSA_Test, copyAndAssign) { + // Copy construct + generic::TLSA rdata_tlsa2(rdata_tlsa); + EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2)); + + // Assignment, mainly to confirm it doesn't cause disruption. + rdata_tlsa2 = rdata_tlsa; + EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2)); +} + +TEST_F(Rdata_TLSA_Test, createFromWire) { + // Basic test + EXPECT_EQ(0, rdata_tlsa.compare( + *rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire"))); + // Combination of lowercase and uppercase + EXPECT_EQ(0, rdata_tlsa.compare( + *rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire2"))); + // certificate_usage=0, selector=0, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire3.wire")); + + // certificate_usage=255, selector=0, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire4.wire")); + + // certificate_usage=0, selector=255, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire5.wire")); + + // certificate_usage=0, selector=0, matching_type=255 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire6.wire")); + + // certificate_usage=3, selector=1, matching_type=2 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire7.wire")); + + // short certificate association data + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire8.wire")); + + // certificate association data is shorter than rdata len + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire9"), + InvalidBufferPosition); + + // certificate association data is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire10"), + InvalidBufferPosition); + + // certificate association data is empty + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire12"), + InvalidRdataLength); + + // all RDATA is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire11"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TLSA_Test, createFromParams) { + const generic::TLSA rdata_tlsa2( + 0, 0, 1, "d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"); + EXPECT_EQ(0, rdata_tlsa2.compare(rdata_tlsa)); + + // empty certificate association data should throw + EXPECT_THROW(const generic::TLSA rdata_tlsa2(0, 0, 1, ""), + InvalidRdataText); +} + +TEST_F(Rdata_TLSA_Test, toText) { + EXPECT_TRUE(boost::iequals(tlsa_txt, rdata_tlsa.toText())); +} + +TEST_F(Rdata_TLSA_Test, toWire) { + this->obuffer.clear(); + rdata_tlsa.toWire(this->obuffer); + + EXPECT_EQ(sizeof (rdata_tlsa_wiredata), + this->obuffer.getLength()); + + matchWireData(rdata_tlsa_wiredata, sizeof(rdata_tlsa_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_TLSA_Test, compare) { + const generic::TLSA rdata_tlsa2("0 0 0 d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"); + EXPECT_EQ(-1, rdata_tlsa2.compare(rdata_tlsa)); + EXPECT_EQ(1, rdata_tlsa.compare(rdata_tlsa2)); +} + +TEST_F(Rdata_TLSA_Test, getCertificateUsage) { + EXPECT_EQ(0, rdata_tlsa.getCertificateUsage()); +} + +TEST_F(Rdata_TLSA_Test, getSelector) { + EXPECT_EQ(0, rdata_tlsa.getSelector()); +} + +TEST_F(Rdata_TLSA_Test, getMatchingType) { + EXPECT_EQ(1, rdata_tlsa.getMatchingType()); +} + +TEST_F(Rdata_TLSA_Test, getDataLength) { + EXPECT_EQ(32, rdata_tlsa.getDataLength()); +} +} diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc new file mode 100644 index 0000000..7b45b3b --- /dev/null +++ b/src/lib/dns/tests/rdata_tsig_unittest.cc @@ -0,0 +1,432 @@ +// Copyright (C) 2010-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 <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsigerror.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_TSIG_Test : public RdataTest { +protected: + Rdata_TSIG_Test() : + // no MAC or Other Data + valid_text1("hmac-md5.sig-alg.reg.int. 1286779327 300 " + "0 16020 BADKEY 0"), + // MAC but no Other Data + valid_text2("hmac-sha256. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADSIG 0"), + // MAC and Other Data + valid_text3("hmac-sha1. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE"), + // MAC and Other Data (with Error that doesn't expect Other Data) + valid_text4("hmac-sha1. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE"), + // numeric error code + valid_text5("hmac-sha256. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 2845 0"), + rdata_tsig(valid_text1) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<any::TSIG, isc::Exception, isc::Exception>( + rdata_str, rdata_tsig, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<any::TSIG, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<any::TSIG, BadValue, BadValue>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <any::TSIG, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_TooLongLabel(const string& rdata_str) { + checkFromText<any::TSIG, TooLongLabel, TooLongLabel>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<any::TSIG, EmptyLabel, EmptyLabel>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <any::TSIG, InvalidRdataText, isc::Exception>( + rdata_str, rdata_tsig, true, false); + } + + template <typename Output> + void toWireCommonChecks(Output& output) const; + + const string valid_text1; + const string valid_text2; + const string valid_text3; + const string valid_text4; + const string valid_text5; + vector<uint8_t> expect_data; + const any::TSIG rdata_tsig; // commonly used test RDATA +}; + +TEST_F(Rdata_TSIG_Test, fromText) { + // normal case. it also tests getter methods. + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), rdata_tsig.getAlgorithm()); + EXPECT_EQ(1286779327, rdata_tsig.getTimeSigned()); + EXPECT_EQ(300, rdata_tsig.getFudge()); + EXPECT_EQ(0, rdata_tsig.getMACSize()); + EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getMAC()); + EXPECT_EQ(16020, rdata_tsig.getOriginalID()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, rdata_tsig.getError()); + EXPECT_EQ(0, rdata_tsig.getOtherLen()); + EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getOtherData()); + + any::TSIG tsig2(valid_text2); + EXPECT_EQ(12, tsig2.getMACSize()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig2.getError()); + + any::TSIG tsig3(valid_text3); + EXPECT_EQ(6, tsig3.getOtherLen()); + + // The other data is unusual, but we don't reject it. + EXPECT_NO_THROW(any::TSIG tsig4(valid_text4)); + + // numeric representation of TSIG error + any::TSIG tsig5(valid_text5); + EXPECT_EQ(2845, tsig5.getError()); + + // not fully qualified algorithm name + any::TSIG tsig1("hmac-md5.sig-alg.reg.int 1286779327 300 " + "0 16020 BADKEY 0"); + EXPECT_EQ(0, tsig1.compare(rdata_tsig)); + + // multi-line rdata + checkFromText_None("hmac-md5.sig-alg.reg.int. ( 1286779327 300 \n" + "0 16020 BADKEY 0 )"); + + // short-form HMAC-MD5 name + const any::TSIG tsig6("hmac-md5. 1286779327 300 0 16020 BADKEY 0"); + EXPECT_EQ(0, tsig6.compare(rdata_tsig)); +}; + +TEST_F(Rdata_TSIG_Test, badText) { + // too many fields + checkFromText_BadString(valid_text1 + " 0 0"); + // not enough fields + checkFromText_LexerError("foo 0 0 0 0 BADKEY"); + // bad domain name + checkFromText_TooLongLabel( + "0123456789012345678901234567890123456789012345678901234567890123" + " 0 0 0 0 BADKEY 0"); + checkFromText_EmptyLabel("foo..bar 0 0 0 0 BADKEY"); + // time is too large (2814...6 is 2^48) + checkFromText_InvalidText("foo 281474976710656 0 0 0 BADKEY 0"); + // invalid time (negative) + checkFromText_InvalidText("foo -1 0 0 0 BADKEY 0"); + // invalid time (not a number) + checkFromText_InvalidText("foo TIME 0 0 0 BADKEY 0"); + // fudge is too large + checkFromText_InvalidText("foo 0 65536 0 0 BADKEY 0"); + // invalid fudge (negative) + checkFromText_LexerError("foo 0 -1 0 0 BADKEY 0"); + // invalid fudge (not a number) + checkFromText_LexerError("foo 0 FUDGE 0 0 BADKEY 0"); + // MAC size is too large + checkFromText_InvalidText("foo 0 0 65536 0 BADKEY 0"); + // invalid MAC size (negative) + checkFromText_LexerError("foo 0 0 -1 0 BADKEY 0"); + // invalid MAC size (not a number) + checkFromText_LexerError("foo 0 0 MACSIZE 0 BADKEY 0"); + // MAC size and MAC mismatch + checkFromText_InvalidText("foo 0 0 9 FAKE 0 BADKEY 0"); + // MAC is bad base64 + checkFromText_BadValue("foo 0 0 3 FAK= 0 BADKEY 0"); + // Unknown error code + checkFromText_InvalidText("foo 0 0 0 0 TEST 0"); + // Numeric error code is too large + checkFromText_InvalidText("foo 0 0 0 0 65536 0"); + // Numeric error code is negative + checkFromText_InvalidText("foo 0 0 0 0 -1 0"); + // Other len is too large + checkFromText_InvalidText("foo 0 0 0 0 NOERROR 65536 FAKE"); + // Other len is negative + checkFromText_LexerError("foo 0 0 0 0 NOERROR -1 FAKE"); + // invalid Other len + checkFromText_LexerError("foo 0 0 0 0 NOERROR LEN FAKE"); + // Other len and data mismatch + checkFromText_InvalidText("foo 0 0 0 0 NOERROR 9 FAKE"); +} + +void +fromWireCommonChecks(const any::TSIG& tsig) { + EXPECT_EQ(Name("hmac-sha256"), tsig.getAlgorithm()); + EXPECT_EQ(1286978795, tsig.getTimeSigned()); + EXPECT_EQ(300, tsig.getFudge()); + + vector<uint8_t> expect_mac(32, 'x'); + matchWireData(&expect_mac[0], expect_mac.size(), + tsig.getMAC(), tsig.getMACSize()); + + EXPECT_EQ(2845, tsig.getOriginalID()); + + EXPECT_EQ(0, tsig.getOtherLen()); + EXPECT_EQ(static_cast<const void*>(NULL), tsig.getOtherData()); +} + +TEST_F(Rdata_TSIG_Test, createFromWire) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire1.wire")); + fromWireCommonChecks(dynamic_cast<any::TSIG&>(*rdata)); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithOtherData) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire2.wire")); + const any::TSIG& tsig(dynamic_cast<any::TSIG&>(*rdata)); + + EXPECT_EQ(18, tsig.getError()); + const uint64_t otherdata = 1286978795 + 300 + 1; // time-signed + fudge + 1 + expect_data.resize(6); + expect_data[0] = (otherdata >> 40); + expect_data[1] = ((otherdata >> 32) & 0xff); + expect_data[2] = ((otherdata >> 24) & 0xff); + expect_data[3] = ((otherdata >> 16) & 0xff); + expect_data[4] = ((otherdata >> 8) & 0xff); + expect_data[5] = (otherdata & 0xff); + matchWireData(&expect_data[0], expect_data.size(), + tsig.getOtherData(), tsig.getOtherLen()); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithoutMAC) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire3.wire")); + const any::TSIG& tsig(dynamic_cast<any::TSIG&>(*rdata)); + EXPECT_EQ(16, tsig.getError()); + EXPECT_EQ(0, tsig.getMACSize()); + EXPECT_EQ(static_cast<const void*>(NULL), tsig.getMAC()); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithCompression) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire4.wire", + // we need to skip the dummy name: + Name("hmac-sha256").getLength())); + fromWireCommonChecks(dynamic_cast<any::TSIG&>(*rdata)); +} + +TEST_F(Rdata_TSIG_Test, badFromWire) { + // RDLENGTH is too short: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire5.wire"), + InvalidRdataLength); + // RDLENGTH is too long: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire6.wire"), + InvalidRdataLength); + // Algorithm name is broken: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire7.wire"), + DNSMessageFORMERR); + // MAC size is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire8.wire"), + InvalidBufferPosition); + // Other-data length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire9.wire"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TSIG_Test, copyConstruct) { + const any::TSIG copy(rdata_tsig); + EXPECT_EQ(0, copy.compare(rdata_tsig)); + + // Check the copied data is valid even after the original is deleted + any::TSIG* copy2 = new any::TSIG(rdata_tsig); + any::TSIG copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tsig)); +} + +TEST_F(Rdata_TSIG_Test, createFromParams) { + EXPECT_EQ(0, rdata_tsig.compare(any::TSIG(Name("hmac-md5.sig-alg.reg.int"), + 1286779327, 300, 0, NULL, 16020, + 17, 0, NULL))); + + const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84, + 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, any::TSIG(valid_text2).compare( + any::TSIG(Name("hmac-sha256"), 1286779327, 300, 12, + fake_data, 16020, 16, 0, NULL))); + + const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, any::TSIG(valid_text3).compare( + any::TSIG(Name("hmac-sha1"), 1286779327, 300, 12, + fake_data, 16020, 18, 6, fake_data2))); + + EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 1ULL << 48, 300, 12, + fake_data, 16020, 18, 6, fake_data2), + isc::OutOfRange); + EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, fake_data, 16020, + 18, 0, NULL), + isc::InvalidParameter); + EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 12, NULL, 16020, + 18, 0, NULL), + isc::InvalidParameter); + EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020, + 18, 0, fake_data), + isc::InvalidParameter); + EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020, + 18, 6, NULL), + isc::InvalidParameter); +} + +TEST_F(Rdata_TSIG_Test, assignment) { + any::TSIG copy(valid_text2); + copy = rdata_tsig; + EXPECT_EQ(0, copy.compare(rdata_tsig)); + + // Check if the copied data is valid even after the original is deleted + any::TSIG* copy2 = new any::TSIG(rdata_tsig); + any::TSIG copy3(valid_text2); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tsig)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_tsig)); +} + +template <typename Output> +void +Rdata_TSIG_Test::toWireCommonChecks(Output& output) const { + vector<uint8_t> expect_data; + + output.clear(); + expect_data.clear(); + rdata_tsig.toWire(output); + // read the expected wire format data and trim the RDLEN part. + UnitTestUtil::readWireData("rdata_tsig_toWire1.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + any::TSIG(valid_text2).toWire(output); + UnitTestUtil::readWireData("rdata_tsig_toWire2.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + any::TSIG(valid_text3).toWire(output); + UnitTestUtil::readWireData("rdata_tsig_toWire3.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); +} + +TEST_F(Rdata_TSIG_Test, toWireBuffer) { + toWireCommonChecks<OutputBuffer>(obuffer); +} + +TEST_F(Rdata_TSIG_Test, toWireRenderer) { + toWireCommonChecks<MessageRenderer>(renderer); + + // check algorithm name won't compressed when it would otherwise. + expect_data.clear(); + renderer.clear(); + renderer.writeName(Name("hmac-md5.sig-alg.reg.int")); + renderer.writeUint16(42); // RDLEN + rdata_tsig.toWire(renderer); + UnitTestUtil::readWireData("rdata_tsig_toWire4.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); + + // check algorithm can be used as a compression target. + expect_data.clear(); + renderer.clear(); + renderer.writeUint16(42); + rdata_tsig.toWire(renderer); + renderer.writeName(Name("hmac-md5.sig-alg.reg.int")); + UnitTestUtil::readWireData("rdata_tsig_toWire5.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_TSIG_Test, toText) { + EXPECT_EQ(valid_text1, rdata_tsig.toText()); + EXPECT_EQ(valid_text2, any::TSIG(valid_text2).toText()); + EXPECT_EQ(valid_text3, any::TSIG(valid_text3).toText()); + EXPECT_EQ(valid_text5, any::TSIG(valid_text5).toText()); +} + +TEST_F(Rdata_TSIG_Test, compare) { + // test RDATAs, sorted in the ascending order. + // "AAAA" encoded in BASE64 corresponds to 0x000000, so it should be the + // smallest data of the same length. + vector<any::TSIG> compare_set; + compare_set.push_back(any::TSIG("a.example 0 300 0 16020 0 0")); + compare_set.push_back(any::TSIG("example 0 300 0 16020 0 0")); + compare_set.push_back(any::TSIG("example 1 300 0 16020 0 0")); + compare_set.push_back(any::TSIG("example 1 600 0 16020 0 0")); + compare_set.push_back(any::TSIG("example 1 600 3 AAAA 16020 0 0")); + compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16020 0 0")); + compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 0 0")); + compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 0")); + compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 3 AAAA")); + compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 3 FAKE")); + + EXPECT_EQ(0, compare_set[0].compare( + any::TSIG("A.EXAMPLE 0 300 0 16020 0 0"))); + + vector<any::TSIG>::const_iterator it; + vector<any::TSIG>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_tsig.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc new file mode 100644 index 0000000..e2828c2 --- /dev/null +++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2011-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/. + +// This is the common code for TXT and SPF tests. + +#include <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/rdataclass.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> + +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <string> +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +template<class T> +class RRTYPE : public RRType { +public: + RRTYPE(); +}; + +template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {} +template<> RRTYPE<generic::SPF>::RRTYPE() : RRType(RRType::SPF()) {} + +const uint8_t wiredata_txt_like[] = { + sizeof("Test-String") - 1, + 'T', 'e', 's', 't', '-', 'S', 't', 'r', 'i', 'n', 'g' +}; + +const uint8_t wiredata_nulltxt[] = { 0 }; + +template<class TXT_LIKE> +class Rdata_TXT_LIKE_Test : public RdataTest { +protected: + Rdata_TXT_LIKE_Test() : + wiredata_longesttxt(256, 'a'), + rdata_txt_like("Test-String"), + rdata_txt_like_empty("\"\""), + rdata_txt_like_quoted("\"Test-String\"") + { + wiredata_longesttxt[0] = 255; // adjust length + } + +protected: + vector<uint8_t> wiredata_longesttxt; + const TXT_LIKE rdata_txt_like; + const TXT_LIKE rdata_txt_like_empty; + const TXT_LIKE rdata_txt_like_quoted; +}; + +// The list of types we want to test. +typedef testing::Types<generic::TXT, generic::SPF> Implementations; + +#ifdef TYPED_TEST_SUITE +TYPED_TEST_SUITE(Rdata_TXT_LIKE_Test, Implementations); +#else +TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations); +#endif + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) { + // Below we check the behavior for the "from text" constructors, both + // from std::string and with MasterLexer. The underlying implementation + // is the same, so both should work exactly same, but we confirm both + // cases. + + const std::string multi_line = "(\n \"Test-String\" )"; + const std::string escaped_txt = "Test\\045Strin\\g"; + + // test input for the lexer version + std::stringstream ss; + ss << "Test-String\n"; + ss << "\"Test-String\"\n"; // explicitly surrounded by '"'s + ss << multi_line << "\n"; // multi-line text with () + ss << escaped_txt << "\n"; // using the two types of escape with '\' + ss << "\"\"\n"; // empty string (note: still valid char-str) + ss << string(255, 'a') << "\n"; // Longest possible character-string. + ss << string(256, 'a') << "\n"; // char-string too long + ss << "\"Test-String\\\"\n"; // unbalanced quote + ss << "\"Test-String\\\"\"\n"; + this->lexer.pushSource(ss); + + // commonly used Rdata to compare below, created from wire + ConstRdataPtr const rdata = + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), "rdata_txt_fromWire1"); + + // normal case is covered in toWireBuffer. First check the std::string + // case, then with MasterLexer. For the latter, we need to read and skip + // '\n'. These apply to most of the other cases below. + EXPECT_EQ(0, this->rdata_txt_like.compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // surrounding double-quotes shouldn't change the result. + EXPECT_EQ(0, this->rdata_txt_like_quoted.compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // multi-line input with () + EXPECT_EQ(0, TypeParam(multi_line).compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // for the same data using escape + EXPECT_EQ(0, TypeParam(escaped_txt).compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Null character-string. + this->obuffer.clear(); + TypeParam(string("\"\"")).toWire(this->obuffer); + matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt), + this->obuffer.getData(), this->obuffer.getLength()); + + this->obuffer.clear(); + TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb). + toWire(this->obuffer); + matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt), + this->obuffer.getData(), this->obuffer.getLength()); + + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Longest possible character-string. + this->obuffer.clear(); + TypeParam(string(255, 'a')).toWire(this->obuffer); + matchWireData(&this->wiredata_longesttxt[0], + this->wiredata_longesttxt.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + this->obuffer.clear(); + TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb). + toWire(this->obuffer); + matchWireData(&this->wiredata_longesttxt[0], + this->wiredata_longesttxt.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Too long text for a valid character-string. + EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong); + EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb), CharStringTooLong); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // The escape character makes the double quote a part of character-string, + // so this is invalid input and should be rejected. + EXPECT_THROW(TypeParam("\"Test-String\\\""), InvalidRdataText); + EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb), MasterLexer::LexerError); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createMultiStringsFromText) { + // Tests for "from text" variants construction with various forms of + // multi character-strings. + + std::vector<std::string > texts; + texts.push_back("\"Test-String\" \"Test-String\""); // most common form + texts.push_back("\"Test-String\"\"Test-String\""); // no space between'em + texts.push_back("\"Test-String\" Test-String"); // no '"' for one + texts.push_back("\"Test-String\"Test-String"); // and no space either + texts.push_back("Test-String \"Test-String\""); // no '"' for the other + texts.push_back("Test-String\"Test-String\""); // and no space either + + std::stringstream ss; + for (std::vector<std::string >::const_iterator it = texts.begin(); + it != texts.end(); ++it) { + ss << *it << "\n"; + } + this->lexer.pushSource(ss); + + // The corresponding Rdata built from wire to compare in the checks below. + ConstRdataPtr const rdata = + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), "rdata_txt_fromWire3.wire"); + + // Confirm we can construct the Rdata from the test text, both from + // std::string and with lexer, and that matches the from-wire data. + for (std::vector<std::string >::const_iterator it = texts.begin(); + it != texts.end(); ++it) { + SCOPED_TRACE(*it); + EXPECT_EQ(0, TypeParam(*it).compare(*rdata)); + + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, + this->lexer.getNextToken().getType()); + } +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromTextExtra) { + // This is for the std::string version only: the input must end with EOF; + // an extra new-line will result in an exception. + EXPECT_THROW(TypeParam("\"Test-String\"\n"), InvalidRdataText); + // Same if there's a space before '\n' + EXPECT_THROW(TypeParam("\"Test-String\" \n"), InvalidRdataText); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, fromTextEmpty) { + // If the input text doesn't contain any character-string, it should be + // rejected + EXPECT_THROW(TypeParam(""), InvalidRdataText); + EXPECT_THROW(TypeParam(" "), InvalidRdataText); // even with a space + EXPECT_THROW(TypeParam("(\n)"), InvalidRdataText); // or multi-line with () +} + +void +makeLargest(vector<uint8_t>& data) { + uint8_t ch = 0; + + // create 255 sets of character-strings, each of which has the longest + // length (255bytes string + 1-byte length field) + for (int i = 0; i < 255; ++i, ++ch) { + data.push_back(255); + data.insert(data.end(), 255, ch); + } + // the last character-string should be 255 bytes (including the one-byte + // length field) in length so that the total length should be in the range + // of 16-bit integers. + data.push_back(254); + data.insert(data.end(), 254, ch); + + assert(data.size() == 65535); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) { + EXPECT_EQ(0, this->rdata_txt_like.compare( + *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), + "rdata_txt_fromWire1"))); + + // Empty character string + EXPECT_EQ(0, this->rdata_txt_like_empty.compare( + *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), + "rdata_txt_fromWire2.wire"))); + + // Multiple character strings + this->obuffer.clear(); + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire3.wire")->toWire(this->obuffer); + // the result should be 'wiredata_txt' repeated twice + vector<uint8_t> expected_data(wiredata_txt_like, wiredata_txt_like + + sizeof(wiredata_txt_like)); + expected_data.insert(expected_data.end(), wiredata_txt_like, + wiredata_txt_like + sizeof(wiredata_txt_like)); + matchWireData(&expected_data[0], expected_data.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + // Largest length of data. There's nothing special, but should be + // constructed safely, and the content should be identical to the original + // data. + vector<uint8_t> largest_txt_like_data; + makeLargest(largest_txt_like_data); + InputBuffer ibuffer(&largest_txt_like_data[0], + largest_txt_like_data.size()); + TypeParam largest_txt_like(ibuffer, largest_txt_like_data.size()); + this->obuffer.clear(); + largest_txt_like.toWire(this->obuffer); + matchWireData(&largest_txt_like_data[0], largest_txt_like_data.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + // rdlen parameter is out of range. This is a rare event because we'd + // normally call the constructor via a polymorphic wrapper, where the + // length is validated. But this should be checked explicitly. + InputBuffer ibuffer2(&largest_txt_like_data[0], + largest_txt_like_data.size()); + EXPECT_THROW(TypeParam(ibuffer2, 65536), InvalidRdataLength); + + // RDATA is empty, which is invalid for TXT_LIKE. + EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire4.wire"), + DNSMessageFORMERR); + + // character-string length is too large, which could cause overrun. + EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire5.wire"), + DNSMessageFORMERR); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromLexer) { + EXPECT_EQ(0, this->rdata_txt_like.compare( + *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "Test-String"))); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) { + this->rdata_txt_like.toWire(this->obuffer); + matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like), + this->obuffer.getData(), this->obuffer.getLength()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) { + this->rdata_txt_like.toWire(this->renderer); + matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like), + this->renderer.getData(), this->renderer.getLength()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toText) { + EXPECT_EQ("\"Test-String\"", this->rdata_txt_like.toText()); + EXPECT_EQ("\"\"", this->rdata_txt_like_empty.toText()); + EXPECT_EQ("\"Test-String\"", this->rdata_txt_like_quoted.toText()); + + // Check escape behavior + const TypeParam double_quotes("Test-String\"Test-String\""); + EXPECT_EQ("\"Test-String\" \"Test-String\"", double_quotes.toText()); + const TypeParam semicolon("Test-String\\;Test-String"); + EXPECT_EQ("\"Test-String\\;Test-String\"", semicolon.toText()); + const TypeParam backslash("Test-String\\\\Test-String"); + EXPECT_EQ("\"Test-String\\\\Test-String\"", backslash.toText()); + const TypeParam before_x20("Test-String\\031Test-String"); + EXPECT_EQ("\"Test-String\\031Test-String\"", before_x20.toText()); + const TypeParam from_x20_to_x7e("\"Test-String ~ Test-String\""); + EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e.toText()); + const TypeParam from_x20_to_x7e_2("Test-String\\032\\126\\032Test-String"); + EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e_2.toText()); + const TypeParam after_x7e("Test-String\\127Test-String"); + EXPECT_EQ("\"Test-String\\127Test-String\"", after_x7e.toText()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) { + TypeParam rdata1("assignment1"); + TypeParam rdata2("assignment2"); + rdata1 = rdata2; + EXPECT_EQ(0, rdata2.compare(rdata1)); + + // Check if the copied data is valid even after the original is deleted + TypeParam* rdata3 = new TypeParam(rdata1); + TypeParam rdata4("assignment3"); + rdata4 = *rdata3; + delete rdata3; + EXPECT_EQ(0, rdata4.compare(rdata1)); + + // Self assignment + rdata2 = *&rdata2; + EXPECT_EQ(0, rdata2.compare(rdata1)); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, compare) { + string const txt1("aaaaaaaa"); + string const txt2("aaaaaaaaaa"); + string const txt3("bbbbbbbb"); + string const txt4(129, 'a'); + string const txt5(128, 'b'); + + EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam("\"\"").compare(TypeParam(txt1)), 0); + EXPECT_GT(TypeParam(txt1).compare(TypeParam("\"\"")), 0); + + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0); + EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt3)), 0); + EXPECT_GT(TypeParam(txt3).compare(TypeParam(txt1)), 0); + + // we're comparing the data raw, starting at the length octet, so a shorter + // string sorts before a longer one no matter the lexicopraphical order + EXPECT_LT(TypeParam(txt3).compare(TypeParam(txt2)), 0); + EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt3)), 0); + + // to make sure the length octet compares unsigned + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt4)), 0); + EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam(txt5).compare(TypeParam(txt4)), 0); + EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt5)), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(TypeParam(txt1).compare(*this->rdata_nomatch), + bad_cast); +} + +} diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc new file mode 100644 index 0000000..e189d0f --- /dev/null +++ b/src/lib/dns/tests/rdata_unittest.cc @@ -0,0 +1,466 @@ +// 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 <config.h> + +#include <functional> +#include <vector> +#include <string> +#include <sstream> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> + +#include <util/unittests/wiredata.h> + +#include <boost/lexical_cast.hpp> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { +namespace rdata { +RdataTest::RdataTest() : + obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0")), + loader_cb(MasterLoaderCallbacks::getNullCallbacks()) +{} + +RdataPtr +RdataTest::rdataFactoryFromFile(const RRType& rrtype, const RRClass& rrclass, + const char* datafile, size_t position) +{ + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + uint16_t rdlen = buffer.readUint16(); + return (createRdata(rrtype, rrclass, buffer, rdlen)); +} + +namespace test { + +RdataPtr +createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass, + const std::string& str) +{ + std::stringstream ss(str); + MasterLexer lexer; + lexer.pushSource(ss); + + MasterLoaderCallbacks callbacks = + MasterLoaderCallbacks::getNullCallbacks(); + const Name origin("example.org."); + + return (createRdata(rrtype, rrclass, lexer, &origin, + MasterLoader::MANY_ERRORS, callbacks)); +} + +} // end of namespace isc::dns::rdata::test + +// A mock class to check parameters passed via loader callbacks. Its callback +// records the passed parameters, allowing the test to check them later via +// the check() method. +class CreateRdataCallback { +public: + enum CallbackType { NONE, ERROR, WARN }; + CreateRdataCallback() : type_(NONE), line_(0) {} + void callback(CallbackType type, const string& source, size_t line, + const string& reason_txt) { + type_ = type; + source_ = source; + line_ = line; + reason_txt_ = reason_txt; + } + + void clear() { + type_ = NONE; + source_.clear(); + line_ = 0; + reason_txt_.clear(); + } + + // Return if callback is called since the previous call to clear(). + bool isCalled() const { return (type_ != NONE); } + + void check(const string& expected_srcname, size_t expected_line, + CallbackType expected_type, const string& expected_reason) + const + { + EXPECT_EQ(expected_srcname, source_); + EXPECT_EQ(expected_line, line_); + EXPECT_EQ(expected_type, type_); + EXPECT_EQ(expected_reason, reason_txt_); + } + +private: + CallbackType type_; + string source_; + size_t line_; + string reason_txt_; +}; + +// Test class/type-independent behavior of createRdata(). +TEST_F(RdataTest, createRdataWithLexer) { + const in::AAAA aaaa_rdata("2001:db8::1"); + + stringstream ss; + const string src_name = "stream-" + boost::lexical_cast<string>(&ss); + ss << aaaa_rdata.toText() << "\n"; // valid case + ss << aaaa_rdata.toText() << "; comment, should be ignored\n"; + ss << aaaa_rdata.toText() << " extra-token\n"; // extra token + ss << aaaa_rdata.toText() << " extra token\n"; // 2 extra tokens + ss << ")\n"; // causing lexer error in parsing the RDATA text + ss << "192.0.2.1\n"; // semantics error: IPv4 address is given for AAAA + ss << aaaa_rdata.toText(); // valid, but end with EOF, not EOL + lexer.pushSource(ss); + + CreateRdataCallback callback; + MasterLoaderCallbacks callbacks( + std::bind(&CreateRdataCallback::callback, &callback, + CreateRdataCallback::ERROR, ph::_1, ph::_2, ph::_3), + std::bind(&CreateRdataCallback::callback, &callback, + CreateRdataCallback::WARN, ph::_1, ph::_2, ph::_3)); + + size_t line = 0; + + // Valid case. + ++line; + ConstRdataPtr rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, + NULL, MasterLoader::MANY_ERRORS, + callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + EXPECT_FALSE(callback.isCalled()); + + // Similar to the previous case, but RDATA is followed by a comment. + // It should cause any confusion. + ++line; + callback.clear(); + rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + EXPECT_FALSE(callback.isCalled()); + + // Broken RDATA text: extra token. createRdata() returns NULL, error + // callback is called. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed near 'extra-token': " + "extra input text"); + + // Similar to the previous case, but only the first extra token triggers + // callback. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed near 'extra': " + "extra input text"); + + // Lexer error will happen, corresponding error callback will be triggered. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed: unbalanced parentheses"); + + // Semantics level error will happen, corresponding error callback will be + // triggered. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed: Bad IN/AAAA RDATA text: " + "'192.0.2.1'"); + + // Input is valid and parse will succeed, but with a warning that the + // file is not ended with a newline. + ++line; + callback.clear(); + rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + callback.check(src_name, line, CreateRdataCallback::WARN, + "file does not end with newline"); +} + +TEST_F(RdataTest, getLength) { + const in::AAAA aaaa_rdata("2001:db8::1"); + EXPECT_EQ(16, aaaa_rdata.getLength()); + + const generic::TXT txt_rdata("Hello World"); + EXPECT_EQ(12, txt_rdata.getLength()); +} + +} +} +} + +namespace { + +// Wire-format data correspond to rdata_unknown. Note that it doesn't +// include RDLENGTH. +const uint8_t wiredata_unknown[] = { 0xa1, 0xb2, 0xc3, 0x0d }; + +class Rdata_Unknown_Test : public RdataTest { +public: + Rdata_Unknown_Test() : + // "Unknown" RR Type used for the test cases below. If/when we + // use this type number as a "well-known" (probably + // experimental) type, we'll need to renumber it. + unknown_rrtype(RRType(65000)), + rdata_unknowntxt("\\# 4 a1b2c30d"), + rdata_unknown(rdata_unknowntxt) + {} +protected: + static string getLongestRdataTxt(); + static void getLongestRdataWire(vector<uint8_t>& v); + + const RRType unknown_rrtype; + const std::string rdata_unknowntxt; + const generic::Generic rdata_unknown; +}; + +string +Rdata_Unknown_Test::getLongestRdataTxt() { + ostringstream oss; + + oss << "\\# " << MAX_RDLENGTH << " "; + oss.fill('0'); + oss << right << hex; + for (int i = 0; i < MAX_RDLENGTH; i++) { + oss << setw(2) << (i & 0xff); + } + + return (oss.str()); +} + +void +Rdata_Unknown_Test::getLongestRdataWire(vector<uint8_t>& v) { + unsigned char ch = 0; + for (int i = 0; i < MAX_RDLENGTH; ++i, ++ch) { + v.push_back(ch); + } +} + +TEST_F(Rdata_Unknown_Test, createFromText) { + // valid construction. This also tests a normal case of "FromWire". + EXPECT_EQ(0, generic::Generic("\\# 4 a1b2c30d").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire"))); + // upper case hexadecimal digits should also be okay. + EXPECT_EQ(0, generic::Generic("\\# 4 A1B2C30D").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire"))); + // 0-length RDATA should be accepted + EXPECT_EQ(0, generic::Generic("\\# 0").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire", 6))); + // hex encoding can be space-separated + EXPECT_EQ(0, generic::Generic("\\# 4 a1 b2c30d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1b2 c30d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1 b2 c3 0d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1\tb2c3 0d").compare(rdata_unknown)); + + // Max-length RDATA + vector<uint8_t> v; + getLongestRdataWire(v); + InputBuffer ibuffer(&v[0], v.size()); + EXPECT_EQ(0, generic::Generic(getLongestRdataTxt()).compare( + generic::Generic(ibuffer, v.size()))); + + // the length field must match the encoding data length. + EXPECT_THROW(generic::Generic("\\# 4 1080c0ff00"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 5 1080c0ff"), InvalidRdataLength); + // RDATA encoding part must consist of an even number of hex digits. + EXPECT_THROW(generic::Generic("\\# 1 1"), InvalidRdataText); + EXPECT_THROW(generic::Generic("\\# 1 ax"), InvalidRdataText); + // the length should be 16-bit unsigned integer + EXPECT_THROW(generic::Generic("\\# 65536 a1b2c30d"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# -1 a1b2c30d"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 0a 00010203040506070809"), + InvalidRdataLength); + // should reject if the special token is missing. + EXPECT_THROW(generic::Generic("4 a1b2c30d"), InvalidRdataText); + // the special token, the RDLENGTH and the data must be space separated. + EXPECT_THROW(generic::Generic("\\#0"), InvalidRdataText); + EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataLength); +} + +TEST_F(Rdata_Unknown_Test, createFromWire) { + // normal case (including 0-length data) is covered in createFromText. + + // buffer too short. the error should be detected in buffer read + EXPECT_THROW(rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire", 8), + InvalidBufferPosition); + + // too large data + vector<uint8_t> v; + getLongestRdataWire(v); + v.push_back(0); // making it too long + InputBuffer ibuffer(&v[0], v.size()); + EXPECT_THROW(generic::Generic(ibuffer, v.size()), InvalidRdataLength); +} + +// The following 3 sets of tests check the behavior of createRdata() variants +// with the "unknown" RRtype. The result should be RRclass independent. +TEST_F(Rdata_Unknown_Test, createRdataFromString) { + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), + rdata_unknowntxt))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), + rdata_unknowntxt))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass("CLASS65000"), + rdata_unknowntxt))); +} + +TEST_F(Rdata_Unknown_Test, createRdataFromWire) { + InputBuffer ibuffer(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), + ibuffer, sizeof(wiredata_unknown)))); + + InputBuffer ibuffer2(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), + ibuffer2, sizeof(wiredata_unknown)))); + + InputBuffer ibuffer3(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass(65000), + ibuffer3, sizeof(wiredata_unknown)))); +} + +TEST_F(Rdata_Unknown_Test, createRdataByCopy) { + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), rdata_unknown))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), rdata_unknown))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass(65000), + rdata_unknown))); +} + +TEST_F(Rdata_Unknown_Test, copyConstruct) { + generic::Generic copy(rdata_unknown); + EXPECT_EQ(0, copy.compare(rdata_unknown)); + + // Check the copied data is valid even after the original is deleted + generic::Generic* copy2 = new generic::Generic(rdata_unknown); + generic::Generic copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_unknown)); +} + +TEST_F(Rdata_Unknown_Test, assignment) { + generic::Generic copy("\\# 1 10"); + copy = rdata_unknown; + EXPECT_EQ(0, copy.compare(rdata_unknown)); + + // Check if the copied data is valid even after the original is deleted + generic::Generic* copy2 = new generic::Generic(rdata_unknown); + generic::Generic copy3("\\# 1 10"); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_unknown)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_unknown)); +} + +TEST_F(Rdata_Unknown_Test, toText) { + EXPECT_EQ(rdata_unknowntxt, rdata_unknown.toText()); + EXPECT_EQ(getLongestRdataTxt(), + generic::Generic(getLongestRdataTxt()).toText()); +} + +TEST_F(Rdata_Unknown_Test, toWireBuffer) { + rdata_unknown.toWire(obuffer); + matchWireData(wiredata_unknown, sizeof(wiredata_unknown), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_Unknown_Test, toWireRenderer) { + rdata_unknown.toWire(renderer); + matchWireData(wiredata_unknown, sizeof(wiredata_unknown), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_Unknown_Test, compare) { + // comparison as left-justified unsigned octet sequences: + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, rdata_unknown.compare(rdata_unknown)); + + generic::Generic rdata_unknown_small("\\# 4 00b2c3ff"); + EXPECT_GT(0, rdata_unknown_small.compare(rdata_unknown)); + EXPECT_LT(0, rdata_unknown.compare(rdata_unknown_small)); + + generic::Generic rdata_unknown_large("\\# 4 ffb2c300"); + EXPECT_LT(0, rdata_unknown_large.compare(rdata_unknown)); + EXPECT_GT(0, rdata_unknown.compare(rdata_unknown_large)); + + // the absence of an octet sorts before a zero octet. + generic::Generic rdata_unknown_short("\\# 3 a1b2c3"); + EXPECT_GT(0, rdata_unknown_short.compare(rdata_unknown)); + EXPECT_LT(0, rdata_unknown.compare(rdata_unknown_short)); +} + +TEST_F(Rdata_Unknown_Test, LeftShiftOperator) { + ostringstream oss; + oss << rdata_unknown; + EXPECT_EQ(rdata_unknown.toText(), oss.str()); +} + +// +// Tests for global utility functions +// +TEST_F(RdataTest, compareNames) { + Name small("a.example"); + Name large("example"); + + // Check the case where the order is different from the owner name + // comparison: + EXPECT_TRUE(small > large); + EXPECT_EQ(-1, compareNames(small, large)); + EXPECT_EQ(1, compareNames(large, small)); + + // Check case insensitive comparison: + Name small_upper("A.EXAMPLE"); + EXPECT_EQ(0, compareNames(small, small_upper)); + + // the absence of an octet sorts before a zero octet. + Name large2("a.example2"); + EXPECT_EQ(-1, compareNames(small, large2)); + EXPECT_EQ(1, compareNames(large2, small)); +} +} diff --git a/src/lib/dns/tests/rdata_unittest.h b/src/lib/dns/tests/rdata_unittest.h new file mode 100644 index 0000000..0c9178e --- /dev/null +++ b/src/lib/dns/tests/rdata_unittest.h @@ -0,0 +1,92 @@ +// 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_UNITTEST_H +#define RDATA_UNITTEST_H 1 + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <sstream> + +namespace isc { +namespace dns { +namespace rdata { +class RdataTest : public ::testing::Test { +protected: + RdataTest(); + static RdataPtr rdataFactoryFromFile(const RRType& rrtype, + const RRClass& rrclass, + const char* datafile, + size_t position = 0); + + // Common check to see the result of Rdata construction of given type + // (template parameter RdataType) either from std::string or with + // MasterLexer object. If it's expected to succeed the result should be + // identical to the commonly used test data (rdata_expected); otherwise it + // should result in the exception specified as the template parameter: + // ExForString for the string version, and ExForLexer for the lexer + // version. throw_str_version and throw_lexer_version are set to true + // iff the string/lexer version is expected to throw, respectively. + // Parameter origin can be set to non NULL for the origin parameter of + // the lexer version of Rdata constructor. + template <typename RdataType, typename ExForString, typename ExForLexer> + void checkFromText(const std::string& rdata_txt, + const RdataType& rdata_expected, + bool throw_str_version = true, + bool throw_lexer_version = true, + const Name* origin = NULL) + { + SCOPED_TRACE(rdata_txt); + + if (throw_str_version) { + EXPECT_THROW(RdataType rdata(rdata_txt), ExForString); + } else { + EXPECT_EQ(0, RdataType(rdata_txt).compare(rdata_expected)); + } + + std::stringstream ss(rdata_txt); + MasterLexer lexer; + lexer.pushSource(ss); + if (throw_lexer_version) { + EXPECT_THROW(RdataType rdata(lexer, origin, MasterLoader::DEFAULT, + loader_cb), ExForLexer); + } else { + EXPECT_EQ(0, RdataType(lexer, origin, MasterLoader::DEFAULT, + loader_cb).compare(rdata_expected)); + } + } + + isc::util::OutputBuffer obuffer; + MessageRenderer renderer; + /// This is an RDATA object of some "unknown" RR type so that it can be + /// used to test the compare() method against a well-known RR type. + RdataPtr rdata_nomatch; + MasterLexer lexer; + MasterLoaderCallbacks loader_cb; +}; + +namespace test { +RdataPtr +createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass, + const std::string& str); +} + +} +} +} +#endif // RDATA_UNITTEST_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc new file mode 100644 index 0000000..0531439 --- /dev/null +++ b/src/lib/dns/tests/rdatafields_unittest.cc @@ -0,0 +1,366 @@ +// 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 <config.h> + +#include <vector> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdatafields.h> +#include <dns/tests/unittest_util.h> + +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::OutputBuffer; +using isc::util::InputBuffer; +using isc::util::unittests::matchWireData; + +namespace { +class RdataFieldsTest : public ::testing::Test { +protected: + RdataFieldsTest() : obuffer(0), ns_name("example.com"), + other_name("www.example.com") + {} + void constructCommonTests(const RdataFields& fields, + const uint8_t* const expected_data, + const size_t expected_data_len); + void constructCommonTestsNS(const RdataFields& fields); + void constructCommonTestsTXT(const RdataFields& fields); + void constructCommonTestsRRSIG(const RdataFields& fields); + void constructCommonTestsOPT(const RdataFields& fields); + OutputBuffer obuffer; + MessageRenderer renderer; + const Name ns_name; + const Name other_name; + vector<unsigned char> expected_wire; + vector<unsigned char> fields_wire; +}; + +const uint8_t in_a_data[] = { 192, 0, 2, 1 }; +// binary representation of example.com. +const uint8_t ns_data[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x03, 0x63, 0x6f, 0x6d, 0x00 }; + +// +// IN/A RDATA: fixed length, single data field +// +void +RdataFieldsTest::constructCommonTests(const RdataFields& fields, + const uint8_t* const expected_data, + const size_t expected_data_len) +{ + matchWireData(expected_data, expected_data_len, + fields.getData(), fields.getDataLength()); + + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(4, fields.getFieldSpec(0).len); + + fields.toWire(obuffer); + matchWireData(expected_data, expected_data_len, + obuffer.getData(), obuffer.getLength()); + + fields.toWire(renderer); + matchWireData(expected_data, expected_data_len, + renderer.getData(), renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdata) { + const RdataFields fields(in::A("192.0.2.1")); + constructCommonTests(fields, in_a_data, sizeof(in_a_data)); +} + +TEST_F(RdataFieldsTest, constructFromParams) { + const RdataFields::FieldSpec spec(RdataFields::DATA, 4); + const RdataFields fields(&spec, sizeof(spec), in_a_data, + sizeof(in_a_data)); + constructCommonTests(fields, in_a_data, sizeof(in_a_data)); +} + +// +// NS RDATA: containing a compressible name. +// +void +RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) { + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::COMPRESSIBLE_NAME, fields.getFieldSpec(0).type); + EXPECT_EQ(ns_name.getLength(), fields.getFieldSpec(0).len); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields1.wire", expected_wire); + other_name.toWire(obuffer); + fields.toWire(obuffer); + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields2.wire", expected_wire); + other_name.toWire(renderer); + fields.toWire(renderer); + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataNS) { + const RdataFields fields_ns((generic::NS(ns_name))); + constructCommonTestsNS(fields_ns); +} + +TEST_F(RdataFieldsTest, constructFromParamsNS) { + const RdataFields::FieldSpec spec(RdataFields::COMPRESSIBLE_NAME, + sizeof(ns_data)); + const RdataFields fields_ns(&spec, sizeof(spec), ns_data, sizeof(ns_data)); + constructCommonTestsNS(fields_ns); +} + +// +// TXT RDATA: multiple fields, lengths vary +// +void +RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) { + // Since all fields are plain data, they are handled as a single data + // field. + EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize()); + EXPECT_EQ(1, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(expected_wire.size(), fields.getFieldSpec(0).len); + + fields.toWire(obuffer); + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); + + fields.toWire(renderer); + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataTXT) { + UnitTestUtil::readWireData("rdatafields3.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::TXT(ibuffer, rdlen)); + + // drop the RDLEN part + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + constructCommonTestsTXT(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsTXT) { + UnitTestUtil::readWireData("rdatafields3.wire", expected_wire); + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + const RdataFields::FieldSpec spec(RdataFields::DATA, expected_wire.size()); + const RdataFields fields(&spec, sizeof(spec), &expected_wire[0], + expected_wire.size()); + constructCommonTestsTXT(fields); +} + +// +// RRSIG: multiple fields, with an incompressible name +// +void +RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) { + // In terms of RdataFields RRSIG RDATA consists of 3 fields: + // - 18-byte data field (from the "type covered" field to "key tag" field) + // - an incompressible name field (for the signer's name field). + // this is a variable length field. In this test it's a 13-byte field. + // - a variable-length data field for the signature. In this tests + // it's a 15-byte field. + EXPECT_EQ(3 * sizeof(RdataFields::FieldSpec), + fields.getFieldSpecDataSize()); + EXPECT_EQ(3, fields.getFieldCount()); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type); + EXPECT_EQ(18, fields.getFieldSpec(0).len); + EXPECT_EQ(RdataFields::INCOMPRESSIBLE_NAME, fields.getFieldSpec(1).type); + EXPECT_EQ(13, fields.getFieldSpec(1).len); + EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(2).type); + EXPECT_EQ(15, fields.getFieldSpec(2).len); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields5.wire", expected_wire); + Name("com").toWire(obuffer); + obuffer.writeUint16(fields.getDataLength()); + fields.toWire(obuffer); + other_name.toWire(obuffer); + matchWireData(&expected_wire[0], expected_wire.size(), + obuffer.getData(), obuffer.getLength()); + + expected_wire.clear(); + UnitTestUtil::readWireData("rdatafields6.wire", expected_wire); + Name("com").toWire(renderer); + renderer.writeUint16(fields.getDataLength()); + fields.toWire(renderer); // the signer field won't be compressed + other_name.toWire(renderer); // but will be used as a compression target + matchWireData(&expected_wire[0], expected_wire.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataRRSIG) { + UnitTestUtil::readWireData("rdatafields4.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::RRSIG(ibuffer, rdlen)); + + // drop the RDLEN part + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + constructCommonTestsRRSIG(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsRRSIG) { + UnitTestUtil::readWireData("rdatafields4.wire", fields_wire); + fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2); + + const RdataFields::FieldSpec specs[] = { + RdataFields::FieldSpec(RdataFields::DATA, 18), + RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13), + RdataFields::FieldSpec(RdataFields::DATA, 15) + }; + const RdataFields fields(specs, sizeof(specs), &fields_wire[0], + fields_wire.size()); + constructCommonTestsRRSIG(fields); +} + +TEST_F(RdataFieldsTest, convertRdatatoParams) { + // Confirm we can restore the original data from the serialized data. + // We use RRSIG as a relatively complicated field structure. + UnitTestUtil::readWireData("rdatafields4.wire", expected_wire); + InputBuffer ibuffer(&expected_wire[0], expected_wire.size()); + const uint16_t rdlen = ibuffer.readUint16(); + const RdataFields fields(generic::RRSIG(ibuffer, rdlen)); + + expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2); + + // Copy the data in separate storage + vector<uint8_t> spec_store(fields.getFieldSpecDataSize()); + void* cp_spec = &spec_store[0]; + memcpy(cp_spec, fields.getFieldSpecData(), spec_store.size()); + vector<uint8_t> data_store(fields.getDataLength()); + memcpy(&data_store[0], fields.getData(), fields.getDataLength()); + + // Restore the data in the form of RdataFields + const RdataFields fields_byparams(cp_spec, fields.getFieldSpecDataSize(), + &data_store[0], fields.getDataLength()); + + // Check it's valid + constructCommonTestsRRSIG(fields_byparams); +} + +// +// OPT: an empty RDATA +// +void +RdataFieldsTest::constructCommonTestsOPT(const RdataFields& fields) { + EXPECT_EQ(0, fields.getFieldSpecDataSize()); + EXPECT_EQ(0, fields.getFieldCount()); + EXPECT_EQ(0, fields.getDataLength()); + EXPECT_EQ((const uint8_t*) NULL, fields.getData()); + fields.toWire(obuffer); + EXPECT_EQ(0, obuffer.getLength()); + fields.toWire(renderer); + EXPECT_EQ(0, renderer.getLength()); +} + +TEST_F(RdataFieldsTest, constructFromRdataOPT) { + InputBuffer ibuffer(NULL, 0); + const RdataFields fields(generic::OPT(ibuffer, 0)); + constructCommonTestsOPT(fields); +} + +TEST_F(RdataFieldsTest, constructFromParamsOPT) { + const RdataFields fields(NULL, 0, NULL, 0); + constructCommonTestsOPT(fields); +} + +// Invalid input to the "from parameter" constructor: sum of the field lengths +// is not equal to the data length. +TEST_F(RdataFieldsTest, invalidFieldLength) { + UnitTestUtil::readWireData("rdatafields4.wire", fields_wire); + fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2); + + const RdataFields::FieldSpec specs[] = { + RdataFields::FieldSpec(RdataFields::DATA, 18), + RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13), + RdataFields::FieldSpec(RdataFields::DATA, 14) + }; + // sum of field len < data len + EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], fields_wire.size()), + isc::InvalidParameter); + // sum of field len > data len + EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], + fields_wire.size() - 2), + isc::InvalidParameter); +} + +// Invalid input to the "from parameter" constructor: NULL vs length mismatch +TEST_F(RdataFieldsTest, mismatchFieldLengthAndData) { + const unsigned char dummy_data = 0; + const RdataFields::FieldSpec dummy_spec(RdataFields::DATA, 1); + + EXPECT_THROW(RdataFields(NULL, 1, &dummy_data, 1), isc::InvalidParameter); + EXPECT_THROW(RdataFields(&dummy_spec, 0, NULL, 0), isc::InvalidParameter); + EXPECT_THROW(RdataFields(&dummy_spec, 1, NULL, 1), isc::InvalidParameter); + EXPECT_THROW(RdataFields(NULL, 0, &dummy_data, 0), isc::InvalidParameter); +} + +// Bogus input to getFieldSpec() +TEST_F(RdataFieldsTest, getFieldSpecWithBadFieldId) { + const RdataFields fields_in_a(in::A("192.0.2.1")); + EXPECT_THROW(fields_in_a.getFieldSpec(1), isc::OutOfRange); +} + +// Tests for unexpected methods in RdataFieldComposerTest. Confirm +// a call to these methods triggers an exception. Expected methods are +// tested via other tests above. +class DummyRdata : public Rdata { +public: + enum Mode { CLEAR, SKIP, TRIM }; + explicit DummyRdata(Mode mode) : mode_(mode) {} + DummyRdata(const DummyRdata& source) : Rdata(), mode_(source.mode_) {} + virtual ~DummyRdata() {} + virtual void toWire(AbstractMessageRenderer& renderer) const { + // call the unexpected method corresponding to the test mode. + // method parameters don't matter. + switch (mode_) { + case CLEAR: + renderer.clear(); + break; + case SKIP: + renderer.skip(2); + break; + case TRIM: + renderer.trim(2); + break; + } + } + + // These are defined only to make the compiler happy. We don't use them + // for the test. + virtual string toText() const { return (""); } + virtual void toWire(OutputBuffer&) const {} + virtual int compare(const Rdata&) const { return (0); } +private: + const int mode_; +}; + +TEST(RdataFieldComposerTest, unusedMethods) { + EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::CLEAR)), isc::Unexpected); +} +} diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc new file mode 100644 index 0000000..ae85f01 --- /dev/null +++ b/src/lib/dns/tests/rrclass_unittest.cc @@ -0,0 +1,174 @@ +// Copyright (C) 2010-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/. + +#include <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrclass.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <boost/scoped_ptr.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using boost::scoped_ptr; +using isc::util::unittests::matchWireData; + +namespace { +class RRClassTest : public ::testing::Test { +protected: + RRClassTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRClass rrclassFactoryFromWire(const char* datafile); + static const RRClass rrclass_1, rrclass_0x80, rrclass_0x800, + rrclass_0x8000, rrclass_max; + static const uint8_t wiredata[]; +}; + +const RRClass RRClassTest::rrclass_1(1); +const RRClass RRClassTest::rrclass_0x80(0x80); +const RRClass RRClassTest::rrclass_0x800(0x800); +const RRClass RRClassTest::rrclass_0x8000(0x8000); +const RRClass RRClassTest::rrclass_max(0xffff); +// This is wire-format data for the above sample RRClass rendered in the +// appearing order. +const uint8_t RRClassTest::wiredata[] = { 0x00, 0x01, 0x00, 0x80, 0x08, + 0x00, 0x80, 0x00, 0xff, 0xff }; + +RRClass +RRClassTest::rrclassFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRClass(buffer)); +} + +TEST_F(RRClassTest, fromTextConstructor) { + EXPECT_EQ("IN", RRClass("IN").toText()); + EXPECT_EQ("CH", RRClass("CH").toText()); + + EXPECT_EQ("CLASS65535", RRClass("CLASS65535").toText()); + + // some uncommon cases: see the corresponding RRType tests. + EXPECT_EQ(53, RRClass("CLASS00053").getCode()); + EXPECT_THROW(RRClass("CLASS000053"), InvalidRRClass); + + // bogus CLASSnnn representations: should trigger an exception + EXPECT_THROW(RRClass("CLASS"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS-1"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASSxxx"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS65536"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS6500x"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS65000 "), InvalidRRClass); +} + +TEST_F(RRClassTest, fromWire) { + EXPECT_EQ(0x1234, + rrclassFactoryFromWire("rrcode16_fromWire1").getCode()); + EXPECT_THROW(rrclassFactoryFromWire("rrcode16_fromWire2"), + IncompleteRRClass); +} + +TEST_F(RRClassTest, caseConstruct) { + EXPECT_EQ("IN", RRClass("in").toText()); + EXPECT_EQ("CH", RRClass("ch").toText()); + EXPECT_EQ("CLASS65535", RRClass("class65535").toText()); +} + +TEST_F(RRClassTest, toText) { + EXPECT_EQ("IN", RRClass(1).toText()); + EXPECT_EQ("CLASS65000", RRClass(65000).toText()); +} + +TEST_F(RRClassTest, createFromText) { + scoped_ptr<RRClass> chclass(RRClass::createFromText("CH")); + EXPECT_TRUE(chclass); + EXPECT_EQ("CH", chclass->toText()); + + scoped_ptr<RRClass> zzclass(RRClass::createFromText("ZZ")); + EXPECT_FALSE(zzclass); +} + +TEST_F(RRClassTest, toWireBuffer) { + rrclass_1.toWire(obuffer); + rrclass_0x80.toWire(obuffer); + rrclass_0x800.toWire(obuffer); + rrclass_0x8000.toWire(obuffer); + rrclass_max.toWire(obuffer); + + matchWireData(wiredata, sizeof (wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRClassTest, toWireRenderer) { + rrclass_1.toWire(renderer); + rrclass_0x80.toWire(renderer); + rrclass_0x800.toWire(renderer); + rrclass_0x8000.toWire(renderer); + rrclass_max.toWire(renderer); + + matchWireData(wiredata, sizeof (wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRClassTest, wellKnownClass) { + EXPECT_EQ(1, RRClass::IN().getCode()); + EXPECT_EQ("IN", RRClass::IN().toText()); +} + +TEST_F(RRClassTest, compare) { + EXPECT_TRUE(RRClass(1) == RRClass("IN")); + EXPECT_TRUE(RRClass(1).equals(RRClass("IN"))); + EXPECT_TRUE(RRClass(0).nequals(RRClass("IN"))); + + EXPECT_TRUE(RRClass("IN") < RRClass("CH")); + EXPECT_TRUE(RRClass(100) < RRClass(65535)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRClassTest, LeftShiftOperator) { + ostringstream oss; + oss << RRClass::IN(); + EXPECT_EQ(RRClass::IN().toText(), oss.str()); +} + +// Below, we'll check definitions for all well-known RR classes; whether they +// are defined and have the correct parameter values. Test data are generated +// from the list available at: +// http://www.iana.org/assignments/dns-parameters/dns-parameters.xml +struct ClassParam { + const char* const txt; // "IN", "CH", etc + const uint16_t code; // 1, 3, + const RRClass& (*obj)(); // RRClass::IN(), etc +} known_classes[] = { + {"IN", 1, RRClass::IN}, {"CH", 3, RRClass::CH}, {"HS", 4, RRClass::HS}, + {"NONE", 254, RRClass::NONE}, {"ANY", 255, RRClass::ANY}, + {NULL, 0, NULL} +}; + +TEST(RRClassConstTest, wellKnowns) { + for (int i = 0; known_classes[i].txt; ++i) { + SCOPED_TRACE("Checking well known RRClass: " + + string(known_classes[i].txt)); + EXPECT_EQ(known_classes[i].code, + RRClass(known_classes[i].txt).getCode()); + EXPECT_EQ(known_classes[i].code, + (*known_classes[i].obj)().getCode()); + } +} +} diff --git a/src/lib/dns/tests/rrcollator_unittest.cc b/src/lib/dns/tests/rrcollator_unittest.cc new file mode 100644 index 0000000..4247911 --- /dev/null +++ b/src/lib/dns/tests/rrcollator_unittest.cc @@ -0,0 +1,208 @@ +// 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 <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/name.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> +#include <dns/rrclass.h> +#include <dns/rrcollator.h> +#include <dns/rdata.h> +#include <dns/rrset.h> +#include <dns/rrttl.h> + +#include <gtest/gtest.h> + +#include <functional> +#include <sstream> +#include <vector> + +using std::vector; +using namespace isc::dns; +using namespace isc::dns::rdata; +namespace ph = std::placeholders; + +namespace { + +typedef RRCollator::AddRRsetCallback AddRRsetCallback; + +void +addRRset(const RRsetPtr& rrset, vector<ConstRRsetPtr>* to_append, + const bool* do_throw) { + if (*do_throw) { + isc_throw(isc::Unexpected, "faked failure"); + } + to_append->push_back(rrset); +} + +class RRCollatorTest : public ::testing::Test { +protected: + RRCollatorTest() : + origin_("example.com"), rrclass_(RRClass::IN()), rrttl_(3600), + throw_from_callback_(false), + collator_(std::bind(addRRset, ph::_1, &rrsets_, &throw_from_callback_)), + rr_callback_(collator_.getCallback()), + a_rdata1_(createRdata(RRType::A(), rrclass_, "192.0.2.1")), + a_rdata2_(createRdata(RRType::A(), rrclass_, "192.0.2.2")), + txt_rdata_(createRdata(RRType::TXT(), rrclass_, "test")), + sig_rdata1_(createRdata(RRType::RRSIG(), rrclass_, + "A 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKE")), + sig_rdata2_(createRdata(RRType::RRSIG(), rrclass_, + "NS 5 3 3600 20000101000000 20000201000000 " + "12345 example.com. FAKE")) + {} + + void checkRRset(const Name& expected_name, const RRClass& expected_class, + const RRType& expected_type, const RRTTL& expected_ttl, + const vector<ConstRdataPtr>& expected_rdatas) { + SCOPED_TRACE(expected_name.toText(true) + "/" + + expected_class.toText() + "/" + expected_type.toText()); + + // This test always clears rrsets_ to confirm RRsets are added + // one-by-one + ASSERT_EQ(1, rrsets_.size()); + + ConstRRsetPtr actual = rrsets_[0]; + EXPECT_EQ(expected_name, actual->getName()); + EXPECT_EQ(expected_class, actual->getClass()); + EXPECT_EQ(expected_type, actual->getType()); + EXPECT_EQ(expected_ttl, actual->getTTL()); + ASSERT_EQ(expected_rdatas.size(), actual->getRdataCount()); + vector<ConstRdataPtr>::const_iterator it = expected_rdatas.begin(); + for (RdataIteratorPtr rit = actual->getRdataIterator(); + !rit->isLast(); + rit->next()) { + EXPECT_EQ(0, rit->getCurrent().compare(**it)); + ++it; + } + + rrsets_.clear(); + } + + const Name origin_; + const RRClass rrclass_; + const RRTTL rrttl_; + vector<ConstRRsetPtr> rrsets_; + bool throw_from_callback_; + RRCollator collator_; + AddRRCallback rr_callback_; + const RdataPtr a_rdata1_, a_rdata2_, txt_rdata_, sig_rdata1_, sig_rdata2_; + vector<ConstRdataPtr> rdatas_; // placeholder for expected data +}; + +TEST_F(RRCollatorTest, basicCases) { + // Add two RRs belonging to the same RRset. These will be buffered. + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_); + EXPECT_TRUE(rrsets_.empty()); // not yet given as an RRset + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata2_); + EXPECT_TRUE(rrsets_.empty()); // still not given + + // Add another type of RR. This completes the construction of the A RRset, + // which will be given via the callback. + rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_, txt_rdata_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); + + // Add the same type of RR but of different name. This should make another + // callback for the previous TXT RR. + rr_callback_(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_, + txt_rdata_); + rdatas_.clear(); + rdatas_.push_back(txt_rdata_); + checkRRset(origin_, rrclass_, RRType::TXT(), rrttl_, rdatas_); + + // Add the same type and name of RR but of different class (rare case + // in practice) + rr_callback_(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_, + txt_rdata_); + rdatas_.clear(); + rdatas_.push_back(txt_rdata_); + checkRRset(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_, + rdatas_); + + // Tell the collator we are done, then we'll see the last RR as an RRset. + collator_.flush(); + checkRRset(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_, + rdatas_); + + // Redundant flush() will be no-op. + collator_.flush(); + EXPECT_TRUE(rrsets_.empty()); +} + +TEST_F(RRCollatorTest, minTTLFirst) { + // RRs of the same RRset but has different TTLs. The first RR has + // the smaller TTL, which should be used for the TTL of the RRset. + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata1_); + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata2_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + collator_.flush(); + checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_); +} + +TEST_F(RRCollatorTest, maxTTLFirst) { + // RRs of the same RRset but has different TTLs. The second RR has + // the smaller TTL, which should be used for the TTL of the RRset. + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata1_); + rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata2_); + rdatas_.push_back(a_rdata1_); + rdatas_.push_back(a_rdata2_); + collator_.flush(); + checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_); +} + +TEST_F(RRCollatorTest, addRRSIGs) { + // RRSIG is special; they are also distinguished by their covered types. + rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata1_); + rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata2_); + + rdatas_.push_back(sig_rdata1_); + checkRRset(origin_, rrclass_, RRType::RRSIG(), rrttl_, rdatas_); +} + +TEST_F(RRCollatorTest, emptyFlush) { + collator_.flush(); + EXPECT_TRUE(rrsets_.empty()); +} + +TEST_F(RRCollatorTest, throwFromCallback) { + // Adding an A RR + rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_); + + // Adding a TXT RR, which would trigger RRset callback, but in this test + // it throws. The added TXT RR will be effectively lost. + throw_from_callback_ = true; + EXPECT_THROW(rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_, + txt_rdata_), isc::Unexpected); + + // We'll only see the A RR. + throw_from_callback_ = false; + collator_.flush(); + rdatas_.push_back(a_rdata1_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); +} + +TEST_F(RRCollatorTest, withMasterLoader) { + // Test a simple case with MasterLoader. There shouldn't be anything + // special, but that's the mainly intended usage of the collator, so we + // check it explicitly. + std::istringstream ss("example.com. 3600 IN A 192.0.2.1\n"); + MasterLoader loader(ss, origin_, rrclass_, + MasterLoaderCallbacks::getNullCallbacks(), + collator_.getCallback()); + loader.load(); + collator_.flush(); + rdatas_.push_back(a_rdata1_); + checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_); +} + +} diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc new file mode 100644 index 0000000..90574d0 --- /dev/null +++ b/src/lib/dns/tests/rrparamregistry_unittest.cc @@ -0,0 +1,185 @@ +// 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 <config.h> + +#include <string> +#include <sstream> + +#include <stdint.h> + +#include <gtest/gtest.h> + +#include <dns/rrclass.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrparamregistry.h> +#include <dns/rrtype.h> +#include <dns/master_loader.h> + +#include <boost/scoped_ptr.hpp> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class RRParamRegistryTest : public ::testing::Test { +protected: + RRParamRegistryTest() + { + ostringstream oss1; + oss1 << test_class_code; + // cppcheck-suppress useInitializationList + test_class_unknown_str = "CLASS" + oss1.str(); + + ostringstream oss2; + oss2 << test_type_code; + test_type_unknown_str = "TYPE" + oss2.str(); + } + ~RRParamRegistryTest() + { + // cleanup any non well-known parameters that possibly remain + // as a side effect. + RRParamRegistry::getRegistry().removeType(test_type_code); + RRParamRegistry::getRegistry().removeClass(test_class_code); + RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code), RRClass(test_class_code)); + RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code)); + } + + string test_class_unknown_str; + string test_type_unknown_str; + + // we assume class/type numbers are officially unassigned. If not we'll + // need to update the test cases. + static const uint16_t test_class_code = 65533; + static const uint16_t test_type_code = 65534; + static const string test_class_str; + static const string test_type_str; +}; + +const string RRParamRegistryTest::test_class_str("TESTCLASS"); +const string RRParamRegistryTest::test_type_str("TESTTYPE"); + +TEST_F(RRParamRegistryTest, addRemove) { + RRParamRegistry::getRegistry().addType(test_type_str, test_type_code); + RRParamRegistry::getRegistry().addClass(test_class_str, test_class_code); + EXPECT_EQ(65533, RRClass("TESTCLASS").getCode()); + EXPECT_EQ(65534, RRType("TESTTYPE").getCode()); + + // the first removal attempt should succeed + EXPECT_TRUE(RRParamRegistry::getRegistry().removeType(test_type_code)); + // then toText() should treat it as an "unknown" + EXPECT_EQ(test_type_unknown_str, RRType(test_type_code).toText()); + // attempt of removing non-existent mapping should result in 'false' + EXPECT_FALSE(RRParamRegistry::getRegistry().removeType(test_type_code)); + + // same set of tests for RR class. + EXPECT_TRUE(RRParamRegistry::getRegistry().removeClass(test_class_code)); + EXPECT_EQ(test_class_unknown_str, RRClass(test_class_code).toText()); + EXPECT_FALSE(RRParamRegistry::getRegistry().removeClass(test_class_code)); +} + +TEST_F(RRParamRegistryTest, addError) { + // An attempt to override a pre-registered class should fail with an + // exception, and the pre-registered one should remain in the registry. + EXPECT_THROW(RRParamRegistry::getRegistry().addClass(test_class_str, 1), + RRClassExists); + EXPECT_EQ("IN", RRClass(1).toText()); + + // Same for RRType + EXPECT_THROW(RRParamRegistry::getRegistry().addType(test_type_str, 1), + RRTypeExists); + EXPECT_EQ("A", RRType(1).toText()); +} + +class TestRdataFactory : public AbstractRdataFactory { +public: + virtual RdataPtr create(const string& rdata_str) const + { return (RdataPtr(new in::A(rdata_str))); } + virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const + { return (RdataPtr(new in::A(buffer, rdata_len))); } + virtual RdataPtr create(const Rdata& source) const + { return (RdataPtr(new in::A(dynamic_cast<const in::A&>(source)))); } + virtual RdataPtr create(MasterLexer& lexer, const Name* origin, + MasterLoader::Options options, + MasterLoaderCallbacks& callbacks) const + { return (RdataPtr(new in::A(lexer, origin, options, callbacks))); } +}; + +TEST_F(RRParamRegistryTest, addRemoveFactory) { + // By default, the test type/code pair should be considered "unknown", + // so the following should trigger an exception. + EXPECT_THROW(createRdata(RRType(test_type_code), RRClass(test_class_code), + "192.0.2.1"), + InvalidRdataText); + // Add factories so that we can treat this pair just like in::A. + RRParamRegistry::getRegistry().add(test_type_str, test_type_code, + test_class_str, test_class_code, + RdataFactoryPtr(new TestRdataFactory)); + // Now it should be accepted, and should be identical to the same data of + // in::A. + EXPECT_EQ(0, in::A("192.0.2.1").compare( + *createRdata(RRType(test_type_code), RRClass(test_class_code), + "192.0.2.1"))); + // It should still fail with other classes as we specified the factories + // as class-specific. + EXPECT_THROW(createRdata(RRType(test_type_code), RRClass("IN"), + "192.0.2.1"), + InvalidRdataText); + // Add the factories also as a class independent RRtype + RRParamRegistry::getRegistry().add(test_type_str, test_type_code, + RdataFactoryPtr(new TestRdataFactory)); + // Now it should be okay for other classes than the test class. + EXPECT_EQ(0, in::A("192.0.2.1").compare( + *createRdata(RRType(test_type_code), RRClass("IN"), + "192.0.2.1"))); + + // Remove the added factories: first attempt should succeed; the second + // should return false as there's no match + EXPECT_TRUE(RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code), RRClass(test_class_code))); + EXPECT_FALSE(RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code), RRClass(test_class_code))); + EXPECT_TRUE(RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code))); + EXPECT_FALSE(RRParamRegistry::getRegistry().removeRdataFactory( + RRType(test_type_code))); +} + +RdataPtr +createRdataHelper(const std::string& str) { + boost::scoped_ptr<AbstractRdataFactory> rdf(new TestRdataFactory); + + std::stringstream ss(str); + MasterLexer lexer; + lexer.pushSource(ss); + + MasterLoaderCallbacks callbacks(MasterLoaderCallbacks::getNullCallbacks()); + const Name origin("example.org."); + + return (rdf->create(lexer, &origin, + MasterLoader::MANY_ERRORS, + callbacks)); +} + +TEST_F(RRParamRegistryTest, createFromLexer) { + // This test basically checks that the string version of + // AbstractRdataFactory::create() is called by the MasterLexer + // variant of create(). + EXPECT_EQ(0, in::A("192.168.0.1").compare( + *createRdataHelper("192.168.0.1"))); + + // This should parse only up to the end of line. Everything that + // comes afterwards is not parsed. + EXPECT_EQ(0, in::A("192.168.0.42").compare( + *createRdataHelper("192.168.0.42\na b c d e f"))); +} + +} diff --git a/src/lib/dns/tests/rrset_collection_unittest.cc b/src/lib/dns/tests/rrset_collection_unittest.cc new file mode 100644 index 0000000..be16ab5 --- /dev/null +++ b/src/lib/dns/tests/rrset_collection_unittest.cc @@ -0,0 +1,240 @@ +// 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 <config.h> + +#include <dns/rrset_collection.h> +#include <dns/rrttl.h> +#include <dns/rdataclass.h> + +#include <gtest/gtest.h> + +#include <list> +#include <fstream> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace std; + +namespace { + +class RRsetCollectionTest : public ::testing::Test { +public: + RRsetCollectionTest() : + rrclass("IN"), + origin("example.org"), + collection(TEST_DATA_SRCDIR "/example.org", origin, rrclass) + {} + + const RRClass rrclass; + const Name origin; + RRsetCollection collection; +}; + +TEST_F(RRsetCollectionTest, istreamConstructor) { + std::ifstream fs(TEST_DATA_SRCDIR "/example.org"); + RRsetCollection collection2(fs, origin, rrclass); + + RRsetCollectionBase::Iterator iter = collection.begin(); + RRsetCollectionBase::Iterator iter2 = collection2.begin(); + while (iter != collection.end()) { + ASSERT_TRUE(iter2 != collection2.end()); + EXPECT_EQ((*iter).toText(), (*iter2).toText()); + ++iter; + ++iter2; + } + ASSERT_TRUE(iter2 == collection2.end()); +} + +template <typename T, typename TP> +void doFind(T& collection, const RRClass& rrclass) { + // Test the find() that returns ConstRRsetPtr + TP rrset = collection.find(Name("www.example.org"), rrclass, RRType::A()); + EXPECT_TRUE(rrset); + EXPECT_EQ(RRType::A(), rrset->getType()); + EXPECT_EQ(RRTTL(3600), rrset->getTTL()); + EXPECT_EQ(RRClass("IN"), rrset->getClass()); + EXPECT_EQ(Name("www.example.org"), rrset->getName()); + + // foo.example.org doesn't exist + rrset = collection.find(Name("foo.example.org"), rrclass, RRType::A()); + EXPECT_FALSE(rrset); + + // www.example.org exists, but not with MX + rrset = collection.find(Name("www.example.org"), rrclass, RRType::MX()); + EXPECT_FALSE(rrset); + + // www.example.org exists, with AAAA + rrset = collection.find(Name("www.example.org"), rrclass, RRType::AAAA()); + EXPECT_TRUE(rrset); + + // www.example.org with AAAA does not exist in RRClass::CH() + rrset = collection.find(Name("www.example.org"), RRClass::CH(), + RRType::AAAA()); + EXPECT_FALSE(rrset); +} + +TEST_F(RRsetCollectionTest, findConst) { + // Test the find() that returns ConstRRsetPtr + const RRsetCollection& ccln = collection; + doFind<const RRsetCollection, ConstRRsetPtr>(ccln, rrclass); +} + +TEST_F(RRsetCollectionTest, find) { + // Test the find() that returns RRsetPtr + doFind<RRsetCollection, RRsetPtr>(collection, rrclass); +} + +void +doAddAndRemove(RRsetCollection& collection, const RRClass& rrclass) { + // foo.example.org/A doesn't exist + RRsetPtr rrset_found = collection.find(Name("foo.example.org"), rrclass, + RRType::A()); + EXPECT_FALSE(rrset_found); + + // Add foo.example.org/A + RRsetPtr rrset(new BasicRRset(Name("foo.example.org"), rrclass, RRType::A(), + RRTTL(7200))); + rrset->addRdata(in::A("192.0.2.1")); + collection.addRRset(rrset); + + // foo.example.org/A should now exist + rrset_found = collection.find(Name("foo.example.org"), rrclass, + RRType::A()); + EXPECT_TRUE(rrset_found); + EXPECT_EQ(RRType::A(), rrset_found->getType()); + EXPECT_EQ(RRTTL(7200), rrset_found->getTTL()); + EXPECT_EQ(RRClass("IN"), rrset_found->getClass()); + EXPECT_EQ(Name("foo.example.org"), rrset_found->getName()); + + // The collection must not be empty. + EXPECT_TRUE(collection.end() != collection.begin()); + + // Adding a duplicate RRset must throw. + EXPECT_THROW({ + collection.addRRset(rrset); + }, isc::InvalidParameter); + + // Remove foo.example.org/A, which should pass + EXPECT_TRUE(collection.removeRRset(Name("foo.example.org"), + rrclass, RRType::A())); + // foo.example.org/A should not exist now + rrset_found = collection.find(Name("foo.example.org"), rrclass, + RRType::A()); + EXPECT_FALSE(rrset_found); + + // Removing foo.example.org/A should fail now + EXPECT_FALSE(collection.removeRRset(Name("foo.example.org"), + rrclass, RRType::A())); +} + +TEST_F(RRsetCollectionTest, addAndRemove) { + doAddAndRemove(collection, rrclass); +} + +TEST_F(RRsetCollectionTest, empty) { + RRsetCollection cln; + + // Here, cln is empty. + EXPECT_TRUE(cln.end() == cln.begin()); + + doAddAndRemove(cln, rrclass); + + // cln should be empty again here, after the add and remove + // operations. + EXPECT_TRUE(cln.end() == cln.begin()); +} + +TEST_F(RRsetCollectionTest, iteratorTest) { + // The collection must not be empty. + EXPECT_TRUE(collection.end() != collection.begin()); + + // Here, we just count the records and do some basic tests on them. + size_t count = 0; + for (RRsetCollection::Iterator it = collection.begin(); + it != collection.end(); ++it) { + ++count; + const AbstractRRset& rrset = *it; + EXPECT_EQ(rrclass, rrset.getClass()); + EXPECT_EQ(RRTTL(3600), rrset.getTTL()); + } + + // example.org master file has SOA, NS, A, AAAA + EXPECT_EQ(4, count); +} + +// This is a dummy class which is used in iteratorCompareDifferent test +// to compare iterators from different RRsetCollectionBase +// implementations. +class MyRRsetCollection : public RRsetCollectionBase { +public: + MyRRsetCollection() + {} + + virtual isc::dns::ConstRRsetPtr find(const isc::dns::Name&, + const isc::dns::RRClass&, + const isc::dns::RRType&) const { + return (ConstRRsetPtr()); + } + + typedef std::list<isc::dns::RRset> MyCollection; + +protected: + class MyIter : public RRsetCollectionBase::Iter { + public: + MyIter(MyCollection::iterator& iter) : + iter_(iter) + {} + + virtual const isc::dns::AbstractRRset& getValue() { + return (*iter_); + } + + virtual IterPtr getNext() { + MyCollection::iterator it = iter_; + ++it; + return (RRsetCollectionBase::IterPtr(new MyIter(it))); + } + + virtual bool equals(Iter& other) { + const MyIter* other_real = dynamic_cast<MyIter*>(&other); + if (other_real == NULL) { + return (false); + } + return (iter_ == other_real->iter_); + } + + private: + MyCollection::iterator iter_; + }; + + virtual RRsetCollectionBase::IterPtr getBeginning() { + MyCollection::iterator it = dummy_list_.begin(); + return (RRsetCollectionBase::IterPtr(new MyIter(it))); + } + + virtual RRsetCollectionBase::IterPtr getEnd() { + MyCollection::iterator it = dummy_list_.end(); + return (RRsetCollectionBase::IterPtr(new MyIter(it))); + } + +private: + MyCollection dummy_list_; +}; + +TEST_F(RRsetCollectionTest, iteratorCompareDifferent) { + // Create objects of two different RRsetCollectionBase + // implementations. + RRsetCollection cln1; + MyRRsetCollection cln2; + + // Comparing two iterators from different RRsetCollectionBase + // implementations must not throw. + EXPECT_TRUE(cln2.begin() != cln1.begin()); + EXPECT_TRUE(cln1.end() != cln2.end()); +} + +} // namespace diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc new file mode 100644 index 0000000..0425812 --- /dev/null +++ b/src/lib/dns/tests/rrset_unittest.cc @@ -0,0 +1,442 @@ +// 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 <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rrset.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <stdexcept> +#include <sstream> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class RRsetTest : public ::testing::Test { +protected: + RRsetTest() : buffer(0), + test_name("test.example.com"), + test_domain("example.com"), + test_nsname("ns.example.com"), + rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)), + rrset_a_empty(test_name, RRClass::IN(), RRType::A(), + RRTTL(3600)), + rrset_any_a_empty(test_name, RRClass::ANY(), RRType::A(), + RRTTL(3600)), + rrset_none_a_empty(test_name, RRClass::NONE(), RRType::A(), + RRTTL(3600)), + rrset_ns(test_domain, RRClass::IN(), RRType::NS(), + RRTTL(86400)), + rrset_ch_txt(test_domain, RRClass::CH(), RRType::TXT(), + RRTTL(0)) + { + rrset_a.addRdata(in::A("192.0.2.1")); + rrset_a.addRdata(in::A("192.0.2.2")); + } + + OutputBuffer buffer; + MessageRenderer renderer; + Name test_name; + Name test_domain; + Name test_nsname; + RRset rrset_a; + RRset rrset_a_empty; + RRset rrset_any_a_empty; + RRset rrset_none_a_empty; + RRset rrset_ns; + RRset rrset_ch_txt; + std::vector<unsigned char> wiredata; + + // max number of Rdata objects added to a test RRset object. + // this is an arbitrary chosen limit, but should be sufficiently large + // in practice and reasonable even as an extreme test case. + static const int MAX_RDATA_COUNT = 100; +}; + +TEST_F(RRsetTest, getRdataCount) { + for (int i = 0; i < MAX_RDATA_COUNT; ++i) { + EXPECT_EQ(i, rrset_a_empty.getRdataCount()); + rrset_a_empty.addRdata(in::A("192.0.2.1")); + } +} + +TEST_F(RRsetTest, getName) { + EXPECT_EQ(test_name, rrset_a.getName()); + EXPECT_EQ(test_domain, rrset_ns.getName()); +} + +TEST_F(RRsetTest, getClass) { + EXPECT_EQ(RRClass("IN"), rrset_a.getClass()); + EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass()); +} + +TEST_F(RRsetTest, getType) { + EXPECT_EQ(RRType("A"), rrset_a.getType()); + EXPECT_EQ(RRType("NS"), rrset_ns.getType()); + EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType()); +} + +TEST_F(RRsetTest, getTTL) { + EXPECT_EQ(RRTTL(3600), rrset_a.getTTL()); + EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL()); + EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL()); +} + +TEST_F(RRsetTest, setTTL) { + rrset_a.setTTL(RRTTL(86400)); + EXPECT_EQ(RRTTL(86400), rrset_a.getTTL()); + rrset_a.setTTL(RRTTL(0)); + EXPECT_EQ(RRTTL(0), rrset_a.getTTL()); +} + +TEST_F(RRsetTest, isSameKind) { + RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); + RRset rrset_x(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); + RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600)); + RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600)); + RRset rrset_p(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600)); + + EXPECT_TRUE(rrset_w.isSameKind(rrset_w)); + EXPECT_TRUE(rrset_w.isSameKind(rrset_x)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_y)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_z)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_p)); +} + +void +addRdataTestCommon(const RRset& rrset) { + ASSERT_EQ(2, rrset.getRdataCount()); + + RdataIteratorPtr it = rrset.getRdataIterator(); // cursor is set to the 1st + EXPECT_FALSE(it->isLast()); + EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1"))); + it->next(); + EXPECT_FALSE(it->isLast()); + EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2"))); + it->next(); + EXPECT_TRUE(it->isLast()); +} + +TEST_F(RRsetTest, addRdata) { + addRdataTestCommon(rrset_a); + + // Reference version of addRdata() doesn't allow to add a different + // type of Rdata. + EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), std::bad_cast); +} + +TEST_F(RRsetTest, addRdataPtr) { + rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), + rrset_a_empty.getClass(), + "192.0.2.1")); + rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), + rrset_a_empty.getClass(), + "192.0.2.2")); + addRdataTestCommon(rrset_a_empty); +} + +TEST_F(RRsetTest, addRdataPtrMismatched) { + // Pointer version of addRdata() doesn't type check and does allow to + //add a different type of Rdata as a result. + + // Type mismatch + rrset_a_empty.addRdata(createRdata(RRType::NS(), RRClass::IN(), + "ns.example.com.")); + EXPECT_EQ(1, rrset_a_empty.getRdataCount()); + + // Class mismatch + rrset_ch_txt.addRdata(createRdata(RRType::TXT(), RRClass::IN(), + "Test String")); + EXPECT_EQ(1, rrset_ch_txt.getRdataCount()); +} + +TEST_F(RRsetTest, addRdataString) { + rrset_a_empty.addRdata("192.0.2.1"); + rrset_a_empty.addRdata("192.0.2.2"); + + addRdataTestCommon(rrset_a_empty); + + // String version of addRdata() will throw for bad RDATA for + // RRType::A(). + EXPECT_THROW(rrset_a_empty.addRdata("ns.example.com."), InvalidRdataText); + addRdataTestCommon(rrset_a_empty); +} + +TEST_F(RRsetTest, iterator) { + // Iterator for an empty RRset. + RdataIteratorPtr it = rrset_a_empty.getRdataIterator(); + EXPECT_TRUE(it->isLast()); + + // Normal case (already tested, but do it again just in case) + rrset_a_empty.addRdata(in::A("192.0.2.1")); + rrset_a_empty.addRdata(in::A("192.0.2.2")); + addRdataTestCommon(rrset_a_empty); + + // Rewind test: should be repeat the iteration by calling first(). + for (int i = 0; i < 2; ++i) { + it = rrset_a_empty.getRdataIterator(); + it->first(); + EXPECT_FALSE(it->isLast()); + it->next(); + EXPECT_FALSE(it->isLast()); + it->next(); + EXPECT_TRUE(it->isLast()); + } +} + +TEST_F(RRsetTest, toText) { + EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n" + "test.example.com. 3600 IN A 192.0.2.2\n", + rrset_a.toText()); + + // toText() cannot be performed for an empty RRset + EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset); + + // Unless it is type ANY or NONE + EXPECT_EQ("test.example.com. 3600 ANY A\n", + rrset_any_a_empty.toText()); + EXPECT_EQ("test.example.com. 3600 NONE A\n", + rrset_none_a_empty.toText()); +} + +TEST_F(RRsetTest, getLength) { + // Empty RRset should throw + EXPECT_THROW(rrset_a_empty.getLength(), EmptyRRset); + + // Unless it is type ANY or NONE: + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // Total = 18 + 2 + 2 + 4 + 2 = 28 octets + EXPECT_EQ(28, rrset_any_a_empty.getLength()); + EXPECT_EQ(28, rrset_none_a_empty.getLength()); + + // RRset with single RDATA + // 28 (above) + 4 octets (A RDATA) = 32 octets + rrset_a_empty.addRdata(in::A("192.0.2.1")); + EXPECT_EQ(32, rrset_a_empty.getLength()); + + // 2 A RRs + rrset_a_empty.addRdata(in::A("192.0.2.2")); + EXPECT_EQ(32 + 32, rrset_a_empty.getLength()); +} + +TEST_F(RRsetTest, toWireBuffer) { + rrset_a.toWire(buffer); + + UnitTestUtil::readWireData("rrset_toWire1", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); + + // toWire() cannot be performed for an empty RRset except when + // class=ANY or class=NONE. + buffer.clear(); + EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset); + + // When class=ANY or class=NONE, toWire() can also be performed for + // an empty RRset. + buffer.clear(); + rrset_any_a_empty.toWire(buffer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire3", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); + + buffer.clear(); + rrset_none_a_empty.toWire(buffer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire4", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); +} + +TEST_F(RRsetTest, toWireRenderer) { + rrset_ns.addRdata(generic::NS(test_nsname)); + + rrset_a.toWire(renderer); + rrset_ns.toWire(renderer); + + UnitTestUtil::readWireData("rrset_toWire2", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // toWire() cannot be performed for an empty RRset except when + // class=ANY or class=NONE. + renderer.clear(); + EXPECT_THROW(rrset_a_empty.toWire(renderer), EmptyRRset); + + // When class=ANY or class=NONE, toWire() can also be performed for + // an empty RRset. + renderer.clear(); + rrset_any_a_empty.toWire(renderer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire3", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + renderer.clear(); + rrset_none_a_empty.toWire(renderer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire4", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRsetTest, LeftShiftOperator) { + ostringstream oss; + oss << rrset_a; + EXPECT_EQ(rrset_a.toText(), oss.str()); +} + +class RRsetRRSIGTest : public ::testing::Test { +protected: + RRsetRRSIGTest() : test_name("test.example.com") + { + rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::A(), RRTTL(3600))); + rrset_a->addRdata(in::A("192.0.2.1")); + rrset_a->addRdata(in::A("192.0.2.2")); + + rrset_aaaa = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::AAAA(), RRTTL(3600))); + rrset_aaaa->addRdata(in::AAAA("2001:db8::1234")); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + rrset_rrsig->addRdata(generic::RRSIG("AAAA 5 3 7200 20100322084538 " + "20100220084538 1 example.com. " + "FAKEFAKEFAKEFAKE")); + rrset_aaaa->addRRsig(rrset_rrsig); + } + + const Name test_name; + RRsetPtr rrset_a; // A RRset with two RDATAs + RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG + RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset +}; + +TEST_F(RRsetRRSIGTest, getRRsig) { + RRsetPtr sp = rrset_a->getRRsig(); + EXPECT_EQ(static_cast<void*>(NULL), sp.get()); + + sp = rrset_aaaa->getRRsig(); + EXPECT_NE(static_cast<void*>(NULL), sp.get()); +} + +TEST_F(RRsetRRSIGTest, addRRsig) { + RRsetPtr sp = rrset_a->getRRsig(); + EXPECT_EQ(static_cast<void*>(NULL), sp.get()); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + + sp = rrset_a->getRRsig(); + EXPECT_NE(static_cast<void*>(NULL), sp.get()); + EXPECT_EQ(2, sp->getRdataCount()); + + // add to existing RRSIG + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // another signature algorithm (4 = ECC) + rrset_rrsig->addRdata(generic::RRSIG("A 4 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(3, sp->getRdataCount()); +} + +TEST_F(RRsetRRSIGTest, getRRsigDataCount) { + EXPECT_EQ(1, rrset_aaaa->getRRsigDataCount()); + EXPECT_EQ(0, rrset_a->getRRsigDataCount()); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(2, rrset_a->getRRsigDataCount()); + + rrset_a->removeRRsig(); + EXPECT_EQ(0, rrset_a->getRRsigDataCount()); +} + +TEST_F(RRsetRRSIGTest, toText) { + // toText() should also return the associated RRSIG. + EXPECT_EQ("test.example.com. 3600 IN AAAA 2001:db8::1234\n" + "test.example.com. 3600 IN RRSIG AAAA 5 3 7200 " + "20100322084538 20100220084538 1 example.com. FAKEFAKEFAKEFAKE\n", + rrset_aaaa->toText()); +} + +TEST_F(RRsetRRSIGTest, getLength) { + // A RR + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // A RDATA = 4 octets + // Total = 18 + 2 + 2 + 4 + 2 + 4 = 32 octets + + // 2 A RRs + EXPECT_EQ(32 + 32, rrset_a->getLength()); + + // RRSIG + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // RRSIG RDATA = 40 octets + // Total = 18 + 2 + 2 + 4 + 2 + 40 = 68 octets + RRsetPtr my_rrsig(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + my_rrsig->addRdata(generic::RRSIG("A 4 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + EXPECT_EQ(68, my_rrsig->getLength()); + + // RRset with attached RRSIG + rrset_a->addRRsig(my_rrsig); + + EXPECT_EQ(32 + 32 + 68, rrset_a->getLength()); +} +} diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc new file mode 100644 index 0000000..3cada14 --- /dev/null +++ b/src/lib/dns/tests/rrttl_unittest.cc @@ -0,0 +1,279 @@ +// 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 <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrttl.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <boost/scoped_ptr.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using boost::scoped_ptr; +using isc::util::unittests::matchWireData; + +namespace { +class RRTTLTest : public ::testing::Test { +protected: + RRTTLTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRTTL rrttlFactoryFromWire(const char* datafile); + static const RRTTL ttl_0, ttl_1h, ttl_1d, ttl_32bit, ttl_max; + static const RRTTL ttl_small, ttl_large; + static const uint8_t wiredata[20]; +}; + +const RRTTL RRTTLTest::ttl_0(0); +const RRTTL RRTTLTest::ttl_1h(3600); +const RRTTL RRTTLTest::ttl_1d(86400); +const RRTTL RRTTLTest::ttl_32bit(0x12345678); +const RRTTL RRTTLTest::ttl_max(0xffffffff); + +const RRTTL RRTTLTest::ttl_small(1); +const RRTTL RRTTLTest::ttl_large(0x80000001); +// This is wire-format data for the above sample RRTTLs rendered in the +// appearing order. +const uint8_t RRTTLTest::wiredata[20] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x01, 0x51, 0x80, + 0x12, 0x34, 0x56, 0x78, + 0xff, 0xff, 0xff, 0xff }; + +RRTTL +RRTTLTest::rrttlFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRTTL(buffer)); +} + +TEST_F(RRTTLTest, getValue) { + EXPECT_EQ(0, ttl_0.getValue()); + EXPECT_EQ(3600, ttl_1h.getValue()); + EXPECT_EQ(86400, ttl_1d.getValue()); + EXPECT_EQ(0x12345678, ttl_32bit.getValue()); + EXPECT_EQ(0xffffffff, ttl_max.getValue()); +} + +TEST_F(RRTTLTest, copyConstruct) { + const RRTTL ttl1(3600); + const RRTTL ttl2(ttl1); + EXPECT_EQ(ttl1.getValue(), ttl2.getValue()); +} + +TEST_F(RRTTLTest, fromText) { + // Border cases + EXPECT_EQ(0, RRTTL("0").getValue()); + EXPECT_EQ(4294967295U, RRTTL("4294967295").getValue()); + + // Invalid cases + EXPECT_THROW(RRTTL("0xdeadbeef"), InvalidRRTTL); // must be decimal + EXPECT_THROW(RRTTL("-1"), InvalidRRTTL); // must be positive + EXPECT_THROW(RRTTL("1.1"), InvalidRRTTL); // must be integer + EXPECT_THROW(RRTTL("4294967296"), InvalidRRTTL); // must be 32-bit +} + +TEST_F(RRTTLTest, createFromText) { + // It returns an actual RRTTL iff the given text is recognized as a + // valid RR TTL. + scoped_ptr<RRTTL> good_ttl(RRTTL::createFromText("3600")); + EXPECT_TRUE(good_ttl); + EXPECT_EQ(RRTTL(3600), *good_ttl); + + scoped_ptr<RRTTL> bad_ttl(RRTTL::createFromText("bad")); + EXPECT_FALSE(bad_ttl); +} + +void +checkUnit(unsigned multiply, char suffix) { + SCOPED_TRACE(string("Unit check with suffix ") + suffix); + const uint32_t value = 10 * multiply; + const string num = "10"; + // Check both lower and upper version of the suffix + EXPECT_EQ(value, + RRTTL(num + static_cast<char>(tolower(suffix))).getValue()); + EXPECT_EQ(value, + RRTTL(num + static_cast<char>(toupper(suffix))).getValue()); +} + +// Check parsing the unit form (1D, etc) +TEST_F(RRTTLTest, fromTextUnit) { + // Check each of the units separately + checkUnit(1, 'S'); + checkUnit(60, 'M'); + checkUnit(60 * 60, 'H'); + checkUnit(24 * 60 * 60, 'D'); + checkUnit(7 * 24 * 60 * 60, 'W'); + + // Some border cases (with units) + EXPECT_EQ(4294967295U, RRTTL("4294967295S").getValue()); + EXPECT_EQ(0, RRTTL("0W0D0H0M0S").getValue()); + EXPECT_EQ(4294967295U, RRTTL("1193046H1695S").getValue()); + // Leading zeroes are accepted + EXPECT_EQ(4294967295U, RRTTL("0000000000000004294967295S").getValue()); + + // Now some compound ones. We allow any order (it would be much work to + // check the order anyway). + EXPECT_EQ(60 * 60 + 3, RRTTL("1H3S").getValue()); + + // Awkward, but allowed case - the same unit used twice. + EXPECT_EQ(20 * 3600, RRTTL("12H8H").getValue()); + + // Negative number in part of the expression, but the total is positive. + // Rejected. + EXPECT_THROW(RRTTL("-1S1H"), InvalidRRTTL); + + // Some things out of range in the ttl, but it wraps to number in range + // in int64_t. Should still not get fooled and reject it. + + // First part out of range + EXPECT_THROW(RRTTL("9223372036854775807S9223372036854775807S2S"), + InvalidRRTTL); + // Second part out of range, but it immediately wraps (2S+2^64-2S) + EXPECT_THROW(RRTTL("2S18446744073709551614S"), InvalidRRTTL); + // The whole thing wraps right away (2^64S) + EXPECT_THROW(RRTTL("18446744073709551616S"), InvalidRRTTL); + // Second part out of range, and will become negative with the unit, + EXPECT_THROW(RRTTL("256S307445734561825856M"), InvalidRRTTL); + + // Missing before unit. + EXPECT_THROW(RRTTL("W5H"), InvalidRRTTL); + EXPECT_THROW(RRTTL("5hW"), InvalidRRTTL); + + // Empty string is not allowed + EXPECT_THROW(RRTTL(""), InvalidRRTTL); + // Missing the last unit is not allowed + EXPECT_THROW(RRTTL("3D5"), InvalidRRTTL); + + // There are some wrong units + EXPECT_THROW(RRTTL("13X"), InvalidRRTTL); + EXPECT_THROW(RRTTL("3D5F"), InvalidRRTTL); +} + +TEST_F(RRTTLTest, fromWire) { + EXPECT_EQ(0x12345678, + rrttlFactoryFromWire("rrcode32_fromWire1").getValue()); + EXPECT_THROW(rrttlFactoryFromWire("rrcode32_fromWire2"), + IncompleteRRTTL); +} + +TEST_F(RRTTLTest, toText) { + EXPECT_EQ("0", ttl_0.toText()); + EXPECT_EQ("3600", ttl_1h.toText()); + EXPECT_EQ("86400", ttl_1d.toText()); + EXPECT_EQ("305419896", ttl_32bit.toText()); + EXPECT_EQ("4294967295", ttl_max.toText()); +} + +TEST_F(RRTTLTest, toWireBuffer) { + ttl_0.toWire(obuffer); + ttl_1h.toWire(obuffer); + ttl_1d.toWire(obuffer); + ttl_32bit.toWire(obuffer); + ttl_max.toWire(obuffer); + + matchWireData(wiredata, sizeof(wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRTTLTest, toWireRenderer) { + ttl_0.toWire(renderer); + ttl_1h.toWire(renderer); + ttl_1d.toWire(renderer); + ttl_32bit.toWire(renderer); + ttl_max.toWire(renderer); + + matchWireData(wiredata, sizeof(wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRTTLTest, equal) { + EXPECT_TRUE(RRTTL("3600") == ttl_1h); + EXPECT_TRUE(RRTTL("86400").equals(ttl_1d)); + + EXPECT_TRUE(ttl_1d != ttl_1h); + EXPECT_TRUE(ttl_1d.nequals(ttl_max)); +} + +// +// The following set of tests confirm the result of <=, <, >=, > +// The test logic is simple, and all tests are just straightforward variations +// of the first one. +// +TEST_F(RRTTLTest, leq) { + // small <= large is true + EXPECT_TRUE(ttl_small.leq(ttl_large)); + EXPECT_TRUE(ttl_small <= ttl_large); + + // small <= small is true + EXPECT_TRUE(ttl_small.leq(ttl_small)); + EXPECT_LE(ttl_small, ttl_small); + + // large <= small is false + EXPECT_FALSE(ttl_large.leq(ttl_small)); + EXPECT_FALSE(ttl_large <= ttl_small); +} + +TEST_F(RRTTLTest, geq) { + EXPECT_TRUE(ttl_large.geq(ttl_small)); + EXPECT_TRUE(ttl_large >= ttl_small); + + EXPECT_TRUE(ttl_large.geq(ttl_large)); + EXPECT_GE(ttl_large, ttl_large); + + EXPECT_FALSE(ttl_small.geq(ttl_large)); + EXPECT_FALSE(ttl_small >= ttl_large); +} + +TEST_F(RRTTLTest, lthan) { + EXPECT_TRUE(ttl_small.lthan(ttl_large)); + EXPECT_TRUE(ttl_small < ttl_large); + + EXPECT_FALSE(ttl_small.lthan(ttl_small)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(ttl_small < ttl_small); + + EXPECT_FALSE(ttl_large.lthan(ttl_small)); + EXPECT_FALSE(ttl_large < ttl_small); +} + +TEST_F(RRTTLTest, gthan) { + EXPECT_TRUE(ttl_large.gthan(ttl_small)); + EXPECT_TRUE(ttl_large > ttl_small); + + EXPECT_FALSE(ttl_large.gthan(ttl_large)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(ttl_large > ttl_large); + + EXPECT_FALSE(ttl_small.gthan(ttl_large)); + EXPECT_FALSE(ttl_small > ttl_large); +} + +TEST_F(RRTTLTest, maxTTL) { + EXPECT_EQ((1u << 31) - 1, RRTTL::MAX_TTL().getValue()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRTTLTest, LeftShiftOperator) { + ostringstream oss; + oss << ttl_1h; + EXPECT_EQ(ttl_1h.toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc new file mode 100644 index 0000000..a2f8ba5 --- /dev/null +++ b/src/lib/dns/tests/rrtype_unittest.cc @@ -0,0 +1,195 @@ +// 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 <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrtype.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using isc::util::unittests::matchWireData; + +namespace { +class RRTypeTest : public ::testing::Test { +protected: + RRTypeTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRType rrtypeFactoryFromWire(const char* datafile); + static const RRType rrtype_1, rrtype_0x80, rrtype_0x800, rrtype_0x8000, + rrtype_max; + static const uint8_t wiredata[]; +}; + +const RRType RRTypeTest::rrtype_1(1); +const RRType RRTypeTest::rrtype_0x80(0x80); +const RRType RRTypeTest::rrtype_0x800(0x800); +const RRType RRTypeTest::rrtype_0x8000(0x8000); +const RRType RRTypeTest::rrtype_max(0xffff); +// This is wire-format data for the above sample RRTypes rendered in the +// appearing order. +const uint8_t RRTypeTest::wiredata[] = { 0x00, 0x01, 0x00, 0x80, 0x08, + 0x00, 0x80, 0x00, 0xff, 0xff }; + +RRType +RRTypeTest::rrtypeFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRType(buffer)); +} + +TEST_F(RRTypeTest, fromText) { + EXPECT_EQ("A", RRType("A").toText()); + EXPECT_EQ("NS", RRType("NS").toText()); + + EXPECT_EQ("TYPE65535", RRType("TYPE65535").toText()); + + // something unusual, but existing implementations accept this form, + // so do we. + EXPECT_EQ(53, RRType("TYPE00053").getCode()); + // again, unusual, and the majority of other implementations reject it. + // In any case, there should be no reasonable reason to accept such a + // ridiculously long input. + EXPECT_THROW(RRType("TYPE000053"), InvalidRRType); + + // bogus TYPEnnn representations: should trigger an exception + EXPECT_THROW(RRType("TYPE"), InvalidRRType); + EXPECT_THROW(RRType("TYPE-1"), InvalidRRType); + EXPECT_THROW(RRType("TYPExxx"), InvalidRRType); + EXPECT_THROW(RRType("TYPE65536"), InvalidRRType); + EXPECT_THROW(RRType("TYPE6500x"), InvalidRRType); + EXPECT_THROW(RRType("TYPE65000 "), InvalidRRType); +} + +TEST_F(RRTypeTest, fromWire) { + EXPECT_EQ(0x1234, + rrtypeFactoryFromWire("rrcode16_fromWire1").getCode()); + EXPECT_THROW(rrtypeFactoryFromWire("rrcode16_fromWire2"), IncompleteRRType); +} + +// from string, lower case +TEST_F(RRTypeTest, caseConstruct) { + EXPECT_EQ("A", RRType("a").toText()); + EXPECT_EQ("NS", RRType("ns").toText()); + EXPECT_EQ("TYPE65535", RRType("type65535").toText()); +} + +TEST_F(RRTypeTest, toText) { + EXPECT_EQ("A", RRType(1).toText()); + EXPECT_EQ("TYPE65000", RRType(65000).toText()); +} + +TEST_F(RRTypeTest, toWireBuffer) { + rrtype_1.toWire(obuffer); + rrtype_0x80.toWire(obuffer); + rrtype_0x800.toWire(obuffer); + rrtype_0x8000.toWire(obuffer); + rrtype_max.toWire(obuffer); + + matchWireData(wiredata, sizeof(wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRTypeTest, toWireRenderer) { + rrtype_1.toWire(renderer); + rrtype_0x80.toWire(renderer); + rrtype_0x800.toWire(renderer); + rrtype_0x8000.toWire(renderer); + rrtype_max.toWire(renderer); + + matchWireData(wiredata, sizeof(wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRTypeTest, wellKnownTypes) { + EXPECT_EQ(1, RRType::A().getCode()); + EXPECT_EQ("A", RRType::A().toText()); +} + +TEST_F(RRTypeTest, compare) { + EXPECT_TRUE(RRType(1) == RRType("A")); + EXPECT_TRUE(RRType(1).equals(RRType("A"))); + EXPECT_TRUE(RRType(0) != RRType("A")); + EXPECT_TRUE(RRType(0).nequals(RRType("A"))); + + EXPECT_TRUE(RRType("A") < RRType("NS")); + EXPECT_TRUE(RRType(100) < RRType(65535)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRTypeTest, LeftShiftOperator) { + ostringstream oss; + oss << RRType::A(); + EXPECT_EQ(RRType::A().toText(), oss.str()); +} + +// Below, we'll check definitions for all well-known RR types; whether they +// are defined and have the correct parameter values. Test data are generated +// from the list available at: +// http://www.iana.org/assignments/dns-parameters/dns-parameters.xml +struct TypeParam { + const char* const txt; // "A", "AAAA", "NS", etc + const uint16_t code; // 1, 28, 2, etc + const RRType& (*obj)(); // RRType::A(), etc +} known_types[] = { + {"A", 1, RRType::A}, {"NS", 2, RRType::NS}, {"MD", 3, RRType::MD}, + {"MF", 4, RRType::MF}, {"CNAME", 5, RRType::CNAME}, + {"SOA", 6, RRType::SOA}, {"MB", 7, RRType::MB}, {"MG", 8, RRType::MG}, + {"MR", 9, RRType::MR}, {"NULL", 10, RRType::Null}, + {"WKS", 11, RRType::WKS}, {"PTR", 12, RRType::PTR}, + {"HINFO", 13, RRType::HINFO}, {"MINFO", 14, RRType::MINFO}, + {"MX", 15, RRType::MX}, {"TXT", 16, RRType::TXT}, {"RP", 17, RRType::RP}, + {"AFSDB", 18, RRType::AFSDB}, {"X25", 19, RRType::X25}, + {"ISDN", 20, RRType::ISDN}, {"RT", 21, RRType::RT}, + {"NSAP", 22, RRType::NSAP}, {"NSAP-PTR", 23, RRType::NSAP_PTR}, + {"SIG", 24, RRType::SIG}, {"KEY", 25, RRType::KEY}, + {"PX", 26, RRType::PX}, {"GPOS", 27, RRType::GPOS}, + {"AAAA", 28, RRType::AAAA}, {"LOC", 29, RRType::LOC}, + {"NXT", 30, RRType::NXT}, {"SRV", 33, RRType::SRV}, + {"NAPTR", 35, RRType::NAPTR}, {"KX", 36, RRType::KX}, + {"CERT", 37, RRType::CERT}, {"A6", 38, RRType::A6}, + {"DNAME", 39, RRType::DNAME}, {"OPT", 41, RRType::OPT}, + {"APL", 42, RRType::APL}, {"DS", 43, RRType::DS}, + {"SSHFP", 44, RRType::SSHFP}, {"IPSECKEY", 45, RRType::IPSECKEY}, + {"RRSIG", 46, RRType::RRSIG}, {"NSEC", 47, RRType::NSEC}, + {"DNSKEY", 48, RRType::DNSKEY}, {"DHCID", 49, RRType::DHCID}, + {"NSEC3", 50, RRType::NSEC3}, {"NSEC3PARAM", 51, RRType::NSEC3PARAM}, + {"TLSA", 52, RRType::TLSA}, {"HIP", 55, RRType::HIP}, + {"SPF", 99, RRType::SPF}, {"UNSPEC", 103, RRType::UNSPEC}, + {"NID", 104, RRType::NID}, {"L32", 105, RRType::L32}, + {"L64", 106, RRType::L64}, {"LP", 107, RRType::LP}, + {"TKEY", 249, RRType::TKEY}, {"TSIG", 250, RRType::TSIG}, + {"IXFR", 251, RRType::IXFR}, {"AXFR", 252, RRType::AXFR}, + {"MAILB", 253, RRType::MAILB}, {"MAILA", 254, RRType::MAILA}, + {"ANY", 255, RRType::ANY}, {"URI", 256, RRType::URI}, + {"CAA", 257, RRType::CAA}, {"DLV", 32769, RRType::DLV}, + {NULL, 0, NULL} +}; + +TEST(RRTypeConstTest, wellKnowns) { + for (int i = 0; known_types[i].txt; ++i) { + SCOPED_TRACE("Checking well known RRType: " + + string(known_types[i].txt)); + EXPECT_EQ(known_types[i].code, RRType(known_types[i].txt).getCode()); + EXPECT_EQ(known_types[i].code, + (*known_types[i].obj)().getCode()); + } +} +} diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc new file mode 100644 index 0000000..de396fa --- /dev/null +++ b/src/lib/dns/tests/run_unittests.cc @@ -0,0 +1,24 @@ +// 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 <config.h> + +#include <gtest/gtest.h> +#include <util/unittests/run_all.h> + +#include <util/unittests/testdata.h> +#include <dns/tests/unittest_util.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR); + isc::util::unittests::addTestDataPath(TEST_DATA_SRCDIR); + isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR); + isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR); + + return (isc::util::unittests::run_all()); +} diff --git a/src/lib/dns/tests/serial_unittest.cc b/src/lib/dns/tests/serial_unittest.cc new file mode 100644 index 0000000..305b0b0 --- /dev/null +++ b/src/lib/dns/tests/serial_unittest.cc @@ -0,0 +1,173 @@ +// Copyright (C) 2011-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 <config.h> + +#include <gtest/gtest.h> + +#include <dns/serial.h> + +using namespace isc::dns; + +class SerialTest : public ::testing::Test { +public: + SerialTest() : one(1), one_2(1), two(2), + date_zero(1980120100), date_one(1980120101), + min(0), max(4294967295u), + number_low(12345), + number_medium(2000000000), + number_high(4000000000u) + {} + Serial one, one_2, two, date_zero, date_one, min, max, number_low, number_medium, number_high; +}; + +// +// Basic tests +// + +TEST_F(SerialTest, get_value) { + EXPECT_EQ(1, one.getValue()); + EXPECT_NE(2, one.getValue()); + EXPECT_EQ(2, two.getValue()); + EXPECT_EQ(1980120100, date_zero.getValue()); + EXPECT_EQ(1980120101, date_one.getValue()); + EXPECT_EQ(0, min.getValue()); + EXPECT_EQ(4294967295u, max.getValue()); + EXPECT_EQ(12345, number_low.getValue()); + EXPECT_EQ(2000000000, number_medium.getValue()); + EXPECT_EQ(4000000000u, number_high.getValue()); +} + +TEST_F(SerialTest, equals) { + EXPECT_EQ(one, one); + EXPECT_EQ(one, one_2); + EXPECT_NE(one, two); + EXPECT_NE(two, one); + EXPECT_EQ(Serial(12345), number_low); + EXPECT_NE(Serial(12346), number_low); +} + +TEST_F(SerialTest, comparison) { + // These should be true/false even without serial arithmetic + EXPECT_LE(one, one); + EXPECT_LE(one, one_2); + EXPECT_LT(one, two); + EXPECT_LE(one, two); + EXPECT_GE(two, two); + EXPECT_GT(two, one); + EXPECT_GE(two, one); + EXPECT_LT(one, number_low); + EXPECT_LT(number_low, number_medium); + EXPECT_LT(number_medium, number_high); + + // now let's try some that 'wrap', as it were + EXPECT_GT(min, max); + EXPECT_LT(max, min); + EXPECT_LT(number_high, number_low); +} + +// +// RFC 1982 Section 3.1 +// +TEST_F(SerialTest, addition) { + EXPECT_EQ(two, one + one); + EXPECT_EQ(two, one + one_2); + EXPECT_EQ(max, max + min); + EXPECT_EQ(min, max + one); + EXPECT_EQ(one, max + two); + EXPECT_EQ(one, max + one + one); + + EXPECT_EQ(one + 100, max + 102); + EXPECT_EQ(min + 2147483645, max + 2147483646); + EXPECT_EQ(min + 2147483646, max + MAX_SERIAL_INCREMENT); +} + +// +// RFC 1982 Section 3.2 has been checked by the basic tests above +// + +// +// RFC 1982 Section 4.1 +// + +// Helper function for addition_always_larger test, add some numbers +// and check that the result is always larger than the original +void do_addition_larger_test(const Serial& number) { + EXPECT_GE(number + 0, number); + EXPECT_EQ(number + 0, number); + EXPECT_GT(number + 1, number); + EXPECT_GT(number + 2, number); + EXPECT_GT(number + 100, number); + EXPECT_GT(number + 1111111, number); + EXPECT_GT(number + 2147483646, number); + EXPECT_GT(number + MAX_SERIAL_INCREMENT, number); + // Try MAX_SERIAL_INCREMENT as a hardcoded number as well + EXPECT_GT(number + 2147483647, number); +} + +TEST_F(SerialTest, addition_always_larger) { + do_addition_larger_test(one); + do_addition_larger_test(two); + do_addition_larger_test(date_zero); + do_addition_larger_test(date_one); + do_addition_larger_test(min); + do_addition_larger_test(max); + do_addition_larger_test(number_low); + do_addition_larger_test(number_medium); + do_addition_larger_test(number_high); +} + +// +// RFC 1982 Section 4.2 +// + +// Helper function to do the second addition +void +do_two_additions_test_second(const Serial &original, + const Serial &number) +{ + EXPECT_NE(original, number); + EXPECT_NE(original, number + 0); + EXPECT_NE(original, number + 1); + EXPECT_NE(original, number + 2); + EXPECT_NE(original, number + 100); + EXPECT_NE(original, number + 1111111); + EXPECT_NE(original, number + 2147483646); + EXPECT_NE(original, number + MAX_SERIAL_INCREMENT); + EXPECT_NE(original, number + 2147483647); +} + +void do_two_additions_test_first(const Serial &number) { + do_two_additions_test_second(number, number + 1); + do_two_additions_test_second(number, number + 2); + do_two_additions_test_second(number, number + 100); + do_two_additions_test_second(number, number + 1111111); + do_two_additions_test_second(number, number + 2147483646); + do_two_additions_test_second(number, number + MAX_SERIAL_INCREMENT); + do_two_additions_test_second(number, number + 2147483647); +} + +TEST_F(SerialTest, two_additions_never_equal) { + do_two_additions_test_first(one); + do_two_additions_test_first(two); + do_two_additions_test_first(date_zero); + do_two_additions_test_first(date_one); + do_two_additions_test_first(min); + do_two_additions_test_first(max); + do_two_additions_test_first(number_low); + do_two_additions_test_first(number_medium); + do_two_additions_test_first(number_high); +} + +// +// RFC 1982 Section 4.3 and 4.4 have nothing to test +// + +// +// Tests from RFC 1982 examples +// +TEST(SerialTextRFCExamples, rfc_example_tests) { +} diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am new file mode 100644 index 0000000..e5c8081 --- /dev/null +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -0,0 +1,219 @@ +CLEANFILES = + +# NOTE: keep this in sync with real file listing +# so is included in tarball +EXTRA_DIST = edns_toWire1.spec edns_toWire2.spec +EXTRA_DIST += edns_toWire3.spec edns_toWire4.spec +EXTRA_DIST += masterload.txt +EXTRA_DIST += message_fromWire1 message_fromWire2 +EXTRA_DIST += message_fromWire3 message_fromWire4 +EXTRA_DIST += message_fromWire5 message_fromWire6 +EXTRA_DIST += message_fromWire7 message_fromWire8 +EXTRA_DIST += message_fromWire9 message_fromWire10.spec +EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec +EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec +EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec +EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec +EXTRA_DIST += message_fromWire19.spec message_fromWire20.spec +EXTRA_DIST += message_fromWire21.spec message_fromWire22.spec +EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec +EXTRA_DIST += message_toWire4.spec message_toWire5.spec +EXTRA_DIST += message_toWire6 message_toWire7 +EXTRA_DIST += message_toText1.txt message_toText1.spec +EXTRA_DIST += message_toText2.txt message_toText2.spec +EXTRA_DIST += message_toText3.txt message_toText3.spec +EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2 +EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8 +EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12 +EXTRA_DIST += name_fromWire13 name_fromWire14 +EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4 +EXTRA_DIST += name_toWire5.spec name_toWire6.spec +EXTRA_DIST += name_toWire7 name_toWire8 name_toWire9 +EXTRA_DIST += question_fromWire question_toWire1 question_toWire2 +EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec +EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec +EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire +EXTRA_DIST += rdata_dnskey_fromWire.spec rdata_dnskey_empty_keydata_fromWire.spec +EXTRA_DIST += rdata_dhcid_fromWire rdata_dhcid_toWire +EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire +EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2 +EXTRA_DIST += rdata_ns_fromWire +EXTRA_DIST += rdata_nsec_fromWire1 rdata_nsec_fromWire2 rdata_nsec_fromWire3 +EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec +EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec +EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec +EXTRA_DIST += rdata_nsec_fromWire10.spec +EXTRA_DIST += rdata_nsec_fromWire16.spec +EXTRA_DIST += rdata_nsec3param_fromWire1 +EXTRA_DIST += rdata_nsec3param_fromWire2.spec +EXTRA_DIST += rdata_nsec3param_fromWire11.spec +EXTRA_DIST += rdata_nsec3param_fromWire13.spec +EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire1.spec +EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3 +EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec +EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec +EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec +EXTRA_DIST += rdata_nsec3_fromWire10.spec rdata_nsec3_fromWire11.spec +EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec +EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec +EXTRA_DIST += rdata_nsec3_fromWire16.spec rdata_nsec3_fromWire17.spec +EXTRA_DIST += rdata_opt_fromWire1 rdata_opt_fromWire2 +EXTRA_DIST += rdata_opt_fromWire3 rdata_opt_fromWire4 +EXTRA_DIST += rdata_rrsig_fromWire1 +EXTRA_DIST += rdata_rrsig_fromWire2.spec +EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec +EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec +EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec +EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec +EXTRA_DIST += rdata_sshfp_fromWire rdata_sshfp_fromWire2 +EXTRA_DIST += rdata_sshfp_fromWire1.spec rdata_sshfp_fromWire2.spec +EXTRA_DIST += rdata_sshfp_fromWire3.spec rdata_sshfp_fromWire4.spec +EXTRA_DIST += rdata_sshfp_fromWire5.spec rdata_sshfp_fromWire6.spec +EXTRA_DIST += rdata_sshfp_fromWire7.spec rdata_sshfp_fromWire8.spec +EXTRA_DIST += rdata_sshfp_fromWire9 rdata_sshfp_fromWire10 +EXTRA_DIST += rdata_sshfp_fromWire11 rdata_sshfp_fromWire12 +EXTRA_DIST += rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec +EXTRA_DIST += rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec +EXTRA_DIST += rdata_afsdb_fromWire5.spec +EXTRA_DIST += rdata_afsdb_toWire1.spec rdata_afsdb_toWire2.spec +EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec +EXTRA_DIST += rdata_srv_fromWire +EXTRA_DIST += rdata_minfo_fromWire1.spec rdata_minfo_fromWire2.spec +EXTRA_DIST += rdata_minfo_fromWire3.spec rdata_minfo_fromWire4.spec +EXTRA_DIST += rdata_minfo_fromWire5.spec rdata_minfo_fromWire6.spec +EXTRA_DIST += rdata_minfo_toWire1.spec rdata_minfo_toWire2.spec +EXTRA_DIST += rdata_minfo_toWireUncompressed1.spec +EXTRA_DIST += rdata_minfo_toWireUncompressed2.spec +EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec +EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec +EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire +EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2 +EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2 +EXTRA_DIST += rrset_toWire1 rrset_toWire2 +EXTRA_DIST += rrset_toWire3 rrset_toWire4 +EXTRA_DIST += rdata_tkey_fromWire1.spec rdata_tkey_fromWire2.spec +EXTRA_DIST += rdata_tkey_fromWire3.spec rdata_tkey_fromWire4.spec +EXTRA_DIST += rdata_tkey_fromWire5.spec rdata_tkey_fromWire6.spec +EXTRA_DIST += rdata_tkey_fromWire7.spec rdata_tkey_fromWire8.spec +EXTRA_DIST += rdata_tkey_fromWire9.spec +EXTRA_DIST += rdata_tkey_toWire1.spec rdata_tkey_toWire2.spec +EXTRA_DIST += rdata_tkey_toWire3.spec rdata_tkey_toWire4.spec +EXTRA_DIST += rdata_tkey_toWire5.spec +EXTRA_DIST += rdata_tlsa_fromWire rdata_tlsa_fromWire2 +EXTRA_DIST += rdata_tlsa_fromWire3.spec rdata_tlsa_fromWire4.spec +EXTRA_DIST += rdata_tlsa_fromWire5.spec rdata_tlsa_fromWire6.spec +EXTRA_DIST += rdata_tlsa_fromWire7.spec rdata_tlsa_fromWire8.spec +EXTRA_DIST += rdata_tlsa_fromWire9 rdata_tlsa_fromWire10 +EXTRA_DIST += rdata_tlsa_fromWire11 rdata_tlsa_fromWire12 +EXTRA_DIST += rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec +EXTRA_DIST += rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec +EXTRA_DIST += rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec +EXTRA_DIST += rdata_tsig_fromWire7.spec rdata_tsig_fromWire8.spec +EXTRA_DIST += rdata_tsig_fromWire9.spec +EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec +EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec +EXTRA_DIST += rdata_tsig_toWire5.spec +EXTRA_DIST += rdata_caa_fromWire1.spec rdata_caa_fromWire2.spec +EXTRA_DIST += rdata_caa_fromWire3.spec rdata_caa_fromWire4.spec +EXTRA_DIST += rdata_caa_fromWire5 rdata_caa_fromWire6 +EXTRA_DIST += tsigrecord_toWire1.spec tsigrecord_toWire2.spec +EXTRA_DIST += tsig_verify1.spec tsig_verify2.spec tsig_verify3.spec +EXTRA_DIST += tsig_verify4.spec tsig_verify5.spec tsig_verify6.spec +EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec +EXTRA_DIST += tsig_verify10.spec tsig_verify11.spec +EXTRA_DIST += example.org +EXTRA_DIST += broken.zone +EXTRA_DIST += origincheck.txt +EXTRA_DIST += omitcheck.txt + +# Generated .wire files +EXTRA_DIST += edns_toWire1.wire edns_toWire2.wire +EXTRA_DIST += edns_toWire3.wire edns_toWire4.wire +EXTRA_DIST += message_fromWire10.wire +EXTRA_DIST += message_fromWire11.wire message_fromWire12.wire +EXTRA_DIST += message_fromWire13.wire message_fromWire14.wire +EXTRA_DIST += message_fromWire15.wire message_fromWire16.wire +EXTRA_DIST += message_fromWire17.wire message_fromWire18.wire +EXTRA_DIST += message_fromWire19.wire message_fromWire20.wire +EXTRA_DIST += message_fromWire21.wire message_fromWire22.wire +EXTRA_DIST += message_toWire1 message_toWire2.wire message_toWire3.wire +EXTRA_DIST += message_toWire4.wire message_toWire5.wire +EXTRA_DIST += message_toText1.txt message_toText1.wire +EXTRA_DIST += message_toText2.txt message_toText2.wire +EXTRA_DIST += message_toText3.txt message_toText3.wire +EXTRA_DIST += name_toWire5.wire name_toWire6.wire +EXTRA_DIST += rdatafields1.wire rdatafields2.wire rdatafields3.wire +EXTRA_DIST += rdatafields4.wire rdatafields5.wire rdatafields6.wire +EXTRA_DIST += rdata_dnskey_fromWire.wire rdata_dnskey_empty_keydata_fromWire.wire +EXTRA_DIST += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire +EXTRA_DIST += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire +EXTRA_DIST += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire +EXTRA_DIST += rdata_nsec_fromWire10.wire +EXTRA_DIST += rdata_nsec_fromWire16.wire +EXTRA_DIST += rdata_nsec3param_fromWire2.wire +EXTRA_DIST += rdata_nsec3param_fromWire11.wire +EXTRA_DIST += rdata_nsec3param_fromWire13.wire +EXTRA_DIST += rdata_nsec3_fromWire2.wire rdata_nsec3_fromWire3 +EXTRA_DIST += rdata_nsec3_fromWire4.wire rdata_nsec3_fromWire5.wire +EXTRA_DIST += rdata_nsec3_fromWire6.wire rdata_nsec3_fromWire7.wire +EXTRA_DIST += rdata_nsec3_fromWire8.wire rdata_nsec3_fromWire9.wire +EXTRA_DIST += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire +EXTRA_DIST += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire +EXTRA_DIST += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire +EXTRA_DIST += rdata_nsec3_fromWire16.wire rdata_nsec3_fromWire17.wire +EXTRA_DIST += rdata_rrsig_fromWire2.wire +EXTRA_DIST += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire +EXTRA_DIST += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire +EXTRA_DIST += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire +EXTRA_DIST += rdata_rp_toWire1.wire rdata_rp_toWire2.wire +EXTRA_DIST += rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire +EXTRA_DIST += rdata_sshfp_fromWire3.wire rdata_sshfp_fromWire4.wire +EXTRA_DIST += rdata_sshfp_fromWire5.wire rdata_sshfp_fromWire6.wire +EXTRA_DIST += rdata_sshfp_fromWire7.wire rdata_sshfp_fromWire8.wire +EXTRA_DIST += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire +EXTRA_DIST += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire +EXTRA_DIST += rdata_afsdb_fromWire5.wire +EXTRA_DIST += rdata_afsdb_toWire1.wire rdata_afsdb_toWire2.wire +EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.wire +EXTRA_DIST += rdata_minfo_fromWire1.wire rdata_minfo_fromWire2.wire +EXTRA_DIST += rdata_minfo_fromWire3.wire rdata_minfo_fromWire4.wire +EXTRA_DIST += rdata_minfo_fromWire5.wire rdata_minfo_fromWire6.wire +EXTRA_DIST += rdata_minfo_toWire1.wire rdata_minfo_toWire2.wire +EXTRA_DIST += rdata_minfo_toWireUncompressed1.wire +EXTRA_DIST += rdata_minfo_toWireUncompressed2.wire +EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.wire +EXTRA_DIST += rdata_txt_fromWire3.wire rdata_txt_fromWire4.wire +EXTRA_DIST += rdata_txt_fromWire5.wire rdata_unknown_fromWire +EXTRA_DIST += rdata_tlsa_fromWire3.wire rdata_tlsa_fromWire4.wire +EXTRA_DIST += rdata_tlsa_fromWire5.wire rdata_tlsa_fromWire6.wire +EXTRA_DIST += rdata_tlsa_fromWire7.wire rdata_tlsa_fromWire8.wire +EXTRA_DIST += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire +EXTRA_DIST += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire +EXTRA_DIST += rdata_tsig_fromWire5.wire rdata_tsig_fromWire6.wire +EXTRA_DIST += rdata_tsig_fromWire7.wire rdata_tsig_fromWire8.wire +EXTRA_DIST += rdata_tsig_fromWire9.wire +EXTRA_DIST += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire +EXTRA_DIST += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire +EXTRA_DIST += rdata_tsig_toWire5.wire +EXTRA_DIST += rdata_caa_fromWire1.wire rdata_caa_fromWire2.wire +EXTRA_DIST += rdata_caa_fromWire3.wire rdata_caa_fromWire4.wire +EXTRA_DIST += rdata_tkey_fromWire1.wire rdata_tkey_fromWire2.wire +EXTRA_DIST += rdata_tkey_fromWire3.wire rdata_tkey_fromWire4.wire +EXTRA_DIST += rdata_tkey_fromWire5.wire rdata_tkey_fromWire6.wire +EXTRA_DIST += rdata_tkey_fromWire7.wire rdata_tkey_fromWire8.wire +EXTRA_DIST += rdata_tkey_fromWire9.wire +EXTRA_DIST += rdata_tkey_toWire1.wire rdata_tkey_toWire2.wire +EXTRA_DIST += rdata_tkey_toWire3.wire rdata_tkey_toWire4.wire +EXTRA_DIST += rdata_tkey_toWire5.wire +EXTRA_DIST += tsigrecord_toWire1.wire tsigrecord_toWire2.wire +EXTRA_DIST += tsig_verify1.wire tsig_verify2.wire tsig_verify3.wire +EXTRA_DIST += tsig_verify4.wire tsig_verify5.wire tsig_verify6.wire +EXTRA_DIST += tsig_verify7.wire tsig_verify8.wire tsig_verify9.wire +EXTRA_DIST += tsig_verify10.wire tsig_verify11.wire + +# We no longer use gen_wiredata.py during build process, so the +# dependency is no longer needed. However, we'll keep this dependency +# commented till the gen_wiredata.py script is removed. + +#.spec.wire: +# $(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $< diff --git a/src/lib/dns/tests/testdata/Makefile.in b/src/lib/dns/tests/testdata/Makefile.in new file mode 100644 index 0000000..44e95c3 --- /dev/null +++ b/src/lib/dns/tests/testdata/Makefile.in @@ -0,0 +1,716 @@ +# 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/tests/testdata +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 $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +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 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +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@ +CLEANFILES = + +# NOTE: keep this in sync with real file listing +# so is included in tarball + +# Generated .wire files +EXTRA_DIST = edns_toWire1.spec edns_toWire2.spec edns_toWire3.spec \ + edns_toWire4.spec masterload.txt message_fromWire1 \ + message_fromWire2 message_fromWire3 message_fromWire4 \ + message_fromWire5 message_fromWire6 message_fromWire7 \ + message_fromWire8 message_fromWire9 message_fromWire10.spec \ + message_fromWire11.spec message_fromWire12.spec \ + message_fromWire13.spec message_fromWire14.spec \ + message_fromWire15.spec message_fromWire16.spec \ + message_fromWire17.spec message_fromWire18.spec \ + message_fromWire19.spec message_fromWire20.spec \ + message_fromWire21.spec message_fromWire22.spec \ + message_toWire1 message_toWire2.spec message_toWire3.spec \ + message_toWire4.spec message_toWire5.spec message_toWire6 \ + message_toWire7 message_toText1.txt message_toText1.spec \ + message_toText2.txt message_toText2.spec message_toText3.txt \ + message_toText3.spec name_fromWire1 name_fromWire2 \ + name_fromWire3_1 name_fromWire3_2 name_fromWire4 \ + name_fromWire6 name_fromWire7 name_fromWire8 name_fromWire9 \ + name_fromWire10 name_fromWire11 name_fromWire12 \ + name_fromWire13 name_fromWire14 name_toWire1 name_toWire2 \ + name_toWire3 name_toWire4 name_toWire5.spec name_toWire6.spec \ + name_toWire7 name_toWire8 name_toWire9 question_fromWire \ + question_toWire1 question_toWire2 rdatafields1.spec \ + rdatafields2.spec rdatafields3.spec rdatafields4.spec \ + rdatafields5.spec rdatafields6.spec rdata_cname_fromWire \ + rdata_dname_fromWire rdata_dnskey_fromWire.spec \ + rdata_dnskey_empty_keydata_fromWire.spec rdata_dhcid_fromWire \ + rdata_dhcid_toWire rdata_ds_fromWire rdata_in_a_fromWire \ + rdata_in_aaaa_fromWire rdata_mx_fromWire rdata_mx_toWire1 \ + rdata_mx_toWire2 rdata_ns_fromWire rdata_nsec_fromWire1 \ + rdata_nsec_fromWire2 rdata_nsec_fromWire3 \ + rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec \ + rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec \ + rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec \ + rdata_nsec_fromWire10.spec rdata_nsec_fromWire16.spec \ + rdata_nsec3param_fromWire1 rdata_nsec3param_fromWire2.spec \ + rdata_nsec3param_fromWire11.spec \ + rdata_nsec3param_fromWire13.spec rdata_nsec3_fromWire1 \ + rdata_nsec3_fromWire1.spec rdata_nsec3_fromWire2.spec \ + rdata_nsec3_fromWire3 rdata_nsec3_fromWire4.spec \ + rdata_nsec3_fromWire5.spec rdata_nsec3_fromWire6.spec \ + rdata_nsec3_fromWire7.spec rdata_nsec3_fromWire8.spec \ + rdata_nsec3_fromWire9.spec rdata_nsec3_fromWire10.spec \ + rdata_nsec3_fromWire11.spec rdata_nsec3_fromWire12.spec \ + rdata_nsec3_fromWire13.spec rdata_nsec3_fromWire14.spec \ + rdata_nsec3_fromWire15.spec rdata_nsec3_fromWire16.spec \ + rdata_nsec3_fromWire17.spec rdata_opt_fromWire1 \ + rdata_opt_fromWire2 rdata_opt_fromWire3 rdata_opt_fromWire4 \ + rdata_rrsig_fromWire1 rdata_rrsig_fromWire2.spec \ + rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec \ + rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec \ + rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec \ + rdata_rp_toWire1.spec rdata_rp_toWire2.spec \ + rdata_sshfp_fromWire rdata_sshfp_fromWire2 \ + rdata_sshfp_fromWire1.spec rdata_sshfp_fromWire2.spec \ + rdata_sshfp_fromWire3.spec rdata_sshfp_fromWire4.spec \ + rdata_sshfp_fromWire5.spec rdata_sshfp_fromWire6.spec \ + rdata_sshfp_fromWire7.spec rdata_sshfp_fromWire8.spec \ + rdata_sshfp_fromWire9 rdata_sshfp_fromWire10 \ + rdata_sshfp_fromWire11 rdata_sshfp_fromWire12 \ + rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec \ + rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec \ + rdata_afsdb_fromWire5.spec rdata_afsdb_toWire1.spec \ + rdata_afsdb_toWire2.spec rdata_soa_fromWire \ + rdata_soa_toWireUncompressed.spec rdata_srv_fromWire \ + rdata_minfo_fromWire1.spec rdata_minfo_fromWire2.spec \ + rdata_minfo_fromWire3.spec rdata_minfo_fromWire4.spec \ + rdata_minfo_fromWire5.spec rdata_minfo_fromWire6.spec \ + rdata_minfo_toWire1.spec rdata_minfo_toWire2.spec \ + rdata_minfo_toWireUncompressed1.spec \ + rdata_minfo_toWireUncompressed2.spec rdata_txt_fromWire1 \ + rdata_txt_fromWire2.spec rdata_txt_fromWire3.spec \ + rdata_txt_fromWire4.spec rdata_txt_fromWire5.spec \ + rdata_unknown_fromWire rrcode16_fromWire1 rrcode16_fromWire2 \ + rrcode32_fromWire1 rrcode32_fromWire2 rrset_toWire1 \ + rrset_toWire2 rrset_toWire3 rrset_toWire4 \ + rdata_tkey_fromWire1.spec rdata_tkey_fromWire2.spec \ + rdata_tkey_fromWire3.spec rdata_tkey_fromWire4.spec \ + rdata_tkey_fromWire5.spec rdata_tkey_fromWire6.spec \ + rdata_tkey_fromWire7.spec rdata_tkey_fromWire8.spec \ + rdata_tkey_fromWire9.spec rdata_tkey_toWire1.spec \ + rdata_tkey_toWire2.spec rdata_tkey_toWire3.spec \ + rdata_tkey_toWire4.spec rdata_tkey_toWire5.spec \ + rdata_tlsa_fromWire rdata_tlsa_fromWire2 \ + rdata_tlsa_fromWire3.spec rdata_tlsa_fromWire4.spec \ + rdata_tlsa_fromWire5.spec rdata_tlsa_fromWire6.spec \ + rdata_tlsa_fromWire7.spec rdata_tlsa_fromWire8.spec \ + rdata_tlsa_fromWire9 rdata_tlsa_fromWire10 \ + rdata_tlsa_fromWire11 rdata_tlsa_fromWire12 \ + rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec \ + rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec \ + rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec \ + rdata_tsig_fromWire7.spec rdata_tsig_fromWire8.spec \ + rdata_tsig_fromWire9.spec rdata_tsig_toWire1.spec \ + rdata_tsig_toWire2.spec rdata_tsig_toWire3.spec \ + rdata_tsig_toWire4.spec rdata_tsig_toWire5.spec \ + rdata_caa_fromWire1.spec rdata_caa_fromWire2.spec \ + rdata_caa_fromWire3.spec rdata_caa_fromWire4.spec \ + rdata_caa_fromWire5 rdata_caa_fromWire6 \ + tsigrecord_toWire1.spec tsigrecord_toWire2.spec \ + tsig_verify1.spec tsig_verify2.spec tsig_verify3.spec \ + tsig_verify4.spec tsig_verify5.spec tsig_verify6.spec \ + tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec \ + tsig_verify10.spec tsig_verify11.spec example.org broken.zone \ + origincheck.txt omitcheck.txt edns_toWire1.wire \ + edns_toWire2.wire edns_toWire3.wire edns_toWire4.wire \ + message_fromWire10.wire message_fromWire11.wire \ + message_fromWire12.wire message_fromWire13.wire \ + message_fromWire14.wire message_fromWire15.wire \ + message_fromWire16.wire message_fromWire17.wire \ + message_fromWire18.wire message_fromWire19.wire \ + message_fromWire20.wire message_fromWire21.wire \ + message_fromWire22.wire message_toWire1 message_toWire2.wire \ + message_toWire3.wire message_toWire4.wire message_toWire5.wire \ + message_toText1.txt message_toText1.wire message_toText2.txt \ + message_toText2.wire message_toText3.txt message_toText3.wire \ + name_toWire5.wire name_toWire6.wire rdatafields1.wire \ + rdatafields2.wire rdatafields3.wire rdatafields4.wire \ + rdatafields5.wire rdatafields6.wire rdata_dnskey_fromWire.wire \ + rdata_dnskey_empty_keydata_fromWire.wire \ + rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire \ + rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire \ + rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire \ + rdata_nsec_fromWire10.wire rdata_nsec_fromWire16.wire \ + rdata_nsec3param_fromWire2.wire \ + rdata_nsec3param_fromWire11.wire \ + rdata_nsec3param_fromWire13.wire rdata_nsec3_fromWire2.wire \ + rdata_nsec3_fromWire3 rdata_nsec3_fromWire4.wire \ + rdata_nsec3_fromWire5.wire rdata_nsec3_fromWire6.wire \ + rdata_nsec3_fromWire7.wire rdata_nsec3_fromWire8.wire \ + rdata_nsec3_fromWire9.wire rdata_nsec3_fromWire10.wire \ + rdata_nsec3_fromWire11.wire rdata_nsec3_fromWire12.wire \ + rdata_nsec3_fromWire13.wire rdata_nsec3_fromWire14.wire \ + rdata_nsec3_fromWire15.wire rdata_nsec3_fromWire16.wire \ + rdata_nsec3_fromWire17.wire rdata_rrsig_fromWire2.wire \ + rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire \ + rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire \ + rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire \ + rdata_rp_toWire1.wire rdata_rp_toWire2.wire \ + rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire \ + rdata_sshfp_fromWire3.wire rdata_sshfp_fromWire4.wire \ + rdata_sshfp_fromWire5.wire rdata_sshfp_fromWire6.wire \ + rdata_sshfp_fromWire7.wire rdata_sshfp_fromWire8.wire \ + rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire \ + rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire \ + rdata_afsdb_fromWire5.wire rdata_afsdb_toWire1.wire \ + rdata_afsdb_toWire2.wire rdata_soa_fromWire \ + rdata_soa_toWireUncompressed.wire rdata_minfo_fromWire1.wire \ + rdata_minfo_fromWire2.wire rdata_minfo_fromWire3.wire \ + rdata_minfo_fromWire4.wire rdata_minfo_fromWire5.wire \ + rdata_minfo_fromWire6.wire rdata_minfo_toWire1.wire \ + rdata_minfo_toWire2.wire rdata_minfo_toWireUncompressed1.wire \ + rdata_minfo_toWireUncompressed2.wire rdata_txt_fromWire1 \ + rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire \ + rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire \ + rdata_unknown_fromWire rdata_tlsa_fromWire3.wire \ + rdata_tlsa_fromWire4.wire rdata_tlsa_fromWire5.wire \ + rdata_tlsa_fromWire6.wire rdata_tlsa_fromWire7.wire \ + rdata_tlsa_fromWire8.wire rdata_tsig_fromWire1.wire \ + rdata_tsig_fromWire2.wire rdata_tsig_fromWire3.wire \ + rdata_tsig_fromWire4.wire rdata_tsig_fromWire5.wire \ + rdata_tsig_fromWire6.wire rdata_tsig_fromWire7.wire \ + rdata_tsig_fromWire8.wire rdata_tsig_fromWire9.wire \ + rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire \ + rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire \ + rdata_tsig_toWire5.wire rdata_caa_fromWire1.wire \ + rdata_caa_fromWire2.wire rdata_caa_fromWire3.wire \ + rdata_caa_fromWire4.wire rdata_tkey_fromWire1.wire \ + rdata_tkey_fromWire2.wire rdata_tkey_fromWire3.wire \ + rdata_tkey_fromWire4.wire rdata_tkey_fromWire5.wire \ + rdata_tkey_fromWire6.wire rdata_tkey_fromWire7.wire \ + rdata_tkey_fromWire8.wire rdata_tkey_fromWire9.wire \ + rdata_tkey_toWire1.wire rdata_tkey_toWire2.wire \ + rdata_tkey_toWire3.wire rdata_tkey_toWire4.wire \ + rdata_tkey_toWire5.wire tsigrecord_toWire1.wire \ + tsigrecord_toWire2.wire tsig_verify1.wire tsig_verify2.wire \ + tsig_verify3.wire tsig_verify4.wire tsig_verify5.wire \ + tsig_verify6.wire tsig_verify7.wire tsig_verify8.wire \ + tsig_verify9.wire tsig_verify10.wire tsig_verify11.wire +all: all-am + +.SUFFIXES: +$(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/tests/testdata/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/dns/tests/testdata/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# We no longer use gen_wiredata.py during build process, so the +# dependency is no longer needed. However, we'll keep this dependency +# commented till the gen_wiredata.py script is removed. + +#.spec.wire: +# $(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $< + +# 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/tests/testdata/broken.zone b/src/lib/dns/tests/testdata/broken.zone new file mode 100644 index 0000000..70f4540 --- /dev/null +++ b/src/lib/dns/tests/testdata/broken.zone @@ -0,0 +1,3 @@ +; This should fail due to broken TTL +; The file should _NOT_ end with EOLN +broken. 3600X IN A 192.0.2.2 More data
\ No newline at end of file diff --git a/src/lib/dns/tests/testdata/edns_toWire1.spec b/src/lib/dns/tests/testdata/edns_toWire1.spec new file mode 100644 index 0000000..483aefa --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire1.spec @@ -0,0 +1,5 @@ +# +# A simplest form of EDNS: all default parameters +# +[edns] + diff --git a/src/lib/dns/tests/testdata/edns_toWire1.wire b/src/lib/dns/tests/testdata/edns_toWire1.wire new file mode 100644 index 0000000..2884e29 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire1.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from edns_toWire1.spec +### + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=0 DO=0 +00 0029 1000 0000 0000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/edns_toWire2.spec b/src/lib/dns/tests/testdata/edns_toWire2.spec new file mode 100644 index 0000000..7fe1ffd --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire2.spec @@ -0,0 +1,5 @@ +# +# Same as edns_toWire1 but setting the DO bit +# +[edns] +do: 1 diff --git a/src/lib/dns/tests/testdata/edns_toWire2.wire b/src/lib/dns/tests/testdata/edns_toWire2.wire new file mode 100644 index 0000000..cb09000 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire2.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from edns_toWire2.spec +### + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=0 DO=1 +00 0029 1000 0000 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/edns_toWire3.spec b/src/lib/dns/tests/testdata/edns_toWire3.spec new file mode 100644 index 0000000..0332097 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire3.spec @@ -0,0 +1,7 @@ +# +# Same as edns_toWire1 but setting the DO bit, and extended Rcode being non 0 +# (for BADVER) +# +[edns] +do: 1 +extrcode: 0x1 diff --git a/src/lib/dns/tests/testdata/edns_toWire3.wire b/src/lib/dns/tests/testdata/edns_toWire3.wire new file mode 100644 index 0000000..b8d0775 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire3.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from edns_toWire3.spec +### + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=1 Version=0 DO=1 +00 0029 1000 0100 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/edns_toWire4.spec b/src/lib/dns/tests/testdata/edns_toWire4.spec new file mode 100644 index 0000000..ea1f5e3 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire4.spec @@ -0,0 +1,7 @@ +# +# Same as edns_toWire1 but setting the DO bit, and using an unusual +# UDP payload size +# +[edns] +do: 1 +udpsize = 511 diff --git a/src/lib/dns/tests/testdata/edns_toWire4.wire b/src/lib/dns/tests/testdata/edns_toWire4.wire new file mode 100644 index 0000000..73bf757 --- /dev/null +++ b/src/lib/dns/tests/testdata/edns_toWire4.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from edns_toWire4.spec +### + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=511 ExtRcode=0 Version=0 DO=1 +00 0029 01ff 0000 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/example.org b/src/lib/dns/tests/testdata/example.org new file mode 100644 index 0000000..2708ef4 --- /dev/null +++ b/src/lib/dns/tests/testdata/example.org @@ -0,0 +1,17 @@ +example.org. 3600 IN SOA ( ; The SOA, split across lines for testing + ns1.example.org. + admin.example.org. + 1234 + 3600 + 1800 + 2419200 + 7200 + ) +; Check it accepts quoted name too +"\101xample.org." 3600 IN NS ns1.example.org. + + +; Some empty lines here. They are to make sure the loader can skip them. +www 3600 IN A 192.0.2.1 ; Test a relative name as well. + 3600 IN AAAA 2001:db8::1 ; And initial whitespace handling + ; Here be just some space, no RRs diff --git a/src/lib/dns/tests/testdata/masterload.txt b/src/lib/dns/tests/testdata/masterload.txt new file mode 100644 index 0000000..0d2f942 --- /dev/null +++ b/src/lib/dns/tests/testdata/masterload.txt @@ -0,0 +1,5 @@ +;; a simple (incomplete) zone file + +example.com. 3600 IN TXT "test data" +www.example.com. 60 IN A 192.0.2.1 +www.example.com. 60 IN A 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire1 b/src/lib/dns/tests/testdata/message_fromWire1 new file mode 100644 index 0000000..5b76e3f --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire1 @@ -0,0 +1,22 @@ +# +# A simple DNS response message +# ID = 0x1035 +# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=2, other COUNTS=0 +# Question: test.example.com. IN A +# Answer: +# test.example.com. 3600 IN A 192.0.2.1 +# test.example.com. 7200 IN A 192.0.2.2 +# +1035 8500 +0001 0002 0000 0000 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# same name, fully compressed +c0 0c +# TTL=3600, A, IN, RDLENGTH=4, RDATA +0001 0001 00000e10 0004 c0 00 02 01 +# mostly same, with the slight difference in RDATA and TTL +c0 0c +0001 0001 00001c20 0004 c0 00 02 02 diff --git a/src/lib/dns/tests/testdata/message_fromWire10.spec b/src/lib/dns/tests/testdata/message_fromWire10.spec new file mode 100644 index 0000000..d3fb014 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire10.spec @@ -0,0 +1,13 @@ +# +# A simple DNS response message with an EDNS0 indicating a BADVERS error +# + +[header] +qr: response +rd: 1 +arcount: 1 +[question] +# use default +[edns] +do: 1 +extrcode: 1 diff --git a/src/lib/dns/tests/testdata/message_fromWire10.wire b/src/lib/dns/tests/testdata/message_fromWire10.wire new file mode 100644 index 0000000..fa76b92 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire10.wire @@ -0,0 +1,19 @@ +### +### This data file was auto-generated from message_fromWire10.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) RD +1035 8100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1) +076578616d706c6503636f6d00 0001 0001 + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=1 Version=0 DO=1 +00 0029 1000 0100 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire11.spec b/src/lib/dns/tests/testdata/message_fromWire11.spec new file mode 100644 index 0000000..5f31746 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire11.spec @@ -0,0 +1,15 @@ +# +# A simple DNS response message with an EDNS0 indicating the maximum error code +# (0xfff) +# + +[header] +qr: response +rd: 1 +rcode: 0xf +arcount: 1 +[question] +# use default +[edns] +do: 1 +extrcode: 0xff diff --git a/src/lib/dns/tests/testdata/message_fromWire11.wire b/src/lib/dns/tests/testdata/message_fromWire11.wire new file mode 100644 index 0000000..f20132c --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire11.wire @@ -0,0 +1,19 @@ +### +### This data file was auto-generated from message_fromWire11.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=15 RD +1035 810f +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1) +076578616d706c6503636f6d00 0001 0001 + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=255 Version=0 DO=1 +00 0029 1000 ff00 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire12.spec b/src/lib/dns/tests/testdata/message_fromWire12.spec new file mode 100644 index 0000000..4eadeed --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire12.spec @@ -0,0 +1,21 @@ +# +# A simple DNS response message with TSIG signed, but the owner name of TSIG +# is compressed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: ptr=12 +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire12.wire b/src/lib/dns/tests/testdata/message_fromWire12.wire new file mode 100644 index 0000000..9ceb356 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire12.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_fromWire12.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=ptr=12 Class=ANY(255) TTL=0, RDLEN=58) +c00c 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire13.spec b/src/lib/dns/tests/testdata/message_fromWire13.spec new file mode 100644 index 0000000..e81ec4c --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire13.spec @@ -0,0 +1,20 @@ +# +# Invalid TSIG: containing 2 TSIG RRs. +# + +[custom] +sections: header:question:tsig:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 2 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire13.wire b/src/lib/dns/tests/testdata/message_fromWire13.wire new file mode 100644 index 0000000..05b064a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire13.wire @@ -0,0 +1,35 @@ +### +### This data file was auto-generated from message_fromWire13.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=2 +0001 0000 0000 0002 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire14.spec b/src/lib/dns/tests/testdata/message_fromWire14.spec new file mode 100644 index 0000000..bf68a93 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire14.spec @@ -0,0 +1,21 @@ +# +# Invalid TSIG: not in the additional section. +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +# TSIG goes to the answer section +ancount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire14.wire b/src/lib/dns/tests/testdata/message_fromWire14.wire new file mode 100644 index 0000000..17d0e21 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire14.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_fromWire14.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=0 +0001 0001 0000 0000 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire15.spec b/src/lib/dns/tests/testdata/message_fromWire15.spec new file mode 100644 index 0000000..25d810f --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire15.spec @@ -0,0 +1,22 @@ +# +# Invalid TSIG: not at the end of the message +# + +[custom] +sections: header:question:tsig:edns +[header] +id: 0x2d65 +rd: 1 +arcount: 2 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 +[edns] +# (all default) diff --git a/src/lib/dns/tests/testdata/message_fromWire15.wire b/src/lib/dns/tests/testdata/message_fromWire15.wire new file mode 100644 index 0000000..e3f36d0 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire15.wire @@ -0,0 +1,30 @@ +### +### This data file was auto-generated from message_fromWire15.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=2 +0001 0000 0000 0002 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=0 DO=0 +00 0029 1000 0000 0000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire16.spec b/src/lib/dns/tests/testdata/message_fromWire16.spec new file mode 100644 index 0000000..be0abc3 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire16.spec @@ -0,0 +1,21 @@ +# +# Invalid TSIG: not in the additional section. +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_class: IN +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire16.wire b/src/lib/dns/tests/testdata/message_fromWire16.wire new file mode 100644 index 0000000..04a791a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire16.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_fromWire16.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=IN(1) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 0001 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire17.spec b/src/lib/dns/tests/testdata/message_fromWire17.spec new file mode 100644 index 0000000..366cf05 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire17.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with TSIG signed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x22c2 +rd: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e179212 +mac_size: 16 +mac: 0x8214b04634e32323d651ac60b08e6388 +original_id: 0x22c2 diff --git a/src/lib/dns/tests/testdata/message_fromWire17.wire b/src/lib/dns/tests/testdata/message_fromWire17.wire new file mode 100644 index 0000000..e607c52 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire17.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_fromWire17.spec +### + +# Header Section +# ID=8898 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +22c2 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=TXT(16) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0010 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1310167570 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004e179212 012c +# MAC Size=16 MAC=(see hex) +0010 8214b04634e32323d651ac60b08e6388 +# Original-ID=8898 Error=0 +22c2 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire18.spec b/src/lib/dns/tests/testdata/message_fromWire18.spec new file mode 100644 index 0000000..0b2592a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire18.spec @@ -0,0 +1,23 @@ +# +# Another simple DNS query message with TSIG signed. Only ID and time signed +# (and MAC as a result) are different. +# + +[custom] +sections: header:question:tsig +[header] +id: 0xd6e2 +rd: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e17b38d +mac_size: 16 +mac: 0x903b5b194a799b03a37718820c2404f2 +original_id: 0xd6e2 diff --git a/src/lib/dns/tests/testdata/message_fromWire18.wire b/src/lib/dns/tests/testdata/message_fromWire18.wire new file mode 100644 index 0000000..82bdf6b --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire18.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_fromWire18.spec +### + +# Header Section +# ID=55010 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +d6e2 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=TXT(16) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0010 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1310176141 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004e17b38d 012c +# MAC Size=16 MAC=(see hex) +0010 903b5b194a799b03a37718820c2404f2 +# Original-ID=55010 Error=0 +d6e2 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire19.spec b/src/lib/dns/tests/testdata/message_fromWire19.spec new file mode 100644 index 0000000..8212dbf --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire19.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# answer section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +ancount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire19.wire b/src/lib/dns/tests/testdata/message_fromWire19.wire new file mode 100644 index 0000000..d154244 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire19.wire @@ -0,0 +1,28 @@ +### +### This data file was auto-generated from message_fromWire19.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) +1035 8000 +# QDCNT=1, ANCNT=3, NSCNT=0, ARCNT=0 +0001 0003 0000 0000 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# AAAA RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=16) +076578616d706c6503636f6d00 001c 0001 00015180 0010 +# Address=2001:db8::1 +20010db8000000000000000000000001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.2 +c0000202 diff --git a/src/lib/dns/tests/testdata/message_fromWire2 b/src/lib/dns/tests/testdata/message_fromWire2 new file mode 100644 index 0000000..194cbf2 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire2 @@ -0,0 +1,22 @@ +# +# A simple DNS query message with a valid EDNS0 OPT RR +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +# Question: test.example.com. IN A +1035 0100 +0001 0000 0000 0001 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire20.spec b/src/lib/dns/tests/testdata/message_fromWire20.spec new file mode 100644 index 0000000..91986e4 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire20.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# authority section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +nscount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire20.wire b/src/lib/dns/tests/testdata/message_fromWire20.wire new file mode 100644 index 0000000..887dd1e --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire20.wire @@ -0,0 +1,28 @@ +### +### This data file was auto-generated from message_fromWire20.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) +1035 8000 +# QDCNT=1, ANCNT=0, NSCNT=3, ARCNT=0 +0001 0000 0003 0000 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# AAAA RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=16) +076578616d706c6503636f6d00 001c 0001 00015180 0010 +# Address=2001:db8::1 +20010db8000000000000000000000001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.2 +c0000202 diff --git a/src/lib/dns/tests/testdata/message_fromWire21.spec b/src/lib/dns/tests/testdata/message_fromWire21.spec new file mode 100644 index 0000000..cd6aac9 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire21.spec @@ -0,0 +1,20 @@ +# +# A non realistic DNS response message containing mixed types of RRs in the +# additional section in a mixed order. +# + +[custom] +sections: header:question:a/1:aaaa:a/2 +[header] +qr: 1 +arcount: 3 +[question] +name: www.example.com +rrtype: A +[a/1] +as_rr: True +[aaaa] +as_rr: True +[a/2] +as_rr: True +address: 192.0.2.2 diff --git a/src/lib/dns/tests/testdata/message_fromWire21.wire b/src/lib/dns/tests/testdata/message_fromWire21.wire new file mode 100644 index 0000000..14cfcc0 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire21.wire @@ -0,0 +1,28 @@ +### +### This data file was auto-generated from message_fromWire21.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) +1035 8000 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=3 +0001 0000 0000 0003 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# AAAA RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=16) +076578616d706c6503636f6d00 001c 0001 00015180 0010 +# Address=2001:db8::1 +20010db8000000000000000000000001 + +# A RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=4) +076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.2 +c0000202 diff --git a/src/lib/dns/tests/testdata/message_fromWire22.spec b/src/lib/dns/tests/testdata/message_fromWire22.spec new file mode 100644 index 0000000..a52523b --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire22.spec @@ -0,0 +1,14 @@ +# +# A simple DNS message containing one SOA RR in the answer section. This is +# intended to be trimmed to emulate a bogus message. +# + +[custom] +sections: header:question:soa +[header] +qr: 1 +ancount: 1 +[question] +rrtype: SOA +[soa] +as_rr: True diff --git a/src/lib/dns/tests/testdata/message_fromWire22.wire b/src/lib/dns/tests/testdata/message_fromWire22.wire new file mode 100644 index 0000000..69c3254 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire22.wire @@ -0,0 +1,20 @@ +### +### This data file was auto-generated from message_fromWire22.spec +### + +# Header Section +# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) +1035 8000 +# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=0 +0001 0001 0000 0000 + +# Question Section +# QNAME=example.com. QTYPE=SOA(6) QCLASS=IN(1) +076578616d706c6503636f6d00 0006 0001 + +# SOA RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=54) +076578616d706c6503636f6d00 0006 0001 00015180 0036 +# NNAME=ns.example.com RNAME=root.example.com +026e73076578616d706c6503636f6d00 04726f6f74076578616d706c6503636f6d00 +# SERIAL(2010012601) REFRESH(3600) RETRY(300) EXPIRE(3600000) MINIMUM(1200) +77ce5bb9 00000e10 0000012c 0036ee80 000004b0 diff --git a/src/lib/dns/tests/testdata/message_fromWire3 b/src/lib/dns/tests/testdata/message_fromWire3 new file mode 100644 index 0000000..9bd536a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire3 @@ -0,0 +1,22 @@ +# +# A simple DNS query message with a valid EDNS0 OPT RR, DO bit off +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +# Question: test.example.com. IN A +1035 0100 +0001 0000 0000 0001 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=0 +0000 0000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire4 b/src/lib/dns/tests/testdata/message_fromWire4 new file mode 100644 index 0000000..23eb7cf --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire4 @@ -0,0 +1,23 @@ +# +# A simple DNS query message with a bogus EDNS0 OPT RR (included in the +# answer section) +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=1, NSCOUNT=0, ARCOUNT=0 +# Question: test.example.com. IN A +1035 0100 +0001 0001 0000 0000 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire5 b/src/lib/dns/tests/testdata/message_fromWire5 new file mode 100644 index 0000000..6f08d22 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire5 @@ -0,0 +1,33 @@ +# +# A simple DNS query message with multiple EDNS0 OPT RRs (bogus) +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=2 +# Question: test.example.com. IN A +1035 0100 +0001 0000 0000 0002 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR (1st) +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 +# EDNS0 OPT RR (2nd) +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire6 b/src/lib/dns/tests/testdata/message_fromWire6 new file mode 100644 index 0000000..2783fd0 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire6 @@ -0,0 +1,23 @@ +# +# A simple DNS query message with EDNS0 OPT RRs of non root name (bogus) +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +1035 0100 +0001 0000 0000 0001 +# Question: test.example.com. IN A +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "example.com" +#(7) e x a m p l e (3) c o m . + 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire7 b/src/lib/dns/tests/testdata/message_fromWire7 new file mode 100644 index 0000000..4d85314 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire7 @@ -0,0 +1,27 @@ +# +# A simple DNS query message with EDNS0 OPT RRs of compressed owner name +# pointing to root (is this bogus?) +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +#0 1 2 3 +1035 0100 +#4 5 6 7 8 9 10 1 +0001 0000 0000 0001 +# Question: test.example.com. IN A +# 2 3 4 5 6 7 8 9 20 1 2 3 4 5 6 7 8 9 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "example.com" +# pointer = 29 (end of question section) + c0 1d +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire8 b/src/lib/dns/tests/testdata/message_fromWire8 new file mode 100644 index 0000000..c950b5e --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire8 @@ -0,0 +1,22 @@ +# +# A simple DNS query message with a valid EDNS0 OPT RR (but unusual UDP size) +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +# Question: test.example.com. IN A +1035 0100 +0001 0000 0000 0001 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 500 +01f4 +# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO +0000 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_fromWire9 b/src/lib/dns/tests/testdata/message_fromWire9 new file mode 100644 index 0000000..f9ff950 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire9 @@ -0,0 +1,22 @@ +# +# A simple DNS query message with an unsupported version of EDNS0 +# ID = 0x1035 +# QR=0 (query), Opcode=0, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1 +# Question: test.example.com. IN A +1035 0100 +0001 0000 0000 0001 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# EDNS0 OPT RR +# owner name: "." +00 +# TYPE: OPT (41 = 0x29) +00 29 +# CLASS (= UDP size): 4096 +1000 +# TTL (extended RCODE and flags): RCODE=0, version=1, flags=DO +0001 8000 +# RDLEN = 0 +0000 diff --git a/src/lib/dns/tests/testdata/message_toText1.spec b/src/lib/dns/tests/testdata/message_toText1.spec new file mode 100644 index 0000000..b31310e --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText1.spec @@ -0,0 +1,24 @@ +# +# A standard DNS message (taken from an invocation of dig) +# + +[custom] +sections: header:question:a/1:ns:a/2 +[header] +id: 29174 +qr: 1 +aa: 1 +ancount: 1 +nscount: 1 +arcount: 1 +[question] +name: www.example.com +[a/1] +as_rr: True +rr_name: www.example.com +address: 192.0.2.80 +[ns] +as_rr: True +[a/2] +as_rr: True +rr_name: ns.example.com diff --git a/src/lib/dns/tests/testdata/message_toText1.txt b/src/lib/dns/tests/testdata/message_toText1.txt new file mode 100644 index 0000000..58c7239 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText1.txt @@ -0,0 +1,14 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29174 +;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1 + +;; QUESTION SECTION: +;www.example.com. IN A + +;; ANSWER SECTION: +www.example.com. 86400 IN A 192.0.2.80 + +;; AUTHORITY SECTION: +example.com. 86400 IN NS ns.example.com. + +;; ADDITIONAL SECTION: +ns.example.com. 86400 IN A 192.0.2.1 diff --git a/src/lib/dns/tests/testdata/message_toText1.wire b/src/lib/dns/tests/testdata/message_toText1.wire new file mode 100644 index 0000000..2a959bd --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText1.wire @@ -0,0 +1,28 @@ +### +### This data file was auto-generated from message_toText1.spec +### + +# Header Section +# ID=29174 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA +71f6 8400 +# QDCNT=1, ANCNT=1, NSCNT=1, ARCNT=1 +0001 0001 0001 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=www.example.com Class=IN(1) TTL=86400, RDLEN=4) +03777777076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.80 +c0000250 + +# NS RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=16) +076578616d706c6503636f6d00 0002 0001 00015180 0010 +# NS name=ns.example.com +026e73076578616d706c6503636f6d00 + +# A RR (QNAME=ns.example.com Class=IN(1) TTL=86400, RDLEN=4) +026e73076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 diff --git a/src/lib/dns/tests/testdata/message_toText2.spec b/src/lib/dns/tests/testdata/message_toText2.spec new file mode 100644 index 0000000..978aab3 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText2.spec @@ -0,0 +1,14 @@ +# +# A standard DNS message with EDNS (taken from an invocation of dig) +# + +[custom] +sections: header:question:edns +[header] +id: 45981 +qr: 1 +rcode: refused +arcount: 1 +[question] +[edns] +do: 1 diff --git a/src/lib/dns/tests/testdata/message_toText2.txt b/src/lib/dns/tests/testdata/message_toText2.txt new file mode 100644 index 0000000..42cc2c1 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText2.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 45981 +;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags: do; udp: 4096 + +;; QUESTION SECTION: +;example.com. IN A diff --git a/src/lib/dns/tests/testdata/message_toText2.wire b/src/lib/dns/tests/testdata/message_toText2.wire new file mode 100644 index 0000000..1047b63 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText2.wire @@ -0,0 +1,19 @@ +### +### This data file was auto-generated from message_toText2.spec +### + +# Header Section +# ID=45981 QR=Response Opcode=QUERY(0) Rcode=REFUSED(5) +b39d 8005 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1) +076578616d706c6503636f6d00 0001 0001 + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=0 DO=1 +00 0029 1000 0000 8000 +# RDLEN=0 +0000 diff --git a/src/lib/dns/tests/testdata/message_toText3.spec b/src/lib/dns/tests/testdata/message_toText3.spec new file mode 100644 index 0000000..a74ea1b --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText3.spec @@ -0,0 +1,31 @@ +# +# A standard DNS message with TSIG (taken from an invocation of dig) +# + +[custom] +sections: header:question:a/1:ns:a/2:tsig +[header] +id: 10140 +qr: 1 +aa: 1 +ancount: 1 +nscount: 1 +arcount: 2 +[question] +name: www.example.com +[a/1] +as_rr: True +rr_name: www.example.com +address: 192.0.2.80 +[ns] +as_rr: True +[a/2] +as_rr: True +rr_name: ns.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 1304384318 +original_id: 10140 +mac: 0x5257c80396f2fa95b20c77ae9a652fb2 diff --git a/src/lib/dns/tests/testdata/message_toText3.txt b/src/lib/dns/tests/testdata/message_toText3.txt new file mode 100644 index 0000000..359b9c5 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText3.txt @@ -0,0 +1,17 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10140 +;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2 + +;; QUESTION SECTION: +;www.example.com. IN A + +;; ANSWER SECTION: +www.example.com. 86400 IN A 192.0.2.80 + +;; AUTHORITY SECTION: +example.com. 86400 IN NS ns.example.com. + +;; ADDITIONAL SECTION: +ns.example.com. 86400 IN A 192.0.2.1 + +;; TSIG PSEUDOSECTION: +www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1304384318 300 16 UlfIA5by+pWyDHeummUvsg== 10140 NOERROR 0 diff --git a/src/lib/dns/tests/testdata/message_toText3.wire b/src/lib/dns/tests/testdata/message_toText3.wire new file mode 100644 index 0000000..eb3632b --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText3.wire @@ -0,0 +1,39 @@ +### +### This data file was auto-generated from message_toText3.spec +### + +# Header Section +# ID=10140 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA +279c 8400 +# QDCNT=1, ANCNT=1, NSCNT=1, ARCNT=2 +0001 0001 0001 0002 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=www.example.com Class=IN(1) TTL=86400, RDLEN=4) +03777777076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.80 +c0000250 + +# NS RR (QNAME=example.com Class=IN(1) TTL=86400, RDLEN=16) +076578616d706c6503636f6d00 0002 0001 00015180 0010 +# NS name=ns.example.com +026e73076578616d706c6503636f6d00 + +# A RR (QNAME=ns.example.com Class=IN(1) TTL=86400, RDLEN=4) +026e73076578616d706c6503636f6d00 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1304384318 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004dbf533e 012c +# MAC Size=16 MAC=(see hex) +0010 5257c80396f2fa95b20c77ae9a652fb2 +# Original-ID=10140 Error=0 +279c 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_toWire1 b/src/lib/dns/tests/testdata/message_toWire1 new file mode 100644 index 0000000..daeb85a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire1 @@ -0,0 +1,22 @@ +# +# A simple DNS query message +# ID = 0x1035 +# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=2, other COUNTS=0 +# Question: test.example.com. IN A +# Answer: +# test.example.com. 3600 IN A 192.0.2.1 +# test.example.com. 7200 IN A 192.0.2.2 +# +1035 8500 +0001 0002 0000 0000 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# same name, fully compressed +c0 0c +# TTL=3600, A, IN, RDLENGTH=4, RDATA +0001 0001 00000e10 0004 c0 00 02 01 +# mostly same, with the slight difference in RDATA +c0 0c +0001 0001 00000e10 0004 c0 00 02 02 diff --git a/src/lib/dns/tests/testdata/message_toWire2.spec b/src/lib/dns/tests/testdata/message_toWire2.spec new file mode 100644 index 0000000..d256052 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire2.spec @@ -0,0 +1,21 @@ +# +# A simple DNS query message with TSIG signed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_toWire2.wire b/src/lib/dns/tests/testdata/message_toWire2.wire new file mode 100644 index 0000000..a495253 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire2.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_toWire2.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_toWire3.spec b/src/lib/dns/tests/testdata/message_toWire3.spec new file mode 100644 index 0000000..c8e9453 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire3.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with EDNS and TSIG +# + +[custom] +sections: header:question:edns:tsig +[header] +id: 0x06cd +rd: 1 +arcount: 2 +[question] +name: www.example.com +[edns] +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4db60d1f +mac_size: 16 +mac: 0x93444053881c83d7eb120e86f25b369e +original_id: 0x06cd diff --git a/src/lib/dns/tests/testdata/message_toWire3.wire b/src/lib/dns/tests/testdata/message_toWire3.wire new file mode 100644 index 0000000..46808b9 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire3.wire @@ -0,0 +1,30 @@ +### +### This data file was auto-generated from message_toWire3.spec +### + +# Header Section +# ID=1741 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +06cd 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=2 +0001 0000 0000 0002 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# EDNS OPT RR +# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=0 DO=0 +00 0029 1000 0000 0000 +# RDLEN=0 +0000 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1303776543 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004db60d1f 012c +# MAC Size=16 MAC=(see hex) +0010 93444053881c83d7eb120e86f25b369e +# Original-ID=1741 Error=0 +06cd 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_toWire4.spec b/src/lib/dns/tests/testdata/message_toWire4.spec new file mode 100644 index 0000000..aab7e10 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire4.spec @@ -0,0 +1,27 @@ +# +# Truncated DNS response with TSIG signed +# This is expected to be a response to "fromWire17" +# + +[custom] +sections: header:question:tsig +[header] +id: 0x22c2 +rd: 1 +qr: 1 +aa: 1 +# It's "truncated": +tc: 1 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e179212 +mac_size: 16 +mac: 0x88adc3811d1d6bec7c684438906fc694 +original_id: 0x22c2 diff --git a/src/lib/dns/tests/testdata/message_toWire4.wire b/src/lib/dns/tests/testdata/message_toWire4.wire new file mode 100644 index 0000000..d2cda30 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire4.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from message_toWire4.spec +### + +# Header Section +# ID=8898 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA TC RD +22c2 8700 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=TXT(16) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0010 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1310167570 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004e179212 012c +# MAC Size=16 MAC=(see hex) +0010 88adc3811d1d6bec7c684438906fc694 +# Original-ID=8898 Error=0 +22c2 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_toWire5.spec b/src/lib/dns/tests/testdata/message_toWire5.spec new file mode 100644 index 0000000..e316833 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire5.spec @@ -0,0 +1,36 @@ +# +# A longest possible (without EDNS) DNS response with TSIG, i.e. total +# length should be 512 bytes. +# + +[custom] +sections: header:question:txt/1:txt/2:tsig +[header] +id: 0xd6e2 +rd: 1 +qr: 1 +aa: 1 +ancount: 2 +arcount: 1 +[question] +name: www.example.com +rrtype: TXT +[txt/1] +as_rr: True +# QNAME is fully compressed +rr_name: ptr=12 +string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde +[txt/2] +as_rr: True +# QNAME is fully compressed +rr_name: ptr=12 +string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0 +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4e17b38d +mac_size: 16 +mac: 0xbe2ba477373d2496891e2fda240ee4ec +original_id: 0xd6e2 diff --git a/src/lib/dns/tests/testdata/message_toWire5.wire b/src/lib/dns/tests/testdata/message_toWire5.wire new file mode 100644 index 0000000..ef7ee6e --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire5.wire @@ -0,0 +1,34 @@ +### +### This data file was auto-generated from message_toWire5.spec +### + +# Header Section +# ID=55010 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA RD +d6e2 8500 +# QDCNT=1, ANCNT=2, NSCNT=0, ARCNT=1 +0001 0002 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=TXT(16) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0010 0001 + +# TXT RR (QNAME=ptr=12 Class=IN(1) TTL=86400, RDLEN=256) +c00c 0010 0001 00015180 0100 +# String Len=255, String="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde" +ff 303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465 + +# TXT RR (QNAME=ptr=12 Class=IN(1) TTL=86400, RDLEN=114) +c00c 0010 0001 00015180 0072 +# String Len=113, String="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0" +71 3031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630313233343536373839616263646566303132333435363738396162636465663031323334353637383961626364656630 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1310176141 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004e17b38d 012c +# MAC Size=16 MAC=(see hex) +0010 be2ba477373d2496891e2fda240ee4ec +# Original-ID=55010 Error=0 +d6e2 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/message_toWire6 b/src/lib/dns/tests/testdata/message_toWire6 new file mode 100644 index 0000000..996c99c --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire6 @@ -0,0 +1,48 @@ +# +# A simple DNS query message (with a signed response) +# ID = 0x75c1 +# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=4, other COUNTS=0 +# Question: test.example.com. IN A +# Answer: +# test.example.com. 3600 IN A 192.0.2.1 +# test.example.com. 7200 IN A 192.0.2.2 +# +75c1 8500 +0001 0004 0000 0000 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0001 0001 +# same name, fully compressed +c0 0c +# TTL=3600, A, IN, RDLENGTH=4, RDATA +0001 0001 00000e10 0004 c0 00 02 01 +# mostly same, with the slight difference in RDATA +c0 0c +0001 0001 00000e10 0004 c0 00 02 02 + +# signature 1 + +# same name +c0 0c +# RRSIG, IN, TTL=3600, RDLENGTH=0x28 TYPE_COV=A ALGO=5 (RSA/SHA-1) LABELS=3 ORIG_TTL=3600 +002e 0001 00000e10 0028 0001 05 03 00000e10 +# SIG_EXPIRY=20000101000000 SIG_INCEP=20000201000000 KEY_ID=12345 +386d4380 38962200 3039 +#(7) e x a m p l e (3) c o m . + 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# FAKEFAKEFAKE +14 02 84 14 02 84 14 02 84 + +# signature 2 + +# same name +c0 0c +# RRSIG, IN, TTL=3600, RDLENGTH=0x28 TYPE_COV=A ALGO=3 (DSA/SHA-1) LABELS=3 ORIG_TTL=3600 +002e 0001 00000e10 0028 0001 03 03 00000e10 +# SIG_EXPIRY=20000101000000 SIG_INCEP=20000201000000 KEY_ID=12345 +386d4380 38962200 3039 +#(7) e x a m p l e (3) c o m . + 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# FAKEFAKEFAKE +14 02 84 14 02 84 14 02 84 diff --git a/src/lib/dns/tests/testdata/message_toWire7 b/src/lib/dns/tests/testdata/message_toWire7 new file mode 100644 index 0000000..ba22634 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire7 @@ -0,0 +1,35 @@ +# +# A simple DNS query message (with a signed response) +# ID = 0x75c1 +# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0) +# QDCOUNT=1, ANCOUNT=1, ADCOUNT=0 +# Question: test.example.com. IN TXT +# Answer: +# test.example.com. 3600 IN TXT aaaaa... +# +75c1 8700 +0001 0001 0000 0000 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +0010 0001 +# same name, fully compressed +c0 0c +# TTL=3600, TXT, IN, RDLENGTH=256, RDATA +0010 0001 00000e10 0100 ff +# 'a' repeated 255 times +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 diff --git a/src/lib/dns/tests/testdata/name_fromWire1 b/src/lib/dns/tests/testdata/name_fromWire1 new file mode 100644 index 0000000..42fc61d --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire1 @@ -0,0 +1,14 @@ +# +# a global14 compression pointer +# +000a85800001000300000003 +# V i x c o m +0356697803636f6d0000020001c00c00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire10 b/src/lib/dns/tests/testdata/name_fromWire10 new file mode 100644 index 0000000..65be775 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire10 @@ -0,0 +1,12 @@ +# +# Too large name; should trigger an exception. +# +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 040102030400 diff --git a/src/lib/dns/tests/testdata/name_fromWire11 b/src/lib/dns/tests/testdata/name_fromWire11 new file mode 100644 index 0000000..32184f6 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire11 @@ -0,0 +1,12 @@ +# +# A name with possible maximum number of labels; should be accepted safely. +# +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 0100010000 diff --git a/src/lib/dns/tests/testdata/name_fromWire12 b/src/lib/dns/tests/testdata/name_fromWire12 new file mode 100644 index 0000000..073adda --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire12 @@ -0,0 +1,13 @@ +# +# Wire format including an invalid label length +# +#(1) a (7) e x a m p l e + 01 61 07 65 78 61 6d 70 6c 65 +# invalid label length: 64 +40 +# a "label" of 64 characters: shouldn't be parsed +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f +10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f +20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f +30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f +00 diff --git a/src/lib/dns/tests/testdata/name_fromWire13 b/src/lib/dns/tests/testdata/name_fromWire13 new file mode 100644 index 0000000..447f54b --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire13 @@ -0,0 +1,5 @@ +# +# A name including all "printable" characters +# + +3f2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f1f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e00 diff --git a/src/lib/dns/tests/testdata/name_fromWire14 b/src/lib/dns/tests/testdata/name_fromWire14 new file mode 100644 index 0000000..3123aec --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire14 @@ -0,0 +1,7 @@ +# +# A name including all "non-printable" characters +# + +3f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f207f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c +3f9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadb +24dcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff00 diff --git a/src/lib/dns/tests/testdata/name_fromWire2 b/src/lib/dns/tests/testdata/name_fromWire2 new file mode 100644 index 0000000..0758a68 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire2 @@ -0,0 +1,15 @@ +# +# bogus label character (looks like a local compression pointer) +# +000a85800001000300000003 +#this is the bogus label character: +83 +76697803636f6d0000020001c00c00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire3_1 b/src/lib/dns/tests/testdata/name_fromWire3_1 new file mode 100644 index 0000000..e38efcc --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire3_1 @@ -0,0 +1,11 @@ +# +# a bad compression pointer starting with the bits 1111 (too big pointer) +# +000a85800001000300000003 +03766978 03636f6d 00 0002 0001 +f00c 0002 0001 0000 0e10 000b 056973727631 027061 c00c +c00c 0002 0001 0000 0e10 0009 066e732d657874 c00c +c00c 0002 0001 0000 0e10 000e 036e7331 04676e6163 03636f6d 00 +c025 0001 0001 0000 0e10 0004 cc98b886 +c03c 0001 0001 0000 0e10 0004 cc98b840 +c051 0001 0001 0002 a14a 0004 c697f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire3_2 b/src/lib/dns/tests/testdata/name_fromWire3_2 new file mode 100644 index 0000000..c377bb1 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire3_2 @@ -0,0 +1,13 @@ +# +# a bad compression pointer due to forward reference of 0x30 to +# another compression pointer with a valid backreference +# +000a85800001000300000003 +03766978 03636f6d 00 0002 0001 +#'30' is the forward reference, 'c00c' at the end is the valid pointer: +c030 0002 0001 0000 0e10 000b 056973727631 027061 c00c +c00c 0002 0001 0000 0e10 0009 066e732d657874 c00c +c00c 0002 0001 0000 0e10 000e 036e7331 04676e6163 03636f6d 00 +c025 0001 0001 0000 0e10 0004 cc98b886 +c03c 0001 0001 0000 0e10 0004 cc98b840 +c051 0001 0001 0002 a14a 0004 c697f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire4 b/src/lib/dns/tests/testdata/name_fromWire4 new file mode 100644 index 0000000..dba6035 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire4 @@ -0,0 +1,45 @@ +# +# invalid name length, pointer at offset 0x0226 points to +# long name at offset 0x25 +# +000a 8580 0001 0003 0000 0001 +03 766978 03 636f6d 00 0002 0001 +c00c 0002 0001 00000e10 +0101 +# long name starts here +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a 00 +# compression pointer start here and refers back to long name +c023 0002 0001 00000e10 0009 066e732d657874 c00c +c00c 0002 0001 00000e10 000e 036e733104676e616303636f6d00 +c025 0001 0001 00000e10 0004 cc98b886 diff --git a/src/lib/dns/tests/testdata/name_fromWire6 b/src/lib/dns/tests/testdata/name_fromWire6 new file mode 100644 index 0000000..fa1abe6 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire6 @@ -0,0 +1,14 @@ +# +# a bad pointer +# +000a85800001000300000003 +# the bad pointer is f00c (offset = 300c) which is too large +0376697803636f6d0000020001 f00c 00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire7 b/src/lib/dns/tests/testdata/name_fromWire7 new file mode 100644 index 0000000..2dedd4a --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire7 @@ -0,0 +1,6 @@ +# +# input ends unexpectedly +# +000a85800001000300000003 +# parser will start at the ending 'c0', which is an incomplete sequence. +0376697803636f6d0000020001 c0 diff --git a/src/lib/dns/tests/testdata/name_fromWire8 b/src/lib/dns/tests/testdata/name_fromWire8 new file mode 100644 index 0000000..575563d --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire8 @@ -0,0 +1,27 @@ +# +# many hops of compression. absolutely many, but should be decompressed. +# +000a85800001000300000013 +03 766978 03 636f6d 00 0002 0001 +c00c 0002 0001 00000e10 000b 056973727631027061 c00c +c019 0002 0001 00000e10 0009 066e732d657874 c00c +c030 0002 0001 00000e10 000e 036e7331 04676e6163 03636f6d 00 +c045 0001 0001 00000e10 0004 cc98b886 +c05f 0001 0001 00000e10 0004 cc98b840 +c06f 0001 0001 0002a14a 0004 c697f8f6 +c07f 0001 0001 0002a14a 0004 c697f8f6 +c08f 0001 0001 0002a14a 0004 c697f8f6 +c09f 0001 0001 0002a14a 0004 c697f8f6 +c0af 0001 0001 0002a14a 0004 c697f8f6 +c0bf 0001 0001 0002a14a 0004 c697f8f6 +c0cf 0001 0001 0002a14a 0004 c697f8f6 +c0df 0001 0001 0002a14a 0004 c697f8f6 +c0ef 0001 0001 0002a14a 0004 c697f8f6 +c0ff 0001 0001 0002a14a 0004 c697f8f6 +c10f 0001 0001 0002a14a 0004 c697f8f6 +c11f 0001 0001 0002a14a 0004 c697f8f6 +c12f 0001 0001 0002a14a 0004 c697f8f6 +c13f 0001 0001 0002a14a 0004 c697f8f6 +c14f 0001 0001 0002a14a 0004 c697f8f6 +c15f 0001 0001 0002a14a 0004 c697f8f6 +c16f 0001 0001 0002a14a 0004 c697f8f6 diff --git a/src/lib/dns/tests/testdata/name_fromWire9 b/src/lib/dns/tests/testdata/name_fromWire9 new file mode 100644 index 0000000..79b2978 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_fromWire9 @@ -0,0 +1,12 @@ +# +# A possible longest name; should be accepted safely. +# +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 0301020300 diff --git a/src/lib/dns/tests/testdata/name_toWire1 b/src/lib/dns/tests/testdata/name_toWire1 new file mode 100644 index 0000000..c06ec4b --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire1 @@ -0,0 +1,12 @@ +# +# Rendering 3 names with compression. [x] means a compression pointer pointing +# to offset 'x'. +# +#bytes: +# 0 1 2 3 4 5 6 7 8 9 a b c d e +#(1)a(7)e x a m p l e(3)c o m . + 0161076578616d706c6503636f6d00 +#(1)b [2] + 0162c002 +# a . e x a m p l e . o r g . + 0161076578616d706c65036f726700 diff --git a/src/lib/dns/tests/testdata/name_toWire2 b/src/lib/dns/tests/testdata/name_toWire2 new file mode 100644 index 0000000..2377121 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire2 @@ -0,0 +1,14 @@ +# +# Rendering names in a large buffer. [x] means a compression pointer pointing +# to offset 'x'. +# +#bytes: +#3f 40 +#ff 00 +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#[3fff] = a.example.com: can be compressed + ffff +#(1) b(7) e x a m p l e (3) c o m .; cannot compress as the pointer +# (0x4001) would exceed 0x4000 + 01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/tests/testdata/name_toWire3 b/src/lib/dns/tests/testdata/name_toWire3 new file mode 100644 index 0000000..08c1474 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire3 @@ -0,0 +1,14 @@ +# +# Rendering names including one explicitly uncompressed. +# [x] means a compression pointer pointing to offset 'x'. +# +# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (bytes) +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 + +#15 29 (bytes) +#(1) b (7) e x a m p l e (3) c o m .; specified to be not compressed, +# but can be pointed to from others + 01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#[0f] referring to the second (uncompressed name) + c0 0f diff --git a/src/lib/dns/tests/testdata/name_toWire4 b/src/lib/dns/tests/testdata/name_toWire4 new file mode 100644 index 0000000..740d718 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire4 @@ -0,0 +1,16 @@ +# +# Rendering 3 names with compression, including one resulting in a chain of +# pointers (the last one). +# legend: [x] means a compression pointer pointing to offset 'x'. +#bytes: +#00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 + +#0f 10 11 12 +#(1) b [2] (b.example.com.) + 01 62 c0 02 + +#13 14 +# [0f]: (b.example.com.) + c0 0f diff --git a/src/lib/dns/tests/testdata/name_toWire5.spec b/src/lib/dns/tests/testdata/name_toWire5.spec new file mode 100644 index 0000000..87e140d --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire5.spec @@ -0,0 +1,19 @@ +# +# A sequence of names that would be compressed case-sensitive manner. +# First name: "a.example.com" +# Second name: "b.eXample.com". Due to case-sensitive comparison only "com" +# can be compressed. +# Third name: "c.eXample.com". "eXample.com" part matches that of the second +# name and can be compressed. +# + +[custom] +sections: name/1:name/2:name/3 +[name/1] +name: a.example.com +[name/2] +name: b.eXample +pointer: 10 +[name/3] +name: c +pointer: 17 diff --git a/src/lib/dns/tests/testdata/name_toWire5.wire b/src/lib/dns/tests/testdata/name_toWire5.wire new file mode 100644 index 0000000..c6e62ed --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire5.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from name_toWire5.spec +### + +# DNS Name: a.example.com +0161076578616d706c6503636f6d00 + +# DNS Name: b.eXample + compression pointer: 10 +0162076558616d706c65c00a + +# DNS Name: c + compression pointer: 17 +0163c011 diff --git a/src/lib/dns/tests/testdata/name_toWire6.spec b/src/lib/dns/tests/testdata/name_toWire6.spec new file mode 100644 index 0000000..a536f5d --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire6.spec @@ -0,0 +1,19 @@ +# +# A sequence of names that would be compressed both case-sensitive and +# case-insensitive manner (unusual, but allowed). +# First and second name: see name_toWire5.spec. +# Third name: "c.b.EXAMPLE.com". This is rendered with case-insensitive +# compression, so "b.EXAMPLE.com" part of the name matches that of the +# second name. +# + +[custom] +sections: name/1:name/2:name/3 +[name/1] +name: a.example.com +[name/2] +name: b.eXample +pointer: 10 +[name/3] +name: c +pointer: 15 diff --git a/src/lib/dns/tests/testdata/name_toWire6.wire b/src/lib/dns/tests/testdata/name_toWire6.wire new file mode 100644 index 0000000..dcaa39f --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire6.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from name_toWire6.spec +### + +# DNS Name: a.example.com +0161076578616d706c6503636f6d00 + +# DNS Name: b.eXample + compression pointer: 10 +0162076558616d706c65c00a + +# DNS Name: c + compression pointer: 15 +0163c00f diff --git a/src/lib/dns/tests/testdata/name_toWire7 b/src/lib/dns/tests/testdata/name_toWire7 new file mode 100644 index 0000000..bff599f --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire7 @@ -0,0 +1,10 @@ +# +# Rendering names including one explicitly uncompressed. +# [x] means a compression pointer pointing to offset 'x'. +# +# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (bytes) +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 + +#[02] pointing to -> "example.com." + c0 02 diff --git a/src/lib/dns/tests/testdata/name_toWire8 b/src/lib/dns/tests/testdata/name_toWire8 new file mode 100644 index 0000000..d01093b --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire8 @@ -0,0 +1,7 @@ +# +# Rendering names. +# [x] means a compression pointer pointing to offset 'x'. +# +# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (bytes) +#(1) a (7) e x a m p l e (3) c o m + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/name_toWire9 b/src/lib/dns/tests/testdata/name_toWire9 new file mode 100644 index 0000000..51a1987 --- /dev/null +++ b/src/lib/dns/tests/testdata/name_toWire9 @@ -0,0 +1,13 @@ +# +# Rendering names including one explicitly uncompressed. +# [x] means a compression pointer pointing to offset 'x'. +# +# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (bytes) +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#(1) a (7) e x a m p l e (3) c o m + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d +#(1) a (7) e x a m p l e + 01 61 07 65 78 61 6d 70 6c 65 +#[1f] pointing to ^^ "example" + c0 1f diff --git a/src/lib/dns/tests/testdata/omitcheck.txt b/src/lib/dns/tests/testdata/omitcheck.txt new file mode 100644 index 0000000..580cab4 --- /dev/null +++ b/src/lib/dns/tests/testdata/omitcheck.txt @@ -0,0 +1 @@ + 1H IN A 192.0.2.1 diff --git a/src/lib/dns/tests/testdata/origincheck.txt b/src/lib/dns/tests/testdata/origincheck.txt new file mode 100644 index 0000000..c370ed2 --- /dev/null +++ b/src/lib/dns/tests/testdata/origincheck.txt @@ -0,0 +1,5 @@ +; We change the origin here. We want to check it is not propagated +; outside of the included file. +$ORIGIN www.example.org. + +@ 1H IN A 192.0.2.1 diff --git a/src/lib/dns/tests/testdata/question_fromWire b/src/lib/dns/tests/testdata/question_fromWire new file mode 100644 index 0000000..cbc28c0 --- /dev/null +++ b/src/lib/dns/tests/testdata/question_fromWire @@ -0,0 +1,33 @@ +# +# Wire-format data of a sequence of DNS questions. +# foo.example.com. IN NS +# bar.example.com. CH A (owner name is compressed) +# and some pathological cases +# +# 0 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 (-th byte) +#(3) f o o (7) e x a m p l e (3) c o m . + 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#7 8 9 20 +# type/class: NS = 2, IN = 1 +00 02 00 01 + +# 1 2 3 4 5 6 +#(3) b a r [ptr=0x04] + 03 62 61 72 c0 04 +#7 8 9 30 +# type/class: A = 1, CH = 3 +00 01 00 03 + +# owner name is broken +#1 +# invalid label type +ff +#2 3 4 5 +#type/class IN/A +00 01 00 01 + +# short buffer +# (root name) +00 +#class is missing +00 01 diff --git a/src/lib/dns/tests/testdata/question_toWire1 b/src/lib/dns/tests/testdata/question_toWire1 new file mode 100644 index 0000000..77886db --- /dev/null +++ b/src/lib/dns/tests/testdata/question_toWire1 @@ -0,0 +1,14 @@ +# +# Rendering two DNS Questions without name compression +# foo.example.com. IN NS +# bar.example.com. CH A +# +#(3) f o o (7) e x a m p l e (3) c o m . + 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: NS = 2, IN = 1 +00 02 00 01 +#(3) b a r (7) e x a m p l e (3) c o m . + 03 62 61 72 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#7 8 9 30 +# type/class: A = 1, CH = 3 +00 01 00 03 diff --git a/src/lib/dns/tests/testdata/question_toWire2 b/src/lib/dns/tests/testdata/question_toWire2 new file mode 100644 index 0000000..9117ab2 --- /dev/null +++ b/src/lib/dns/tests/testdata/question_toWire2 @@ -0,0 +1,14 @@ +# +# Rendering two DNS Questions with name compression +# foo.example.com. IN NS +# bar.example.com. CH A +# +# 0 1 2 3 4 ... (-th byte) +#(3) f o o (7) e x a m p l e (3) c o m . + 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: NS = 2, IN = 1 +00 02 00 01 +#(3) b a r [ptr=0x04] + 03 62 61 72 c0 04 +# type/class: A = 1, CH = 3 +00 01 00 03 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec new file mode 100644 index 0000000..f831313 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec @@ -0,0 +1,3 @@ +[custom] +sections: afsdb +[afsdb] diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire new file mode 100644 index 0000000..b32776b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_afsdb_fromWire1.spec +### + +# AFSDB RDATA, RDLEN=21 +0015 +# SUBTYPE=1 SERVER=afsdb.example.com +0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec new file mode 100644 index 0000000..f33e768 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec @@ -0,0 +1,6 @@ +[custom] +sections: name:afsdb +[name] +name: example.com +[afsdb] +server: afsdb.ptr=0 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire new file mode 100644 index 0000000..fdc53c5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire @@ -0,0 +1,11 @@ +### +### This data file was auto-generated from rdata_afsdb_fromWire2.spec +### + +# DNS Name: example.com +076578616d706c6503636f6d00 + +# AFSDB RDATA, RDLEN=10 +000a +# SUBTYPE=1 SERVER=afsdb.ptr=0 +0001 056166736462c000 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec new file mode 100644 index 0000000..993032f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire new file mode 100644 index 0000000..7ea4342 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_afsdb_fromWire3.spec +### + +# AFSDB RDATA, RDLEN=3 +0003 +# SUBTYPE=1 SERVER=afsdb.example.com +0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec new file mode 100644 index 0000000..37abf13 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire new file mode 100644 index 0000000..79ff6c2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_afsdb_fromWire4.spec +### + +# AFSDB RDATA, RDLEN=80 +0050 +# SUBTYPE=1 SERVER=afsdb.example.com +0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec new file mode 100644 index 0000000..0ea79dd --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +server: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire new file mode 100644 index 0000000..13eb016 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_afsdb_fromWire5.spec +### + +# AFSDB RDATA, RDLEN=71 +0047 +# SUBTYPE=1 SERVER="01234567890123456789012345678901234567890123456789012345678901234" +0001 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec new file mode 100644 index 0000000..1946458 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec @@ -0,0 +1,4 @@ +[custom] +sections: afsdb +[afsdb] +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire new file mode 100644 index 0000000..7746fa6 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_afsdb_toWire1.spec +### + +# AFSDB RDATA + +# SUBTYPE=1 SERVER=afsdb.example.com +0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec new file mode 100644 index 0000000..c80011a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec @@ -0,0 +1,8 @@ +[custom] +sections: name:afsdb +[name] +name: example.com. +[afsdb] +subtype: 0 +server: root.example.com +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire new file mode 100644 index 0000000..7f01512 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire @@ -0,0 +1,11 @@ +### +### This data file was auto-generated from rdata_afsdb_toWire2.spec +### + +# DNS Name: example.com. +076578616d706c6503636f6d00 + +# AFSDB RDATA + +# SUBTYPE=0 SERVER=root.example.com +0000 04726f6f74076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec new file mode 100644 index 0000000..d21987c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec @@ -0,0 +1,6 @@ +# +# The simplest form of CAA: all default parameters +# +[custom] +sections: caa +[caa] diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire new file mode 100644 index 0000000..780bd8d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_caa_fromWire1.spec +### + +# CAA RDATA, RDLEN=21 +0015 +# FLAGS=0 TAG=issue VALUE=ca.example.net +00 05 697373756563612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec new file mode 100644 index 0000000..867e43d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec @@ -0,0 +1,7 @@ +# +# Mixed case CAA tag field. +# +[custom] +sections: caa +[caa] +tag: 'ISSue' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire new file mode 100644 index 0000000..09ca24d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_caa_fromWire2.spec +### + +# CAA RDATA, RDLEN=21 +0015 +# FLAGS=0 TAG=ISSue VALUE=ca.example.net +00 05 495353756563612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec new file mode 100644 index 0000000..9297151 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec @@ -0,0 +1,7 @@ +# +# Missing CAA value field. +# +[custom] +sections: caa +[caa] +value: '' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire new file mode 100644 index 0000000..16b5c43 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_caa_fromWire3.spec +### + +# CAA RDATA, RDLEN=7 +0007 +# FLAGS=0 TAG=issue VALUE= +00 05 6973737565 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec new file mode 100644 index 0000000..53e16b1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec @@ -0,0 +1,7 @@ +# +# Missing CAA value field. +# +[custom] +sections: caa +[caa] +tag: '' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire new file mode 100644 index 0000000..3225e60 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_caa_fromWire4.spec +### + +# CAA RDATA, RDLEN=16 +0010 +# FLAGS=0 TAG= VALUE=ca.example.net +00 00 63612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire5 b/src/lib/dns/tests/testdata/rdata_caa_fromWire5 new file mode 100644 index 0000000..123011f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire5 @@ -0,0 +1,6 @@ +# Test where CAA value field is shorter than the RDATA length + +# CAA RDATA, RDLEN=32 +0020 +# FLAGS=0 TAG=c VALUE=ca.example.net +00 01 63 63612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire6 b/src/lib/dns/tests/testdata/rdata_caa_fromWire6 new file mode 100644 index 0000000..1a35a1a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_caa_fromWire6 @@ -0,0 +1,4 @@ +# Test where RDATA is completely missing + +# CAA RDATA, RDLEN=32 +0020 diff --git a/src/lib/dns/tests/testdata/rdata_cname_fromWire b/src/lib/dns/tests/testdata/rdata_cname_fromWire new file mode 100644 index 0000000..57eae13 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_cname_fromWire @@ -0,0 +1,44 @@ +# +# various kinds of CNAME RDATA stored in an input buffer +# +# Valid non-compressed RDATA for cn.example.com. +# RDLENGTH=16 bytes +# 0 1 + 00 10 +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) +#(2) c n (7) e x a m p l e (3) c o m . + 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# short length +# 8 9 + 00 0f +#20 1 2 3 4 5 6 7 8 9 30 1 2 3 4 5 + 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# length too long +# 6 7 + 00 11 +# +# 8 9 40 1 2 3 4 5 6 7 8 9 50 1 2 3 4 + 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 +# +# Valid compressed CNAME name: 'cn2' + pointer +# 5 6 + 00 06 +# 7 8 9 60 1 2 +#(3) c n 2 ptr=5 + 03 63 6e 32 c0 05 +# +# Valid compressed CNAME name but RDLENGTH is incorrect: it must be the length +# of the sequence from the head to the pointer, not the decompressed name +# length. +# 3 4 + 00 11 +# 5 6 7 8 9 70 + 03 63 6e 32 c0 05 +# incomplete name (no trailing dot). this can be tested only at the end of +# the buffer. +# 1 2 + 00 0f +# 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 + 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/rdata_dhcid_fromWire b/src/lib/dns/tests/testdata/rdata_dhcid_fromWire new file mode 100644 index 0000000..b28b5b3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dhcid_fromWire @@ -0,0 +1,12 @@ +# +# DHCID RDATA stored in an input buffer +# +# Valid RDATA for 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA= +# +# RDLENGTH=41 bytes +# 0 1 + 00 29 +# 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA= +d0 b2 20 d0 bb d0 b5 d1 81 d1 83 20 d1 80 d0 be +d0 b4 d0 b8 d0 bb d0 b0 d1 81 d1 8c 20 d1 91 d0 +bb d0 be d1 87 d0 ba d0 b0 diff --git a/src/lib/dns/tests/testdata/rdata_dhcid_toWire b/src/lib/dns/tests/testdata/rdata_dhcid_toWire new file mode 100644 index 0000000..99ec229 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dhcid_toWire @@ -0,0 +1,7 @@ +# +# DHCID RDATA stored in an output buffer +# +# 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA= +d0 b2 20 d0 bb d0 b5 d1 81 d1 83 20 d1 80 d0 be +d0 b4 d0 b8 d0 bb d0 b0 d1 81 d1 8c 20 d1 91 d0 +bb d0 be d1 87 d0 ba d0 b0 diff --git a/src/lib/dns/tests/testdata/rdata_dname_fromWire b/src/lib/dns/tests/testdata/rdata_dname_fromWire new file mode 100644 index 0000000..1c899bf --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dname_fromWire @@ -0,0 +1,44 @@ +# +# various kinds of DNAME RDATA stored in an input buffer +# +# Valid non-compressed RDATA for dn.example.com. +# RDLENGTH=16 bytes +# 0 1 + 00 10 +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) +#(2) d n (7) e x a m p l e (3) c o m . + 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# short length +# 8 9 + 00 0f +#20 1 2 3 4 5 6 7 8 9 30 1 2 3 4 5 + 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# length too long +# 6 7 + 00 11 +# +# 8 9 40 1 2 3 4 5 6 7 8 9 50 1 2 3 4 + 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 +# +# Valid compressed DNAME name: 'dn2' + pointer +# 5 6 + 00 06 +# 7 8 9 60 1 2 +#(3) d n 2 ptr=5 + 03 64 6e 32 c0 05 +# +# Valid compressed DNAME name but RDLENGTH is incorrect: it must be the length +# of the sequence from the head to the pointer, not the decompressed name +# length. +# 3 4 + 00 11 +# 5 6 7 8 9 70 + 03 64 6e 32 c0 05 +# incomplete name (no trailing dot). this can be tested only at the end of +# the buffer. +# 1 2 + 00 0f +# 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 + 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec new file mode 100644 index 0000000..b65271d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec @@ -0,0 +1,7 @@ +# DNSKEY test data with empty digest + +[custom] +sections: dnskey + +[dnskey] +digest: diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire new file mode 100644 index 0000000..35388b1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_dnskey_empty_keydata_fromWire.spec +### + +# DNSKEY RDATA, RDLEN=4 +0004 +# FLAGS=257 +0101 +# PROTOCOL=3 +03 +# ALGORITHM=5 +05 +# DIGEST= + diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec new file mode 100644 index 0000000..87e66db --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec @@ -0,0 +1,7 @@ +# DNSKEY test data + +[custom] +sections: dnskey + +[dnskey] +digest: BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGrhhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy347cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQzBkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bByBNsO70aEFTd diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire new file mode 100644 index 0000000..0a2e41f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_dnskey_fromWire.spec +### + +# DNSKEY RDATA, RDLEN=265 +0109 +# FLAGS=257 +0101 +# PROTOCOL=3 +03 +# ALGORITHM=5 +05 +# DIGEST=BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGrhhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy347cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQzBkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bByBNsO70aEFTd +0440000003a11d00c1ae141bb69860ab6c10529110e6de03b541f1a0c545bb68562c332fa0e3115e31ab86109e16f0198a1ef22477fc6467d6ea1777f215c6ff1ca56023ba2aba5b7688f0c7c60c5cb039fe403ebb9d1620bf1947547a2936ec61531ffd0c7946235b3c2970faf4fe53c79710998edb48c84b550b82acb7e3b701075ccc9e7cffe0b26903475af426ca8f7036e784f9d79b0d20c730b01f3fdbed84eb7ff366b4330648f406b37ff417b18e98a4b378d18596ad12c5e7ddd4f2e3b474f548b1e56709b7ec73a99efecacc8b28e39e752dfd67b4839ac9f6780d052ad429c00e8b5de1b6c3e8f19b0de803c95552011ffebcde0bf6c1c8136c3bbd1a1054dd diff --git a/src/lib/dns/tests/testdata/rdata_ds_fromWire b/src/lib/dns/tests/testdata/rdata_ds_fromWire new file mode 100644 index 0000000..81c412c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_ds_fromWire @@ -0,0 +1,6 @@ +# RDLENGTH 36 bytes +00 24 +# DS record, keyid 12892 +32 5c 05 02 f1 e1 84 c0 e1 d6 15 d2 0e b3 c2 23 +ac ed 3b 03 c7 73 dd 95 2d 5f 0e b5 c7 77 58 6d +e1 8d a6 b5 diff --git a/src/lib/dns/tests/testdata/rdata_in_a_fromWire b/src/lib/dns/tests/testdata/rdata_in_a_fromWire new file mode 100644 index 0000000..12f508b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_in_a_fromWire @@ -0,0 +1,19 @@ +# +# various kinds of IN/A RDATA stored in an input buffer +# +# valid RDATA for 192.0.2.1 +# +# 0 1 2 3 4 5 (bytes) + 00 04 c0 00 02 01 +# +# short length +# 6 7 8 9 10 11 (bytes) + 00 03 c0 00 02 01 +# +# length too long +#12 13 14 15 16 17 18 + 00 05 c0 00 02 01 00 +# +# short buffer (this can be tested only at the end of the buffer) +#19 20 21 22 23 + 00 04 c0 00 02 diff --git a/src/lib/dns/tests/testdata/rdata_in_aaaa_fromWire b/src/lib/dns/tests/testdata/rdata_in_aaaa_fromWire new file mode 100644 index 0000000..22fdd1f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_in_aaaa_fromWire @@ -0,0 +1,18 @@ +# +# various kinds of IN/AAAA RDATA stored in an input buffer +# +# valid RDATA for 2001:db8::1234 +# +#RDLENGTH=16 +0010 +#IPv6 address (18 bytes) +2001 0db8 0000 0000 0000 0000 0000 1234 +# +# short length (36 bytes) +0008 2001 0db8 0000 0000 0000 0000 0000 1234 +# +# length too long (55 bytes) +0011 2001 0db8 0000 0000 0000 0000 0000 1234 ff +# +# short buffer (this can be tested only at the end of the buffer) +0010 2001 0db8 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec new file mode 100644 index 0000000..2c43db0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec @@ -0,0 +1,3 @@ +[custom] +sections: minfo +[minfo] diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire new file mode 100644 index 0000000..3483e9b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire1.spec +### + +# MINFO RDATA, RDLEN=44 +002c +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec new file mode 100644 index 0000000..d781cac --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec @@ -0,0 +1,7 @@ +[custom] +sections: name:minfo +[name] +name: a.example.com. +[minfo] +rmailbox: rmailbox.ptr=02 +emailbox: emailbox.ptr=02 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire new file mode 100644 index 0000000..d79e936 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire @@ -0,0 +1,11 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire2.spec +### + +# DNS Name: a.example.com. +0161076578616d706c6503636f6d00 + +# MINFO RDATA, RDLEN=22 +0016 +# RMAILBOX=rmailbox.ptr=02 EMAILBOX=emailbox.ptr=02 +08726d61696c626f78c002 08656d61696c626f78c002 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec new file mode 100644 index 0000000..a1d4b76 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +# rdlength too short +[minfo] +emailbox: emailbox.ptr=11 +rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire new file mode 100644 index 0000000..55df1a2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire3.spec +### + +# MINFO RDATA, RDLEN=3 +0003 +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=11 +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c00b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec new file mode 100644 index 0000000..269a6ce --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +# rdlength too long +[minfo] +emailbox: emailbox.ptr=11 +rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire new file mode 100644 index 0000000..746f55a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire4.spec +### + +# MINFO RDATA, RDLEN=80 +0050 +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=11 +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c00b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec new file mode 100644 index 0000000..3a888e3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +# bogus rmailbox name +[minfo] +rmailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire new file mode 100644 index 0000000..dacd9a0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire5.spec +### + +# MINFO RDATA, RDLEN=91 +005b +# RMAILBOX="01234567890123456789012345678901234567890123456789012345678901234" EMAILBOX=emailbox.example.com +432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec new file mode 100644 index 0000000..c75ed8e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +# bogus emailbox name +[minfo] +emailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire new file mode 100644 index 0000000..4199fee --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_fromWire6.spec +### + +# MINFO RDATA, RDLEN=91 +005b +# RMAILBOX=rmailbox.example.com EMAILBOX="01234567890123456789012345678901234567890123456789012345678901234" +08726d61696c626f78076578616d706c6503636f6d00 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec new file mode 100644 index 0000000..7b340a3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec @@ -0,0 +1,5 @@ +[custom] +sections: minfo +[minfo] +emailbox: emailbox.ptr=09 +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire new file mode 100644 index 0000000..8ac4afe --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_toWire1.spec +### + +# MINFO RDATA + +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=09 +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c009 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec new file mode 100644 index 0000000..132f118 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec @@ -0,0 +1,6 @@ +[custom] +sections: minfo +[minfo] +rmailbox: root.example.com. +emailbox: emailbox.ptr=05 +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire new file mode 100644 index 0000000..7fb847c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_toWire2.spec +### + +# MINFO RDATA + +# RMAILBOX=root.example.com. EMAILBOX=emailbox.ptr=05 +04726f6f74076578616d706c6503636f6d00 08656d61696c626f78c005 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec new file mode 100644 index 0000000..d99a381 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec @@ -0,0 +1,7 @@ +# +# A simplest form of MINFO: all default parameters +# +[custom] +sections: minfo +[minfo] +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire new file mode 100644 index 0000000..6de233e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_toWireUncompressed1.spec +### + +# MINFO RDATA + +# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com +08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec new file mode 100644 index 0000000..0f78fcc --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec @@ -0,0 +1,8 @@ +# +# A simplest form of MINFO: custom rmailbox and default emailbox +# +[custom] +sections: minfo +[minfo] +rmailbox: root.example.com. +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire new file mode 100644 index 0000000..51e9e35 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_minfo_toWireUncompressed2.spec +### + +# MINFO RDATA + +# RMAILBOX=root.example.com. EMAILBOX=emailbox.example.com +04726f6f74076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_fromWire b/src/lib/dns/tests/testdata/rdata_mx_fromWire new file mode 100644 index 0000000..8e09154 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_mx_fromWire @@ -0,0 +1,15 @@ +# +# various kinds of MX RDATA stored in an input buffer +# +# Valid RDATA for "10 mail.example.com" +# +# RDLENGTH=18 bytes +# 0 1 + 00 12 +# 2 3 +# PREFERENCE: 10 + 00 0a +# EXCHANGE: non compressed +# 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 (bytes) +#(2) m x (7) e x a m p l e (3) c o m . + 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire1 b/src/lib/dns/tests/testdata/rdata_mx_toWire1 new file mode 100644 index 0000000..3049373 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_mx_toWire1 @@ -0,0 +1,12 @@ +# +# compressed MX RDATA stored in an output buffer +# +# sentinel name: example.com. +# 0 1 2 3 4 5 6 7 8 9 10 1 2 (bytes) +#(7) e x a m p l e (3) c o m . + 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# PREFERENCE: 10 + 00 0a +# EXCHANGE: compressed +#(4) m x ptr=0 + 02 6d 78 c0 00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire2 b/src/lib/dns/tests/testdata/rdata_mx_toWire2 new file mode 100644 index 0000000..ebd2f27 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_mx_toWire2 @@ -0,0 +1,12 @@ +# +# compressed MX RDATA stored in an output buffer +# +# sentinel name: example.com. +# 0 1 2 3 4 5 6 7 8 9 10 1 2 (bytes) +#(7) e x a m p l e (3) c o m . + 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# PREFERENCE: 10 + 00 0a +# EXCHANGE: not compressed +#(4) m x ptr=0 + 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/tests/testdata/rdata_ns_fromWire b/src/lib/dns/tests/testdata/rdata_ns_fromWire new file mode 100644 index 0000000..973365f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_ns_fromWire @@ -0,0 +1,44 @@ +# +# various kinds of NS RDATA stored in an input buffer +# +# Valid non-compressed RDATA for ns.example.com. +# RDLENGTH=16 bytes +# 0 1 + 00 10 +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) +#(2) n s (7) e x a m p l e (3) c o m . + 02 6e 73 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# short length +# 8 9 + 00 0f +#20 1 2 3 4 5 6 7 8 9 30 1 2 3 4 5 + 02 6e 73 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# length too long +# 6 7 + 00 11 +# +# 8 9 40 1 2 3 4 5 6 7 8 9 50 1 2 3 4 + 02 6e 73 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 +# +# Valid compressed NS name: 'ns2' + pointer +# 5 6 + 00 06 +# 7 8 9 60 1 2 +#(3) n s 2 ptr=5 + 03 6e 73 32 c0 05 +# +# Valid compressed NS name but RDLENGTH is incorrect: it must be the length +# of the sequence from the head to the pointer, not the decompressed name +# length. +# 3 4 + 00 11 +# 5 6 7 8 9 70 + 03 6e 73 32 c0 05 +# incomplete name (no trailing dot). this can be tested only at the end of +# the buffer. +# 1 2 + 00 0f +# 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 + 02 6e 73 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 new file mode 100644 index 0000000..1dd5f52 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 @@ -0,0 +1,7 @@ +# RDLENGTH, 39 bytes +00 27 +# NSEC3 record: +# 1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS SOA RRSIG DNSKEY NSEC3PARAM +01 01 00 01 04 d3 99 ea ab 14 8a 77 c7 ac ef cb +c5 54 46 03 2b 2d 96 1c c5 eb 68 21 ef 26 00 07 +22 00 00 00 00 02 90 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec new file mode 100644 index 0000000..39a78d7 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec @@ -0,0 +1,7 @@ +# +# A malformed NSEC3 RDATA: bit map length is too large, causing overflow +# + +[custom] +sections: nsec3 +[nsec3] diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec new file mode 100644 index 0000000..30417f5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC3 RDATA: a bitmap block containing empty bytes +# + +[custom] +sections: nsec3 +[nsec3] +bitmap: '01000000' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire new file mode 100644 index 0000000..99c5afd --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire10.spec +### + +# NSEC3 RDATA, RDLEN=37 +0025 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=4 +00 04 01000000 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec new file mode 100644 index 0000000..80ec59f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC3 RDATA: Saltlen is too large +# + +[custom] +sections: nsec3 +[nsec3] +rdlen: 7 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire new file mode 100644 index 0000000..48dc589 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire11.spec +### + +# NSEC3 RDATA, RDLEN=7 +0007 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec new file mode 100644 index 0000000..1e01655 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec @@ -0,0 +1,9 @@ +# +# An invalid NSEC3 RDATA: Hash length is too large +# + +[custom] +sections: nsec3 +[nsec3] +# only contains the first byte of hash +rdlen: 12 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire new file mode 100644 index 0000000..e880d55 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire12.spec +### + +# NSEC3 RDATA, RDLEN=12 +000c +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec new file mode 100644 index 0000000..fcc9d53 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec @@ -0,0 +1,9 @@ +# +# A valid (but unusual) NSEC3 RDATA: salt is empty. +# + +[custom] +sections: nsec3 +[nsec3] +saltlen: 0 +salt: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire new file mode 100644 index 0000000..86a06c5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire13.spec +### + +# NSEC3 RDATA, RDLEN=34 +0022 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=0, Salt='' +00 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec new file mode 100644 index 0000000..a0550d5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec @@ -0,0 +1,9 @@ +# +# An invalid NSEC3 RDATA: empty hash +# + +[custom] +sections: nsec3 +[nsec3] +hashlen: 0 +hash: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire new file mode 100644 index 0000000..9d76b5a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire14.spec +### + +# NSEC3 RDATA, RDLEN=19 +0013 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=0, Hash='' +00 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec new file mode 100644 index 0000000..4993e03 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec @@ -0,0 +1,10 @@ +# +# NSEC3 RDATA with empty type bitmap. It's okay. +# The test data includes bytes for a bitmap field, but RDLEN indicates +# it's not part of the RDATA and so it will be ignored. +# + +[custom] +sections: nsec3 +[nsec3] +rdlen: 31 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire new file mode 100644 index 0000000..bd636a7 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire15.spec +### + +# NSEC3 RDATA, RDLEN=31 +001f +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec new file mode 100644 index 0000000..dac14ea --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec @@ -0,0 +1,8 @@ +# +# NSEC3 RDATA with an empty bitmap +# + +[custom] +sections: nsec3 +[nsec3] +nbitmap: 0 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire new file mode 100644 index 0000000..bab957e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire16.spec +### + +# NSEC3 RDATA, RDLEN=31 +001f +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec new file mode 100644 index 0000000..4253349 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC3 RDATA: RDLEN is too short to include the hash len field. +# + +[custom] +sections: nsec3 +[nsec3] +rdlen: 10 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire new file mode 100644 index 0000000..f7972a0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire17.spec +### + +# NSEC3 RDATA, RDLEN=10 +000a +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec new file mode 100644 index 0000000..f35de83 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec @@ -0,0 +1,9 @@ +# +# A malformed NSEC3 RDATA: RDLEN indicates it doesn't even contain the fixed +# 5 octets +# + +[custom] +sections: nsec3 +[nsec3] +rdlen: 4 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire new file mode 100644 index 0000000..ec14a58 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire2.spec +### + +# NSEC3 RDATA, RDLEN=4 +0004 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=6 +00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 new file mode 100644 index 0000000..a0c8f59 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 @@ -0,0 +1,6 @@ +# RDLENGTH, 39 bytes +00 27 +# NSEC3 record with a broken type bitmap +01 01 00 01 04 d3 99 ea ab 14 8a 77 c7 ac ef cb +c5 54 46 03 2b 2d 96 1c c5 eb 68 21 ef 26 00 ff +22 00 00 00 00 02 90 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec new file mode 100644 index 0000000..06d6eb4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec @@ -0,0 +1,9 @@ +# +# A malformed NSEC3 RDATA: bit map length is too large, causing overflow +# + +[custom] +sections: nsec3 +[nsec3] +maplen: 31 +bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire new file mode 100644 index 0000000..527860c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire4.spec +### + +# NSEC3 RDATA, RDLEN=34 +0022 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=31 +00 1f 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec new file mode 100644 index 0000000..2d5713c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec @@ -0,0 +1,13 @@ +# +# A malformed NSEC3 RDATA: incomplete bit map field +# + +[custom] +sections: nsec3 +[nsec3] +# only containing the block field of the bitmap +rdlen: 32 +#dummy data +maplen: 31 +#dummy data +bitmap: '00' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire new file mode 100644 index 0000000..97b798b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire5.spec +### + +# NSEC3 RDATA, RDLEN=32 +0020 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=31 +00 1f 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec new file mode 100644 index 0000000..36e9e59 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec @@ -0,0 +1,11 @@ +# +# A malformed NSEC3 RDATA: bit map length being 0 +# + +[custom] +sections: nsec3 +[nsec3] +rdlen: 33 +maplen: 0 +# dummy data: +bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire new file mode 100644 index 0000000..4cf1189 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire6.spec +### + +# NSEC3 RDATA, RDLEN=33 +0021 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=0 +00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec new file mode 100644 index 0000000..338c0c9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec @@ -0,0 +1,9 @@ +# +# NSEC3 RDATA with a longest bitmap field (32 bitmap bytes) +# + +[custom] +sections: nsec3 +[nsec3] +maplen: 32 +bitmap: '0101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire new file mode 100644 index 0000000..1fddd58 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire7.spec +### + +# NSEC3 RDATA, RDLEN=65 +0041 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=32 +00 20 0101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec new file mode 100644 index 0000000..041714e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec @@ -0,0 +1,9 @@ +# +# An invalid NSEC3 RDATA with an oversized bitmap field (33 bitmap bytes) +# + +[custom] +sections: nsec3 +[nsec3] +maplen: 33 +bitmap: '010101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire new file mode 100644 index 0000000..9569094 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire8.spec +### + +# NSEC3 RDATA, RDLEN=66 +0042 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=0, Length=33 +00 21 010101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec new file mode 100644 index 0000000..b04c84f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec @@ -0,0 +1,10 @@ +# +# An invalid NSEC3 RDATA: disordered bitmap blocks +# + +[custom] +sections: nsec3 +[nsec3] +nbitmap: 2 +block0: 2 +block1: 1 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire new file mode 100644 index 0000000..9c0de10 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire @@ -0,0 +1,16 @@ +### +### This data file was auto-generated from rdata_nsec3_fromWire9.spec +### + +# NSEC3 RDATA, RDLEN=47 +002f +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 +# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' +14 6868686868686868686868686868686868686868 +# Bitmap: Block=2, Length=6 +02 06 040000000003 +# Bitmap: Block=1, Length=6 +01 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 new file mode 100644 index 0000000..1b8697f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 @@ -0,0 +1,5 @@ +# RDLENGTH, 9 bytes +00 09 +# NSEC3PARAM record +# 1 1 1 D399EAAB +01 01 00 01 04 d3 99 ea ab diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec new file mode 100644 index 0000000..41e1784 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC3PARAM RDATA: Saltlen is too large +# + +[custom] +sections: nsec3param +[nsec3param] +rdlen: 7 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire new file mode 100644 index 0000000..e9ffb46 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec3param_fromWire11.spec +### + +# NSEC3PARAM RDATA, RDLEN=7 +0007 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec new file mode 100644 index 0000000..311b2dd --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec @@ -0,0 +1,9 @@ +# +# A valid (but unusual) NSEC3PARAM RDATA: salt is empty. +# + +[custom] +sections: nsec3param +[nsec3param] +saltlen: 0 +salt: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire new file mode 100644 index 0000000..511da87 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec3param_fromWire13.spec +### + +# NSEC3PARAM RDATA, RDLEN=5 +0005 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=0, Salt='' +00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec new file mode 100644 index 0000000..e04b818 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec @@ -0,0 +1,9 @@ +# +# A malformed NSEC3PARAM RDATA: RDLEN indicates it doesn't even contain the +# fixed 5 octets +# + +[custom] +sections: nsec3param +[nsec3param] +rdlen: 4 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire new file mode 100644 index 0000000..adc5ea9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec3param_fromWire2.spec +### + +# NSEC3PARAM RDATA, RDLEN=4 +0004 +# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 +01 00 0001 +# Salt Len=5, Salt='sssss' +05 7373737373 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 new file mode 100644 index 0000000..9c34394 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 @@ -0,0 +1,6 @@ +# RDLENGTH, 22 bytes +00 16 +# NSEC record +# www2.isc.org. CNAME RRSIG NSEC +04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 06 +04 00 00 00 00 03 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec new file mode 100644 index 0000000..39d19ae --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC RDATA: a bitmap block containing empty bytes +# + +[custom] +sections: nsec +[nsec] +bitmap: '01000000' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire new file mode 100644 index 0000000..1145dbd --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire10.spec +### + +# NSEC RDATA, RDLEN=24 +0018 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=4 +00 04 01000000 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec new file mode 100644 index 0000000..d7faeed --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec @@ -0,0 +1,8 @@ +# +# An invalid NSEC RDATA: with an empty bitmap +# + +[custom] +sections: nsec +[nsec] +nbitmap: 0 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire new file mode 100644 index 0000000..943601a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire16.spec +### + +# NSEC RDATA, RDLEN=18 +0012 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 new file mode 100644 index 0000000..e9b41e2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 @@ -0,0 +1,10 @@ +# +# NSEC RDATA with a bogus RDLEN (too short) +# + +# RDLENGTH, 13 bytes (should be 22) +00 0d +# NSEC record +# www2.isc.org. CNAME RRSIG NSEC +04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 06 +04 00 00 00 00 03 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 new file mode 100644 index 0000000..67e6b3f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 @@ -0,0 +1,10 @@ +# +# NSEC RDATA with an invalid type bitmap +# + +# RDLENGTH, 22 bytes +00 16 +# NSEC record +# www2.isc.org. followed by a broken bitmap +04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 ff +00 00 00 00 00 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec @@ -0,0 +1,9 @@ +# +# A malformed NSEC RDATA: bit map length is too large, causing overflow +# + +[custom] +sections: nsec +[nsec] +maplen: 31 +bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire new file mode 100644 index 0000000..1c4d4a4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire4.spec +### + +# NSEC RDATA, RDLEN=21 +0015 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=31 +00 1f 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec new file mode 100644 index 0000000..795d1e0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec @@ -0,0 +1,13 @@ +# +# A malformed NSEC RDATA: incomplete bit map field +# + +[custom] +sections: nsec +[nsec] +# only containing the block field of the bitmap +rdlen: 19 +#dummy data +maplen: 31 +#dummy data +bitmap: '00' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire new file mode 100644 index 0000000..104a2b9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire5.spec +### + +# NSEC RDATA, RDLEN=19 +0013 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=31 +00 1f 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec new file mode 100644 index 0000000..cb864ab --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec @@ -0,0 +1,11 @@ +# +# A malformed NSEC RDATA: bit map length being 0 +# + +[custom] +sections: nsec +[nsec] +rdlen: 20 +maplen: 0 +# dummy data: +bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire new file mode 100644 index 0000000..3108ed7 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire6.spec +### + +# NSEC RDATA, RDLEN=20 +0014 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=0 +00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec new file mode 100644 index 0000000..46b521b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec @@ -0,0 +1,9 @@ +# +# NSEC RDATA with a longest bitmap field (32 bitmap bytes) +# + +[custom] +sections: nsec +[nsec] +maplen: 32 +bitmap: '0101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire new file mode 100644 index 0000000..e265a3b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire7.spec +### + +# NSEC RDATA, RDLEN=52 +0034 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=32 +00 20 0101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec new file mode 100644 index 0000000..3f957a3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec @@ -0,0 +1,9 @@ +# +# An invalid NSEC RDATA with an oversized bitmap field (33 bitmap bytes) +# + +[custom] +sections: nsec +[nsec] +maplen: 33 +bitmap: '010101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire new file mode 100644 index 0000000..066cd70 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire8.spec +### + +# NSEC RDATA, RDLEN=53 +0035 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=0, Length=33 +00 21 010101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec new file mode 100644 index 0000000..a3bd0c9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec @@ -0,0 +1,10 @@ +# +# An invalid NSEC RDATA: disordered bitmap blocks +# + +[custom] +sections: nsec +[nsec] +nbitmap: 2 +block0: 2 +block1: 1 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire new file mode 100644 index 0000000..53ad6cc --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from rdata_nsec_fromWire9.spec +### + +# NSEC RDATA, RDLEN=34 +0022 +# Next Name=next.example.com (18 bytes) +046e657874076578616d706c6503636f6d00 +# Bitmap: Block=2, Length=6 +02 06 040000000003 +# Bitmap: Block=1, Length=6 +01 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_opt_fromWire1 b/src/lib/dns/tests/testdata/rdata_opt_fromWire1 new file mode 100644 index 0000000..f2eb680 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_opt_fromWire1 @@ -0,0 +1,15 @@ +# Various kinds of OPT RDATA stored in an input buffer +# +# Empty RDATA (which is okay) +# +# 0 1 (bytes) + 00 00 +# +# An OPT RR containing an NSID Option +# code=3 len=3 ID value (opaque) +# 2 3 4 5 6 7 8 9 10 + 00 07 00 2a 00 03 00 01 02 +# +# Short buffer (this can be tested only at the end of the buffer) +# 1 2 3 4 5 + 00 04 c0 00 02 diff --git a/src/lib/dns/tests/testdata/rdata_opt_fromWire2 b/src/lib/dns/tests/testdata/rdata_opt_fromWire2 new file mode 100644 index 0000000..2c5a11f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_opt_fromWire2 @@ -0,0 +1,4 @@ +# Short RDATA length +# +# OPT RDATA, RDLEN=1 +0001 diff --git a/src/lib/dns/tests/testdata/rdata_opt_fromWire3 b/src/lib/dns/tests/testdata/rdata_opt_fromWire3 new file mode 100644 index 0000000..52db1d8 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_opt_fromWire3 @@ -0,0 +1,8 @@ +# Short RDATA length (in second pseudo RR) +# +# OPT RDATA, RDLEN=8 +0008 +# Pseudo RR 1 of size 7 (code=3, len=3) +00 03 00 03 00 01 02 +# Pseudo RR 2 of size 7 exhausts RDLEN (code=4, len=3) +00 04 00 03 00 01 02 diff --git a/src/lib/dns/tests/testdata/rdata_opt_fromWire4 b/src/lib/dns/tests/testdata/rdata_opt_fromWire4 new file mode 100644 index 0000000..a302127 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_opt_fromWire4 @@ -0,0 +1,9 @@ +# Sum of option lengths would overflow RDLEN +# +# OPT RDATA, RDLEN=14 (0x000e) +000e +# Pseudo RR 1 (code=3, len=3) +00 03 00 03 00 01 02 +# Pseudo RR 2 (code=4, len=65535 overflows RDLEN) +00 04 ff ff 00 01 02 +# Rest of option data is omitted... diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec new file mode 100644 index 0000000..edb9f34 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec @@ -0,0 +1,6 @@ +# +# A simplest form of RP: all default parameters +# +[custom] +sections: rp +[rp] diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire new file mode 100644 index 0000000..aa93986 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_fromWire1.spec +### + +# RP RDATA, RDLEN=39 +0027 +# MAILBOX=root.example.com TEXT=rp-text.example.com +04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec new file mode 100644 index 0000000..57adb5a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec @@ -0,0 +1,12 @@ +# +# A simplest form of RP: names are compressed. +# +[custom] +sections: name/1:name/2:rp +[name/1] +name: a.example.com +[name/2] +name: b.example.net +[rp] +mailbox: root.ptr=2 +text: rp-text.ptr=17 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire new file mode 100644 index 0000000..507bb1a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_rp_fromWire2.spec +### + +# DNS Name: a.example.com +0161076578616d706c6503636f6d00 + +# DNS Name: b.example.net +0162076578616d706c65036e657400 + +# RP RDATA, RDLEN=17 +0011 +# MAILBOX=root.ptr=2 TEXT=rp-text.ptr=17 +04726f6f74c002 0772702d74657874c011 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec new file mode 100644 index 0000000..a238b7e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec @@ -0,0 +1,7 @@ +# +# RP-like RDATA but RDLEN is too short. +# +[custom] +sections: rp +[rp] +rdlen: 38 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire new file mode 100644 index 0000000..c2a7086 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_fromWire3.spec +### + +# RP RDATA, RDLEN=38 +0026 +# MAILBOX=root.example.com TEXT=rp-text.example.com +04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec new file mode 100644 index 0000000..6f3abd1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec @@ -0,0 +1,7 @@ +# +# RP-like RDATA but RDLEN is too long. +# +[custom] +sections: rp +[rp] +rdlen: 40 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire new file mode 100644 index 0000000..56c643e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_fromWire4.spec +### + +# RP RDATA, RDLEN=40 +0028 +# MAILBOX=root.example.com TEXT=rp-text.example.com +04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec new file mode 100644 index 0000000..b8d5e29 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec @@ -0,0 +1,7 @@ +# +# RP-like RDATA but mailbox name is broken. +# +[custom] +sections: rp +[rp] +mailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire new file mode 100644 index 0000000..1127047 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_fromWire5.spec +### + +# RP RDATA, RDLEN=90 +005a +# MAILBOX="01234567890123456789012345678901234567890123456789012345678901234" TEXT=rp-text.example.com +432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec new file mode 100644 index 0000000..e9e79f3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec @@ -0,0 +1,7 @@ +# +# RP-like RDATA but text name is broken. +# +[custom] +sections: rp +[rp] +text: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire new file mode 100644 index 0000000..49aedc6 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_fromWire6.spec +### + +# RP RDATA, RDLEN=87 +0057 +# MAILBOX=root.example.com TEXT="01234567890123456789012345678901234567890123456789012345678901234" +04726f6f74076578616d706c6503636f6d00 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec new file mode 100644 index 0000000..948bd1a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec @@ -0,0 +1,8 @@ +# +# A simplest form of RP for toWire test: all default parameters except rdlen, +# which is to be omitted. +# +[custom] +sections: rp +[rp] +rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire b/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire new file mode 100644 index 0000000..dcadb15 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_rp_toWire1.spec +### + +# RP RDATA + +# MAILBOX=root.example.com TEXT=rp-text.example.com +04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec new file mode 100644 index 0000000..09a7ddc --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec @@ -0,0 +1,14 @@ +# +# A simple form of RP: names could be compressed (but MUST NOT). +# rdlen is omitted for the "to wire" test. +# +[custom] +sections: name/1:name/2:rp +[name/1] +name: a.example.com +[name/2] +name: b.example.net +[rp] +rdlen: -1 +mailbox: root.example.com +text: rp-text.example.net diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire b/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire new file mode 100644 index 0000000..38057ef --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_rp_toWire2.spec +### + +# DNS Name: a.example.com +0161076578616d706c6503636f6d00 + +# DNS Name: b.example.net +0162076578616d706c65036e657400 + +# RP RDATA + +# MAILBOX=root.example.com TEXT=rp-text.example.net +04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c65036e657400 diff --git a/src/lib/dns/tests/testdata/rdata_rrsig_fromWire1 b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire1 new file mode 100644 index 0000000..1b799c2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire1 @@ -0,0 +1,13 @@ +# RDLENGTH 155 bytes +00 9b +# RRSIG record +00 01 05 02 00 00 a8 c0 4b ad ad 5d 4b 86 20 5d +0a 62 03 69 73 63 03 6f 72 67 00 1e 42 64 ff 16 +53 bf 37 8f 53 c3 44 36 5f e5 7b 2f 1b 6d 4b a6 +86 4d 61 5d c8 b2 aa 12 e7 cf 55 50 17 39 03 a2 +87 a3 ea 77 97 66 05 99 cd 02 9e 4c a3 ce 61 6a +e7 62 d7 59 5b 83 e7 3d 01 5e 52 5d e8 ae 02 de +bf b1 7c 27 a1 59 94 39 f4 cb f2 7f 4e 14 79 9b +7e 8c a3 6f c6 77 18 e3 f2 52 7f 22 33 d5 91 da +4a c8 1a 5c 9d 83 43 f0 a1 08 99 ae 4c c8 d0 8d +7b 23 b5 52 47 cf 41 91 87 35 24 diff --git a/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.spec new file mode 100644 index 0000000..582975a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.spec @@ -0,0 +1,8 @@ +# +# RRSIG RDATA with a bogus RDLEN (too short) +# + +[custom] +sections: rrsig +[rrsig] +rdlen: 19 diff --git a/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.wire new file mode 100644 index 0000000..b3601a1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_rrsig_fromWire2.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from rdata_rrsig_fromWire2.spec +### + +# RRSIG RDATA, RDLEN=19 +0013 +# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 +0001 05 02 00000e10 +# Expiration=1264935600, Inception=1262343600 +4b6562b0 4b3dd5b0 +# Tag=4149 Signer=example.com and Signature +1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef diff --git a/src/lib/dns/tests/testdata/rdata_soa_fromWire b/src/lib/dns/tests/testdata/rdata_soa_fromWire new file mode 100644 index 0000000..5cd67f0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_soa_fromWire @@ -0,0 +1,20 @@ +# +# A common style of SOA RDATA stored in an input buffer +# +# Valid compressed RDATA for "(ns.example.com. root.example.com. +# 2010012601 3600 300 3600000 1200)" +# RDLENGTH=43 bytes +# 0 1 + 00 2b +# MNAME: non compressed +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) +#(2) n s (7) e x a m p l e (3) c o m . + 02 6e 73 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# RNAME: compressed +# 8 9 20 1 2 3 4 +#(4) r o o t ptr=5 + 04 72 6f 6f 74 c0 05 +# other numeric parameters +# 28 32 36 40 44 +# serial, refresh, retry, expire, minimum + 77ce5bb9 00000e10 0000012c 0036ee80 000004b0 diff --git a/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.spec b/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.spec new file mode 100644 index 0000000..389cec9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.spec @@ -0,0 +1,7 @@ +# +# A simple SOA RDATA without name compression +# + +[custom] +sections: soa +[soa] diff --git a/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.wire b/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.wire new file mode 100644 index 0000000..4b6442f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_soa_toWireUncompressed.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_soa_toWireUncompressed.spec +### + +# SOA RDATA, RDLEN=54 +0036 +# NNAME=ns.example.com RNAME=root.example.com +026e73076578616d706c6503636f6d00 04726f6f74076578616d706c6503636f6d00 +# SERIAL(2010012601) REFRESH(3600) RETRY(300) EXPIRE(3600000) MINIMUM(1200) +77ce5bb9 00000e10 0000012c 0036ee80 000004b0 diff --git a/src/lib/dns/tests/testdata/rdata_srv_fromWire b/src/lib/dns/tests/testdata/rdata_srv_fromWire new file mode 100644 index 0000000..0f1e4ec --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_srv_fromWire @@ -0,0 +1,36 @@ +# +# various kinds of SRV RDATA stored in an input buffer +# +# RDLENGTH=21 bytes +# 0 1 + 00 15 +# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# short length +# 3 4 + 00 12 +# 5 6 7 8 9 30 1 2 3 4 5 6 7 8 9 40 1 2 3 4 5 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# length too long +# 6 7 + 00 19 +# +# 8 9 50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# +# +# incomplete target name +# 9 70 + 00 12 +# 1 2 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 8 + 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 +# +# +# Valid compressed target name: 'a' + pointer +# 9 90 + 00 0a +# +# 1 2 3 4 5 6 7 8 9 100 + 00 01 00 05 05 dc 01 61 c0 0a diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire new file mode 100644 index 0000000..added40 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire @@ -0,0 +1,4 @@ +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec new file mode 100644 index 0000000..e28a62f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec @@ -0,0 +1,6 @@ +# +# A simplest form of SSHFP: all default parameters +# +[custom] +sections: sshfp +[sshfp] diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire new file mode 100644 index 0000000..c7059e1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire1.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 new file mode 100644 index 0000000..220e570 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 @@ -0,0 +1,6 @@ +# Test where fingerprint is missing + +# SSHFP RDATA, RDLEN=32 +0020 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=(none) +02 01 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 new file mode 100644 index 0000000..a2f8636 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 @@ -0,0 +1,4 @@ +# Test where RDATA is completely missing + +# SSHFP RDATA, RDLEN=32 +0020 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 new file mode 100644 index 0000000..eabd06b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 @@ -0,0 +1,4 @@ +# SSHFP RDATA, RDLEN=02 +0002 +# ALGORITHM=4 FINGERPRINT_TYPE=9 +04 09 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 new file mode 100644 index 0000000..a695548 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 @@ -0,0 +1,4 @@ +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789ABCDEF67890123456789abcdef67890 +02 01 123456789ABCDEF67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec new file mode 100644 index 0000000..59a336e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec @@ -0,0 +1,7 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +fingerprint: 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire new file mode 100644 index 0000000..e492316 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire2.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec new file mode 100644 index 0000000..d111afd --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec @@ -0,0 +1,8 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +algorithm: 1 +fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire new file mode 100644 index 0000000..daa102f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire3.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=1 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +01 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec new file mode 100644 index 0000000..b9b2658 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec @@ -0,0 +1,8 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +algorithm: 255 +fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire new file mode 100644 index 0000000..f05faad --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire4.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=255 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +ff 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec new file mode 100644 index 0000000..b3a19fa --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec @@ -0,0 +1,8 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +algorithm: 0 +fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire new file mode 100644 index 0000000..ddc8edb --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire5.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=0 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +00 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec new file mode 100644 index 0000000..437e282 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec @@ -0,0 +1,8 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +algorithm: 5 +fingerprint_type: 0 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire new file mode 100644 index 0000000..d4e591e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire6.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=5 FINGERPRINT_TYPE=0 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +05 00 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec new file mode 100644 index 0000000..8e21d11 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec @@ -0,0 +1,8 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +algorithm: 255 +fingerprint_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire new file mode 100644 index 0000000..c8b9d8b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire7.spec +### + +# SSHFP RDATA, RDLEN=22 +0016 +# ALGORITHM=255 FINGERPRINT_TYPE=255 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +ff ff 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec new file mode 100644 index 0000000..98aa00f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec @@ -0,0 +1,9 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +fingerprint: 082359342fd9 +algorithm: 255 +fingerprint_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire new file mode 100644 index 0000000..32cede8 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_sshfp_fromWire8.spec +### + +# SSHFP RDATA, RDLEN=8 +0008 +# ALGORITHM=255 FINGERPRINT_TYPE=255 FINGERPRINT=082359342fd9 +ff ff 082359342fd9 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 new file mode 100644 index 0000000..05fc806 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 @@ -0,0 +1,6 @@ +# Test where fingerprint length is smaller than what RDATA len indicates + +# SSHFP RDATA, RDLEN=32 +0020 +# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 +02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.spec new file mode 100644 index 0000000..e46d9b3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.spec @@ -0,0 +1,6 @@ +# +# A simplest form of TKEY: all default parameters +# +[custom] +sections: tkey +[tkey] diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.wire new file mode 100644 index 0000000..e8ee944 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire1.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire1.spec +### + +# TKEY RDATA, RDLEN=58 +003a +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.spec new file mode 100644 index 0000000..e4a1920 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.spec @@ -0,0 +1,8 @@ +# +# TKEY with other data +# +[custom] +sections: tkey +[tkey] +other_len: 8 +other_data: abcd0123 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.wire new file mode 100644 index 0000000..614844f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire2.spec +### + +# TKEY RDATA, RDLEN=66 +0042 +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=8 Other-Data=(see hex) +0008 6162636430313233 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.spec new file mode 100644 index 0000000..2566b58 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.spec @@ -0,0 +1,9 @@ +# +# TKEY without Key +# +[custom] +sections: tkey +[tkey] +key_len: 0 +other_len: 8 +other_data: abcd0123 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.wire new file mode 100644 index 0000000..df27910 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire3.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire3.spec +### + +# TKEY RDATA, RDLEN=34 +0022 +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=0 Key=(see hex) +0000 +# Other-Len=8 Other-Data=(see hex) +0008 6162636430313233 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.spec new file mode 100644 index 0000000..33459eb --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.spec @@ -0,0 +1,11 @@ +# +# A simplest form of TKEY, but the algorithm name is compressed (quite +# pathological, but we accept it) +# +[custom] +sections: name:tkey +[name] +name: gss-tsig +[tkey] +algorithm: ptr=0 +key_len: 32 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.wire new file mode 100644 index 0000000..550052e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire4.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire4.spec +### + +# DNS Name: gss-tsig +086773732d7473696700 + +# TKEY RDATA, RDLEN=50 +0032 +# Algorithm=ptr=0 +c000 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.spec new file mode 100644 index 0000000..6cfa4b4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.spec @@ -0,0 +1,7 @@ +# +# TKEY-like RDATA but RDLEN is too short. +# +[custom] +sections: tkey +[tkey] +rdlen: 57 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.wire new file mode 100644 index 0000000..fa32566 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire5.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire5.spec +### + +# TKEY RDATA, RDLEN=57 +0039 +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.spec new file mode 100644 index 0000000..87460a2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.spec @@ -0,0 +1,7 @@ +# +# TKEY-like RDATA but RDLEN is too long. +# +[custom] +sections: tkey +[tkey] +rdlen: 60 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.wire new file mode 100644 index 0000000..7f5f112 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire6.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire6.spec +### + +# TKEY RDATA, RDLEN=60 +003c +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.spec new file mode 100644 index 0000000..3fc0929 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.spec @@ -0,0 +1,8 @@ +# +# TKEY-like RDATA but algorithm name is broken. +# +[custom] +sections: tkey +[tkey] +algorithm: "01234567890123456789012345678901234567890123456789012345678901234" +key_len: 32 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.wire new file mode 100644 index 0000000..73b277c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire7.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire7.spec +### + +# TKEY RDATA, RDLEN=117 +0075 +# Algorithm="01234567890123456789012345678901234567890123456789012345678901234" +432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.spec new file mode 100644 index 0000000..8338279 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.spec @@ -0,0 +1,8 @@ +# +# TKEY-like RDATA but Key len is bogus +# +[custom] +sections: tkey +[tkey] +key_len: 65535 +key: "dummy data" diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.wire new file mode 100644 index 0000000..abeb95b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire8.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire8.spec +### + +# TKEY RDATA, RDLEN=38 +0026 +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=65535 Key=(see hex) +ffff 2264756d6d79206461746122 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.spec new file mode 100644 index 0000000..9fb63e0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.spec @@ -0,0 +1,8 @@ +# +# TKEY-like RDATA but Other-Data length is bogus +# +[custom] +sections: tkey +[tkey] +other_len: 65535 +otherdata: "dummy data" diff --git a/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.wire new file mode 100644 index 0000000..8e5f943 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_fromWire9.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_fromWire9.spec +### + +# TKEY RDATA, RDLEN=58 +003a +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=32 Key=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Other-Len=65535 Other-Data=(see hex) +ffff diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire1.spec b/src/lib/dns/tests/testdata/rdata_tkey_toWire1.spec new file mode 100644 index 0000000..42521a3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire1.spec @@ -0,0 +1,8 @@ +# +# An artificial TKEY RDATA for toWire test. +# +[custom] +sections: tkey +[tkey] +algorithm: gss-tsig +key_len: 0 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire1.wire b/src/lib/dns/tests/testdata/rdata_tkey_toWire1.wire new file mode 100644 index 0000000..3f49a69 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire1.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_toWire1.spec +### + +# TKEY RDATA, RDLEN=26 +001a +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=0 Key=(see hex) +0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire2.spec b/src/lib/dns/tests/testdata/rdata_tkey_toWire2.spec new file mode 100644 index 0000000..25d47e5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire2.spec @@ -0,0 +1,11 @@ +# +# An artificial TKEY RDATA for toWire test. +# +[custom] +sections: tkey +[tkey] +algorithm: GSS-TSIG +error: 16 +key_len: 12 +# 0x1402... would be FAKEFAKE... if encoded in BASE64 +key: 0x140284140284140284140284 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire2.wire b/src/lib/dns/tests/testdata/rdata_tkey_toWire2.wire new file mode 100644 index 0000000..a1fdb9a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_toWire2.spec +### + +# TKEY RDATA, RDLEN=38 +0026 +# Algorithm=GSS-TSIG +084753532d5453494700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=16 +608d42c0 608d50d0 0003 0010 +# Key Len=12 Key=(see hex) +000c 140284140284140284140284 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire3.spec b/src/lib/dns/tests/testdata/rdata_tkey_toWire3.spec new file mode 100644 index 0000000..b3ea3db --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire3.spec @@ -0,0 +1,13 @@ +# +# An artificial TKEY RDATA for toWire test. +# +[custom] +sections: tkey +[tkey] +algorithm: gss.tsig +error: 16 +key_len: 12 +# 0x1402... would be FAKEFAKE... if encoded in BASE64 +key: 0x140284140284140284140284 +other_len: 6 +other_data: 0x140284140284 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire3.wire b/src/lib/dns/tests/testdata/rdata_tkey_toWire3.wire new file mode 100644 index 0000000..f2f8a6f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire3.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tkey_toWire3.spec +### + +# TKEY RDATA, RDLEN=44 +002c +# Algorithm=gss.tsig +03677373047473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=16 +608d42c0 608d50d0 0003 0010 +# Key Len=12 Key=(see hex) +000c 140284140284140284140284 +# Other-Len=6 Other-Data=(see hex) +0006 140284140284 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire4.spec b/src/lib/dns/tests/testdata/rdata_tkey_toWire4.spec new file mode 100644 index 0000000..e403c00 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire4.spec @@ -0,0 +1,10 @@ +# +# An artificial TKEY RDATA for toWire test. +# +[custom] +sections: name:tkey +[name] +name: gss-tsig +[tkey] +algorithm: gss-tsig +key_len: 0 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire4.wire b/src/lib/dns/tests/testdata/rdata_tkey_toWire4.wire new file mode 100644 index 0000000..81a1443 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire4.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tkey_toWire4.spec +### + +# DNS Name: gss-tsig +086773732d7473696700 + +# TKEY RDATA, RDLEN=26 +001a +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=0 Key=(see hex) +0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire5.spec b/src/lib/dns/tests/testdata/rdata_tkey_toWire5.spec new file mode 100644 index 0000000..b4a1bca --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire5.spec @@ -0,0 +1,10 @@ +# +# An artificial TKEY RDATA for toWire test. +# +[custom] +sections: tkey:name +[tkey] +algorithm: gss-tsig +key_len: 0 +[name] +name: ptr=2 diff --git a/src/lib/dns/tests/testdata/rdata_tkey_toWire5.wire b/src/lib/dns/tests/testdata/rdata_tkey_toWire5.wire new file mode 100644 index 0000000..e59a1f0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tkey_toWire5.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tkey_toWire5.spec +### + +# TKEY RDATA, RDLEN=26 +001a +# Algorithm=gss-tsig +086773732d7473696700 +# Inception=1619870400 Expire=1619874000 Mode=3 Error=0 +608d42c0 608d50d0 0003 0000 +# Key Len=0 Key=(see hex) +0000 +# Other-Len=0 Other-Data=(see hex) +0000 + +# DNS Name: ptr=2 +c002 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire new file mode 100644 index 0000000..38e279c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire @@ -0,0 +1,4 @@ +# TLSA RDATA, RDLEN=35 +0023 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=... +00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 new file mode 100644 index 0000000..67cecb1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 @@ -0,0 +1,6 @@ +# Test where certificate association data is missing. + +# TLSA RDATA, RDLEN=35 +0023 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(missing) +00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 new file mode 100644 index 0000000..4b8ec93 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 @@ -0,0 +1,4 @@ +# Test where RDATA is completely missing + +# TLSA RDATA, RDLEN=35 +0023 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 new file mode 100644 index 0000000..71c7b9c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 @@ -0,0 +1,4 @@ +# TLSA RDATA, RDLEN=3 +0003 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(none) +03 01 02 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 new file mode 100644 index 0000000..36ce278 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 @@ -0,0 +1,4 @@ +# TLSA RDATA, RDLEN=35 +0023 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=... +00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983A1D16E8A410E4561CB106618E971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec new file mode 100644 index 0000000..39c8057 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 0 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire new file mode 100644 index 0000000..6a8b552 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire3.spec +### + +# TLSA RDATA, RDLEN=34 +0022 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 +00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec new file mode 100644 index 0000000..d97ae6a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire new file mode 100644 index 0000000..b5a39c8 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire4.spec +### + +# TLSA RDATA, RDLEN=34 +0022 +# CERTIFICATE_USAGE=255 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 +ff 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec new file mode 100644 index 0000000..cc3e296 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +selector: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire new file mode 100644 index 0000000..b79f97d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire5.spec +### + +# TLSA RDATA, RDLEN=34 +0022 +# CERTIFICATE_USAGE=0 SELECTOR=255 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 +00 ff 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec new file mode 100644 index 0000000..eed0ab9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +matching_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire new file mode 100644 index 0000000..02afe27 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire6.spec +### + +# TLSA RDATA, RDLEN=34 +0022 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=255 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 +00 00 ff d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec new file mode 100644 index 0000000..576df1e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec @@ -0,0 +1,9 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 3 +selector: 1 +matching_type: 2 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire new file mode 100644 index 0000000..e5c23a4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire7.spec +### + +# TLSA RDATA, RDLEN=34 +0022 +# CERTIFICATE_USAGE=3 SELECTOR=1 MATCHING_TYPE=2 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 +03 01 02 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec new file mode 100644 index 0000000..ef5c108 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_association_data: '0123' diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire new file mode 100644 index 0000000..dc29a0b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_tlsa_fromWire8.spec +### + +# TLSA RDATA, RDLEN=4 +0004 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=0123 +00 00 01 0123 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 new file mode 100644 index 0000000..fc4560a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 @@ -0,0 +1,7 @@ +# Test where certificate association data length is smaller than what +# RDATA length indicates. + +# TLSA RDATA, RDLEN=64 +0040 +# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(32 bytes) +00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec new file mode 100644 index 0000000..a30c371 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec @@ -0,0 +1,6 @@ +# +# A simplest form of TSIG: all default parameters +# +[custom] +sections: tsig +[tsig] diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.wire new file mode 100644 index 0000000..cec3a0f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire1.spec +### + +# TSIG RDATA, RDLEN=61 +003d +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec new file mode 100644 index 0000000..d1e49a5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec @@ -0,0 +1,8 @@ +# +# TSIG with other data (error = BADTIME(18)) +# +[custom] +sections: tsig +[tsig] +mac_size: 0 +error: 18 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.wire new file mode 100644 index 0000000..ca85df4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire2.spec +### + +# TSIG RDATA, RDLEN=35 +0023 +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=2845 Error=18 +0b1d 0012 +# Other-Len=6 Other-Data=(see hex) +0006 00004cb5be18 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec new file mode 100644 index 0000000..57f8e83 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec @@ -0,0 +1,8 @@ +# +# TSIG without MAC (error = BADSIG(16)) +# +[custom] +sections: tsig +[tsig] +mac_size: 0 +error: 16 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.wire new file mode 100644 index 0000000..16c3e39 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire3.spec +### + +# TSIG RDATA, RDLEN=29 +001d +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=2845 Error=16 +0b1d 0010 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec new file mode 100644 index 0000000..8c38e9e --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec @@ -0,0 +1,11 @@ +# +# A simplest form of TSIG, but the algorithm name is compressed (quite +# pathological, but we accept it) +# +[custom] +sections: name:tsig +[name] +name: hmac-sha256 +[tsig] +algorithm: ptr=0 +mac_size: 32 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.wire new file mode 100644 index 0000000..6b8e0f3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire4.spec +### + +# DNS Name: hmac-sha256 +0b686d61632d73686132353600 + +# TSIG RDATA, RDLEN=50 +0032 +# Algorithm=ptr=0 Time-Signed=1286978795 Fudge=300 +c000 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec new file mode 100644 index 0000000..da90b18 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec @@ -0,0 +1,7 @@ +# +# TSIG-like RDATA but RDLEN is too short. +# +[custom] +sections: tsig +[tsig] +rdlen: 60 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.wire new file mode 100644 index 0000000..9656ce2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire5.spec +### + +# TSIG RDATA, RDLEN=60 +003c +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec new file mode 100644 index 0000000..9d2f627 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec @@ -0,0 +1,7 @@ +# +# TSIG-like RDATA but RDLEN is too long. +# +[custom] +sections: tsig +[tsig] +rdlen: 63 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.wire new file mode 100644 index 0000000..bb1ecfb --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire6.spec +### + +# TSIG RDATA, RDLEN=63 +003f +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec new file mode 100644 index 0000000..ed7a81c --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec @@ -0,0 +1,8 @@ +# +# TSIG-like RDATA but algorithm name is broken. +# +[custom] +sections: tsig +[tsig] +algorithm: "01234567890123456789012345678901234567890123456789012345678901234" +mac_size: 32 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.wire new file mode 100644 index 0000000..327211f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire7.spec +### + +# TSIG RDATA, RDLEN=117 +0075 +# Algorithm="01234567890123456789012345678901234567890123456789012345678901234" Time-Signed=1286978795 Fudge=300 +432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec new file mode 100644 index 0000000..0b44f87 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec @@ -0,0 +1,8 @@ +# +# TSIG-like RDATA but MAC size is bogus +# +[custom] +sections: tsig +[tsig] +mac_size: 65535 +mac: "dummy data" diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.wire new file mode 100644 index 0000000..a4209f9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire8.spec +### + +# TSIG RDATA, RDLEN=41 +0029 +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=65535 MAC=(see hex) +ffff 2264756d6d79206461746122 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec new file mode 100644 index 0000000..f512fb4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec @@ -0,0 +1,8 @@ +# +# TSIG-like RDATA but Other-Data length is bogus +# +[custom] +sections: tsig +[tsig] +other_len: 65535 +otherdata: "dummy data" diff --git a/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.wire new file mode 100644 index 0000000..b356825 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_fromWire9.spec +### + +# TSIG RDATA, RDLEN=61 +003d +# Algorithm=hmac-sha256 Time-Signed=1286978795 Fudge=300 +0b686d61632d73686132353600 00004cb5bceb 012c +# MAC Size=32 MAC=(see hex) +0020 7878787878787878787878787878787878787878787878787878787878787878 +# Original-ID=2845 Error=0 +0b1d 0000 +# Other-Len=65535 Other-Data=(see hex) +ffff diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec b/src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec new file mode 100644 index 0000000..eb74000 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec @@ -0,0 +1,11 @@ +# +# An artificial TSIG RDATA for toWire test. +# +[custom] +sections: tsig +[tsig] +algorithm: hmac-md5 +time_signed: 1286779327 +mac_size: 0 +original_id: 16020 +error: 17 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire1.wire b/src/lib/dns/tests/testdata/rdata_tsig_toWire1.wire new file mode 100644 index 0000000..c368853 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire1.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_toWire1.spec +### + +# TSIG RDATA, RDLEN=42 +002a +# Algorithm=hmac-md5 Time-Signed=1286779327 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004cb2b1bf 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=16020 Error=17 +3e94 0011 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec b/src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec new file mode 100644 index 0000000..b2c38e9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec @@ -0,0 +1,13 @@ +# +# An artificial TSIG RDATA for toWire test. +# +[custom] +sections: tsig +[tsig] +algorithm: hmac-sha256 +time_signed: 1286779327 +mac_size: 12 +# 0x1402... would be FAKEFAKE... if encoded in BASE64 +mac: 0x140284140284140284140284 +original_id: 16020 +error: 16 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire2.wire b/src/lib/dns/tests/testdata/rdata_tsig_toWire2.wire new file mode 100644 index 0000000..7ea336d --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire2.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_toWire2.spec +### + +# TSIG RDATA, RDLEN=41 +0029 +# Algorithm=hmac-sha256 Time-Signed=1286779327 Fudge=300 +0b686d61632d73686132353600 00004cb2b1bf 012c +# MAC Size=12 MAC=(see hex) +000c 140284140284140284140284 +# Original-ID=16020 Error=16 +3e94 0010 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec b/src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec new file mode 100644 index 0000000..6520a08 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec @@ -0,0 +1,15 @@ +# +# An artificial TSIG RDATA for toWire test. +# +[custom] +sections: tsig +[tsig] +algorithm: hmac-sha1 +time_signed: 1286779327 +mac_size: 12 +# 0x1402... would be FAKEFAKE... if encoded in BASE64 +mac: 0x140284140284140284140284 +original_id: 16020 +error: 18 +other_len: 6 +other_data: 0x140284140284 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire3.wire b/src/lib/dns/tests/testdata/rdata_tsig_toWire3.wire new file mode 100644 index 0000000..535a83b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire3.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from rdata_tsig_toWire3.spec +### + +# TSIG RDATA, RDLEN=45 +002d +# Algorithm=hmac-sha1 Time-Signed=1286779327 Fudge=300 +09686d61632d7368613100 00004cb2b1bf 012c +# MAC Size=12 MAC=(see hex) +000c 140284140284140284140284 +# Original-ID=16020 Error=18 +3e94 0012 +# Other-Len=6 Other-Data=(see hex) +0006 140284140284 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec b/src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec new file mode 100644 index 0000000..d95cd23 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec @@ -0,0 +1,13 @@ +# +# An artificial TSIG RDATA for toWire test. +# +[custom] +sections: name:tsig +[name] +name: hmac-md5.sig-alg.reg.int. +[tsig] +algorithm: hmac-md5 +time_signed: 1286779327 +mac_size: 0 +original_id: 16020 +error: 17 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire4.wire b/src/lib/dns/tests/testdata/rdata_tsig_toWire4.wire new file mode 100644 index 0000000..5e8f68b --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire4.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tsig_toWire4.spec +### + +# DNS Name: hmac-md5.sig-alg.reg.int. +08686d61632d6d6435077369672d616c670372656703696e7400 + +# TSIG RDATA, RDLEN=42 +002a +# Algorithm=hmac-md5 Time-Signed=1286779327 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004cb2b1bf 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=16020 Error=17 +3e94 0011 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec b/src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec new file mode 100644 index 0000000..81e3a78 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec @@ -0,0 +1,13 @@ +# +# An artificial TSIG RDATA for toWire test. +# +[custom] +sections: tsig:name +[tsig] +algorithm: hmac-md5 +time_signed: 1286779327 +mac_size: 0 +original_id: 16020 +error: 17 +[name] +name: ptr=2 diff --git a/src/lib/dns/tests/testdata/rdata_tsig_toWire5.wire b/src/lib/dns/tests/testdata/rdata_tsig_toWire5.wire new file mode 100644 index 0000000..bc83de6 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tsig_toWire5.wire @@ -0,0 +1,17 @@ +### +### This data file was auto-generated from rdata_tsig_toWire5.spec +### + +# TSIG RDATA, RDLEN=42 +002a +# Algorithm=hmac-md5 Time-Signed=1286779327 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004cb2b1bf 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=16020 Error=17 +3e94 0011 +# Other-Len=0 Other-Data=(see hex) +0000 + +# DNS Name: ptr=2 +c002 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire1 b/src/lib/dns/tests/testdata/rdata_txt_fromWire1 new file mode 100644 index 0000000..547d76f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire1 @@ -0,0 +1,9 @@ +# +# various kinds of TXT RDATA stored in an input buffer +# +# Valid RDATA for "Test-String" +# +# RDLENGTH=12 bytes + 00 0c +# T e s t - S t r i n g + 0b 54 65 73 74 2d 53 74 72 69 6e 67 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_txt_fromWire2.spec new file mode 100644 index 0000000..c5829d9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire2.spec @@ -0,0 +1,8 @@ +# +# TXT RDATA with empty character-string. unusual, but valid. +# + +[custom] +sections: txt +[txt] +string: '' diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_txt_fromWire2.wire new file mode 100644 index 0000000..83ebea3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire2.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_txt_fromWire2.spec +### + +# TXT RDATA, RDLEN=1 +0001 +# String Len=0, String="" +00 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_txt_fromWire3.spec new file mode 100644 index 0000000..fe5c129 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire3.spec @@ -0,0 +1,8 @@ +# +# TXT RDATA with multiple character-strings. +# + +[custom] +sections: txt +[txt] +nstring: 2 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_txt_fromWire3.wire new file mode 100644 index 0000000..42fdf76 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire3.wire @@ -0,0 +1,10 @@ +### +### This data file was auto-generated from rdata_txt_fromWire3.spec +### + +# TXT RDATA, RDLEN=24 +0018 +# String Len=11, String="Test-String" +0b 546573742d537472696e67 +# String Len=11, String="Test-String" +0b 546573742d537472696e67 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_txt_fromWire4.spec new file mode 100644 index 0000000..0e015d4 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire4.spec @@ -0,0 +1,9 @@ +# +# Malformed TXT RDATA: RDLEN is 0 +# + +[custom] +sections: txt +[txt] +rdlen: 0 +# following data is provided, but that doesn't matter. diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_txt_fromWire4.wire new file mode 100644 index 0000000..6ac73b8 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire4.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_txt_fromWire4.spec +### + +# TXT RDATA, RDLEN=0 +0000 +# String Len=11, String="Test-String" +0b 546573742d537472696e67 diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_txt_fromWire5.spec new file mode 100644 index 0000000..7710cdf --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire5.spec @@ -0,0 +1,9 @@ +# +# Malformed TXT RDATA: character-string length is too large +# + +[custom] +sections: txt +[txt] +stringlen: 255 +string: 'too short' diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_txt_fromWire5.wire new file mode 100644 index 0000000..ea7a9cf --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire5.wire @@ -0,0 +1,8 @@ +### +### This data file was auto-generated from rdata_txt_fromWire5.spec +### + +# TXT RDATA, RDLEN=10 +000a +# String Len=255, String="too short" +ff 746f6f2073686f7274 diff --git a/src/lib/dns/tests/testdata/rdata_unknown_fromWire b/src/lib/dns/tests/testdata/rdata_unknown_fromWire new file mode 100644 index 0000000..69ea8ff --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_unknown_fromWire @@ -0,0 +1,13 @@ +# +# various kinds of "unknown" RDATA stored in an input buffer +# +# 0 1 2 3 4 5 (bytes) + 00 04 a1 b2 c3 0d +# +# 0-length data +# 6 7 + 00 00 +# +# short buffer (this can be tested only at the end of the buffer) +# 8 9 10 1 2 + 00 04 a1 b2 c3 diff --git a/src/lib/dns/tests/testdata/rdatafields1.spec b/src/lib/dns/tests/testdata/rdatafields1.spec new file mode 100644 index 0000000..6e105fb --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields1.spec @@ -0,0 +1,10 @@ +# +# A sequence of names that could be compressed (but not compressed) +# + +[custom] +sections: name/1:name/2 +[name/1] +name: www.example.com +[name/2] +name: example.com diff --git a/src/lib/dns/tests/testdata/rdatafields1.wire b/src/lib/dns/tests/testdata/rdatafields1.wire new file mode 100644 index 0000000..42028c1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields1.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from rdatafields1.spec +### + +# DNS Name: www.example.com +03777777076578616d706c6503636f6d00 + +# DNS Name: example.com +076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdatafields2.spec b/src/lib/dns/tests/testdata/rdatafields2.spec new file mode 100644 index 0000000..920dc95 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields2.spec @@ -0,0 +1,11 @@ +# +# A sequence of names that can be compressed. +# + +[custom] +sections: name/1:name/2 +[name/1] +name: www.example.com +[name/2] +name: '' +pointer: 4 diff --git a/src/lib/dns/tests/testdata/rdatafields2.wire b/src/lib/dns/tests/testdata/rdatafields2.wire new file mode 100644 index 0000000..cbb0ce0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields2.wire @@ -0,0 +1,9 @@ +### +### This data file was auto-generated from rdatafields2.spec +### + +# DNS Name: www.example.com +03777777076578616d706c6503636f6d00 + +# DNS Name: + compression pointer: 4 +c004 diff --git a/src/lib/dns/tests/testdata/rdatafields3.spec b/src/lib/dns/tests/testdata/rdatafields3.spec new file mode 100644 index 0000000..b37fca3 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields3.spec @@ -0,0 +1,11 @@ +# +# TXT RDATA with multiple character-strings. +# + +[custom] +sections: txt +[txt] +nstring: 3 +string0: 'first string' +string1: 'second string' +string2: 'last string' diff --git a/src/lib/dns/tests/testdata/rdatafields3.wire b/src/lib/dns/tests/testdata/rdatafields3.wire new file mode 100644 index 0000000..3ff32f1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields3.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from rdatafields3.spec +### + +# TXT RDATA, RDLEN=39 +0027 +# String Len=12, String="first string" +0c 666972737420737472696e67 +# String Len=13, String="second string" +0d 7365636f6e6420737472696e67 +# String Len=11, String="last string" +0b 6c61737420737472696e67 diff --git a/src/lib/dns/tests/testdata/rdatafields4.spec b/src/lib/dns/tests/testdata/rdatafields4.spec new file mode 100644 index 0000000..24b59aa --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields4.spec @@ -0,0 +1,7 @@ +# +# Simple form of RRSIG (all fields use the default of generator script) +# + +[custom] +sections: rrsig +[rrsig] diff --git a/src/lib/dns/tests/testdata/rdatafields4.wire b/src/lib/dns/tests/testdata/rdatafields4.wire new file mode 100644 index 0000000..e574426 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields4.wire @@ -0,0 +1,12 @@ +### +### This data file was auto-generated from rdatafields4.spec +### + +# RRSIG RDATA, RDLEN=46 +002e +# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 +0001 05 02 00000e10 +# Expiration=1264935600, Inception=1262343600 +4b6562b0 4b3dd5b0 +# Tag=4149 Signer=example.com and Signature +1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef diff --git a/src/lib/dns/tests/testdata/rdatafields5.spec b/src/lib/dns/tests/testdata/rdatafields5.spec new file mode 100644 index 0000000..2c78282 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields5.spec @@ -0,0 +1,12 @@ +# +# Names and RDATA (RRSIG) with an incompressible name. All names are +# rendered without compression. +# + +[custom] +sections: name/1:rrsig:name/2 +[name/1] +name: com +[rrsig] +[name/2] +name: www.example.com diff --git a/src/lib/dns/tests/testdata/rdatafields5.wire b/src/lib/dns/tests/testdata/rdatafields5.wire new file mode 100644 index 0000000..d0cebc9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields5.wire @@ -0,0 +1,18 @@ +### +### This data file was auto-generated from rdatafields5.spec +### + +# DNS Name: com +03636f6d00 + +# RRSIG RDATA, RDLEN=46 +002e +# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 +0001 05 02 00000e10 +# Expiration=1264935600, Inception=1262343600 +4b6562b0 4b3dd5b0 +# Tag=4149 Signer=example.com and Signature +1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef + +# DNS Name: www.example.com +03777777076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdatafields6.spec b/src/lib/dns/tests/testdata/rdatafields6.spec new file mode 100644 index 0000000..f9f0da1 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields6.spec @@ -0,0 +1,13 @@ +# +# Names and RDATA (RRSIG) with an incompressible name. The name in RRSIG +# isn't compressed, but it's used as the compression target. +# + +[custom] +sections: name/1:rrsig:name/2 +[name/1] +name: com +[rrsig] +[name/2] +name: www +pointer: 25 diff --git a/src/lib/dns/tests/testdata/rdatafields6.wire b/src/lib/dns/tests/testdata/rdatafields6.wire new file mode 100644 index 0000000..0e8b3d9 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdatafields6.wire @@ -0,0 +1,18 @@ +### +### This data file was auto-generated from rdatafields6.spec +### + +# DNS Name: com +03636f6d00 + +# RRSIG RDATA, RDLEN=46 +002e +# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 +0001 05 02 00000e10 +# Expiration=1264935600, Inception=1262343600 +4b6562b0 4b3dd5b0 +# Tag=4149 Signer=example.com and Signature +1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef + +# DNS Name: www + compression pointer: 25 +03777777c019 diff --git a/src/lib/dns/tests/testdata/rrcode16_fromWire1 b/src/lib/dns/tests/testdata/rrcode16_fromWire1 new file mode 100644 index 0000000..df2a177 --- /dev/null +++ b/src/lib/dns/tests/testdata/rrcode16_fromWire1 @@ -0,0 +1,4 @@ +# +# a 16 bit wire-format data (network byte order) +# +1234 diff --git a/src/lib/dns/tests/testdata/rrcode16_fromWire2 b/src/lib/dns/tests/testdata/rrcode16_fromWire2 new file mode 100644 index 0000000..fec2dd0 --- /dev/null +++ b/src/lib/dns/tests/testdata/rrcode16_fromWire2 @@ -0,0 +1,4 @@ +# +# an incomplete segment for a 16 bit wire-format data +# +12 diff --git a/src/lib/dns/tests/testdata/rrcode32_fromWire1 b/src/lib/dns/tests/testdata/rrcode32_fromWire1 new file mode 100644 index 0000000..fb2818f --- /dev/null +++ b/src/lib/dns/tests/testdata/rrcode32_fromWire1 @@ -0,0 +1,4 @@ +# +# a 32 bit wire-format data (network byte order) +# +12345678 diff --git a/src/lib/dns/tests/testdata/rrcode32_fromWire2 b/src/lib/dns/tests/testdata/rrcode32_fromWire2 new file mode 100644 index 0000000..504d9d2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rrcode32_fromWire2 @@ -0,0 +1,4 @@ +# +# an incomplete segment for a 32 bit wire-format data +# +123456 diff --git a/src/lib/dns/tests/testdata/rrset_toWire1 b/src/lib/dns/tests/testdata/rrset_toWire1 new file mode 100644 index 0000000..8f81a0e --- /dev/null +++ b/src/lib/dns/tests/testdata/rrset_toWire1 @@ -0,0 +1,23 @@ +# +# Rendering an IN/A RRset containing 2 RRs: +# test.example.com. 3600 IN A 192.0.2.1 +# test.example.com. 3600 IN A 192.0.2.2 +# +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: A = 1, IN = 1 +00 01 00 01 +# TTL: 3600 +00 00 0e 10 +#6 7 +# RDLENGTH: 4 +00 04 +# RDATA: 192.0.2.1 +c0 00 02 01 +# +# 2nd RR: mostly the same except the RDATA +04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +00 01 00 01 +00 00 0e 10 +00 04 +c0 00 02 02 diff --git a/src/lib/dns/tests/testdata/rrset_toWire2 b/src/lib/dns/tests/testdata/rrset_toWire2 new file mode 100644 index 0000000..ca6483f --- /dev/null +++ b/src/lib/dns/tests/testdata/rrset_toWire2 @@ -0,0 +1,38 @@ +# +# Rendering an IN/A RRset and NS RRset as follows: +# test.example.com. 3600 IN A 192.0.2.1 +# test.example.com. 3600 IN A 192.0.2.2 +# example.com. 1D IN NS ns.example.com. +# Names will be compressed when possible. +# +# 0 1 2 3 4 5 +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: A = 1, IN = 1 +00 01 00 01 +# TTL: 3600 +00 00 0e 10 +#6 7 +# RDLENGTH: 4 +00 04 +# RDATA: 192.0.2.1 +c0 00 02 01 +# +# 2nd RR: the owner name is compressed +c0 00 +00 01 00 01 +00 00 0e 10 +00 04 +c0 00 02 02 +# 3rd RR: the owner name and NS name are compressed +# pointing to the 5th octet of the owner name of the 1st RR +c0 05 +# type/class: NS = 2, IN = 1 +00 02 00 01 +# TTL: 1D = 86400sec = 0x15180 +00 01 51 80 +# RDLENGTH: 5 octets +00 05 +# NSDNAME: "ns." + compression pointer +#(2) n s + 02 6e 73 c0 05 diff --git a/src/lib/dns/tests/testdata/rrset_toWire3 b/src/lib/dns/tests/testdata/rrset_toWire3 new file mode 100644 index 0000000..47f8e6b --- /dev/null +++ b/src/lib/dns/tests/testdata/rrset_toWire3 @@ -0,0 +1,12 @@ +# +# Rendering an empty IN/A RRset +# +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: A = 1, ANY = 255 +00 01 00 ff +# TTL: 3600 +00 00 0e 10 +#6 7 +# RDLENGTH: 0 +00 00 diff --git a/src/lib/dns/tests/testdata/rrset_toWire4 b/src/lib/dns/tests/testdata/rrset_toWire4 new file mode 100644 index 0000000..6fb409c --- /dev/null +++ b/src/lib/dns/tests/testdata/rrset_toWire4 @@ -0,0 +1,12 @@ +# +# Rendering an empty IN/A RRset +# +#(4) t e s t (7) e x a m p l e (3) c o m . + 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +# type/class: A = 1, ANY = 255 +00 01 00 fe +# TTL: 3600 +00 00 0e 10 +#6 7 +# RDLENGTH: 0 +00 00 diff --git a/src/lib/dns/tests/testdata/tsig_verify1.spec b/src/lib/dns/tests/testdata/tsig_verify1.spec new file mode 100644 index 0000000..687013a --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify1.spec @@ -0,0 +1,19 @@ +# +# An example of signed AXFR request +# + +[custom] +sections: header:question:tsig +[header] +id: 0x3410 +arcount: 1 +[question] +rrtype: AXFR +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0x35b2fd08268781634400c7c8a5533b13 +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify1.wire b/src/lib/dns/tests/testdata/tsig_verify1.wire new file mode 100644 index 0000000..b2d12f1 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify1.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify1.spec +### + +# Header Section +# ID=13328 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) +3410 0000 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=example.com. QTYPE=AXFR(252) QCLASS=IN(1) +076578616d706c6503636f6d00 00fc 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302915409 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8e951 012c +# MAC Size=16 MAC=(see hex) +0010 35b2fd08268781634400c7c8a5533b13 +# Original-ID=13328 Error=0 +3410 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify10.spec b/src/lib/dns/tests/testdata/tsig_verify10.spec new file mode 100644 index 0000000..33ce83e --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify10.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with TSIG signed whose MAC is too short +# (only 1 byte) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 1 +mac: 0x22 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify10.wire b/src/lib/dns/tests/testdata/tsig_verify10.wire new file mode 100644 index 0000000..9ffe4ea --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify10.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify10.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=43) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 002b +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=1 MAC=(see hex) +0001 22 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify11.spec b/src/lib/dns/tests/testdata/tsig_verify11.spec new file mode 100644 index 0000000..9927b48 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify11.spec @@ -0,0 +1,24 @@ +# +# A simple DNS query message with TSIG signed with truncated MAC +# using common HMAC-SHA512-256 +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-sha512 +time_signed: 0x4da8877a +#mac_size: 64 +mac_size: 32 +#mac: 0xc4bc4053572d62dd1b26998111565c18056be773dedc6ecea60dff31db2f25966e5d9bafbaaed56efbd5ee2d7e2a12ede4caad630ddf69846c980409724da34e +mac: 0xc4bc4053572d62dd1b26998111565c18056be773dedc6ecea60dff31db2f2596 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify11.wire b/src/lib/dns/tests/testdata/tsig_verify11.wire new file mode 100644 index 0000000..f0a8f4f --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify11.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify11.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=61) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003d +# Algorithm=hmac-sha512 Time-Signed=1302890362 Fudge=300 +0b686d61632d73686135313200 00004da8877a 012c +# MAC Size=32 MAC=(see hex) +0020 c4bc4053572d62dd1b26998111565c18056be773dedc6ecea60dff31db2f2596 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify2.spec b/src/lib/dns/tests/testdata/tsig_verify2.spec new file mode 100644 index 0000000..ff98ca3 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify2.spec @@ -0,0 +1,32 @@ +# +# An example of signed AXFR response +# + +[custom] +sections: header:question:soa:tsig +[header] +id: 0x3410 +aa: 1 +qr: 1 +ancount: 1 +arcount: 1 +[question] +rrtype: AXFR +[soa] +# note that names are compressed in this RR +as_rr: True +rr_name: ptr=12 +mname: ns.ptr=12 +rname: root.ptr=12 +serial: 2011041503 +refresh: 7200 +retry: 3600 +expire: 2592000 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0xbdd612cd2c7f9e0648bd6dc23713e83c +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify2.wire b/src/lib/dns/tests/testdata/tsig_verify2.wire new file mode 100644 index 0000000..65f5f89 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify2.wire @@ -0,0 +1,31 @@ +### +### This data file was auto-generated from tsig_verify2.spec +### + +# Header Section +# ID=13328 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA +3410 8400 +# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=1 +0001 0001 0000 0001 + +# Question Section +# QNAME=example.com. QTYPE=AXFR(252) QCLASS=IN(1) +076578616d706c6503636f6d00 00fc 0001 + +# SOA RR (QNAME=ptr=12 Class=IN(1) TTL=86400, RDLEN=32) +c00c 0006 0001 00015180 0020 +# NNAME=ns.ptr=12 RNAME=root.ptr=12 +026e73c00c 04726f6f74c00c +# SERIAL(2011041503) REFRESH(7200) RETRY(3600) EXPIRE(2592000) MINIMUM(1200) +77de0edf 00001c20 00000e10 00278d00 000004b0 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302915409 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8e951 012c +# MAC Size=16 MAC=(see hex) +0010 bdd612cd2c7f9e0648bd6dc23713e83c +# Original-ID=13328 Error=0 +3410 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify3.spec b/src/lib/dns/tests/testdata/tsig_verify3.spec new file mode 100644 index 0000000..7e2f797 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify3.spec @@ -0,0 +1,26 @@ +# +# An example of signed AXFR response (continued) +# + +[custom] +sections: header:ns:tsig +[header] +id: 0x3410 +aa: 1 +qr: 1 +qdcount: 0 +ancount: 1 +arcount: 1 +[ns] +# note that names are compressed in this RR +as_rr: True +rr_name: example.com. +nsname: ns.ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0x102458f7f62ddd7d638d746034130968 +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify3.wire b/src/lib/dns/tests/testdata/tsig_verify3.wire new file mode 100644 index 0000000..c479ac3 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify3.wire @@ -0,0 +1,25 @@ +### +### This data file was auto-generated from tsig_verify3.spec +### + +# Header Section +# ID=13328 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA +3410 8400 +# QDCNT=0, ANCNT=1, NSCNT=0, ARCNT=1 +0000 0001 0000 0001 + +# NS RR (QNAME=example.com. Class=IN(1) TTL=86400, RDLEN=5) +076578616d706c6503636f6d00 0002 0001 00015180 0005 +# NS name=ns.ptr=12 +026e73c00c + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302915409 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8e951 012c +# MAC Size=16 MAC=(see hex) +0010 102458f7f62ddd7d638d746034130968 +# Original-ID=13328 Error=0 +3410 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify4.spec b/src/lib/dns/tests/testdata/tsig_verify4.spec new file mode 100644 index 0000000..4ffbbcf --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify4.spec @@ -0,0 +1,27 @@ +# +# An example of signed DNS response with bogus MAC +# + +[custom] +sections: header:question:a:tsig +[header] +id: 0x2d65 +aa: 1 +qr: 1 +rd: 1 +ancount: 1 +arcount: 1 +[question] +name: www.example.com +[a] +as_rr: True +rr_name: ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +# bogus MAC +mac: 0xdeadbeefdeadbeefdeadbeefdeadbeef +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify4.wire b/src/lib/dns/tests/testdata/tsig_verify4.wire new file mode 100644 index 0000000..de5e050 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify4.wire @@ -0,0 +1,29 @@ +### +### This data file was auto-generated from tsig_verify4.spec +### + +# Header Section +# ID=11621 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA RD +2d65 8500 +# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=1 +0001 0001 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=ptr=12 Class=IN(1) TTL=86400, RDLEN=4) +c00c 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 deadbeefdeadbeefdeadbeefdeadbeef +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify5.spec b/src/lib/dns/tests/testdata/tsig_verify5.spec new file mode 100644 index 0000000..a6cc643 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify5.spec @@ -0,0 +1,26 @@ +# +# An example of signed DNS response +# + +[custom] +sections: header:question:a:tsig +[header] +id: 0x2d65 +aa: 1 +qr: 1 +rd: 1 +ancount: 1 +arcount: 1 +[question] +name: www.example.com +[a] +as_rr: True +rr_name: ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x8fcda66a7cd1a3b9948eb1869d384a9f +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify5.wire b/src/lib/dns/tests/testdata/tsig_verify5.wire new file mode 100644 index 0000000..3cfb89b --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify5.wire @@ -0,0 +1,29 @@ +### +### This data file was auto-generated from tsig_verify5.spec +### + +# Header Section +# ID=11621 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0) AA RD +2d65 8500 +# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=1 +0001 0001 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# A RR (QNAME=ptr=12 Class=IN(1) TTL=86400, RDLEN=4) +c00c 0001 0001 00015180 0004 +# Address=192.0.2.1 +c0000201 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 8fcda66a7cd1a3b9948eb1869d384a9f +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify6.spec b/src/lib/dns/tests/testdata/tsig_verify6.spec new file mode 100644 index 0000000..32e0818 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify6.spec @@ -0,0 +1,21 @@ +# +# Forwarded DNS query message with TSIG signed (header ID != orig ID) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x1035 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify6.wire b/src/lib/dns/tests/testdata/tsig_verify6.wire new file mode 100644 index 0000000..9e3e5b4 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify6.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify6.spec +### + +# Header Section +# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +1035 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify7.spec b/src/lib/dns/tests/testdata/tsig_verify7.spec new file mode 100644 index 0000000..377578e --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify7.spec @@ -0,0 +1,21 @@ +# +# DNS query message with TSIG that has empty MAC (invalidly) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 0 +mac: '' +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify7.wire b/src/lib/dns/tests/testdata/tsig_verify7.wire new file mode 100644 index 0000000..4de7d24 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify7.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify7.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=42) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 002a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify8.spec b/src/lib/dns/tests/testdata/tsig_verify8.spec new file mode 100644 index 0000000..5432d4a --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify8.spec @@ -0,0 +1,23 @@ +# +# DNS query message with TSIG that has empty MAC + BADKEY error +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 0 +mac: '' +# 17: BADKEY +error: 17 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify8.wire b/src/lib/dns/tests/testdata/tsig_verify8.wire new file mode 100644 index 0000000..50845eb --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify8.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify8.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=42) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 002a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=0 MAC=(see hex) +0000 +# Original-ID=11621 Error=17 +2d65 0011 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsig_verify9.spec b/src/lib/dns/tests/testdata/tsig_verify9.spec new file mode 100644 index 0000000..5888455 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify9.spec @@ -0,0 +1,21 @@ +# +# A simple DNS query message with TSIG signed, but TSIG key and algorithm +# names have upper case characters (unusual) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: WWW.EXAMPLE.COM +algorithm: HMAC-MD5.SIG-ALG.REG.INT +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify9.wire b/src/lib/dns/tests/testdata/tsig_verify9.wire new file mode 100644 index 0000000..163fcee --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify9.wire @@ -0,0 +1,24 @@ +### +### This data file was auto-generated from tsig_verify9.spec +### + +# Header Section +# ID=11621 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0) RD +2d65 0100 +# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1 +0001 0000 0000 0001 + +# Question Section +# QNAME=www.example.com QTYPE=A(1) QCLASS=IN(1) +03777777076578616d706c6503636f6d00 0001 0001 + +# TSIG RR (QNAME=WWW.EXAMPLE.COM Class=ANY(255) TTL=0, RDLEN=58) +03575757074558414d504c4503434f4d00 00fa 00ff 00000000 003a +# Algorithm=HMAC-MD5.SIG-ALG.REG.INT Time-Signed=1302890362 Fudge=300 +08484d41432d4d4435075349472d414c470352454703494e5400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 227026ad297beee721ce6c6fff1e9ef3 +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec new file mode 100644 index 0000000..a25dc46 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec @@ -0,0 +1,16 @@ +# +# A simple TSIG RR (some of the parameters are taken from a live example +# and don't have a specific meaning) +# + +[custom] +sections: tsig +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0xdadadadadadadadadadadadadadadada +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire1.wire b/src/lib/dns/tests/testdata/tsigrecord_toWire1.wire new file mode 100644 index 0000000..a125e3d --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire1.wire @@ -0,0 +1,14 @@ +### +### This data file was auto-generated from tsigrecord_toWire1.spec +### + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 dadadadadadadadadadadadadadadada +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec new file mode 100644 index 0000000..f667e4c --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec @@ -0,0 +1,19 @@ +# +# TSIG RR after some names that could (unexpectedly) cause name compression +# + +[custom] +sections: name/1:name/2:tsig +[name/1] +name: hmac-md5.sig-alg.reg.int +[name/2] +name: foo.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0xdadadadadadadadadadadadadadadada +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire2.wire b/src/lib/dns/tests/testdata/tsigrecord_toWire2.wire new file mode 100644 index 0000000..980e1f1 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire2.wire @@ -0,0 +1,20 @@ +### +### This data file was auto-generated from tsigrecord_toWire2.spec +### + +# DNS Name: hmac-md5.sig-alg.reg.int +08686d61632d6d6435077369672d616c670372656703696e7400 + +# DNS Name: foo.example.com +03666f6f076578616d706c6503636f6d00 + +# TSIG RR (QNAME=www.example.com Class=ANY(255) TTL=0, RDLEN=58) +03777777076578616d706c6503636f6d00 00fa 00ff 00000000 003a +# Algorithm=hmac-md5 Time-Signed=1302890362 Fudge=300 +08686d61632d6d6435077369672d616c670372656703696e7400 00004da8877a 012c +# MAC Size=16 MAC=(see hex) +0010 dadadadadadadadadadadadadadadada +# Original-ID=11621 Error=0 +2d65 0000 +# Other-Len=0 Other-Data=(see hex) +0000 diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc new file mode 100644 index 0000000..85b7553 --- /dev/null +++ b/src/lib/dns/tests/tsig_unittest.cc @@ -0,0 +1,1187 @@ +// Copyright (C) 2011-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 <config.h> + +#include <time.h> +#include <string> +#include <stdexcept> +#include <vector> + +#include <boost/scoped_ptr.hpp> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/base64.h> +#include <util/unittests/newhook.h> +#include <util/time_utilities.h> + +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/question.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> +#include <dns/tsigrecord.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +// @note: blocks and SCOPED_TRACE can make buggy cppchecks raise +// a spurious syntax error... + +// See dnssectime.cc +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +namespace { +// See dnssectime_unittest.cc +template <int64_t NOW> +int64_t +testGetTime() { + return (NOW); +} + +// Thin wrapper around TSIGContext to allow access to the +// update method. +class TestTSIGContext : public TSIGContext { +public: + TestTSIGContext(const TSIGKey& key) : + TSIGContext(key) + {} + TestTSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring) : + TSIGContext(key_name, algorithm_name, keyring) + {} + void update(const void* const data, size_t len) { + TSIGContext::update(data, len); + } +}; + +class TSIGTest : public ::testing::Test { +protected: + TSIGTest() : + tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"), + badkey_name("badkey.example.com"), test_class(RRClass::IN()), + test_ttl(86400), message(Message::RENDER), + dummy_data(1024, 0xdd), // should be sufficiently large for all tests + dummy_record(badkey_name, any::TSIG(TSIGKey::HMACMD5_NAME(), + 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + 0, NULL, qid, 0, 0, NULL)) + { + // Make sure we use the system time by default so that we won't be + // confused due to other tests that tweak the time. + isc::util::detail::gettimeFunction = NULL; + + decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret); + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], + secret.size()))); + tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], + secret.size()))); + } + ~TSIGTest() { + isc::util::detail::gettimeFunction = NULL; + } + + // Many of the tests below create some DNS message and sign it under + // some specific TSIG context. This helper method unifies the common + // logic with slightly different parameters. + ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname, + TSIGContext* ctx, + unsigned int message_flags = + RD_FLAG, + RRType qtype = RRType::A(), + const char* answer_data = NULL, + const RRType* answer_type = NULL, + bool add_question = true, + Rcode rcode = Rcode::NOERROR()); + + void createMessageFromFile(const char* datafile); + + // bit-wise constant flags to configure DNS header flags for test + // messages. + static const unsigned int QR_FLAG = 0x1; + static const unsigned int AA_FLAG = 0x2; + static const unsigned int RD_FLAG = 0x4; + + boost::scoped_ptr<TestTSIGContext> tsig_ctx; + boost::scoped_ptr<TSIGContext> tsig_verify_ctx; + TSIGKeyRing keyring; + const uint16_t qid; + const Name test_name; + const Name badkey_name; + const RRClass test_class; + const RRTTL test_ttl; + Message message; + MessageRenderer renderer; + vector<uint8_t> secret; + vector<uint8_t> dummy_data; + const TSIGRecord dummy_record; + vector<uint8_t> received_data; +}; + +ConstTSIGRecordPtr +TSIGTest::createMessageAndSign(uint16_t id, const Name& qname, + TSIGContext* ctx, unsigned int message_flags, + RRType qtype, const char* answer_data, + const RRType* answer_type, bool add_question, + Rcode rcode) +{ + message.clear(Message::RENDER); + message.setQid(id); + message.setOpcode(Opcode::QUERY()); + message.setRcode(rcode); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + if (add_question) { + message.addQuestion(Question(qname, test_class, qtype)); + } + if (answer_data != NULL) { + if (answer_type == NULL) { + answer_type = &qtype; + } + RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type, + test_ttl)); + answer_rrset->addRdata(createRdata(*answer_type, test_class, + answer_data)); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + } + renderer.clear(); + + TSIGContext::State expected_new_state = + (ctx->getState() == TSIGContext::INIT) ? + TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE; + + message.toWire(renderer, ctx); + + message.clear(Message::PARSE); + InputBuffer buffer(renderer.getData(), renderer.getLength()); + message.fromWire(buffer); + + EXPECT_EQ(expected_new_state, ctx->getState()); + + return (ConstTSIGRecordPtr(new TSIGRecord(*message.getTSIGRecord()))); +} + +void +TSIGTest::createMessageFromFile(const char* datafile) { + message.clear(Message::PARSE); + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message.fromWire(buffer); +} + +void +commonSignChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid, + uint64_t expected_timesigned, + const uint8_t* expected_mac, size_t expected_maclen, + uint16_t expected_error = 0, + uint16_t expected_otherlen = 0, + const uint8_t* expected_otherdata = NULL, + const Name& expected_algorithm = TSIGKey::HMACMD5_NAME()) +{ + ASSERT_TRUE(tsig != NULL); + const any::TSIG& tsig_rdata = tsig->getRdata(); + + EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm()); + EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned()); + EXPECT_EQ(300, tsig_rdata.getFudge()); + EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize()); + matchWireData(expected_mac, expected_maclen, + tsig_rdata.getMAC(), tsig_rdata.getMACSize()); + + EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID()); + EXPECT_EQ(expected_error, tsig_rdata.getError()); + EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen()); + matchWireData(expected_otherdata, expected_otherlen, + tsig_rdata.getOtherData(), tsig_rdata.getOtherLen()); +} + +void +commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record, + const void* data, size_t data_len, TSIGError expected_error, + TSIGContext::State expected_new_state = + TSIGContext::VERIFIED_RESPONSE, + bool last_should_throw = false) +{ + EXPECT_EQ(expected_error, ctx.verify(record, data, data_len)); + EXPECT_EQ(expected_error, ctx.getError()); + EXPECT_EQ(expected_new_state, ctx.getState()); + if (last_should_throw) { + EXPECT_THROW(ctx.lastHadSignature(), TSIGContextError); + } else { + EXPECT_EQ(record != NULL, ctx.lastHadSignature()); + } +} + +TEST_F(TSIGTest, initialState) { + // Until signing or verifying, the state should be INIT + EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState()); + + // And there should be no error code. + EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError()); + + // Nothing verified yet + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); +} + +TEST_F(TSIGTest, constructFromKeyRing) { + // Construct a TSIG context with an empty key ring. Key shouldn't be + // found, and the BAD_KEY error should be recorded. + TSIGContext ctx1(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx1.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx1.getError()); + + // Add a matching key (we don't use the secret so leave it empty), and + // construct it again. This time it should be constructed with a valid + // key. + keyring.add(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), NULL, 0)); + TSIGContext ctx2(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx2.getState()); + EXPECT_EQ(TSIGError::NOERROR(), ctx2.getError()); + + // Similar to the first case except that the key ring isn't empty but + // it doesn't contain a matching key. + TSIGContext ctx3(test_name, TSIGKey::HMACSHA1_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx3.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx3.getError()); + + TSIGContext ctx4(Name("different-key.example"), TSIGKey::HMACMD5_NAME(), + keyring); + EXPECT_EQ(TSIGContext::INIT, ctx4.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx4.getError()); + + // "Unknown" algorithm name will result in BADKEY, too. + TSIGContext ctx5(test_name, Name("unknown.algorithm"), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx5.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx5.getError()); +} + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com +// QID: 0x2d65 +// Time Signed: 0x00004da8877a +// MAC: 227026ad297beee721ce6c6fff1e9ef3 +const uint8_t common_expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 +}; +TEST_F(TSIGTest, sign) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + { + SCOPED_TRACE("Sign test for query"); + commonSignChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()), + qid, 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same test as sign, but specifying the key name with upper-case (i.e. +// non canonical) characters. The digest must be the same. It should actually +// be ensured at the level of TSIGKey, but we confirm that at this level, too. +TEST_F(TSIGTest, signUsingUpperCasedKeyName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"), + TSIGKey::HMACMD5_NAME(), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical key name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same as the previous test, but for the algorithm name. +TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(test_name, + Name("HMAC-md5.SIG-alg.REG.int"), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical algorithm name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +TEST_F(TSIGTest, signAtActualTime) { + // Sign the message using the actual time, and check the accuracy of it. + // We cannot reasonably predict the expected MAC, so don't bother to + // check it. + const uint64_t now = static_cast<uint64_t>(time(NULL)); + + { + SCOPED_TRACE("Sign test for query at actual time"); + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + const any::TSIG& tsig_rdata = tsig->getRdata(); + + // Check the resulted time signed is in the range of [now, now + 5] + // (5 is an arbitrary choice). Note that due to the order of the call + // to time() and sign(), time signed must not be smaller than the + // current time. + EXPECT_LE(now, tsig_rdata.getTimeSigned()); + EXPECT_GE(now + 5, tsig_rdata.getTimeSigned()); + } +} + +TEST_F(TSIGTest, signBadData) { + // some specific bad data should be rejected proactively. + const unsigned char dummy_data = 0; + EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter); + EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter); +} + +TEST_F(TSIGTest, verifyBadData) { + // the data must at least hold the DNS message header and the specified + // TSIG. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, &dummy_data[0], + 12 + dummy_record.getLength() - 1), + InvalidParameter); + + // Still nothing verified + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); + + // And the data must not be NULL. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL, + 12 + dummy_record.getLength()), + InvalidParameter); + + // Still nothing verified + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); + +} + +#ifdef ENABLE_CUSTOM_OPERATOR_NEW +// We enable this test only when we enable custom new/delete at build time +// We could enable/disable the test runtime using the gtest filter, but +// we'd basically like to minimize the number of disabled tests (they +// should generally be considered tests that temporarily fail and should +// be fixed). +TEST_F(TSIGTest, signExceptionSafety) { + // Check sign() provides the strong exception guarantee for the simpler + // case (with a key error and empty MAC). The general case is more + // complicated and involves more memory allocation, so the test result + // won't be reliable. + + commonVerifyChecks(*tsig_verify_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + + try { + int dummydata; + isc::util::unittests::force_throw_on_new = true; + isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord); + tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata)); + isc::util::unittests::force_throw_on_new = false; + ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen"; + } catch (const std::bad_alloc&) { + isc::util::unittests::force_throw_on_new = false; + + // sign() threw, so the state should still be RECEIVED_REQUEST + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + } + isc::util::unittests::force_throw_on_new = false; +} +#endif // ENABLE_CUSTOM_OPERATOR_NEW + +// Same test as "sign" but use a different algorithm just to confirm we don't +// naively hardcode constants specific to a particular algorithm. +// Test data generated by +// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com" +// QID: 0x0967, RDflag +// Current Time: 00004da8be86 +// Time Signed: 00004dae7d5f +// HMAC Size: 20 +// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3 +TEST_F(TSIGTest, signUsingHMACSHA1) { + isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>; + + secret.clear(); + decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret); + TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(), + &secret[0], secret.size())); + + const uint16_t sha1_qid = 0x0967; + const uint8_t expected_mac[] = { + 0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e, + 0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3 + }; + { + SCOPED_TRACE("Sign test using HMAC-SHA1"); + commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), + sha1_qid, 0x4dae7d5f, expected_mac, + sizeof(expected_mac), 0, 0, NULL, + TSIGKey::HMACSHA1_NAME()); + } +} + +TEST_F(TSIGTest, signUsingHMACSHA224) { + isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>; + + secret.clear(); + decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret); + TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA224_NAME(), + &secret[0], secret.size())); + + const uint16_t sha1_qid = 0x0967; + const uint8_t expected_mac[] = { + 0x3b, 0x93, 0xd3, 0xc5, 0xf9, 0x64, 0xb9, 0xc5, 0x00, 0x35, + 0x02, 0x69, 0x9f, 0xfc, 0x44, 0xd6, 0xe2, 0x66, 0xf4, 0x08, + 0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3 + }; + { + SCOPED_TRACE("Sign test using HMAC-SHA224"); + commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), + sha1_qid, 0x4dae7d5f, expected_mac, + sizeof(expected_mac), 0, 0, NULL, + TSIGKey::HMACSHA224_NAME()); + } +} + +// The first part of this test checks verifying the signed query used for +// the "sign" test. +// The second part of this test generates a signed response to the signed +// query as follows: +// Answer: www.example.com. 86400 IN A 192.0.2.1 +// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f +TEST_F(TSIGTest, verifyThenSignResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("message_toWire2.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } + + // Transform the original message to a response, then sign the response + // with the context of "verified state". + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::A(), "192.0.2.1"); + const uint8_t expected_mac[] = { + 0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9, + 0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f + }; + { + SCOPED_TRACE("Sign test for response"); + commonSignChecks(tsig, qid, 0x4da8877a, expected_mac, + sizeof(expected_mac)); + } +} + +TEST_F(TSIGTest, verifyUpperCaseNames) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("tsig_verify9.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyForwardedMessage) { + // Similar to the first part of the previous test, but this test emulates + // the "forward" case, where the ID of the Header and the original ID in + // TSIG is different. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageFromFile("tsig_verify6.wire"); + { + SCOPED_TRACE("Verify test for forwarded request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +// Example of signing multiple messages in a single TCP stream, +// taken from data using BIND 9's "one-answer" transfer-format. +// Request: +// QID: 0x3410, flags (none) +// Question: example.com/IN/AXFR +// Time Signed: 0x4da8e951 +// MAC: 35b2fd08268781634400c7c8a5533b13 +// First message: +// QID: 0x3410, flags QR, AA +// Question: example.com/IN/AXFR +// Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. ( +// 2011041503 7200 3600 2592000 1200) +// MAC: bdd612cd2c7f9e0648bd6dc23713e83c +// Second message: +// Answer: example.com. 86400 IN NS ns.example.com. +// MAC: 102458f7f62ddd7d638d746034130968 +TEST_F(TSIGTest, signContinuation) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8e951>; + + const uint16_t axfr_qid = 0x3410; + const Name zone_name("example.com"); + + // Create and sign the AXFR request + ConstTSIGRecordPtr tsig = createMessageAndSign(axfr_qid, zone_name, + tsig_ctx.get(), 0, + RRType::AXFR()); + // Then verify it (the wire format test data should contain the same + // message data, and verification should succeed). + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify1.wire", received_data); + { + SCOPED_TRACE("Verify AXFR query"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR(), + TSIGContext::RECEIVED_REQUEST); + } + + // Create and sign the first response message + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType::AXFR(), + "ns.example.com. root.example.com. " + "2011041503 7200 3600 2592000 1200", + &RRType::SOA()); + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify2.wire", received_data); + { + SCOPED_TRACE("Verify first AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } + + // Create and sign the second response message + const uint8_t expected_mac[] = { + 0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d, + 0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68 + }; + { + SCOPED_TRACE("Sign test for continued response in TCP stream"); + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType::AXFR(), + "ns.example.com.", &RRType::NS(), false); + commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac, + sizeof(expected_mac)); + } + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify3.wire", received_data); + { + SCOPED_TRACE("Verify second AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } +} + +// BADTIME example, taken from data using specially hacked BIND 9's nsupdate +// Query: +// QID: 0x1830, RD flag +// Current Time: 00004da8be86 +// Time Signed: 00004da8b9d6 +// Question: www.example.com/IN/SOA +//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68 +// Response: +// QRbit, RCODE=9(NOTAUTH) +// Time Signed: 00004da8b9d6 (the one in the query) +// MAC: d4b043f6f44495ec8a01260e39159d76 +// Error: 0x12 (BADTIME), Other Len: 6 +// Other data: 00004da8be86 +TEST_F(TSIGTest, badtimeResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + const uint16_t test_qid = 0x7fc4; + ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8be86>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + + // make and sign a response in the context of TSIG error. + tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(), + QR_FLAG, RRType::SOA(), NULL, NULL, + true, Rcode::NOTAUTH()); + const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 }; + const uint8_t expected_mac[] = { + 0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec, + 0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76 + }; + { + SCOPED_TRACE("Sign test for response with BADTIME"); + commonSignChecks(tsig, message.getQid(), 0x4da8b9d6, + expected_mac, sizeof(expected_mac), + 18, // error: BADTIME + sizeof(expected_otherdata), + expected_otherdata); + } +} + +TEST_F(TSIGTest, badtimeResponse2) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "rewind the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to too future SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, badtimeBoundaries) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + // Test various boundary conditions. We intentionally use the magic + // number of 300 instead of the constant variable for testing. + // In the okay cases, signature is not correct, but it's sufficient to + // check the error code isn't BADTIME for the purpose of this test. + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badtimeOverflow) { + isc::util::detail::gettimeFunction = testGetTime<200>; + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // This should be in the okay range, but since "200 - fudge" overflows + // and we compare them as 64-bit unsigned integers, it results in a false + // positive (we intentionally accept that). + isc::util::detail::gettimeFunction = testGetTime<100>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badsigResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // Try to sign a simple message with bogus secret. It should fail + // with BADSIG. + createMessageFromFile("message_toWire2.wire"); + TSIGContext bad_ctx(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), + &dummy_data[0], dummy_data.size())); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(bad_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // Sign the same message (which doesn't matter for this test) with the + // context of "checked state". + { + SCOPED_TRACE("Sign test for response with BADSIG error"); + commonSignChecks(createMessageAndSign(qid, test_name, &bad_ctx), + message.getQid(), 0x4da8877a, NULL, 0, + 16); // 16: BADSIG + } +} + +TEST_F(TSIGTest, badkeyResponse) { + // A similar test as badsigResponse but for BADKEY + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + { + SCOPED_TRACE("Verify resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + } + + { + SCOPED_TRACE("Sign test for response with BADKEY error"); + ConstTSIGRecordPtr sig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + EXPECT_EQ(badkey_name, sig->getName()); + commonSignChecks(sig, qid, 0x4da8877a, NULL, 0, 17); // 17: BADKEY + } +} + +TEST_F(TSIGTest, badkeyForResponse) { + // "BADKEY" case for a response to a signed message + createMessageAndSign(qid, test_name, tsig_ctx.get()); + { + SCOPED_TRACE("Verify a response resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } + + // A similar case with a different algorithm + const TSIGRecord dummy_record2(test_name, + any::TSIG(TSIGKey::HMACSHA1_NAME(), + 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + 0, NULL, qid, 0, 0, NULL)); + { + SCOPED_TRACE("Verify a response resulting in BADKEY due to bad alg"); + commonVerifyChecks(*tsig_ctx, &dummy_record2, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } +} + +TEST_F(TSIGTest, badsigThenValidate) { + // According to RFC2845 4.6, if TSIG verification fails the client + // should discard that message and wait for another signed response. + // This test emulates that situation. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + createMessageFromFile("tsig_verify4.wire"); + { + SCOPED_TRACE("Verify a response that should fail due to BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::SENT_REQUEST); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADSIG failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, nosigThenValidate) { + // Similar to the previous test, but the first response doesn't contain + // TSIG. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + { + SCOPED_TRACE("Verify a response without TSIG that should exist"); + commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0], + dummy_data.size(), TSIGError::FORMERR(), + TSIGContext::SENT_REQUEST, true); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a FORMERR failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, badtimeThenValidate) { + // Similar to the previous test, but the first response results in BADTIME. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a + 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::SENT_REQUEST); + } + + // revert the clock again. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADTIME failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, emptyMAC) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY. + createMessageFromFile("tsig_verify7.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // If the empty MAC comes with a BADKEY error, the error is passed + // transparently. + createMessageFromFile("tsig_verify8.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_KEY(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyAfterSendResponse) { + // Once the context is used for sending a signed response, it shouldn't + // be used for further verification. + + // The following are essentially the same as what verifyThenSignResponse + // does with simplification. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_verify_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + createMessageAndSign(qid, test_name, tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, RRType::A(), "192.0.2.1"); + EXPECT_EQ(TSIGContext::SENT_RESPONSE, tsig_verify_ctx->getState()); + + // Now trying further verification. + createMessageFromFile("message_toWire2.wire"); + EXPECT_THROW(tsig_verify_ctx->verify(message.getTSIGRecord(), + &received_data[0], + received_data.size()), + TSIGContextError); +} + +TEST_F(TSIGTest, signAfterVerified) { + // Likewise, once the context verifies a response, it shouldn't for + // signing any more. + + // The following are borrowed from badsigThenValidate (without the + // intermediate failure) + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageAndSign(qid, test_name, tsig_ctx.get()); + createMessageFromFile("tsig_verify5.wire"); + tsig_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::VERIFIED_RESPONSE, tsig_ctx->getState()); + + // Now trying further signing. + EXPECT_THROW(createMessageAndSign(qid, test_name, tsig_ctx.get()), + TSIGContextError); +} + +TEST_F(TSIGTest, tooShortMAC) { + // Too short MAC should be rejected. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify10.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::FORMERR(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, truncatedMAC) { + // Check truncated MAC support with HMAC-SHA512-256 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + secret.clear(); + decodeBase64("jI/Pa4qRu96t76Pns5Z/Ndxbn3QCkwcxLOgt9vgvnJw5wqTRvNyk3FtD6yIMd1dWVlqZ+Y4fe6Uasc0ckctEmg==", secret); + TSIGContext sha_ctx(TSIGKey(test_name, TSIGKey::HMACSHA512_NAME(), + &secret[0], secret.size(), 256)); + + createMessageFromFile("tsig_verify11.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(sha_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } + + // Try with HMAC-SHA512-264 (should fail) + TSIGContext bad_sha_ctx(TSIGKey(test_name, TSIGKey::HMACSHA512_NAME(), + &secret[0], secret.size(), 264)); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(bad_sha_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_TRUNC(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, getTSIGLength) { + // Check for the most common case with various algorithms + // See the comment in TSIGContext::getTSIGLength() for calculation and + // parameter notation. + // The key name (www.example.com) is the same for most cases, where n1=17 + + // hmac-md5.sig-alg.reg.int.: n2=26, x=16 + EXPECT_EQ(85, tsig_ctx->getTSIGLength()); + + // hmac-md5-80: n2=26, x=10 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], 10, 80))); + EXPECT_EQ(79, tsig_ctx->getTSIGLength()); + + // hmac-sha1: n2=11, x=20 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA1_NAME(), + &dummy_data[0], 20))); + EXPECT_EQ(74, tsig_ctx->getTSIGLength()); + + // hmac-sha256: n2=13, x=32 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA256_NAME(), + &dummy_data[0], 32))); + EXPECT_EQ(88, tsig_ctx->getTSIGLength()); + + // hmac-sha224: n2=13, x=28 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA224_NAME(), + &dummy_data[0], 28))); + EXPECT_EQ(84, tsig_ctx->getTSIGLength()); + + // hmac-sha384: n2=13, x=48 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA384_NAME(), + &dummy_data[0], 48))); + EXPECT_EQ(104, tsig_ctx->getTSIGLength()); + + // hmac-sha512: n2=13, x=64 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA512_NAME(), + &dummy_data[0], 64))); + EXPECT_EQ(120, tsig_ctx->getTSIGLength()); + + // hmac-sha512-256: n2=13, x=32 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA512_NAME(), + &dummy_data[0], 32, 256))); + EXPECT_EQ(88, tsig_ctx->getTSIGLength()); + + // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0 + tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + EXPECT_EQ(72, tsig_ctx->getTSIGLength()); + + // bad sig case: n1=17, n2=26, x=0 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(69, tsig_ctx->getTSIGLength()); + + // bad time case: n1=17, n2=26, x=16, y=6 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>; + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADTIME"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(91, tsig_ctx->getTSIGLength()); +} + +// Verify a stream of multiple messages. Some of them have a signature omitted. +// +// We have two contexts, one that signs, another that verifies. +TEST_F(TSIGTest, verifyMulti) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // First, send query from the verify one to the normal one, so + // we initialize something like AXFR + { + SCOPED_TRACE("Query"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_verify_ctx.get()); + commonVerifyChecks(*tsig_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::RECEIVED_REQUEST); + } + + { + SCOPED_TRACE("First message"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Second message"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Third message. Unsigned."); + // Another message does not carry the TSIG on it. But it should + // be OK, it's in the middle of stream. + message.clear(Message::RENDER); + message.setQid(1234); + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(), + test_ttl)); + answer_rrset->addRdata(createRdata(RRType::A(), test_class, + "192.0.2.1")); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + message.toWire(renderer); + // Update the internal state. We abuse the knowledge of + // internals here a little bit to generate correct test data + tsig_ctx->update(renderer.getData(), renderer.getLength()); + + commonVerifyChecks(*tsig_verify_ctx, NULL, + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + + EXPECT_FALSE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Fourth message. Signed again."); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Filling in bunch of unsigned messages"); + for (size_t i = 0; i < 100; ++i) { + SCOPED_TRACE(i); + // Another message does not carry the TSIG on it. But it should + // be OK, it's in the middle of stream. + message.clear(Message::RENDER); + message.setQid(1234); + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(), + test_ttl)); + answer_rrset->addRdata(createRdata(RRType::A(), test_class, + "192.0.2.1")); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + message.toWire(renderer); + // Update the internal state. We abuse the knowledge of + // internals here a little bit to generate correct test data + tsig_ctx->update(renderer.getData(), renderer.getLength()); + + // 99 unsigned messages is OK. But the 100th must be signed, according + // to the RFC2845, section 4.4 + commonVerifyChecks(*tsig_verify_ctx, NULL, + renderer.getData(), renderer.getLength(), + i == 99 ? TSIGError::FORMERR() : + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + + EXPECT_FALSE(tsig_verify_ctx->lastHadSignature()); + } + } +} + +} // end namespace diff --git a/src/lib/dns/tests/tsigerror_unittest.cc b/src/lib/dns/tests/tsigerror_unittest.cc new file mode 100644 index 0000000..e50f076 --- /dev/null +++ b/src/lib/dns/tests/tsigerror_unittest.cc @@ -0,0 +1,126 @@ +// Copyright (C) 2011-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 <config.h> + +#include <string> +#include <ostream> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> +#include <dns/tsigerror.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; + +namespace { +TEST(TSIGErrorTest, constructFromErrorCode) { + // These are pretty trivial, and also test getCode(); + EXPECT_EQ(0, TSIGError(0).getCode()); + EXPECT_EQ(18, TSIGError(18).getCode()); + EXPECT_EQ(65535, TSIGError(65535).getCode()); +} + +TEST(TSIGErrorTest, constructFromRcode) { + // We use RCODE for code values from 0-15. + EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode()); + EXPECT_EQ(15, TSIGError(Rcode(15)).getCode()); + + // From error code 16 TSIG errors define a separate space, so passing + // corresponding RCODE for such code values should be prohibited. + EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange); +} + +TEST(TSIGErrorTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode()); + EXPECT_EQ(TSIGError::BAD_MODE_CODE, TSIGError(19).getCode()); + EXPECT_EQ(TSIGError::BAD_NAME_CODE, TSIGError(20).getCode()); + EXPECT_EQ(TSIGError::BAD_ALG_CODE, TSIGError(21).getCode()); + EXPECT_EQ(TSIGError::BAD_TRUNC_CODE, TSIGError(22).getCode()); + + EXPECT_EQ(0, TSIGError::NOERROR().getCode()); + EXPECT_EQ(9, TSIGError::NOTAUTH().getCode()); + EXPECT_EQ(14, TSIGError::RESERVED14().getCode()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode()); + EXPECT_EQ(TSIGError::BAD_MODE_CODE, TSIGError::BAD_MODE().getCode()); + EXPECT_EQ(TSIGError::BAD_NAME_CODE, TSIGError::BAD_NAME().getCode()); + EXPECT_EQ(TSIGError::BAD_ALG_CODE, TSIGError::BAD_ALG().getCode()); + EXPECT_EQ(TSIGError::BAD_TRUNC_CODE, TSIGError::BAD_TRUNC().getCode()); +} + +TEST(TSIGErrorTest, equal) { + EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR()); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + + EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16)); + EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG()); + EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16))); + EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG())); +} + +TEST(TSIGErrorTest, nequal) { + EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY()); + EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY())); +} + +TEST(TSIGErrorTest, toText) { + // TSIGError derived from the standard Rcode + EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText()); + + // Well known TSIG errors + EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText()); + EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText()); + EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText()); + EXPECT_EQ("BADMODE", TSIGError::BAD_MODE().toText()); + EXPECT_EQ("BADNAME", TSIGError::BAD_NAME().toText()); + EXPECT_EQ("BADALG", TSIGError::BAD_ALG().toText()); + EXPECT_EQ("BADTRUNC", TSIGError::BAD_TRUNC().toText()); + + // Unknown (or not yet supported) codes. Simply converted as numeric. + EXPECT_EQ("23", TSIGError(23).toText()); + EXPECT_EQ("65535", TSIGError(65535).toText()); +} + +TEST(TSIGErrorTest, toRcode) { + // TSIGError derived from the standard Rcode + EXPECT_EQ(Rcode::NOERROR(), TSIGError(Rcode::NOERROR()).toRcode()); + + // Well known TSIG errors + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_SIG().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_KEY().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TIME().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_MODE().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_NAME().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_ALG().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TRUNC().toRcode()); + + // Unknown (or not yet supported) codes are treated as SERVFAIL. + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(23).toRcode()); + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(65535).toRcode()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(TSIGErrorTest, LeftShiftOperator) { + ostringstream oss; + oss << TSIGError::BAD_KEY(); + EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc new file mode 100644 index 0000000..90c59ac --- /dev/null +++ b/src/lib/dns/tests/tsigkey_unittest.cc @@ -0,0 +1,350 @@ +// Copyright (C) 2021-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 <config.h> + +#include <string> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <cryptolink/cryptolink.h> + +#include <dns/tsigkey.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class TSIGKeyTest : public ::testing::Test { +protected: + TSIGKeyTest() : secret("someRandomData"), key_name("example.com") {} + string secret; + const Name key_name; +}; + +TEST_F(TSIGKeyTest, algorithmNames) { + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME()); + EXPECT_EQ(Name("hmac-md5"), TSIGKey::HMACMD5_SHORT_NAME()); + EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME()); + EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME()); + EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME()); + EXPECT_EQ(Name("hmac-sha384"), TSIGKey::HMACSHA384_NAME()); + EXPECT_EQ(Name("hmac-sha512"), TSIGKey::HMACSHA512_NAME()); + EXPECT_EQ(Name("gss-tsig"), TSIGKey::GSSTSIG_NAME()); + + // Also check conversion to cryptolink definitions + EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::MD5, + TSIGKey(key_name, TSIGKey::HMACMD5_SHORT_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name, + TSIGKey::HMACSHA256_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA224, TSIGKey(key_name, + TSIGKey::HMACSHA224_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA384, TSIGKey(key_name, + TSIGKey::HMACSHA384_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA512, TSIGKey(key_name, + TSIGKey::HMACSHA512_NAME(), + NULL, 0).getAlgorithm()); +} + +TEST_F(TSIGKeyTest, construct) { + TSIGKey key(key_name, TSIGKey::HMACMD5_NAME(), + secret.c_str(), secret.size()); + EXPECT_EQ(key_name, key.getKeyName()); + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName()); + matchWireData(secret.c_str(), secret.size(), + key.getSecret(), key.getSecretLength()); + + TSIGKey key_short_md5(key_name, TSIGKey::HMACMD5_SHORT_NAME(), + secret.c_str(), secret.size()); + EXPECT_EQ(key_name, key_short_md5.getKeyName()); + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), + key_short_md5.getAlgorithmName()); + matchWireData(secret.c_str(), secret.size(), + key_short_md5.getSecret(), key_short_md5.getSecretLength()); + + // "unknown" algorithm is only accepted with empty secret. + EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"), + secret.c_str(), secret.size()), + isc::InvalidParameter); + TSIGKey key2(key_name, Name("unknown-alg"), NULL, 0); + EXPECT_EQ(key_name, key2.getKeyName()); + EXPECT_EQ(Name("unknown-alg"), key2.getAlgorithmName()); + + // The algorithm name should be converted to the canonical form. + EXPECT_EQ("hmac-sha1.", + TSIGKey(key_name, Name("HMAC-sha1"), + secret.c_str(), + secret.size()).getAlgorithmName().toText()); + + // Same for key name + EXPECT_EQ("example.com.", + TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(), + secret.c_str(), + secret.size()).getKeyName().toText()); + + // Check digestbits + EXPECT_EQ(key.getDigestbits(), 0); + TSIGKey key_trunc(key_name, TSIGKey::HMACMD5_NAME(), + secret.c_str(), secret.size(), 120); + EXPECT_EQ(key_trunc.getDigestbits(), 120); + + // Invalid combinations of secret and secret_len: + EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0), + isc::InvalidParameter); + EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), NULL, 16), + isc::InvalidParameter); + + // Empty secret + TSIGKey keye = TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), NULL, 0); + EXPECT_EQ(keye.getSecretLength(), 0); + EXPECT_EQ(keye.getSecret(), (const void*)0); +} + +void +compareTSIGKeys(const TSIGKey& expect, const TSIGKey& actual) { + EXPECT_EQ(expect.getKeyName(), actual.getKeyName()); + EXPECT_EQ(expect.getAlgorithmName(), actual.getAlgorithmName()); + EXPECT_EQ(expect.getDigestbits(), actual.getDigestbits()); + matchWireData(expect.getSecret(), expect.getSecretLength(), + actual.getSecret(), actual.getSecretLength()); +} + +TEST_F(TSIGKeyTest, copyConstruct) { + const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(), + secret.c_str(), secret.size(), 128); + const TSIGKey copy(original); + compareTSIGKeys(original, copy); + + // Check the copied data is valid even after the original is deleted + TSIGKey* copy2 = new TSIGKey(original); + TSIGKey copy3(*copy2); + delete copy2; + compareTSIGKeys(original, copy3); +} + +TEST_F(TSIGKeyTest, assignment) { + const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(), + secret.c_str(), secret.size(), 200); + TSIGKey copy = original; + compareTSIGKeys(original, copy); + + // Check if the copied data is valid even after the original is deleted + TSIGKey* copy2 = new TSIGKey(original); + TSIGKey copy3(original); + copy3 = *copy2; + delete copy2; + compareTSIGKeys(original, copy3); + + // Self assignment + copy = *© + compareTSIGKeys(original, copy); +} + +class TSIGKeyRingTest : public ::testing::Test { +protected: + TSIGKeyRingTest() : + key_name("example.com"), + md5_name("hmac-md5.sig-alg.reg.int"), + sha1_name("hmac-sha1"), + sha256_name("hmac-sha256"), + secretstring("anotherRandomData"), + secret(secretstring.c_str()), + secret_len(secretstring.size()) + {} + TSIGKeyRing keyring; + const Name key_name; + const Name md5_name; + const Name sha1_name; + const Name sha256_name; +private: + const string secretstring; +protected: + const char* secret; + size_t secret_len; +}; + +TEST_F(TSIGKeyRingTest, init) { + EXPECT_EQ(0, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, add) { + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(1, keyring.size()); + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + // keys are identified by their names, the same name of key with a + // different algorithm would be considered a duplicate. + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(Name("example.com"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + // names are compared in a case insensitive manner. + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(Name("EXAMPLE.COM"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + EXPECT_EQ(1, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, addMore) { + // essentially the same test, but try adding more than 1 + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + EXPECT_EQ(3, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, remove) { + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(key_name)); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(key_name)); +} + +TEST_F(TSIGKeyRingTest, removeFromSome) { + // essentially the same test, but try removing from a larger set + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(Name("another.example"))); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(Name("noexist.example"))); + EXPECT_EQ(2, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, find) { + // If the keyring is empty the search should fail. + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name, md5_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(key_name, md5_name).key); + + // Add a key and try to find it. Should succeed. + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + const TSIGKeyRing::FindResult result1(keyring.find(key_name, sha256_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result1.code); + EXPECT_EQ(key_name, result1.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result1.key->getAlgorithmName()); + matchWireData(secret, secret_len, + result1.key->getSecret(), result1.key->getSecretLength()); + + // If either key name or algorithm doesn't match, search should fail. + const TSIGKeyRing::FindResult result2 = + keyring.find(Name("different-key.example"), sha256_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result2.code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result2.key); + + const TSIGKeyRing::FindResult result3 = keyring.find(key_name, md5_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result3.code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result3.key); + + // But with just the name it should work + const TSIGKeyRing::FindResult result4(keyring.find(key_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result4.code); + EXPECT_EQ(key_name, result4.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result4.key->getAlgorithmName()); + matchWireData(secret, secret_len, + result4.key->getSecret(), result4.key->getSecretLength()); +} + +TEST_F(TSIGKeyRingTest, findFromSome) { + // essentially the same test, but search a larger set + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("another.example"), + md5_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("more.example"), + sha1_name, + secret, secret_len))); + + const TSIGKeyRing::FindResult result( + keyring.find(Name("another.example"), md5_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code); + EXPECT_EQ(Name("another.example"), result.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACMD5_NAME(), result.key->getAlgorithmName()); + + EXPECT_EQ(TSIGKeyRing::NOTFOUND, + keyring.find(Name("noexist.example"), sha1_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(Name("noexist.example"), sha256_name).key); + + EXPECT_EQ(TSIGKeyRing::NOTFOUND, + keyring.find(Name("another.example"), sha1_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(Name("another.example"), sha256_name).key); +} + +TEST(TSIGStringTest, TSIGKeyFromToString) { + TSIGKey k1 = TSIGKey("test.example:MSG6Ng==:hmac-md5.sig-alg.reg.int"); + TSIGKey k2 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int."); + TSIGKey k3 = TSIGKey("test.example:MSG6Ng=="); + TSIGKey k4 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:120"); + TSIGKey k5 = TSIGKey(Name("test.example."), Name("hmac-sha1."), NULL, 0); + // "Unknown" key with empty secret is okay + TSIGKey k6 = TSIGKey("test.example.::unknown"); + + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k1.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k2.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k3.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:120", + k4.toText()); + EXPECT_EQ(120, k4.getDigestbits()); + EXPECT_EQ("test.example.::hmac-sha1.", k5.toText()); + EXPECT_EQ(Name("test.example."), k6.getKeyName()); + EXPECT_EQ(Name("unknown"), k6.getAlgorithmName()); + + EXPECT_THROW(TSIGKey(""), isc::InvalidParameter); + EXPECT_THROW(TSIGKey(":"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("..:aa:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example:xxxx:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:unknown"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:"), + isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:xxx"), + isc::InvalidParameter); +} + + +} // end namespace diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc new file mode 100644 index 0000000..d2686fe --- /dev/null +++ b/src/lib/dns/tests/tsigrecord_unittest.cc @@ -0,0 +1,152 @@ +// Copyright (C) 2011-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 <config.h> + +#include <vector> +#include <sstream> + +#include <gtest/gtest.h> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> +#include <dns/tsigrecord.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class TSIGRecordTest : public ::testing::Test { +protected: + TSIGRecordTest() : + test_name("www.example.com"), test_mac(16, 0xda), + test_rdata(any::TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + test_mac.size(), &test_mac[0], + 0x2d65, 0, 0, NULL)), + test_record(test_name, test_rdata), + buffer(0) + {} + const Name test_name; + vector<unsigned char> test_mac; + const any::TSIG test_rdata; + const TSIGRecord test_record; + OutputBuffer buffer; + MessageRenderer renderer; + vector<unsigned char> data; +}; + +TEST_F(TSIGRecordTest, getName) { + EXPECT_EQ(test_name, test_record.getName()); +} + +TEST_F(TSIGRecordTest, getLength) { + // 85 = 17 + 26 + 16 + 26 + // len(www.example.com) = 17 + // len(hmac-md5.sig-alg.reg.int) = 26 + // len(MAC) = 16 + // the rest are fixed length fields (26 in total) + EXPECT_EQ(85, test_record.getLength()); +} + +TEST_F(TSIGRecordTest, fromParams) { + // Construct the same TSIG RR as test_record from parameters. + // See the getLength test for the magic number of 85 (although it + // actually doesn't matter) + const TSIGRecord record(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 85); + // Perform straight sanity checks + EXPECT_EQ(test_name, record.getName()); + EXPECT_EQ(85, record.getLength()); + EXPECT_EQ(0, test_rdata.compare(record.getRdata())); + + // The constructor doesn't check the length... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 82)); + // ...even for impossibly small values... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 1)); + // ...or too large values. + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 65536)); + + // RDATA must indeed be TSIG + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), in::A("192.0.2.1"), 85), + DNSMessageFORMERR); + + // Unexpected class + EXPECT_THROW(TSIGRecord(test_name, RRClass::IN(), TSIGRecord::getTTL(), + test_rdata, 85), DNSMessageFORMERR); + + // Unexpected TTL + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + RRTTL(3600), test_rdata, 85), DNSMessageFORMERR); +} + +TEST_F(TSIGRecordTest, recordToWire) { + UnitTestUtil::readWireData("tsigrecord_toWire1.wire", data); + EXPECT_EQ(1, test_record.toWire(renderer)); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); + + // Same test for a dumb buffer + buffer.clear(); + EXPECT_EQ(1, test_record.toWire(buffer)); + matchWireData(&data[0], data.size(), + buffer.getData(), buffer.getLength()); +} + +TEST_F(TSIGRecordTest, recordToOLongToWire) { + // By setting the limit to "record length - 1", it will fail, and the + // renderer will be marked as "truncated". + renderer.setLengthLimit(test_record.getLength() - 1); + EXPECT_FALSE(renderer.isTruncated()); // not marked before render attempt + EXPECT_EQ(0, test_record.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); +} + +TEST_F(TSIGRecordTest, recordToWireAfterNames) { + // A similar test but the TSIG RR follows some domain names that could + // cause name compression inside TSIG. Our implementation shouldn't + // compress either owner (key) name or the algorithm name. This test + // confirms that. + + UnitTestUtil::readWireData("tsigrecord_toWire2.wire", data); + renderer.writeName(TSIGKey::HMACMD5_NAME()); + renderer.writeName(Name("foo.example.com")); + EXPECT_EQ(1, test_record.toWire(renderer)); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(TSIGRecordTest, toText) { + EXPECT_EQ("www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. " + "1302890362 300 16 2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n", + test_record.toText()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(TSIGRecordTest, LeftShiftOperator) { + ostringstream oss; + oss << test_record; + EXPECT_EQ(test_record.toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc new file mode 100644 index 0000000..b234c1c --- /dev/null +++ b/src/lib/dns/tests/unittest_util.cc @@ -0,0 +1,176 @@ +// 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 <config.h> + +#include <iostream> +#include <fstream> +#include <sstream> +#include <stdexcept> +#include <vector> +#include <string> + +#include <gtest/gtest.h> + +#include <dns/rcode.h> +#include <dns/name.h> +#include <dns/message.h> +#include <dns/tests/unittest_util.h> + +using namespace std; +using namespace isc::dns; + +using isc::UnitTestUtil; + +namespace { +class UnitTestUtilConfig { +private: + // This is a singleton object and cannot be constructed explicitly. + UnitTestUtilConfig() {} + UnitTestUtilConfig(const UnitTestUtilConfig& source); + ~UnitTestUtilConfig() {} +public: + /// Return a singleton unit test configuration object. On first invocation + /// one will be constructed. + static UnitTestUtilConfig& getConfig(); + + /// A list of paths to wire data files. + /// \c UnitTestUtil::readWireData() (first version) + /// will search the directories in this list for the specified data file. + std::vector<string> data_paths_; +}; + +UnitTestUtilConfig& +UnitTestUtilConfig::getConfig() { + static UnitTestUtilConfig config; + return (config); +} +} + +void +UnitTestUtil::readWireData(const char* datafile, vector<unsigned char>& data) { + ifstream ifs; + + const UnitTestUtilConfig& config = UnitTestUtilConfig::getConfig(); + vector<string>::const_iterator it = config.data_paths_.begin(); + for (; it != config.data_paths_.end(); ++it) { + string data_path = *it; + if (data_path.empty() || *data_path.rbegin() != '/') { + data_path.push_back('/'); + } + ifs.open((data_path + datafile).c_str(), ios_base::in); + if ((ifs.rdstate() & istream::failbit) == 0) { + break; + } + } + + if (it == config.data_paths_.end()) { + throw runtime_error("failed to open data file in data paths: " + + string(datafile)); + } + + data.clear(); + + string s; + while (getline(ifs, s), !ifs.eof()) { + if (ifs.bad() || ifs.fail()) { + throw runtime_error("unexpected data line"); + } + if (s.empty() || s[0] == '#') { + continue; + } + + readWireData(s, data); + } +} + +void +UnitTestUtil::addDataPath(const string& directory) { + UnitTestUtilConfig::getConfig().data_paths_.push_back(directory); +} + +void +UnitTestUtil::readWireData(const string& datastr, + vector<unsigned char>& data) +{ + istringstream iss(datastr); + + do { + string bytes; + iss >> bytes; + if (iss.bad() || iss.fail() || (bytes.size() % 2) != 0) { + ostringstream err_oss; + err_oss << "unexpected input or I/O error in reading " << + datastr; + throw runtime_error(err_oss.str()); + } + + for (string::size_type pos = 0; pos < bytes.size(); pos += 2) { + istringstream iss_byte(bytes.substr(pos, 2)); + unsigned int ch; + + iss_byte >> hex >> ch; + if (iss_byte.rdstate() != istream::eofbit) { + ostringstream err_oss; + err_oss << "invalid byte representation: " << iss_byte.str(); + throw runtime_error(err_oss.str()); + } + data.push_back(static_cast<unsigned char>(ch)); + } + } while (!iss.eof()); +} + +::testing::AssertionResult +UnitTestUtil::matchName(const char*, const char*, + const isc::dns::Name& name1, + const isc::dns::Name& name2) +{ + ::testing::Message msg; + + NameComparisonResult cmpresult = name1.compare(name2); + if (cmpresult.getOrder() != 0 || + cmpresult.getRelation() != NameComparisonResult::EQUAL) { + msg << "Two names are expected to be equal but not:\n" + << " One: " << name1 << "\n" + << "Other: " << name2 << "\n"; + return (::testing::AssertionFailure(msg)); + } + return (::testing::AssertionSuccess()); +} + +void +UnitTestUtil::createRequestMessage(Message& message, + const Opcode& opcode, + const uint16_t qid, + const Name& name, + const RRClass& rrclass, + const RRType& rrtype) +{ + message.clear(Message::RENDER); + message.setOpcode(opcode); + message.setRcode(Rcode::NOERROR()); + message.setQid(qid); + message.addQuestion(Question(name, rrclass, rrtype)); +} + +void +UnitTestUtil::createDNSSECRequestMessage(Message& message, + const Opcode& opcode, + const uint16_t qid, + const Name& name, + const RRClass& rrclass, + const RRType& rrtype) +{ + message.clear(Message::RENDER); + message.setOpcode(opcode); + message.setRcode(Rcode::NOERROR()); + message.setQid(qid); + message.addQuestion(Question(name, rrclass, rrtype)); + EDNSPtr edns(new EDNS()); + edns->setUDPSize(4096); + edns->setDNSSECAwareness(true); + message.setEDNS(edns); +} diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h new file mode 100644 index 0000000..8c6b8f5 --- /dev/null +++ b/src/lib/dns/tests/unittest_util.h @@ -0,0 +1,88 @@ +// 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 UNITTEST_UTIL_H +#define UNITTEST_UTIL_H 1 + +#include <vector> +#include <string> + +#include <dns/name.h> +#include <dns/message.h> + +#include <gtest/gtest.h> + +namespace isc { + +class UnitTestUtil { +public: + /// + /// read text format wire data from a file and put it to the given vector. + /// + static void readWireData(const char* datafile, + std::vector<unsigned char>& data); + + /// + /// add a path that \c readWireData() will search for test data files. + /// + static void addDataPath(const std::string& directory); + + /// + /// convert a sequence of hex strings into the corresponding list of + /// 8-bit integers, and append them to the vector. + /// + static void readWireData(const std::string& datastr, + std::vector<unsigned char>& data); + + /// + /// Compare two names. + /// + /// This check method uses \c Name::compare() for comparison, which performs + /// deeper checks including the equality of offsets, and should be better + /// than EXPECT_EQ, which uses operator==. Like the \c matchWireData() + /// method, the usage is a bit awkward; the caller should use + /// \c EXPECT_PRED_FORMAT2. + /// + static ::testing::AssertionResult + matchName(const char* nameexp1, const char* nameexp2, + const isc::dns::Name& name1, const isc::dns::Name& name2); + + /// + /// Populate a request message + /// + /// Create a request message in 'request_message' using the + /// opcode 'opcode' and the name/class/type query tuple specified in + /// 'name', 'rrclass' and 'rrtype. + static void + createRequestMessage(isc::dns::Message& request_message, + const isc::dns::Opcode& opcode, + const uint16_t qid, + const isc::dns::Name& name, + const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype); + + /// + /// Populate a DNSSEC request message + /// + /// Create a request message in 'request_message' using the + /// opcode 'opcode' and the name/class/type query tuple specified in + /// 'name', 'rrclass' and 'rrtype. + /// EDNS will be added with DO=1 and bufsize 4096 + static void + createDNSSECRequestMessage(isc::dns::Message& request_message, + const isc::dns::Opcode& opcode, + const uint16_t qid, + const isc::dns::Name& name, + const isc::dns::RRClass& rrclass, + const isc::dns::RRType& rrtype); + +}; +} +#endif // UNITTEST_UTIL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/zone_checker_unittest.cc b/src/lib/dns/tests/zone_checker_unittest.cc new file mode 100644 index 0000000..863b439 --- /dev/null +++ b/src/lib/dns/tests/zone_checker_unittest.cc @@ -0,0 +1,349 @@ +// 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 <config.h> + +#include <dns/zone_checker.h> + +#include <exceptions/exceptions.h> + +#include <dns/name.h> +#include <dns/rrclass.h> +#include <dns/rrset.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rdataclass.h> +#include <dns/rrset_collection.h> + +#include <gtest/gtest.h> + +#include <boost/scoped_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <string> +#include <sstream> +#include <vector> + +using isc::Unexpected; +using namespace isc::dns; +using namespace isc::dns::rdata; +namespace ph = std::placeholders; + +namespace { + +const char* const soa_txt = "ns.example.com. root.example.com. 0 0 0 0 0"; +const char* const ns_txt1 = "ns.example.com."; +const char* const ns_a_txt1 = "192.0.2.1"; +const char* const ns_txt2 = "ns2.example.com."; +const char* const ns_a_txt2 = "192.0.2.2"; + +class ZoneCheckerTest : public ::testing::Test { +protected: + ZoneCheckerTest() : + zname_("example.com"), zclass_(RRClass::IN()), + soa_(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60))), + ns_(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))), + callbacks_(std::bind(&ZoneCheckerTest::callback, this, ph::_1, true), + std::bind(&ZoneCheckerTest::callback, this, ph::_1, false)) + { + std::stringstream ss; + ss << "example.com. 60 IN SOA " << soa_txt << "\n"; + ss << "example.com. 60 IN NS " << ns_txt1 << "\n"; + ss << "ns.example.com. 60 IN A " << ns_a_txt1 << "\n"; + ss << "ns2.example.com. 60 IN A " << ns_a_txt2 << "\n"; + rrsets_.reset(new RRsetCollection(ss, zname_, zclass_)); + } + +public: + // This one is passed to std::bind. Some compilers seem to require + // it be public. + void callback(const std::string& reason, bool is_error) { + if (is_error) { + errors_.push_back(reason); + } else { + warns_.push_back(reason); + } + } + +protected: + // Check stored issue messages with expected ones. Clear vectors so + // the caller can check other cases. + void checkIssues() { + EXPECT_EQ(expected_errors_.size(), errors_.size()); + for (size_t i = 0; + i < std::min(expected_errors_.size(), errors_.size()); + ++i) { + // The actual message should begin with the expected message. + EXPECT_EQ(0, errors_[0].find(expected_errors_[0])) + << "actual message: " << errors_[0] << " expected: " << + expected_errors_[0]; + } + EXPECT_EQ(expected_warns_.size(), warns_.size()); + for (size_t i = 0; + i < std::min(expected_warns_.size(), warns_.size()); + ++i) { + EXPECT_EQ(0, warns_[0].find(expected_warns_[0])) + << "actual message: " << warns_[0] << " expected: " << + expected_warns_[0]; + } + + errors_.clear(); + expected_errors_.clear(); + warns_.clear(); + expected_warns_.clear(); + } + + const Name zname_; + const RRClass zclass_; + boost::scoped_ptr<RRsetCollection> rrsets_; + RRsetPtr soa_; + RRsetPtr ns_; + std::vector<std::string> errors_; + std::vector<std::string> warns_; + std::vector<std::string> expected_errors_; + std::vector<std::string> expected_warns_; + ZoneCheckerCallbacks callbacks_; +}; + +TEST_F(ZoneCheckerTest, checkGood) { + // Checking a valid case. No errors or warnings should be reported. + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Multiple NS RRs are okay. + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_->addRdata(generic::NS(ns_txt1)); + ns_->addRdata(generic::NS(ns_txt2)); + rrsets_->addRRset(ns_); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); +} + +TEST_F(ZoneCheckerTest, checkSOA) { + // If the zone has no SOA it triggers an error. + rrsets_->removeRRset(zname_, zclass_, RRType::SOA()); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: has 0 SOA records"); + checkIssues(); + + // If null callback is specified, checkZone() only returns the final + // result. + ZoneCheckerCallbacks noerror_callbacks( + 0, std::bind(&ZoneCheckerTest::callback, this, ph::_1, false)); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, noerror_callbacks)); + checkIssues(); + + // If there are more than 1 SOA RR, it's also an error. + errors_.clear(); + soa_->addRdata(generic::SOA(soa_txt)); + soa_->addRdata(generic::SOA("ns2.example.com. . 0 0 0 0 0")); + rrsets_->addRRset(soa_); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: has 2 SOA records"); + checkIssues(); + + // If the SOA RRset is "empty", it's treated as an implementation + // (rather than operational) error and results in an exception. + rrsets_->removeRRset(zname_, zclass_, RRType::SOA()); + soa_.reset(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60))); + rrsets_->addRRset(soa_); + EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected); + checkIssues(); // no error/warning should be reported + + // Likewise, if the SOA RRset contains non SOA Rdata, it should be a bug. + rrsets_->removeRRset(zname_, zclass_, RRType::SOA()); + soa_.reset(new RRset(zname_, zclass_, RRType::SOA(), RRTTL(60))); + soa_->addRdata(createRdata(RRType::NS(), zclass_, "ns.example.com.")); + rrsets_->addRRset(soa_); + EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected); + checkIssues(); // no error/warning should be reported +} + +TEST_F(ZoneCheckerTest, checkNS) { + // If the zone has no NS at origin it triggers an error. + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: has no NS records"); + checkIssues(); + + // Check two buggy cases like the SOA tests + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + rrsets_->addRRset(ns_); + EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected); + checkIssues(); // no error/warning should be reported + + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + ns_->addRdata(createRdata(RRType::TXT(), zclass_, "ns.example.com")); + rrsets_->addRRset(ns_); + EXPECT_THROW(checkZone(zname_, zclass_, *rrsets_, callbacks_), Unexpected); + checkIssues(); // no error/warning should be reported +} + +TEST_F(ZoneCheckerTest, checkNSData) { + const Name ns_name("ns.example.com"); + + // If a ("in-bailiwick") NS name doesn't have an address record, it's + // reported as a warning. + rrsets_->removeRRset(ns_name, zclass_, RRType::A()); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_warns_.push_back("zone example.com/IN: NS has no address"); + checkIssues(); + + // Same check, but disabling warning callback. Same result, but without + // the warning. + ZoneCheckerCallbacks nowarn_callbacks( + std::bind(&ZoneCheckerTest::callback, this, ph::_1, true), 0); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, nowarn_callbacks)); + checkIssues(); + + // A tricky case: if the name matches a wildcard, it should technically + // be considered valid, but this checker doesn't check that far and still + // warns. + RRsetPtr wild(new RRset(Name("*.example.com"), zclass_, RRType::A(), + RRTTL(0))); + wild->addRdata(in::A("192.0.2.255")); + rrsets_->addRRset(wild); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_warns_.push_back("zone example.com/IN: NS has no address"); + checkIssues(); + + // If there's a CNAME at the name instead, it's an error. + rrsets_->removeRRset(Name("*.example.com"), zclass_, RRType::A()); + RRsetPtr cname(new RRset(ns_name, zclass_, RRType::CNAME(), RRTTL(60))); + cname->addRdata(generic::CNAME("cname.example.com.")); + rrsets_->addRRset(cname); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: NS 'ns.example.com' is " + "a CNAME (illegal per RFC2181)"); + checkIssues(); + + // It doesn't have to be A. An AAAA is enough. + rrsets_->removeRRset(ns_name, zclass_, RRType::CNAME()); + RRsetPtr aaaa(new RRset(ns_name, zclass_, RRType::AAAA(), RRTTL(60))); + aaaa->addRdata(in::AAAA("2001:db8::1")); + rrsets_->addRRset(aaaa); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Coexisting CNAME makes it error (CNAME with other record is itself + // invalid, but it's a different issue in this context) + rrsets_->addRRset(cname); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: NS 'ns.example.com' is " + "a CNAME (illegal per RFC2181)"); + checkIssues(); + + // It doesn't matter if the NS name is "out of bailiwick". + rrsets_->removeRRset(ns_name, zclass_, RRType::CNAME()); + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + ns_->addRdata(generic::NS("ns.example.org.")); + rrsets_->addRRset(ns_); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Note that if the NS name is the origin name, it should be checked + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + ns_->addRdata(generic::NS(zname_)); + rrsets_->addRRset(ns_); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_warns_.push_back("zone example.com/IN: NS has no address"); + checkIssues(); +} + +TEST_F(ZoneCheckerTest, checkNSWithDelegation) { + // Tests various cases where there's a zone cut due to delegation between + // the zone origin and the NS name. In each case the NS name doesn't have + // an address record. + const Name ns_name("ns.child.example.com"); + + // Zone cut due to delegation in the middle; the check for the address + // record should be skipped. + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + ns_->addRdata(generic::NS(ns_name)); + rrsets_->addRRset(ns_); + RRsetPtr child_ns(new RRset(Name("child.example.com"), zclass_, + RRType::NS(), RRTTL(60))); + child_ns->addRdata(generic::NS("ns.example.org.")); + rrsets_->addRRset(child_ns); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Zone cut at the NS name. Same result. + rrsets_->removeRRset(child_ns->getName(), zclass_, RRType::NS()); + child_ns.reset(new RRset(ns_name, zclass_, RRType::NS(), RRTTL(60))); + child_ns->addRdata(generic::NS("ns.example.org.")); + rrsets_->addRRset(child_ns); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Zone cut below the NS name. The check applies. + rrsets_->removeRRset(child_ns->getName(), zclass_, RRType::NS()); + child_ns.reset(new RRset(Name("another.ns.child.example.com"), zclass_, + RRType::NS(), RRTTL(60))); + child_ns->addRdata(generic::NS("ns.example.org.")); + rrsets_->addRRset(child_ns); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_warns_.push_back("zone example.com/IN: NS has no address"); + checkIssues(); +} + +TEST_F(ZoneCheckerTest, checkNSWithDNAME) { + // Similar to the above case, but the zone cut is due to DNAME. This is + // an invalid configuration. + const Name ns_name("ns.child.example.com"); + + // Zone cut due to DNAME at the zone origin. This is an invalid case. + rrsets_->removeRRset(zname_, zclass_, RRType::NS()); + ns_.reset(new RRset(zname_, zclass_, RRType::NS(), RRTTL(60))); + ns_->addRdata(generic::NS(ns_name)); + rrsets_->addRRset(ns_); + RRsetPtr dname(new RRset(zname_, zclass_, RRType::DNAME(), RRTTL(60))); + dname->addRdata(generic::DNAME("example.org.")); + rrsets_->addRRset(dname); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: NS 'ns.child.example.com'" + " is below a DNAME 'example.com'"); + checkIssues(); + + // Zone cut due to DNAME in the middle. Same result. + rrsets_->removeRRset(zname_, zclass_, RRType::DNAME()); + dname.reset(new RRset(Name("child.example.com"), zclass_, RRType::DNAME(), + RRTTL(60))); + dname->addRdata(generic::DNAME("example.org.")); + rrsets_->addRRset(dname); + EXPECT_FALSE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_errors_.push_back("zone example.com/IN: NS 'ns.child.example.com'" + " is below a DNAME 'child.example.com'"); + checkIssues(); + + // A tricky case: there's also an NS at the name that has DNAME. It's + // prohibited per RFC6672 so we could say it's "undefined". Nevertheless, + // this implementation prefers the NS and skips further checks. + ns_.reset(new RRset(Name("child.example.com"), zclass_, RRType::NS(), + RRTTL(60))); + ns_->addRdata(generic::NS("ns.example.org.")); + rrsets_->addRRset(ns_); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + checkIssues(); + + // Zone cut due to DNAME at the NS name. In this case DNAME doesn't + // affect the NS name, so it should result in "no address record" warning. + rrsets_->removeRRset(dname->getName(), zclass_, RRType::DNAME()); + rrsets_->removeRRset(ns_->getName(), zclass_, RRType::NS()); + dname.reset(new RRset(ns_name, zclass_, RRType::DNAME(), RRTTL(60))); + dname->addRdata(generic::DNAME("example.org.")); + rrsets_->addRRset(dname); + EXPECT_TRUE(checkZone(zname_, zclass_, *rrsets_, callbacks_)); + expected_warns_.push_back("zone example.com/IN: NS has no address"); + checkIssues(); +} + +} diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc new file mode 100644 index 0000000..d7c85de --- /dev/null +++ b/src/lib/dns/tsig.cc @@ -0,0 +1,581 @@ +// 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/. + +#include <config.h> + +#include <sys/time.h> + +#include <stdint.h> + +#include <cassert> +#include <vector> + +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> + +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/tsig.h> +#include <dns/tsigerror.h> +#include <dns/tsigkey.h> + +#include <cryptolink/cryptolink.h> +#include <cryptolink/crypto_hmac.h> + +using namespace std; +using namespace isc::util; +using namespace isc::cryptolink; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { +namespace { +typedef boost::shared_ptr<HMAC> HMACPtr; + +// TSIG uses 48-bit unsigned integer to represent time signed. +// Since gettimeWrapper() returns a 64-bit *signed* integer, we +// make sure it's stored in an unsigned 64-bit integer variable and +// represents a value in the expected range. (In reality, however, +// gettimeWrapper() will return a positive integer that will fit +// in 48 bits) +uint64_t +getTSIGTime() { + return (detail::gettimeWrapper() & 0x0000ffffffffffffULL); +} +} + +struct TSIGContext::TSIGContextImpl { + TSIGContextImpl(const TSIGKey& key, + TSIGError error = TSIGError::NOERROR()) : + state_(INIT), key_(key), error_(error), + previous_timesigned_(0), digest_len_(0), + last_sig_dist_(-1) { + if (error == TSIGError::NOERROR()) { + // In normal (NOERROR) case, the key should be valid, and we + // should be able to pre-create a corresponding HMAC object, + // which will be likely to be used for sign or verify later. + // We do this in the constructor so that we can know the expected + // digest length in advance. The creation should normally succeed, + // but the key information could be still broken, which could + // trigger an exception inside the cryptolink module. We ignore + // it at this moment; a subsequent sign/verify operation will try + // to create the HMAC, which would also fail. + try { + hmac_.reset(CryptoLink::getCryptoLink().createHMAC( + key_.getSecret(), key_.getSecretLength(), + key_.getAlgorithm()), + deleteHMAC); + } catch (const isc::Exception&) { + return; + } + size_t digestbits = key_.getDigestbits(); + size_t default_digest_len = hmac_->getOutputLength(); + if (digestbits > 0) { + digest_len_ = (digestbits + 7) / 8; + // sanity (cf. RFC 4635) + if ((digest_len_ < 10) || + (digest_len_ < (default_digest_len / 2)) || + (digest_len_ > default_digest_len)) { + // should emit a warning? + digest_len_ = default_digest_len; + } + } else { + digest_len_ = default_digest_len; + } + } + } + + // This helper method is used from verify(). It's expected to be called + // just before verify() returns. It updates internal state based on + // the verification result and return the TSIGError to be returned to + // the caller of verify(), so that verify() can call this method within + // its 'return' statement. + TSIGError postVerifyUpdate(TSIGError error, const void* digest, + uint16_t digest_len) + { + if (state_ == INIT) { + state_ = RECEIVED_REQUEST; + } else if (state_ == SENT_REQUEST && error == TSIGError::NOERROR()) { + state_ = VERIFIED_RESPONSE; + } + if (digest != NULL) { + previous_digest_.assign(static_cast<const uint8_t*>(digest), + static_cast<const uint8_t*>(digest) + + digest_len); + } + error_ = error; + return (error); + } + + // A shortcut method to create an HMAC object for sign/verify. If one + // has been successfully created in the constructor, return it; otherwise + // create a new one and return it. In the former case, the ownership is + // transferred to the caller; the stored HMAC will be reset after the + // call. + HMACPtr createHMAC() { + if (hmac_) { + HMACPtr ret = HMACPtr(); + ret.swap(hmac_); + return (ret); + } + return (HMACPtr(CryptoLink::getCryptoLink().createHMAC( + key_.getSecret(), key_.getSecretLength(), + key_.getAlgorithm()), + deleteHMAC)); + } + + // The following three are helper methods to compute the digest for + // TSIG sign/verify in order to unify the common code logic for sign() + // and verify() and to keep these callers concise. + // These methods take an HMAC object, which will be updated with the + // calculated digest. + // Note: All methods construct a local OutputBuffer as a work space with a + // fixed initial buffer size to avoid intermediate buffer extension. + // This should be efficient enough, especially for fundamentally expensive + // operation like cryptographic sign/verify, but if the creation of the + // buffer in each helper method is still identified to be a severe + // performance bottleneck, we could have this class a buffer as a member + // variable and reuse it throughout the object's lifetime. Right now, + // we prefer keeping the scope for local things as small as possible. + void digestPreviousMAC(HMACPtr hmac); + void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, + uint64_t time_signed, uint16_t fudge, + uint16_t error, uint16_t otherlen, + const void* otherdata, + bool time_variables_only) const; + void digestDNSMessage(HMACPtr hmac, uint16_t qid, const void* data, + size_t data_len) const; + State state_; + const TSIGKey key_; + vector<uint8_t> previous_digest_; + TSIGError error_; + uint64_t previous_timesigned_; // only meaningful for response with BADTIME + size_t digest_len_; + HMACPtr hmac_; + // This is the distance from the last verified signed message. Value of 0 + // means the last message was signed. Special value -1 means there was no + // signed message yet. + int last_sig_dist_; +}; + +void +TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) { + // We should have ensured the digest size fits 16 bits within this class + // implementation. + assert(previous_digest_.size() <= 0xffff); + + if (previous_digest_.empty()) { + // The previous digest was already used. We're in the middle of + // TCP stream somewhere and we already pushed some unsigned message + // into the HMAC state. + return; + } + + OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size()); + const uint16_t previous_digest_len(previous_digest_.size()); + buffer.writeUint16(previous_digest_len); + if (previous_digest_len != 0) { + buffer.writeData(&previous_digest_[0], previous_digest_len); + } + hmac->update(buffer.getData(), buffer.getLength()); +} + +void +TSIGContext::TSIGContextImpl::digestTSIGVariables( + HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed, + uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata, + bool time_variables_only) const { + // It's bit complicated, but we can still predict the necessary size of + // the data to be digested. So we precompute it to avoid possible + // reallocation inside OutputBuffer (not absolutely necessary, but this + // is a bit more efficient) + size_t data_size = 8; + if (!time_variables_only) { + data_size += 10 + key_.getKeyName().getLength() + + key_.getAlgorithmName().getLength(); + } + OutputBuffer buffer(data_size); + + if (!time_variables_only) { + key_.getKeyName().toWire(buffer); + buffer.writeUint16(rrclass); + buffer.writeUint32(rrttl); + key_.getAlgorithmName().toWire(buffer); + } + buffer.writeUint16(time_signed >> 32); + buffer.writeUint32(time_signed & 0xffffffff); + buffer.writeUint16(fudge); + + if (!time_variables_only) { + buffer.writeUint16(error); + buffer.writeUint16(otherlen); + } + + hmac->update(buffer.getData(), buffer.getLength()); + if (!time_variables_only && otherlen > 0) { + hmac->update(otherdata, otherlen); + } +} + +// In digestDNSMessage, we exploit some minimum knowledge of DNS message +// format: +// - the header section has a fixed length of 12 octets (MESSAGE_HEADER_LEN) +// - the offset in the header section to the ID field is 0 +// - the offset in the header section to the ARCOUNT field is 10 (and the field +// length is 2 octets) +// We could construct a separate Message object from the given data, adjust +// fields via the Message interfaces and then render it back to a separate +// buffer, but that would be overkilling. The DNS message header has a +// fixed length and necessary modifications are quite straightforward, so +// we do the job using lower level interfaces. +namespace { +const size_t MESSAGE_HEADER_LEN = 12; +} + +void +TSIGContext::TSIGContextImpl::digestDNSMessage(HMACPtr hmac, + uint16_t qid, const void* data, + size_t data_len) const { + OutputBuffer buffer(MESSAGE_HEADER_LEN); + const uint8_t* msgptr = static_cast<const uint8_t*>(data); + + // Install the original ID + buffer.writeUint16(qid); + msgptr += sizeof(uint16_t); + + // Copy the rest of the header except the ARCOUNT field. + buffer.writeData(msgptr, 8); + msgptr += 8; + + // Install the adjusted ARCOUNT (we don't care even if the value is bogus + // and it underflows; it would simply result in verification failure) + buffer.writeUint16(InputBuffer(msgptr, sizeof(uint16_t)).readUint16() - 1); + msgptr += 2; + + // Digest the header and the rest of the DNS message + hmac->update(buffer.getData(), buffer.getLength()); + hmac->update(msgptr, data_len - MESSAGE_HEADER_LEN); +} + +TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key)) { +} + +TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring) : impl_(NULL) { + const TSIGKeyRing::FindResult result(keyring.find(key_name, + algorithm_name)); + if (result.code == TSIGKeyRing::NOTFOUND) { + // If not key is found, create a dummy key with the specified key + // parameters and empty secret. In the common scenario this will + // be used in subsequent response with a TSIG indicating a BADKEY + // error. + impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name, + NULL, 0), TSIGError::BAD_KEY()); + } else { + impl_ = new TSIGContextImpl(*result.key); + } +} + +TSIGContext::~TSIGContext() { + delete impl_; +} + +size_t +TSIGContext::getTSIGLength() const { + // + // The space required for an TSIG record is: + // + // n1 bytes for the (key) name + // 2 bytes for the type + // 2 bytes for the class + // 4 bytes for the ttl + // 2 bytes for the rdlength + // n2 bytes for the algorithm name + // 6 bytes for the time signed + // 2 bytes for the fudge + // 2 bytes for the MAC size + // x bytes for the MAC + // 2 bytes for the original id + // 2 bytes for the error + // 2 bytes for the other data length + // y bytes for the other data (at most) + // --------------------------------- + // 26 + n1 + n2 + x + y bytes + // + + // Normally the digest length ("x") is the length of the underlying + // hash output. If a key related error occurred, however, the + // corresponding TSIG will be "unsigned", and the digest length will be 0. + const size_t digest_len = + (impl_->error_ == TSIGError::BAD_KEY() || + impl_->error_ == TSIGError::BAD_SIG()) ? 0 : impl_->digest_len_; + + // Other Len ("y") is normally 0; if BAD_TIME error occurred, the + // subsequent TSIG will contain 48 bits of the server current time. + const size_t other_len = (impl_->error_ == TSIGError::BAD_TIME()) ? 6 : 0; + + return (26 + impl_->key_.getKeyName().getLength() + + impl_->key_.getAlgorithmName().getLength() + + digest_len + other_len); +} + +TSIGContext::State +TSIGContext::getState() const { + return (impl_->state_); +} + +TSIGError +TSIGContext::getError() const { + return (impl_->error_); +} + +ConstTSIGRecordPtr +TSIGContext::sign(const uint16_t qid, const void* const data, + const size_t data_len) { + if (impl_->state_ == VERIFIED_RESPONSE) { + isc_throw(TSIGContextError, + "TSIG sign attempt after verifying a response"); + } + + if (data == NULL || data_len == 0) { + isc_throw(InvalidParameter, "TSIG sign error: empty data is given"); + } + + TSIGError error(TSIGError::NOERROR()); + const uint64_t now = getTSIGTime(); + + // For responses adjust the error code. + if (impl_->state_ == RECEIVED_REQUEST) { + error = impl_->error_; + } + + // For errors related to key or MAC, return an unsigned response as + // specified in Section 4.3 of RFC2845. + if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) { + ConstTSIGRecordPtr tsig(new TSIGRecord( + impl_->key_.getKeyName(), + any::TSIG(impl_->key_.getAlgorithmName(), + now, DEFAULT_FUDGE, 0, NULL, + qid, error.getCode(), 0, NULL))); + impl_->previous_digest_.clear(); + impl_->state_ = SENT_RESPONSE; + return (tsig); + } + + HMACPtr hmac(impl_->createHMAC()); + + // If the context has previous MAC (either the Request MAC or its own + // previous MAC), digest it. + if (impl_->state_ != INIT) { + impl_->digestPreviousMAC(hmac); + } + + // Digest the message (without TSIG) + hmac->update(data, data_len); + + // Digest TSIG variables. + // First, prepare some non constant variables. + const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ? + impl_->previous_timesigned_ : now; + // For BADTIME error, we include 6 bytes of other data. + // (6 bytes = size of time signed value) + const uint16_t otherlen = (error == TSIGError::BAD_TIME()) ? 6 : 0; + OutputBuffer otherdatabuf(otherlen); + if (error == TSIGError::BAD_TIME()) { + otherdatabuf.writeUint16(now >> 32); + otherdatabuf.writeUint32(now & 0xffffffff); + } + const void* const otherdata = + (otherlen == 0) ? NULL : otherdatabuf.getData(); + // Then calculate the digest. If state_ is SENT_RESPONSE we are sending + // a continued message in the same TCP stream so skip digesting + // variables except for time related variables (RFC2845 4.4). + impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(), + TSIGRecord::TSIG_TTL, time_signed, + DEFAULT_FUDGE, error.getCode(), + otherlen, otherdata, + impl_->state_ == SENT_RESPONSE); + + // Get the final digest, update internal state, then finish. + vector<uint8_t> digest = hmac->sign(impl_->digest_len_); + assert(digest.size() <= 0xffff); // cryptolink API should have ensured it. + ConstTSIGRecordPtr tsig(new TSIGRecord( + impl_->key_.getKeyName(), + any::TSIG(impl_->key_.getAlgorithmName(), + time_signed, DEFAULT_FUDGE, + digest.size(), &digest[0], + qid, error.getCode(), otherlen, + otherdata))); + // Exception free from now on. + impl_->previous_digest_.swap(digest); + impl_->state_ = (impl_->state_ == INIT) ? SENT_REQUEST : SENT_RESPONSE; + return (tsig); +} + +TSIGError +TSIGContext::verify(const TSIGRecord* const record, const void* const data, + const size_t data_len) { + if (impl_->state_ == SENT_RESPONSE) { + isc_throw(TSIGContextError, + "TSIG verify attempt after sending a response"); + } + + if (record == NULL) { + if (impl_->last_sig_dist_ >= 0 && impl_->last_sig_dist_ < 99) { + // It is not signed, but in the middle of TCP stream. We just + // update the HMAC state and consider this message OK. + update(data, data_len); + // This one is not signed, the last signed is one message further + // now. + impl_->last_sig_dist_++; + // No digest to return now. Just say it's OK. + return (impl_->postVerifyUpdate(TSIGError::NOERROR(), NULL, 0)); + } + // This case happens when we sent a signed request and have received an + // unsigned response. According to RFC2845 Section 4.6 this case should be + // considered a "format error" (although the specific error code + // wouldn't matter much for the caller). + return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0)); + } + + const any::TSIG& tsig_rdata = record->getRdata(); + + // Reject some obviously invalid data + if (data_len < MESSAGE_HEADER_LEN + record->getLength()) { + isc_throw(InvalidParameter, + "TSIG verify: data length is invalid: " << data_len); + } + if (data == NULL) { + isc_throw(InvalidParameter, "TSIG verify: empty data is invalid"); + } + + // This message is signed and we won't throw any more. + impl_->last_sig_dist_ = 0; + + // Check key: whether we first verify it with a known key or we verify + // it using the consistent key in the context. If the check fails we are + // done with BADKEY. + if (impl_->state_ == INIT && impl_->error_ == TSIGError::BAD_KEY()) { + return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0)); + } + if (impl_->key_.getKeyName() != record->getName() || + impl_->key_.getAlgorithmName() != tsig_rdata.getAlgorithm()) { + return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0)); + } + + // Check time: the current time must be in the range of + // [time signed - fudge, time signed + fudge]. Otherwise verification + // fails with BADTIME. (RFC2845 Section 4.6.2) + // Note: for simplicity we don't explicitly catch the case of too small + // current time causing underflow. With the fact that fudge is quite + // small and (for now) non configurable, it shouldn't be a real concern + // in practice. + const uint64_t now = getTSIGTime(); + if (tsig_rdata.getTimeSigned() + DEFAULT_FUDGE < now || + tsig_rdata.getTimeSigned() - DEFAULT_FUDGE > now) { + const void* digest = NULL; + size_t digest_len = 0; + if (impl_->state_ == INIT) { + digest = tsig_rdata.getMAC(); + digest_len = tsig_rdata.getMACSize(); + impl_->previous_timesigned_ = tsig_rdata.getTimeSigned(); + } + return (impl_->postVerifyUpdate(TSIGError::BAD_TIME(), digest, + digest_len)); + } + + // Handling empty MAC. While RFC2845 doesn't explicitly prohibit other + // cases, it can only reasonably happen in a response with BADSIG or + // BADKEY. We reject other cases as if it were BADSIG to avoid unexpected + // acceptance of a bogus signature. This behavior follows the BIND 9 + // implementation. + if (tsig_rdata.getMACSize() == 0) { + TSIGError error = TSIGError(tsig_rdata.getError()); + if (error != TSIGError::BAD_SIG() && error != TSIGError::BAD_KEY()) { + error = TSIGError::BAD_SIG(); + } + return (impl_->postVerifyUpdate(error, NULL, 0)); + } + + HMACPtr hmac(impl_->createHMAC()); + + // If the context has previous MAC (either the Request MAC or its own + // previous MAC), digest it. + if (impl_->state_ != INIT) { + impl_->digestPreviousMAC(hmac); + } + + // Signature length check based on RFC 4635 3.1 + if (tsig_rdata.getMACSize() > hmac->getOutputLength()) { + // signature length too big + return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0)); + } + if ((tsig_rdata.getMACSize() < 10) || + (tsig_rdata.getMACSize() < (hmac->getOutputLength() / 2))) { + // signature length below minimum + return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0)); + } + if (tsig_rdata.getMACSize() < impl_->digest_len_) { + // (truncated) signature length too small + return (impl_->postVerifyUpdate(TSIGError::BAD_TRUNC(), NULL, 0)); + } + + // + // Digest DNS message (excluding the trailing TSIG RR and adjusting the + // QID and ARCOUNT header fields) + // + impl_->digestDNSMessage(hmac, tsig_rdata.getOriginalID(), + data, data_len - record->getLength()); + + // Digest TSIG variables. If state_ is VERIFIED_RESPONSE, it's a + // continuation of the same TCP stream and skip digesting them except + // for time related variables (RFC2845 4.4). + // Note: we use the constant values for RR class and TTL specified + // in RFC2845, not received values (we reject other values in constructing + // the TSIGRecord). + impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(), + TSIGRecord::TSIG_TTL, + tsig_rdata.getTimeSigned(), + tsig_rdata.getFudge(), tsig_rdata.getError(), + tsig_rdata.getOtherLen(), + tsig_rdata.getOtherData(), + impl_->state_ == VERIFIED_RESPONSE); + + // Verify the digest with the received signature. + if (hmac->verify(tsig_rdata.getMAC(), tsig_rdata.getMACSize())) { + return (impl_->postVerifyUpdate(TSIGError::NOERROR(), + tsig_rdata.getMAC(), + tsig_rdata.getMACSize())); + } + + return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0)); +} + +bool +TSIGContext::lastHadSignature() const { + if (impl_->last_sig_dist_ == -1) { + isc_throw(TSIGContextError, "No message was verified yet"); + } + return (impl_->last_sig_dist_ == 0); +} + +void +TSIGContext::update(const void* const data, size_t len) { + HMACPtr hmac(impl_->createHMAC()); + // Use the previous digest and never use it again + impl_->digestPreviousMAC(hmac); + impl_->previous_digest_.clear(); + // Push the message there + hmac->update(data, len); + impl_->hmac_ = hmac; +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h new file mode 100644 index 0000000..ed74e77 --- /dev/null +++ b/src/lib/dns/tsig.h @@ -0,0 +1,445 @@ +// 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/. + +// IMPORTANT: the server side of this code MUST NOT be used until +// it was fixed, cf RFC 8945. Note that Kea uses only the client side. + +#ifndef TSIG_H +#define TSIG_H 1 + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/tsigerror.h> +#include <dns/tsigkey.h> +#include <dns/tsigrecord.h> + +namespace isc { +namespace dns { + +/// An exception that is thrown for logic errors identified in TSIG +/// sign/verify operations. +/// +/// Note that this exception is not thrown for TSIG protocol errors such as +/// verification failures. In general, this exception indicates an internal +/// program bug. +class TSIGContextError : public isc::Exception { +public: + TSIGContextError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// TSIG session context. +/// +/// The \c TSIGContext class maintains a context of a signed session of +/// DNS transactions by TSIG. In many cases a TSIG signed session consists +/// of a single set of request (e.g. normal query) and reply (e.g. normal +/// response), where the request is initially signed by the client, and the +/// reply is signed by the server using the initial signature. As mentioned +/// in RFC2845, a session can consist of multiple exchanges in a TCP +/// connection. As also mentioned in the RFC, an AXFR response often contains +/// multiple DNS messages, which can belong to the same TSIG session. +/// This class supports all these cases. +/// +/// A \c TSIGContext object is generally constructed with a TSIG key to be +/// used for the session, and keeps track of various kinds of session specific +/// information, such as the original digest while waiting for a response or +/// verification error information that is to be used for a subsequent +/// response. +/// +/// This class has two main methods, \c sign() and \c verify(). +/// The \c sign() method signs given data (which is supposed to be a complete +/// DNS message without the TSIG itself) using the TSIG key and other +/// related information associated with the \c TSIGContext object. +/// The \c verify() method verifies a given DNS message that contains a TSIG +/// RR using the key and other internal information. +/// +/// In general, a DNS client that wants to send a signed query will construct +/// a \c TSIGContext object with the TSIG key that the client is intending to +/// use, and sign the query with the context. The client will keeps the +/// context, and verify the response with it. +/// +/// On the other hand, a DNS server will construct a \c TSIGContext object +/// with the information of the TSIG RR included in a query with a set of +/// possible keys (in the form of a \c TSIGKeyRing object). The constructor +/// in this mode will identify the appropriate TSIG key (or internally record +/// an error if it doesn't find a key). The server will then verify the +/// query with the context, and generate a signed response using the same +/// same context. +/// +/// When multiple messages belong to the same TSIG session, either side +/// (signer or verifier) will keep using the same context. It records +/// the latest session state (such as the previous digest) so that repeated +/// calls to \c sign() or \c verify() work correctly in terms of the TSIG +/// protocol. +/// +/// \b Examples +/// +/// This is a typical client application that sends a TSIG signed query +/// and verifies the response. +/// +/// \code +/// // "renderer" is of MessageRenderer to render the message. +/// // (TSIGKey would be configured from config or command line in real app) +/// TSIGContext ctx(TSIGKey("key.example:MSG6Ng==")); +/// Message message(Message::RENDER); +/// message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), +/// RRType::A())); +/// message.toWire(renderer, ctx); +/// +/// // sendto, then recvfrom. received result in (data, data_len) +/// +/// message.clear(Message::PARSE); +/// InputBuffer buffer(data, data_len); +/// message.fromWire(buffer); +/// TSIGError tsig_error = ctx.verify(message.getTSIGRecord(), +/// data, data_len); +/// if (tsig_error == TSIGError::NOERROR()) { +/// // okay. ctx can be continuously used if it's receiving subsequent +/// // signed responses from a TCP stream. +/// } else if (message.getRcode() == Rcode::NOTAUTH()) { +/// // hard error. give up this transaction per RFC2845 4.6. +/// } else { +/// // Other error: discard response keep waiting with the same ctx +/// // for another (again, RFC2845 4.6). +/// } \endcode +/// +/// And this is a typical server application that authenticates a signed +/// query and returns a response according to the result. +/// +/// \code +/// // Assume "message" is of type Message for query handling and +/// // "renderer" is of MessageRenderer to render responses. +/// Message message(Message::RENDER); +/// +/// TSIGKeyRing keyring; // this must be configured with keys somewhere +/// +/// // Receive a query and store it in (data, data_len) +/// InputBuffer buffer(data, data_len); +/// message.clear(Message::PARSE); +/// message.fromWire(buffer); +/// +/// const TSIGRecord* tsig = message.getTSIGRecord(); +/// if (tsig) { +/// TSIGContext ctx(tsig->getName(), tsig->getRdata().getAlgorithm(), +/// keyring); +/// ctx.verify(tsig, data, data_len); +/// +/// // prepare response +/// message.makeResponse(); +/// //... +/// message.toWire(renderer, ctx); +/// +/// // send the response data back to the client. +/// // If this is a beginning of a signed session over a TCP and +/// // server has more data to send to the client, this ctx +/// // will be used to sign subsequent messages. +/// } \endcode +/// +/// <b>TCP Consideration</b> +/// +/// RFC2845 describes the case where a single TSIG session is used for +/// multiple DNS messages (Section 4.4). This class supports signing and +/// verifying the messages in this scenario, but does not care if the messages +/// were delivered over a TCP connection or not. If, for example, the +/// same \c TSIGContext object is used to sign two independent DNS queries +/// sent over UDP, they will be considered to belong to the same TSIG +/// session, and, as a result, verification will be likely to fail. +/// +/// \b Copyability +/// +/// This class is currently non copyable based on the observation of the +/// typical usage as described above. But there is no strong technical +/// reason why this class cannot be copyable. If we see the need for it +/// in future we may change the implementation on this point. +/// +/// <b>Note to developers:</b> +/// One basic design choice is to make the \c TSIGContext class is as +/// independent from the \c Message class. This is because the latter is +/// much more complicated, depending on many other classes, while TSIG is +/// a very specific part of the entire DNS protocol set. If the \c TSIGContext +/// class depends on \c \c Message, it will be more vulnerable to changes +/// to other classes, and will be more difficult to test due to the +/// direct or indirect dependencies. The interface of \c sign() that takes +/// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object) +/// is therefore a deliberate design decision. +class TSIGContext : boost::noncopyable { +public: + /// Internal state of context + /// + /// The constants of this enum type define a specific state of + /// \c TSIGContext to adjust the behavior. The definition is public + /// and the state can be seen via the \c getState() method, but this is + /// mostly private information. It's publicly visible mainly for testing + /// purposes; there is no API for the application to change the state + /// directly. + enum State { + INIT, ///< Initial state + SENT_REQUEST, ///< Client sent a signed request, waiting response + RECEIVED_REQUEST, ///< Server received a signed request + SENT_RESPONSE, ///< Server sent a signed response + VERIFIED_RESPONSE ///< Client successfully verified a response + }; + + /// \name Constructors and destructor + /// + //@{ + /// Constructor from a TSIG key. + /// + /// \exception std::bad_alloc Resource allocation for internal data fails + /// + /// \param key The TSIG key to be used for TSIG sessions with this context. + explicit TSIGContext(const TSIGKey& key); + + /// Constructor from key parameters and key ring. + TSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring); + + /// The destructor. + virtual ~TSIGContext(); + //@} + + /// Sign a DNS message. + /// + /// This method computes the TSIG MAC for the given data, which is + /// generally expected to be a complete, wire-format DNS message + /// that doesn't contain a TSIG RR, based on the TSIG key and + /// other context information of \c TSIGContext, and returns a + /// result in the form of a (pointer object pointing to) + /// \c TSIGRecord object. + /// + /// The caller of this method will use the returned value to render a + /// complete TSIG RR into the message that has been signed so that it + /// will become a complete TSIG-signed message. + /// + /// In general, this method is called once by a client to send a + /// signed request or one more times by a server to sign + /// response(s) to a signed request. To avoid allowing accidental + /// misuse, if this method is called after a "client" validates a + /// response, an exception of class \c TSIGContextError will be + /// thrown. + /// + /// \note Normal applications are not expected to call this method + /// directly; they will usually use the \c Message::toWire() method + /// with a \c TSIGContext object being a parameter and have the + /// \c Message class create a complete signed message. + /// + /// This method treats the given data as opaque, even though it's generally + /// expected to represent a wire-format DNS message (see also the class + /// description), and doesn't inspect it in any way. For example, it + /// doesn't check whether the data length is sane for a valid DNS message. + /// This is also the reason why this method takes the \c qid parameter, + /// which will be used as the original ID of the resulting + /// \c TSIGRecordx object, even though this value should be stored in the + /// first two octets (in wire format) of the given data. + /// + /// \note This method still checks and rejects empty data (null pointer + /// data or the specified data length is 0) in order to avoid catastrophic + /// effect such as program crash. Empty data is not necessarily invalid + /// for HMAC computation, but obviously it doesn't make sense for a DNS + /// message. + /// + /// This method provides the strong exception guarantee; unless the method + /// returns (without an exception being thrown), the internal state of + /// the \c TSIGContext won't be modified. + /// + /// \exception TSIGContextError Context already verified a response. + /// \exception InvalidParameter \c data is 0 or \c data_len is 0 + /// \exception cryptolink::LibraryError Some unexpected error in the + /// underlying crypto operation + /// \exception std::bad_alloc Temporary resource allocation failure + /// + /// \param qid The QID to be as the value of the original ID field of + /// the resulting TSIG record + /// \param data Points to the wire-format data to be signed + /// \param data_len The length of \c data in bytes + /// + /// \return A TSIG record for the given data along with the context. + virtual ConstTSIGRecordPtr + sign(const uint16_t qid, const void* const data, const size_t data_len); + + /// Verify a DNS message. + /// + /// This method verifies given data along with the context and a given + /// TSIG in the form of a \c TSIGRecord object. The data to be verified + /// is generally expected to be a complete, wire-format DNS message, + /// exactly as received by the host, and ending with a TSIG RR. + /// After verification process this method updates its internal state, + /// and returns the result in the form of a \c TSIGError object. + /// Possible return values are (see the \c TSIGError class description + /// for the mnemonics): + /// + /// - \c NOERROR: The data has been verified correctly. + /// - \c FORMERR: \c TSIGRecord is not given (see below). + /// - \c BAD_KEY: Appropriate key is not found or specified key doesn't + /// match for the data. + /// - \c BAD_TIME: The current time doesn't fall in the range specified + /// in the TSIG. + /// - \c BAD_SIG: The signature given in the TSIG doesn't match against + /// the locally computed digest or is the signature is + /// invalid in other way. + /// - \c BAD_MODE: Not yet implemented TKEY error + /// - \c BAD_NAME: Not yet implemented TKEY error + /// - \c BAD_ALG: Not yet implemented TKEY error + /// - \c BAD_TRUNC: The signature or truncated signature length is too + /// small. + /// + /// If this method is called by a DNS client waiting for a signed + /// response and the result is not \c NOERROR, the context can be used + /// to try validating another signed message as described in RFC2845 + /// Section 4.6. + /// + /// If this method is called by a DNS server that tries to authenticate + /// a signed request, and if the result is not \c NOERROR, the + /// corresponding error condition is recorded in the context so that + /// the server can return a response indicating what was wrong by calling + /// \c sign() with the updated context. + /// + /// In general, this method is called once by a server for + /// authenticating a signed request or one more times by a client to + /// validate signed response(s) to a signed request. To avoid allowing + /// accidental misuse, if this method is called after a "server" signs + /// a response, an exception of class \c TSIGContextError will be thrown. + /// + /// The \c record parameter can be 0; in that case this method simply + /// returns \c FORMERR as the case described in Section 4.6 of RFC2845, + /// i.e., receiving an unsigned response to a signed request. This way + /// a client can transparently pass the result of + /// \c Message::getTSIGRecord() without checking whether it isn't 0 + /// and take an appropriate action based on the result of this method. + /// + /// This method handles the given data mostly as opaque. It digests + /// the data assuming it begins with a DNS header and ends with a TSIG + /// RR whose length is given by calling \c TSIGRecord::getLength() on + /// \c record, but otherwise it doesn't parse the data to confirm the + /// assumption. It's caller's responsibility to ensure the data is + /// valid and consistent with \c record. To avoid disruption, this + /// method performs minimal validation on the given \c data and \c record: + /// \c data must not be 0; \c data_len must not be smaller than the + /// sum of the DNS header length (fixed, 12 octets) and the length of + /// the TSIG RR. If this check fails it throws an \c InvalidParameter + /// exception. + /// + /// One unexpected case that is not covered by this method is that a + /// client receives a signed response to an unsigned request. RFC2845 is + /// silent about such cases; BIND 9 explicitly identifies the case and + /// rejects it. With this implementation, the client can know that the + /// response contains a TSIG via the result of + /// \c Message::getTSIGRecord() and that it is an unexpected TSIG due to + /// the fact that it doesn't have a corresponding \c TSIGContext. + /// It's up to the client implementation whether to react to such a case + /// explicitly (for example, it could either ignore the TSIG and accept + /// the response or drop it). + /// + /// This method provides the strong exception guarantee; unless the method + /// returns (without an exception being thrown), the internal state of + /// the \c TSIGContext won't be modified. + /// + /// \todo Signature truncation support based on RFC4635 + /// + /// \exception TSIGContextError Context already signed a response. + /// \exception InvalidParameter \c data is 0 or \c data_len is too small. + /// + /// \param record The \c TSIGRecord to be verified with \c data + /// \param data Points to the wire-format data (exactly as received) to + /// be verified + /// \param data_len The length of \c data in bytes + /// \return The \c TSIGError that indicates verification result + virtual TSIGError + verify(const TSIGRecord* const record, const void* const data, const size_t data_len); + + /// \brief Check whether the last verified message was signed. + /// + /// RFC2845 allows for some of the messages not to be signed. However, + /// the last message must be signed and the class has no knowledge if a + /// given message is the last one, therefore it can't check directly. + /// + /// It is up to the caller to check if the last verified message was signed + /// after all are verified by calling this function. + /// + /// \return If the last message was signed or not. + /// \exception TSIGContextError if no message was verified yet. + virtual bool lastHadSignature() const; + + /// Return the expected length of TSIG RR after \c sign() + /// + /// This method returns the length of the TSIG RR that would be + /// produced as a result of \c sign() with the state of the context + /// at the time of the call. The expected length can be decided + /// from the key and the algorithm (which determines the MAC size if + /// included) and the recorded TSIG error. Specifically, if a key + /// related error has been identified, the MAC will be excluded; if + /// a time error has occurred, the TSIG will include "other data". + /// + /// This method is provided mainly for the convenience of the Message + /// class, which needs to know the expected TSIG length in rendering a + /// signed DNS message so that it can handle truncated messages with TSIG + /// correctly. Normal applications wouldn't need this method. The Python + /// binding for this method won't be provided for the same reason. + /// + /// \exception None + /// + /// \return The expected TSIG RR length in bytes + virtual size_t getTSIGLength() const; + + /// Return the current state of the context + /// + /// \note + /// The states are visible in public mainly for testing purposes. + /// Normal applications won't have to deal with them. + /// + /// \exception None + virtual State getState() const; + + /// Return the TSIG error as a result of the latest verification + /// + /// This method can be called even before verifying anything, but the + /// returned value is meaningless in that case. + /// + /// \exception None + virtual TSIGError getError() const; + + /// \name Protocol constants and defaults + /// + //@{ + /// The recommended fudge value (in seconds) by RFC2845. + /// + /// Right now fudge is not tunable, and all TSIGs generated by this API + /// will have this value of fudge. + static const uint16_t DEFAULT_FUDGE = 300; + //@} + +protected: + /// \brief Update internal HMAC state by more data. + /// + /// This is used mostly internally, when we need to verify a message without + /// TSIG signature in the middle of signed TCP stream. However, it is also + /// used in tests, so it's protected instead of private, to allow tests + /// in. + /// + /// It doesn't contain sanity checks, and it is not tested directly. But + /// we may want to add these one day to allow generating the skipped TSIG + /// messages too. Until then, do not use this method. + void update(const void* const data, size_t len); + +private: + struct TSIGContextImpl; + TSIGContextImpl* impl_; +}; + +typedef boost::shared_ptr<TSIGContext> TSIGContextPtr; +typedef boost::shared_ptr<TSIGKey> TSIGKeyPtr; + +} +} + +#endif // TSIG_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tsigerror.cc b/src/lib/dns/tsigerror.cc new file mode 100644 index 0000000..d51094a --- /dev/null +++ b/src/lib/dns/tsigerror.cc @@ -0,0 +1,66 @@ +// Copyright (C) 2011-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 <config.h> + +#include <ostream> +#include <string> + +#include <boost/lexical_cast.hpp> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> +#include <dns/tsigerror.h> + +namespace isc { +namespace dns { +namespace { +const char* const tsigerror_text[] = { + "BADSIG", + "BADKEY", + "BADTIME", + "BADMODE", + "BADNAME", + "BADALG", + "BADTRUNC" +}; +} + +TSIGError::TSIGError(Rcode rcode) : code_(rcode.getCode()) { + if (code_ > MAX_RCODE_FOR_TSIGERROR) { + isc_throw(OutOfRange, "Invalid RCODE for TSIG Error: " << rcode); + } +} + +std::string +TSIGError::toText() const { + if (code_ <= MAX_RCODE_FOR_TSIGERROR) { + return (Rcode(code_).toText()); + } else if (code_ <= BAD_TRUNC_CODE) { + return (tsigerror_text[code_ - (MAX_RCODE_FOR_TSIGERROR + 1)]); + } else { + return (boost::lexical_cast<std::string>(code_)); + } +} + +Rcode +TSIGError::toRcode() const { + if (code_ <= MAX_RCODE_FOR_TSIGERROR) { + return (Rcode(code_)); + } + if (code_ > BAD_TRUNC_CODE) { + return (Rcode::SERVFAIL()); + } + return (Rcode::NOTAUTH()); +} + +std::ostream& +operator<<(std::ostream& os, const TSIGError& error) { + return (os << error.toText()); +} +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsigerror.h b/src/lib/dns/tsigerror.h new file mode 100644 index 0000000..f7727d7 --- /dev/null +++ b/src/lib/dns/tsigerror.h @@ -0,0 +1,374 @@ +// Copyright (C) 2011-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 TSIGERROR_H +#define TSIGERROR_H 1 + +#include <ostream> +#include <string> + +#include <dns/rcode.h> + +namespace isc { +namespace dns { +/// TSIG errors +/// +/// The \c TSIGError class objects represent standard errors related to +/// TSIG protocol operations as defined in related specifications, mainly +/// in RFC2845, RFC2930 and RFC4635. +class TSIGError { +public: + /// Constants for pre-defined TSIG error values. + /// + /// Code values from 0 through 15 (inclusive) are derived from those of + /// RCODE and are not defined here. See the \c Rcode class. + /// + /// \note Unfortunately some systems define "BADSIG" as a macro in a public + /// header file. To avoid conflict with it we add an underscore to our + /// definitions. + enum CodeValue { + BAD_SIG_CODE = 16, ///< 16: TSIG verification failure + BAD_KEY_CODE = 17, ///< 17: TSIG key is not recognized + BAD_TIME_CODE = 18, ///< 18: Current time and time signed are too different + BAD_MODE_CODE = 19, ///< 19: Bad TKEY mode + BAD_NAME_CODE = 20, ///< 20: Duplicate TKEY name + BAD_ALG_CODE = 21, ///< 21: TKEY algorithm not supported + BAD_TRUNC_CODE = 22 ///< 22: Bad truncation + }; + + /// \name Constructors + /// + /// We use the default versions of destructor, copy constructor, + /// and assignment operator. + //@{ + /// Constructor from the code value. + /// + /// \exception None + /// + /// \param error_code The underlying 16-bit error code value of the \c TSIGError. + explicit TSIGError(uint16_t error_code) : code_(error_code) {} + + /// Constructor from \c Rcode. + /// + /// As defined in RFC2845, error code values from 0 to 15 (inclusive) are + /// derived from the DNS RCODEs, which are represented via the \c Rcode + /// class in this library. This constructor works as a converter from + /// these RCODEs to corresponding TSIGError objects. + /// + /// \exception isc::OutOfRange Given rcode is not convertible to + /// TSIGErrors. + /// + /// \param rcode the \c Rcode from which the TSIGError should be derived. + explicit TSIGError(Rcode rcode); + //@} + + /// \brief Returns the \c TSIGCode error code value. + /// + /// \exception None + /// + /// \return The underlying code value corresponding to the \c TSIGError. + uint16_t getCode() const { return (code_); } + + /// \brief Return true iff two \c TSIGError objects are equal. + /// + /// Two TSIGError objects are equal iff their error codes are equal. + /// + /// \exception None + /// + /// \param other the \c TSIGError object to compare against. + /// \return true if the two TSIGError are equal; otherwise false. + bool equals(const TSIGError& other) const + { return (code_ == other.code_); } + + /// \brief Same as \c equals(). + bool operator==(const TSIGError& other) const { return (equals(other)); } + + /// \brief Return true iff two \c TSIGError objects are not equal. + /// + /// \exception None + /// + /// \param other the \c TSIGError object to compare against. + /// \return true if the two TSIGError objects are not equal; + /// otherwise false. + bool nequals(const TSIGError& other) const + { return (code_ != other.code_); } + + /// \brief Same as \c nequals(). + bool operator!=(const TSIGError& other) const { return (nequals(other)); } + + /// \brief Convert the \c TSIGError to a string. + /// + /// For codes derived from RCODEs up to 15, this method returns the + /// same string as \c Rcode::toText() for the corresponding code. + /// For other pre-defined code values (see TSIGError::CodeValue), + /// this method returns a string representation of the "mnemonic' used + /// for the enum and constant objects as defined in RFC2845. + /// For example, the string for code value 16 is "BADSIG", etc. + /// For other code values it returns a string representation of the decimal + /// number of the value, e.g. "32", "100", etc. + /// + /// \exception std::bad_alloc Resource allocation for the string fails + /// + /// \return A string representation of the \c TSIGError. + std::string toText() const; + + /// \brief Convert the \c TSIGError to a \c Rcode + /// + /// This method returns an \c Rcode object that is corresponding to + /// the TSIG error. The returned \c Rcode is expected to be used + /// by a verifying server to specify the RCODE of a response when + /// TSIG verification fails. + /// + /// Specifically, this method returns \c Rcode::NOTAUTH() for the + /// TSIG specific errors, BADSIG, BADKEY, BADTIME, as described in + /// RFC2845. For errors derived from the standard Rcode (code 0-15), + /// it returns the corresponding \c Rcode. For others, this method + /// returns \c Rcode::SERVFAIL() as a last resort. + /// + /// \exception None + Rcode toRcode() const; + + /// A constant TSIG error object derived from \c Rcode::NOERROR() + static const TSIGError& NOERROR(); + + /// A constant TSIG error object derived from \c Rcode::FORMERR() + static const TSIGError& FORMERR(); + + /// A constant TSIG error object derived from \c Rcode::SERVFAIL() + static const TSIGError& SERVFAIL(); + + /// A constant TSIG error object derived from \c Rcode::NXDOMAIN() + static const TSIGError& NXDOMAIN(); + + /// A constant TSIG error object derived from \c Rcode::NOTIMP() + static const TSIGError& NOTIMP(); + + /// A constant TSIG error object derived from \c Rcode::REFUSED() + static const TSIGError& REFUSED(); + + /// A constant TSIG error object derived from \c Rcode::YXDOMAIN() + static const TSIGError& YXDOMAIN(); + + /// A constant TSIG error object derived from \c Rcode::YXRRSET() + static const TSIGError& YXRRSET(); + + /// A constant TSIG error object derived from \c Rcode::NXRRSET() + static const TSIGError& NXRRSET(); + + /// A constant TSIG error object derived from \c Rcode::NOTAUTH() + static const TSIGError& NOTAUTH(); + + /// A constant TSIG error object derived from \c Rcode::NOTZONE() + static const TSIGError& NOTZONE(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED11() + static const TSIGError& RESERVED11(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED12() + static const TSIGError& RESERVED12(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED13() + static const TSIGError& RESERVED13(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED14() + static const TSIGError& RESERVED14(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED15() + static const TSIGError& RESERVED15(); + + /// A constant TSIG error object for the BADSIG code + /// (see \c TSIGError::BAD_SIG_CODE). + static const TSIGError& BAD_SIG(); + + /// A constant TSIG error object for the BADKEY code + /// (see \c TSIGError::BAD_KEY_CODE). + static const TSIGError& BAD_KEY(); + + /// A constant TSIG error object for the BADTIME code + /// (see \c TSIGError::BAD_TIME_CODE). + static const TSIGError& BAD_TIME(); + + /// A constant TSIG error object for the BADMODE code + /// (see \c TSIGError::BAD_MODE_CODE). + static const TSIGError& BAD_MODE(); + + /// A constant TSIG error object for the BADNAME code + /// (see \c TSIGError::BAD_NAME_CODE). + static const TSIGError& BAD_NAME(); + + /// A constant TSIG error object for the BADALG code + /// (see \c TSIGError::BAD_ALG_CODE). + static const TSIGError& BAD_ALG(); + + /// A constant TSIG error object for the BADTRUNC code + /// (see \c TSIGError::BAD_TRUNC_CODE). + static const TSIGError& BAD_TRUNC(); + +private: + // This is internally used to specify the maximum possible RCODE value + // that can be convertible to TSIGErrors. + static const int MAX_RCODE_FOR_TSIGERROR = 15; + + uint16_t code_; +}; + +inline const TSIGError& +TSIGError::NOERROR() { + static TSIGError e(Rcode::NOERROR()); + return (e); +} + +inline const TSIGError& +TSIGError::FORMERR() { + static TSIGError e(Rcode::FORMERR()); + return (e); +} + +inline const TSIGError& +TSIGError::SERVFAIL() { + static TSIGError e(Rcode::SERVFAIL()); + return (e); +} + +inline const TSIGError& +TSIGError::NXDOMAIN() { + static TSIGError e(Rcode::NXDOMAIN()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTIMP() { + static TSIGError e(Rcode::NOTIMP()); + return (e); +} + +inline const TSIGError& +TSIGError::REFUSED() { + static TSIGError e(Rcode::REFUSED()); + return (e); +} + +inline const TSIGError& +TSIGError::YXDOMAIN() { + static TSIGError e(Rcode::YXDOMAIN()); + return (e); +} + +inline const TSIGError& +TSIGError::YXRRSET() { + static TSIGError e(Rcode::YXRRSET()); + return (e); +} + +inline const TSIGError& +TSIGError::NXRRSET() { + static TSIGError e(Rcode::NXRRSET()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTAUTH() { + static TSIGError e(Rcode::NOTAUTH()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTZONE() { + static TSIGError e(Rcode::NOTZONE()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED11() { + static TSIGError e(Rcode::RESERVED11()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED12() { + static TSIGError e(Rcode::RESERVED12()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED13() { + static TSIGError e(Rcode::RESERVED13()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED14() { + static TSIGError e(Rcode::RESERVED14()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED15() { + static TSIGError e(Rcode::RESERVED15()); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_SIG() { + static TSIGError e(BAD_SIG_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_KEY() { + static TSIGError e(BAD_KEY_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_TIME() { + static TSIGError e(BAD_TIME_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_MODE() { + static TSIGError e(BAD_MODE_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_NAME() { + static TSIGError e(BAD_NAME_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_ALG() { + static TSIGError e(BAD_ALG_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_TRUNC() { + static TSIGError e(BAD_TRUNC_CODE); + return (e); +} + +/// Insert the \c TSIGError as a string into stream. +/// +/// This method convert \c tsig_error 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 tsig_error An \c TSIGError 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 TSIGError& tsig_error); +} +} + +#endif // TSIGERROR_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc new file mode 100644 index 0000000..ad94b62 --- /dev/null +++ b/src/lib/dns/tsigkey.cc @@ -0,0 +1,364 @@ +// 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/. + +#include <config.h> + +#include <map> +#include <utility> +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <cryptolink/cryptolink.h> + +#include <dns/name.h> +#include <util/encode/base64.h> +#include <dns/tsigkey.h> + +#include <boost/lexical_cast.hpp> + +using namespace std; +using namespace isc::cryptolink; + +namespace isc { +namespace dns { +namespace { + HashAlgorithm + convertAlgorithmName(const isc::dns::Name& name) { + if (name == TSIGKey::HMACMD5_NAME()) { + return (isc::cryptolink::MD5); + } + if (name == TSIGKey::HMACMD5_SHORT_NAME()) { + return (isc::cryptolink::MD5); + } + if (name == TSIGKey::HMACSHA1_NAME()) { + return (isc::cryptolink::SHA1); + } + if (name == TSIGKey::HMACSHA256_NAME()) { + return (isc::cryptolink::SHA256); + } + if (name == TSIGKey::HMACSHA224_NAME()) { + return (isc::cryptolink::SHA224); + } + if (name == TSIGKey::HMACSHA384_NAME()) { + return (isc::cryptolink::SHA384); + } + if (name == TSIGKey::HMACSHA512_NAME()) { + return (isc::cryptolink::SHA512); + } + + return (isc::cryptolink::UNKNOWN_HASH); + } +} + +struct +TSIGKey::TSIGKeyImpl { + TSIGKeyImpl(const Name& key_name, const Name& algorithm_name, + isc::cryptolink::HashAlgorithm algorithm, + size_t digestbits) : + + key_name_(key_name), algorithm_name_(algorithm_name), + algorithm_(algorithm), digestbits_(digestbits), + secret_() + { + // Convert the key and algorithm names to the canonical form. + key_name_.downcase(); + if (algorithm == isc::cryptolink::MD5) { + algorithm_name_ = TSIGKey::HMACMD5_NAME(); + } + algorithm_name_.downcase(); + } + TSIGKeyImpl(const Name& key_name, const Name& algorithm_name, + isc::cryptolink::HashAlgorithm algorithm, + size_t digestbits, + const void* secret, size_t secret_len) : + + key_name_(key_name), algorithm_name_(algorithm_name), + algorithm_(algorithm), digestbits_(digestbits), + secret_(static_cast<const uint8_t*>(secret), + static_cast<const uint8_t*>(secret) + secret_len) + { + // Convert the key and algorithm names to the canonical form. + key_name_.downcase(); + if (algorithm == isc::cryptolink::MD5) { + algorithm_name_ = TSIGKey::HMACMD5_NAME(); + } + algorithm_name_.downcase(); + } + Name key_name_; + Name algorithm_name_; + const isc::cryptolink::HashAlgorithm algorithm_; + size_t digestbits_; + const vector<uint8_t> secret_; +}; + +TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name, + const void* secret, size_t secret_len, + size_t digestbits /*= 0*/) : impl_(NULL) { + const HashAlgorithm algorithm = convertAlgorithmName(algorithm_name); + if ((secret != NULL && secret_len == 0) || + (secret == NULL && secret_len != 0)) { + isc_throw(InvalidParameter, + "TSIGKey secret and its length are inconsistent: " << + key_name << ":" << algorithm_name); + } + if (algorithm == isc::cryptolink::UNKNOWN_HASH && secret_len != 0) { + isc_throw(InvalidParameter, + "TSIGKey with unknown algorithm has non empty secret: " << + key_name << ":" << algorithm_name); + } + if (secret == NULL) { + impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, + digestbits); + } else { + impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, + digestbits, secret, secret_len); + } +} + +TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) { + try { + istringstream iss(str); + + string keyname_str; + getline(iss, keyname_str, ':'); + if (iss.fail() || iss.bad() || iss.eof()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + string secret_str; + getline(iss, secret_str, ':'); + if (iss.fail() || iss.bad()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + string algo_str; + if (!iss.eof()) { + getline(iss, algo_str, ':'); + } + if (iss.fail() || iss.bad()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + string dgstbt_str; + if (!iss.eof()) { + getline(iss, dgstbt_str); + } + if (iss.fail() || iss.bad()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" : + algo_str); + const HashAlgorithm algorithm = convertAlgorithmName(algo_name); + size_t digestbits = 0; + try { + if (!dgstbt_str.empty()) { + digestbits = boost::lexical_cast<size_t>(dgstbt_str); + } + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidParameter, + "TSIG key with non-numeric digestbits: " << dgstbt_str); + } + + vector<uint8_t> secret; + isc::util::encode::decodeBase64(secret_str, secret); + + if (algorithm == isc::cryptolink::UNKNOWN_HASH && !secret.empty()) { + isc_throw(InvalidParameter, + "TSIG key with unknown algorithm has non empty secret: " + << str); + } + + if (secret.empty()) { + impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm, + digestbits); + } else { + impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm, + digestbits, &secret[0], secret.size()); + } + } catch (const isc::Exception& e) { + // 'reduce' the several types of exceptions name parsing and + // Base64 decoding can throw to just the InvalidParameter + isc_throw(InvalidParameter, e.what()); + } +} + + +TSIGKey::TSIGKey(const TSIGKey& source) : impl_(new TSIGKeyImpl(*source.impl_)) { +} + +TSIGKey& +TSIGKey::operator=(const TSIGKey& source) { + if (this == &source) { + return (*this); + } + + TSIGKeyImpl* newimpl = new TSIGKeyImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TSIGKey::~TSIGKey() { + delete impl_; +} + +const Name& +TSIGKey::getKeyName() const { + return (impl_->key_name_); +} + +const Name& +TSIGKey::getAlgorithmName() const { + return (impl_->algorithm_name_); +} + +isc::cryptolink::HashAlgorithm +TSIGKey::getAlgorithm() const { + return (impl_->algorithm_); +} + +size_t +TSIGKey::getDigestbits() const { + return (impl_->digestbits_); +} + +const void* +TSIGKey::getSecret() const { + return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL); +} + +size_t +TSIGKey::getSecretLength() const { + return (impl_->secret_.size()); +} + +std::string +TSIGKey::toText() const { + size_t digestbits = getDigestbits(); + const vector<uint8_t> secret_v(static_cast<const uint8_t*>(getSecret()), + static_cast<const uint8_t*>(getSecret()) + + getSecretLength()); + std::string secret_str = isc::util::encode::encodeBase64(secret_v); + + if (digestbits) { + std::string dgstbt_str = boost::lexical_cast<std::string>(static_cast<int>(digestbits)); + return (getKeyName().toText() + ":" + secret_str + ":" + + getAlgorithmName().toText() + ":" + dgstbt_str); + } else { + return (getKeyName().toText() + ":" + secret_str + ":" + + getAlgorithmName().toText()); + } +} + +const +Name& TSIGKey::HMACMD5_NAME() { + static Name alg_name("hmac-md5.sig-alg.reg.int"); + return (alg_name); +} + +const +Name& TSIGKey::HMACMD5_SHORT_NAME() { + static Name alg_name("hmac-md5"); + return (alg_name); +} + +const +Name& TSIGKey::HMACSHA1_NAME() { + static Name alg_name("hmac-sha1"); + return (alg_name); +} + +const +Name& TSIGKey::HMACSHA256_NAME() { + static Name alg_name("hmac-sha256"); + return (alg_name); +} + +const +Name& TSIGKey::HMACSHA224_NAME() { + static Name alg_name("hmac-sha224"); + return (alg_name); +} + +const +Name& TSIGKey::HMACSHA384_NAME() { + static Name alg_name("hmac-sha384"); + return (alg_name); +} + +const +Name& TSIGKey::HMACSHA512_NAME() { + static Name alg_name("hmac-sha512"); + return (alg_name); +} + +const +Name& TSIGKey::GSSTSIG_NAME() { + static Name alg_name("gss-tsig"); + return (alg_name); +} + +struct TSIGKeyRing::TSIGKeyRingImpl { + typedef map<Name, TSIGKey> TSIGKeyMap; + typedef pair<Name, TSIGKey> NameAndKey; + TSIGKeyMap keys; +}; + +TSIGKeyRing::TSIGKeyRing() : impl_(new TSIGKeyRingImpl) { +} + +TSIGKeyRing::~TSIGKeyRing() { + delete impl_; +} + +unsigned int +TSIGKeyRing::size() const { + return (impl_->keys.size()); +} + +TSIGKeyRing::Result +TSIGKeyRing::add(const TSIGKey& key) { + if (impl_->keys.insert( + TSIGKeyRingImpl::NameAndKey(key.getKeyName(), key)).second + == true) { + return (SUCCESS); + } else { + return (EXIST); + } +} + +TSIGKeyRing::Result +TSIGKeyRing::remove(const Name& key_name) { + return (impl_->keys.erase(key_name) == 1 ? SUCCESS : NOTFOUND); +} + +TSIGKeyRing::FindResult +TSIGKeyRing::find(const Name& key_name) const { + TSIGKeyRingImpl::TSIGKeyMap::const_iterator found = + impl_->keys.find(key_name); + if (found == impl_->keys.end()) { + return (FindResult(NOTFOUND, NULL)); + } + return (FindResult(SUCCESS, &((*found).second))); +} + +TSIGKeyRing::FindResult +TSIGKeyRing::find(const Name& key_name, const Name& algorithm_name) const { + TSIGKeyRingImpl::TSIGKeyMap::const_iterator found = + impl_->keys.find(key_name); + if (found == impl_->keys.end() || + (*found).second.getAlgorithmName() != algorithm_name) { + return (FindResult(NOTFOUND, NULL)); + } + return (FindResult(SUCCESS, &((*found).second))); +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h new file mode 100644 index 0000000..ead33e1 --- /dev/null +++ b/src/lib/dns/tsigkey.h @@ -0,0 +1,391 @@ +// 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/. + +#ifndef TSIGKEY_H +#define TSIGKEY_H 1 + +#include <cryptolink/cryptolink.h> + +namespace isc { +namespace dns { + +class Name; + +/// \brief TSIG key. +/// +/// This class holds a TSIG key along with some related attributes as +/// defined in RFC2845. +/// +/// A TSIG key consists of the following attributes: +/// - Key name +/// - Hash algorithm +/// - Digest bits +/// - Shared secret +/// +/// <b>Implementation Notes</b> +/// +/// We may add more attributes in future versions. For example, if and when +/// we support the TKEY protocol (RFC2930), we may need to introduce the +/// notion of inception and expiration times. +/// At that point we may also have to introduce a class hierarchy to handle +/// different types of keys in a polymorphic way. +/// At the moment we use the straightforward value-type class with minimal +/// attributes. +/// +/// In the TSIG protocol, hash algorithms are represented in the form of +/// domain name. +/// Our interfaces provide direct translation of this concept; for example, +/// the constructor from parameters take a \c Name object to specify the +/// algorithm. +/// On one hand, this may be counter intuitive. +/// An API user would rather specify "hmac-md5" instead of +/// <code>Name("hmac-md5.sig-alg.reg.int")</code>. +/// On the other hand, it may be more convenient for some kind of applications +/// if we maintain the algorithm as the expected representation for +/// protocol operations (such as sign and very a message). +/// Considering these points, we adopt the interface closer to the protocol +/// specification for now. +/// To minimize the burden for API users, we also define a set of constants +/// for commonly used algorithm names so that the users don't have to +/// remember the actual domain names defined in the protocol specification. +/// We may also have to add conversion routines between domain names +/// and more intuitive representations (e.g. strings) for algorithms. +class TSIGKey { +public: + /// + /// \name Constructors, Assignment Operator and Destructor. + /// + //@{ + /// \brief Constructor from key parameters + /// + /// \c algorithm_name should generally be a known algorithm to this + /// implementation, which are defined via the + /// <code>static const</code> member functions. + /// + /// Other names are still accepted as long as the secret is empty + /// (\c secret is \c NULL and \c secret_len is 0), however; in some cases + /// we might want to treat just the pair of key name and algorithm name + /// opaquely, e.g., when generating a response TSIG with a BADKEY error + /// because the algorithm is unknown as specified in Section 3.2 of + /// RFC2845 (in which case the algorithm name would be copied from the + /// request to the response, and for that purpose it would be convenient + /// if a \c TSIGKey object can hold a name for an "unknown" algorithm). + /// + /// \note RFC2845 does not specify which algorithm name should be used + /// in such a BADKEY response. The behavior of using the same algorithm + /// is derived from the BIND 9 implementation. + /// + /// It is unlikely that a TSIG key with an unknown algorithm is of any + /// use with actual crypto operation, so care must be taken when dealing + /// with such keys. (The restriction for the secret will prevent + /// accidental creation of such a dangerous key, e.g., due to misspelling + /// in a configuration file). + /// If the given algorithm name is unknown and non empty secret is + /// specified, an exception of type \c InvalidParameter will be thrown. + /// + /// \c secret and \c secret_len must be consistent in that the latter + /// is 0 if and only if the former is \c NULL; + /// otherwise an exception of type \c InvalidParameter will be thrown. + /// + /// \c digestbits is the truncated length in bits or 0 which means no + /// truncation and is the default. Constraints for non-zero value + /// are in RFC 4635 section 3.1: minimum 80 or the half of the + /// full (i.e., not truncated) length, integral number of octets + /// (i.e., multiple of 8), and maximum the full length. + /// + /// This constructor internally involves resource allocation, and if + /// it fails, a corresponding standard exception will be thrown. + /// + /// \param key_name The name of the key as a domain name. + /// \param algorithm_name The hash algorithm used for this key in the + /// form of domain name. For example, it can be + /// \c TSIGKey::HMACSHA256_NAME() for HMAC-SHA256. + /// \param secret Point to a binary sequence of the shared secret to be + /// used for this key, or \c NULL if the secret is empty. + /// \param secret_len The size of the binary %data (\c secret) in bytes. + /// \param digestbits The number of bits to include in the digest + /// (0 means to include all) + TSIGKey(const Name& key_name, const Name& algorithm_name, + const void* secret, size_t secret_len, size_t digestbits = 0); + + /// \brief Constructor from an input string + /// + /// The string must be of the form: + /// name:secret[:algorithm][:digestbits] + /// Where "name" is a domain name for the key, "secret" is a + /// base64 representation of the key secret, and the optional + /// "algorithm" is an algorithm identifier as specified in RFC 4635. + /// The default algorithm is hmac-md5.sig-alg.reg.int. + /// "digestbits" is the minimum truncated length in bits. + /// The default digestbits value is 0 and means truncation is forbidden. + /// + /// The same restriction about the algorithm name (and secret) as that + /// for the other constructor applies. + /// + /// Since ':' is used as a separator here, it is not possible to + /// use this constructor to create keys with a ':' character in + /// their name. + /// + /// \exception InvalidParameter exception if the input string is + /// invalid. + /// + /// \param str The string to make a TSIGKey from + explicit TSIGKey(const std::string& str); + + /// \brief The copy constructor. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This constructor never throws an exception otherwise. + TSIGKey(const TSIGKey& source); + + /// \brief Assignment operator. + /// + /// It internally allocates a resource, and if it fails a corresponding + /// standard exception will be thrown. + /// This operator never throws an exception otherwise. + /// + /// This operator provides the strong exception guarantee: When an + /// exception is thrown the content of the assignment target will be + /// intact. + TSIGKey& operator=(const TSIGKey& source); + + /// The destructor. + virtual ~TSIGKey(); + //@} + + /// + /// \name Getter Methods + /// + /// These methods never throw an exception. + //@{ + /// Return the key name. + const Name& getKeyName() const; + + /// Return the algorithm name. + const Name& getAlgorithmName() const; + + /// Return the hash algorithm name in the form of cryptolink::HashAlgorithm + isc::cryptolink::HashAlgorithm getAlgorithm() const; + + /// Return the minimum truncated length. + size_t getDigestbits() const; + + /// Return the length of the TSIG secret in bytes. + size_t getSecretLength() const; + + /// Return the value of the TSIG secret. + /// + /// If it returns a non NULL pointer, the memory region beginning at the + /// address returned by this method is valid up to the bytes specified + /// by the return value of \c getSecretLength(). + /// + /// The memory region is only valid while the corresponding \c TSIGKey + /// object is valid. The caller must hold the \c TSIGKey object while + /// it needs to refer to the region or it must make a local copy of the + /// region. + const void* getSecret() const; + //@} + + /// \brief Converts the TSIGKey to a string value + /// + /// The resulting string will be of the form + /// name:secret:algorithm[:digestbits] + /// Where "name" is a domain name for the key, "secret" is a + /// base64 representation of the key secret, and "algorithm" is + /// an algorithm identifier as specified in RFC 4635. + /// When not zero, digestbits is appended. + /// + /// \return The string representation of the given TSIGKey. + std::string toText() const; + + /// + /// \name Well known algorithm names as defined in RFC2845 and RFC4635. + /// + /// Note: we begin with the "mandatory" algorithms defined in RFC4635 + /// as a minimal initial set. + /// We'll add others as we see the need for them. + //@{ + static const Name& HMACMD5_NAME(); ///< HMAC-MD5 (RFC2845) + static const Name& HMACMD5_SHORT_NAME(); + static const Name& HMACSHA1_NAME(); ///< HMAC-SHA1 (RFC4635) + static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635) + static const Name& HMACSHA224_NAME(); ///< HMAC-SHA256 (RFC4635) + static const Name& HMACSHA384_NAME(); ///< HMAC-SHA256 (RFC4635) + static const Name& HMACSHA512_NAME(); ///< HMAC-SHA256 (RFC4635) + static const Name& GSSTSIG_NAME(); ///< GSS-TSIG (RFC3645) + //@} + +private: + struct TSIGKeyImpl; + const TSIGKeyImpl* impl_; +}; + +/// \brief A simple repository of a set of \c TSIGKey objects. +/// +/// This is a "key ring" to maintain TSIG keys (\c TSIGKey objects) and +/// provides trivial operations such as add, remove, and find. +/// +/// The keys are identified by their key names. +/// So, for example, two or more keys of the same key name but of different +/// algorithms are considered to be the same, and cannot be stored in the +/// key ring at the same time. +/// +/// <b>Implementation Note:</b> +/// For simplicity the initial implementation requests the application make +/// a copy of keys stored in the key ring if it needs to use the keys for +/// a long period (during which some of the keys may be removed). +/// This is based on the observations that a single server will not hold +/// a huge number of keys nor use keys in many different contexts (such as +/// in different DNS transactions). +/// If this assumption does not hold and memory consumption becomes an issue +/// we may have to revisit the design. +class TSIGKeyRing { +public: + /// Result codes of various public methods of \c TSIGKeyRing + enum Result { + SUCCESS = 0, ///< The operation is successful. + EXIST = 1, ///< A key is already stored in \c TSIGKeyRing. + NOTFOUND = 2 ///< The specified key is not found in \c TSIGKeyRing. + }; + + /// \brief A helper structure to represent the search result of + /// <code>TSIGKeyRing::find()</code>. + /// + /// This is a straightforward pair of the result code and a pointer + /// to the found key to represent the result of \c find(). + /// We use this in order to avoid overloading the return value for both + /// the result code ("success" or "not found") and the found object, + /// i.e., avoid using \c NULL to mean "not found", etc. + /// + /// This is a simple value class with no internal state, so for + /// convenience we allow the applications to refer to the members + /// directly. + /// + /// See the description of \c find() for the semantics of the member + /// variables. + struct FindResult { + FindResult(Result param_code, const TSIGKey* param_key) : + code(param_code), key(param_key) + {} + const Result code; + const TSIGKey* const key; + }; + + /// + /// \name Constructors and Destructor. + /// + /// \b Note: + /// The copy constructor and the assignment operator are + /// intentionally defined as private, making this class non copyable. + /// There is no technical reason why this class cannot be copied, + /// but since the key ring can potentially have a large number of keys, + /// a naive copy operation may cause unexpected overhead. + /// It's generally expected for an application to share the same + /// instance of key ring and share it throughout the program via + /// references, so we prevent the copy operation explicitly to avoid + /// unexpected copy operations. + //@{ +private: + TSIGKeyRing(const TSIGKeyRing& source); + TSIGKeyRing& operator=(const TSIGKeyRing& source); +public: + /// \brief The default constructor. + /// + /// This constructor never throws an exception. + TSIGKeyRing(); + + /// The destructor. + ~TSIGKeyRing(); + //@} + + /// Return the number of keys stored in the \c TSIGKeyRing. + /// + /// This method never throws an exception. + unsigned int size() const; + + /// Add a \c TSIGKey to the \c TSIGKeyRing. + /// + /// This method will create a local copy of the given key, so the caller + /// does not have to keep owning it. + /// + /// If internal resource allocation fails, a corresponding standard + /// exception will be thrown. + /// This method never throws an exception otherwise. + /// + /// \param key A \c TSIGKey to be added. + /// \return \c SUCCESS If the key is successfully added to the key ring. + /// \return \c EXIST The key ring already stores a key whose name is + /// identical to that of \c key. + Result add(const TSIGKey& key); + + /// Remove a \c TSIGKey for the given name from the \c TSIGKeyRing. + /// + /// This method never throws an exception. + /// + /// \param key_name The name of the key to be removed. + /// \return \c SUCCESS If the key is successfully removed from the key + /// ring. + /// \return \c NOTFOUND The key ring does not store the key that matches + /// \c key_name. + Result remove(const Name& key_name); + + /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing. + /// + /// It searches the internal storage for a \c TSIGKey whose name is + /// \c key_name. + /// It returns the result in the form of a \c FindResult + /// object as follows: + /// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND. + /// - \c key: A pointer to the found \c TSIGKey object if one is found; + /// otherwise \c NULL. + /// + /// The pointer returned in the \c FindResult object is only valid until + /// the corresponding key is removed from the key ring. + /// The caller must ensure that the key is held in the key ring while + /// it needs to refer to it, or it must make a local copy of the key. + /// + /// This method never throws an exception. + /// + /// \param key_name The name of the key to be found. + /// \return A \c FindResult object enclosing the search result (see above). + FindResult find(const Name& key_name) const; + + /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing. + /// + /// It searches the internal storage for a \c TSIGKey whose name is + /// \c key_name and that uses the hash algorithm identified by + /// \c algorithm_name. + /// It returns the result in the form of a \c FindResult + /// object as follows: + /// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND. + /// - \c key: A pointer to the found \c TSIGKey object if one is found; + /// otherwise \c NULL. + /// + /// The pointer returned in the \c FindResult object is only valid until + /// the corresponding key is removed from the key ring. + /// The caller must ensure that the key is held in the key ring while + /// it needs to refer to it, or it must make a local copy of the key. + /// + /// This method never throws an exception. + /// + /// \param key_name The name of the key to be found. + /// \param algorithm_name The name of the algorithm of the found key. + /// \return A \c FindResult object enclosing the search result (see above). + FindResult find(const Name& key_name, const Name& algorithm_name) const; + +private: + struct TSIGKeyRingImpl; + TSIGKeyRingImpl* impl_; +}; +} +} + +#endif // TSIGKEY_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc new file mode 100644 index 0000000..483296d --- /dev/null +++ b/src/lib/dns/tsigrecord.cc @@ -0,0 +1,142 @@ +// Copyright (C) 2011-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 <config.h> + +#include <ostream> +#include <string> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/tsigrecord.h> + +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +// Internally used constants: + +// Size in octets for the RR type, class TTL, RDLEN fields. +const size_t RR_COMMON_LEN = 10; + +// Size in octets for the fixed part of TSIG RDATAs. +// - Time Signed (6) +// - Fudge (2) +// - MAC Size (2) +// - Original ID (2) +// - Error (2) +// - Other Len (2) +const size_t RDATA_COMMON_LEN = 16; +} + +namespace isc { +namespace dns { +TSIGRecord::TSIGRecord(const Name& key_name, + const rdata::any::TSIG& tsig_rdata) : + key_name_(key_name), rdata_(tsig_rdata), + length_(RR_COMMON_LEN + RDATA_COMMON_LEN + key_name_.getLength() + + rdata_.getAlgorithm().getLength() + + rdata_.getMACSize() + rdata_.getOtherLen()) +{} + +namespace { +// This is a straightforward wrapper of dynamic_cast<const any::TSIG&>. +// We use this so that we can throw the DNSMessageFORMERR exception when +// unexpected type of RDATA is detected in the member initialization list +// of the constructor below. +const any::TSIG& +castToTSIGRdata(const rdata::Rdata& rdata) { + const any::TSIG* tsig_rdata = + dynamic_cast<const any::TSIG*>(&rdata); + if (!tsig_rdata) { + isc_throw(DNSMessageFORMERR, + "TSIG record is being constructed from " + "incompatible RDATA: " << rdata.toText()); + } + return (*tsig_rdata); +} +} + +TSIGRecord::TSIGRecord(const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const rdata::Rdata& rdata, + size_t length) : + key_name_(name), rdata_(castToTSIGRdata(rdata)), length_(length) +{ + if (rrclass != getClass()) { + isc_throw(DNSMessageFORMERR, "Unexpected TSIG RR class: " << rrclass); + } + if (ttl != RRTTL(TSIG_TTL)) { + isc_throw(DNSMessageFORMERR, "Unexpected TSIG TTL: " << ttl); + } +} + +const RRClass& +TSIGRecord::getClass() { + return (RRClass::ANY()); +} + +const RRTTL& +TSIGRecord::getTTL() { + static RRTTL ttl(TSIG_TTL); + return (ttl); +} + +namespace { +template <typename OUTPUT> +void +toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) { + // RR type, class, TTL are fixed constants. + RRType::TSIG().toWire(output); + TSIGRecord::getClass().toWire(output); + output.writeUint32(TSIGRecord::TSIG_TTL); + + // RDLEN + output.writeUint16(RDATA_COMMON_LEN + rdata.getAlgorithm().getLength() + + rdata.getMACSize() + rdata.getOtherLen()); + + // TSIG RDATA + rdata.toWire(output); +} +} + +int +TSIGRecord::toWire(AbstractMessageRenderer& renderer) const { + // If adding the TSIG would exceed the size limit, don't do it. + if (renderer.getLength() + length_ > renderer.getLengthLimit()) { + renderer.setTruncated(); + return (0); + } + + // key name = owner. note that we disable compression. + renderer.writeName(key_name_, false); + toWireCommon(renderer, rdata_); + return (1); +} + +int +TSIGRecord::toWire(OutputBuffer& buffer) const { + key_name_.toWire(buffer); + toWireCommon(buffer, rdata_); + return (1); +} + +std::string +TSIGRecord::toText() const { + return (key_name_.toText() + " " + RRTTL(TSIG_TTL).toText() + " " + + getClass().toText() + " " + RRType::TSIG().toText() + " " + + rdata_.toText() + "\n"); +} + +std::ostream& +operator<<(std::ostream& os, const TSIGRecord& record) { + return (os << record.toText()); +} +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h new file mode 100644 index 0000000..0d5a375 --- /dev/null +++ b/src/lib/dns/tsigrecord.h @@ -0,0 +1,300 @@ +// Copyright (C) 2011-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 TSIGRECORD_H +#define TSIGRECORD_H 1 + +#include <ostream> +#include <string> + +#include <boost/shared_ptr.hpp> + +#include <util/buffer.h> + +#include <dns/name.h> +#include <dns/rdataclass.h> + +namespace isc { +namespace util { +class OutputBuffer; +} +namespace dns { +class AbstractMessageRenderer; + +/// TSIG resource record. +/// +/// A \c TSIGRecord class object represents a TSIG resource record and is +/// responsible for conversion to and from wire format TSIG record based on +/// the protocol specification (RFC2845). +/// This class is provided so that other classes and applications can handle +/// TSIG without knowing protocol details of TSIG, such as that it uses a +/// fixed constant of TTL. +/// +/// \todo So the plan is to eventually provide the "from wire" constructor. +/// It's not yet provided in the current phase of development. +/// +/// \note +/// This class could be a derived class of \c AbstractRRset. That way +/// it would be able to be used in a polymorphic way; for example, +/// an application can construct a TSIG RR by itself and insert it to a +/// \c Message object as a generic RRset. On the other hand, it would mean +/// this class would have to implement an \c RdataIterator (even though it +/// can be done via straightforward forwarding) while the iterator is mostly +/// redundant since there should be one and only one RDATA for a valid TSIG +/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well +/// defined due to such special rules for TSIG as using a fixed TTL. +/// Overall, TSIG is a very special RR type that simply uses the compatible +/// resource record format, and it will be unlikely that a user wants to +/// handle it through a generic interface in a polymorphic way. +/// We therefore chose to define it as a separate class. This is also +/// similar to why \c EDNS is a separate class. +class TSIGRecord { +public: + /// + /// \name Constructors + /// + /// We use the default copy constructor, default copy assignment operator, + /// (and default destructor) intentionally. + //@{ + /// Constructor from TSIG key name and RDATA + /// + /// \exception std::bad_alloc Resource allocation for copying the name or + /// RDATA fails + TSIGRecord(const Name& key_name, const rdata::any::TSIG& tsig_rdata); + + /// Constructor from resource record (RR) parameters. + /// + /// This constructor is intended to be used in the context of parsing + /// an incoming DNS message that contains a TSIG. The parser would + /// first extract the owner name, RR type (which is TSIG) class, TTL and + /// the TSIG RDATA from the message. This constructor is expected to + /// be given these RR parameters (except the RR type, because it must be + /// TSIG). + /// + /// According to RFC2845, a TSIG RR uses fixed RR class (ANY) and TTL (0). + /// If the RR class or TTL is different from the expected one, this + /// implementation considers it an invalid record and throws an exception + /// of class \c DNSMessageFORMERR. + /// + /// \note This behavior is not specified in the protocol specification, + /// but this implementation rejects unexpected values for the following + /// reasons (but in any case, this won't matter much in practice as + /// RFC2848 clearly states these fields always have the fixed values and + /// any sane implementation of TSIG signer will follow that): + /// According to the RFC editor (in a private communication), the intended + /// use of the TSIG TTL field is to signal protocol extensions (currently + /// no such extension is defined), so this field may actually be + /// validly non 0 in future. However, until the implementation supports + /// that extension it may not always be able to handle the extended + /// TSIG as intended; the extension may even affect digest computation. + /// There's a related issue on this point. Different implementations + /// interpret the RFC in different ways on the received TTL when + /// digesting the message: BIND 9 uses the received value (even if + /// it's not 0) as part of the TSIG variables; NLnet Labs' LDNS and NSD + /// always use a fixed constant of 0 regardless of the received TTL value. + /// This means if and when an extension with non 0 TTL is introduced + /// there will be interoperability problems in the form of verification + /// failure. By explicitly rejecting it (and subsequently returning + /// a response with a format error) we can indicate the source of the + /// problem more clearly than a "bad signature" TSIG error, which can + /// happen for various reasons. On the other hand, rejecting unexpected + /// RR classes is mostly for consistency; the RFC lists these two fields + /// in the same way, so it makes more sense to handle them equally. + /// (BIND 9 rejects unexpected RR classes for TSIG, but that is part of + /// general check on RR classes on received RRs; it generally requests + /// all classes are the same, and if the protocol specifies the use of + /// a particular class for a particular type of RR, it requests that + /// class be used). + /// + /// Likewise, if \c rdata is not of type \c any::TSIG, an exception of + /// class DNSMessageFORMERR will be thrown. When the caller is a + /// DNS message parser and builds \c rdata from incoming wire format + /// data as described above, this case happens when the RR class is + /// different from ANY (in the implementation, the type check takes place + /// before the explicit check against the RR class explained in the + /// previous paragraph). + /// + /// The \c length parameter is intended to be the length of the TSIG RR + /// (from the beginning of the owner name to the end of the RDATA) when + /// the caller is a DNS message parser. Note that it is the actual length + /// for the RR in the format; if the owner name or the algorithm name + /// (in the RDATA) is compressed (although the latter should not be + /// compressed according to RFC3597), the length must be the size of the + /// compressed data. The length is recorded inside the class and will + /// be returned via subsequent calls to \c getLength(). It's intended to + /// be used in the context TSIG verification; in the verify process + /// the MAC computation must be performed for the original data without + /// TSIG, so, to avoid parsing the entire data in the verify process + /// again, it's necessary to record information that can identify the + /// length to be digested for the MAC. This parameter serves for that + /// purpose. + /// + /// \note Since the constructor doesn't take the wire format data per se, + /// it doesn't (and cannot) check the validity of \c length, and simply + /// accepts any given value. It even accepts obviously invalid values + /// such as 0. It's caller's responsibility to provide a valid value of + /// length, and, the verifier's responsibility to use the length safely. + /// + /// <b>DISCUSSION:</b> this design is fragile in that it introduces + /// a tight coupling between message parsing and TSIG verification via + /// the \c TSIGRecord class. In terms of responsibility decoupling, + /// the ideal way to have \c TSIGRecord remember the entire wire data + /// along with the length of the TSIG. Then in the TSIG verification + /// we could refer to the necessary potion of data solely from a + /// \c TSIGRecord object. However, this approach would require expensive + /// heavy copy of the original data or introduce another kind of coupling + /// between the data holder and this class (if the original data is freed + /// while a \c TSIGRecord object referencing the data still exists, the + /// result will be catastrophic). As a "best current compromise", we + /// use the current design. We may reconsider it if it turns out to + /// cause a big problem or we come up with a better idea. + /// + /// \exception DNSMessageFORMERR Given RR parameters are invalid for TSIG. + /// \exception std::bad_alloc Internal resource allocation fails. + /// + /// \param name The owner name of the TSIG RR + /// \param rrclass The RR class of the RR. Must be \c RRClass::ANY() + /// (see above) + /// \param ttl The TTL of the RR. Must be 0 (see above) + /// \param rdata The RDATA of the RR. Must be of type \c any::TSIG. + /// \param length The size of the RR (see above) + TSIGRecord(const Name& name, const RRClass& rrclass, const RRTTL& ttl, + const rdata::Rdata& rdata, size_t length); + //@} + + /// Return the owner name of the TSIG RR, which is the TSIG key name + /// + /// \exception None + const Name& getName() const { return (key_name_); } + + /// Return the RDATA of the TSIG RR + /// + /// \exception None + const rdata::any::TSIG& getRdata() const { return (rdata_); } + + /// \name Protocol constants and defaults + /// + //@{ + /// Return the RR class of TSIG + /// + /// TSIG always uses the ANY RR class. This static method returns it, + /// when, though unlikely, an application wants to know which class TSIG + /// is supposed to use. + /// + /// \exception None + static const RRClass& getClass(); + + /// Return the TTL value of TSIG + /// + /// TSIG always uses 0 TTL. This static method returns it, + /// when, though unlikely, an application wants to know the TTL TSIG + /// is supposed to use. + /// + /// \exception None + static const RRTTL& getTTL(); + //@} + + /// Return the length of the TSIG record + /// + /// When constructed from the key name and RDATA, it is the length of + /// the record when it is rendered by the \c toWire() method. + /// + /// \note When constructed "from wire", that will mean the length of + /// the wire format data for the TSIG RR. The length will be necessary + /// to verify the message once parse is completed. + /// + /// \exception None + size_t getLength() const { return (length_); } + + /// \brief Render the \c TSIG RR in the wire format. + /// + /// This method renders the TSIG record as a form of a DNS TSIG RR + /// via \c renderer, which encapsulates output buffer and other rendering + /// contexts. + /// + /// Normally this version of \c toWire() method tries to compress the + /// owner name of the RR whenever possible, but this method intentionally + /// skips owner name compression. This is due to a report that some + /// Windows clients refuse a TSIG if its owner name is compressed + /// (See http://marc.info/?l=bind-workers&m=126637138430819&w=2). + /// Reportedly this seemed to be specific to GSS-TSIG, but this + /// implementation skip compression regardless of the algorithm. + /// + /// If by adding the TSIG RR the message size would exceed the limit + /// maintained in \c renderer, this method skips rendering the RR + /// and returns 0 and mark \c renderer as "truncated" (so that a + /// subsequent call to \c isTruncated() on \c renderer will result in + /// \c true); otherwise it returns 1, which is the number of RR + /// rendered. + /// + /// \note If the caller follows the specification of adding TSIG + /// as described in RFC2845, this should not happen; the caller is + /// generally expected to leave a sufficient room in the message for + /// the TSIG. But this method checks the unexpected case nevertheless. + /// + /// \exception std::bad_alloc Internal resource allocation fails (this + /// should be rare). + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0. + int toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the \c TSIG RR in the wire format. + /// + /// This method is same as \c toWire(AbstractMessageRenderer&)const + /// except it renders the RR in an \c OutputBuffer and therefore + /// does not care about message size limit. + /// As a consequence it always returns 1. + int toWire(isc::util::OutputBuffer& buffer) const; + + /// Convert the TSIG record to a string. + /// + /// The output format is the same as the result of \c toText() for + /// other normal types of RRsets (with always using the same RR class + /// and TTL). It also ends with a newline. + /// + /// \exception std::bad_alloc Internal resource allocation fails (this + /// should be rare). + /// + /// \return A string representation of \c TSIG record + std::string toText() const; + + /// The TTL value to be used in TSIG RRs. + static const uint32_t TSIG_TTL = 0; + //@} + +private: + const Name key_name_; + const rdata::any::TSIG rdata_; + const size_t length_; +}; + +/// A pointer-like type pointing to a \c TSIGRecord object. +typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr; + +/// A pointer-like type pointing to an immutable \c TSIGRecord object. +typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr; + +/// Insert the \c TSIGRecord as a string into stream. +/// +/// This method convert \c record 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 record A \c TSIGRecord 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 TSIGRecord& record); +} +} + +#endif // TSIGRECORD_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/zone_checker.cc b/src/lib/dns/zone_checker.cc new file mode 100644 index 0000000..def9896 --- /dev/null +++ b/src/lib/dns/zone_checker.cc @@ -0,0 +1,191 @@ +// 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 <config.h> + +#include <dns/zone_checker.h> + +#include <dns/name.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrset.h> +#include <dns/rrset_collection_base.h> + +#include <boost/lexical_cast.hpp> + +#include <functional> +#include <string> + +using boost::lexical_cast; +using std::string; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { + +namespace { +std::string +zoneText(const Name& zone_name, const RRClass& zone_class) { + return (zone_name.toText(true) + "/" + zone_class.toText()); +} + +void +checkSOA(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, + ZoneCheckerCallbacks& callback) { + ConstRRsetPtr rrset = + zone_rrsets.find(zone_name, zone_class, RRType::SOA()); + size_t count = 0; + if (rrset) { + for (RdataIteratorPtr rit = rrset->getRdataIterator(); + !rit->isLast(); + rit->next(), ++count) { + if (dynamic_cast<const rdata::generic::SOA*>(&rit->getCurrent()) == + NULL) { + isc_throw(Unexpected, "Zone checker found bad RDATA in SOA"); + } + } + if (count == 0) { + // this should be an implementation bug, not an operational error. + isc_throw(Unexpected, "Zone checker found an empty SOA RRset"); + } + } + if (count != 1) { + callback.error("zone " + zoneText(zone_name, zone_class) + ": has " + + lexical_cast<string>(count) + " SOA records"); + } +} + +// Check if a target name is beyond zone cut, either due to delegation or +// DNAME. Note that DNAME works on the origin but not on the name itself, +// while delegation works on the name itself (but the NS at the origin is not +// delegation). +ConstRRsetPtr +findZoneCut(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, const Name& target_name) { + const unsigned int origin_count = zone_name.getLabelCount(); + const unsigned int target_count = target_name.getLabelCount(); + assert(origin_count <= target_count); + + for (unsigned int l = origin_count; l <= target_count; ++l) { + const Name& mid_name = (l == target_count) ? target_name : + target_name.split(target_count - l); + + ConstRRsetPtr found; + if (l != origin_count && + (found = zone_rrsets.find(mid_name, zone_class, RRType::NS())) != + NULL) { + return (found); + } + if (l != target_count && + (found = zone_rrsets.find(mid_name, zone_class, RRType::DNAME())) + != NULL) { + return (found); + } + } + return (ConstRRsetPtr()); +} + +// Check if each "in-zone" NS name has an address record, identifying some +// error cases. +void +checkNSNames(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, + ConstRRsetPtr ns_rrset, ZoneCheckerCallbacks& callbacks) { + if (ns_rrset->getRdataCount() == 0) { + // this should be an implementation bug, not an operational error. + isc_throw(Unexpected, "Zone checker found an empty NS RRset"); + } + + for (RdataIteratorPtr rit = ns_rrset->getRdataIterator(); + !rit->isLast(); + rit->next()) { + const rdata::generic::NS* ns_data = + dynamic_cast<const rdata::generic::NS*>(&rit->getCurrent()); + if (ns_data == NULL) { + isc_throw(Unexpected, "Zone checker found bad RDATA in NS"); + } + const Name& ns_name = ns_data->getNSName(); + const NameComparisonResult::NameRelation reln = + ns_name.compare(zone_name).getRelation(); + if (reln != NameComparisonResult::EQUAL && + reln != NameComparisonResult::SUBDOMAIN) { + continue; // not in the zone. we can ignore it. + } + + // Check if there's a zone cut between the origin and the NS name. + ConstRRsetPtr cut_rrset = findZoneCut(zone_name, zone_class, + zone_rrsets, ns_name); + if (cut_rrset) { + if (cut_rrset->getType() == RRType::NS()) { + continue; // delegation; making the NS name "out of zone". + } else if (cut_rrset->getType() == RRType::DNAME()) { + callbacks.error("zone " + zoneText(zone_name, zone_class) + + ": NS '" + ns_name.toText(true) + "' is " + + "below a DNAME '" + + cut_rrset->getName().toText(true) + + "' (illegal per RFC6672)"); + continue; + } else { + assert(false); + } + } + if (zone_rrsets.find(ns_name, zone_class, RRType::CNAME()) != NULL) { + callbacks.error("zone " + zoneText(zone_name, zone_class) + + ": NS '" + ns_name.toText(true) + "' is a CNAME " + + "(illegal per RFC2181)"); + continue; + } + if (zone_rrsets.find(ns_name, zone_class, RRType::A()) == NULL && + zone_rrsets.find(ns_name, zone_class, RRType::AAAA()) == NULL) { + callbacks.warn("zone " + zoneText(zone_name, zone_class) + + ": NS has no address records (A or AAAA)"); + } + } +} + +void +checkNS(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, + ZoneCheckerCallbacks& callbacks) { + ConstRRsetPtr rrset = + zone_rrsets.find(zone_name, zone_class, RRType::NS()); + if (rrset == NULL) { + callbacks.error("zone " + zoneText(zone_name, zone_class) + + ": has no NS records"); + return; + } + checkNSNames(zone_name, zone_class, zone_rrsets, rrset, callbacks); +} + +// The following is a simple wrapper of checker callback so checkZone() +// can also remember any critical errors. +void +errorWrapper(const string& reason, const ZoneCheckerCallbacks* callbacks, + bool* had_error) { + *had_error = true; + callbacks->error(reason); +} +} + +bool +checkZone(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, + const ZoneCheckerCallbacks& callbacks) { + bool had_error = false; + ZoneCheckerCallbacks my_callbacks( + std::bind(errorWrapper, ph::_1, &callbacks, &had_error), + std::bind(&ZoneCheckerCallbacks::warn, &callbacks, ph::_1)); + + checkSOA(zone_name, zone_class, zone_rrsets, my_callbacks); + checkNS(zone_name, zone_class, zone_rrsets, my_callbacks); + + return (!had_error); +} + +} // end namespace dns +} // end namespace isc diff --git a/src/lib/dns/zone_checker.h b/src/lib/dns/zone_checker.h new file mode 100644 index 0000000..be36a32 --- /dev/null +++ b/src/lib/dns/zone_checker.h @@ -0,0 +1,153 @@ +// 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 ZONE_CHECKER_H +#define ZONE_CHECKER_H 1 + +#include <dns/dns_fwd.h> + +#include <functional> +#include <string> + +namespace isc { +namespace dns { + +/// \brief Set of callbacks used in zone checks. +/// +/// Objects of this class are expected to be passed to \c checkZone(). +class ZoneCheckerCallbacks { +public: + /// \brief Functor type of the callback on some issue (error or warning). + /// + /// Its parameter indicates the reason for the corresponding issue. + typedef std::function<void(const std::string& reason)> IssueCallback; + + /// \brief Constructor. + /// + /// Either or both of the callbacks can be empty, in which case the + /// corresponding callback will be effectively no-operation. This can be + /// used, for example, when the caller of \c checkZone() is only + /// interested in the final result. Note that a \c NULL pointer will be + /// implicitly converted to an empty functor object, so passing \c NULL + /// suffices. + /// + /// \throw none + /// + /// \param error_callback Callback functor to be called on critical errors. + /// \param warn_callback Callback functor to be called on non critical + /// issues. + ZoneCheckerCallbacks(const IssueCallback& error_callback, + const IssueCallback& warn_callback) : + error_callback_(error_callback), warn_callback_(warn_callback) + {} + + /// \brief Call the callback for a critical error. + /// + /// This method itself is exception free, but propagates any exception + /// thrown from the callback. + /// + /// \param reason Textual representation of the reason for the error. + void error(const std::string& reason) const { + if (error_callback_) { + error_callback_(reason); + } + } + + /// \brief Call the callback for a non critical issue. + /// + /// This method itself is exception free, but propagates any exception + /// thrown from the callback. + /// + /// \param reason Textual representation of the reason for the issue. + void warn(const std::string& reason) const { + if (warn_callback_) + warn_callback_(reason); + } + +private: + IssueCallback error_callback_; + IssueCallback warn_callback_; +}; + +/// \brief Perform basic integrity checks on zone RRsets. +/// +/// This function performs some lightweight checks on zone's SOA and (apex) +/// NS records. Here, lightweight means it doesn't require traversing +/// the entire zone, and should be expected to complete reasonably quickly +/// regardless of the size of the zone. +/// +/// It distinguishes "critical" errors and other undesirable issues: +/// the former should be interpreted as the resulting zone shouldn't be used +/// further, e.g, by an authoritative server implementation; the latter means +/// the issues are better to be addressed but are not necessarily considered +/// to make the zone invalid. Critical errors are reported via the +/// \c error() method of \c callbacks, and non critical issues are reported +/// via its \c warn() method. +/// +/// Specific checks performed by this function is as follows. Failure of +/// a check is considered a critical error unless noted otherwise: +/// - There is exactly one SOA RR at the zone apex. +/// - There is at least one NS RR at the zone apex. +/// - For each apex NS record, if the NS name (the RDATA of the record) is +/// in the zone (i.e., it's a subdomain of the zone origin and above any +/// zone cut due to delegation), check the following: +/// - the NS name should have an address record (AAAA or A). Failure of +/// this check is considered a non critical issue. +/// - the NS name does not have a CNAME. This is prohibited by Section +/// 10.3 of RFC 2181. +/// - the NS name is not subject to DNAME substitution. This is prohibited +/// by Section 4 of RFC 6672. +/// . +/// +/// In addition, when the check is completed without any critical error, this +/// function guarantees that RRsets for the SOA and (apex) NS stored in the +/// passed RRset collection have the expected type of Rdata objects, +/// i.e., generic::SOA and generic::NS, respectively. (This is normally +/// expected to be the case, but not guaranteed by the API). +/// +/// As for the check on the existence of AAAA or A records for NS names, +/// it should be noted that BIND 9 treats this as a critical error. +/// It's not clear whether it's an implementation dependent behavior or +/// based on the protocol standard (it looks like the former), but to make +/// it sure we need to confirm there is even no wildcard match for the names. +/// This should be a very rare configuration, and more expensive to detect, +/// so we do not check this condition, and treat this case as a non critical +/// issue. +/// +/// This function indicates the result of the checks (whether there is a +/// critical error) via the return value: It returns \c true if there is no +/// critical error and returns \c false otherwise. It doesn't throw an +/// exception on encountering an error so that it can report as many errors +/// as possible in a single call. If an exception is a better way to signal +/// the error, the caller can pass a callback object that throws from its +/// \c error() method. +/// +/// This function can still throw an exception if it finds a really bogus +/// condition that is most likely to be an implementation bug of the caller. +/// Such cases include when an RRset contained in the RRset collection is +/// empty. +/// +/// \throw Unexpected Conditions that suggest a caller's bug (see the +/// description) +/// +/// \param zone_name The name of the zone to be checked +/// \param zone_class The RR class of the zone to be checked +/// \param zone_rrsets The collection of RRsets of the zone +/// \param callbacks Callback object used to report errors and issues +/// +/// \return \c true if no critical errors are found; \c false otherwise. +bool +checkZone(const Name& zone_name, const RRClass& zone_class, + const RRsetCollectionBase& zone_rrsets, + const ZoneCheckerCallbacks& callbacks); + +} // namespace dns +} // namespace isc +#endif // ZONE_CHECKER_H + +// Local Variables: +// mode: c++ +// End: |